diff --git a/cxx/include/mspass/algorithms/Butterworth.h b/cxx/include/mspass/algorithms/Butterworth.h index 83b3e0b4a..416d503b5 100644 --- a/cxx/include/mspass/algorithms/Butterworth.h +++ b/cxx/include/mspass/algorithms/Butterworth.h @@ -1,9 +1,9 @@ #ifndef _MSPASS_BUTTERWORTH_H_ #define _MSPASS_BUTTERWORTH_H_ -#include "mspass/seismic/TimeSeries.h" -#include "mspass/seismic/Seismogram.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" -namespace mspass::algorithms{ +#include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" +namespace mspass::algorithms { /*! \brief MsPASS implementation of Butterworth filter as processing object. MsPASS has an existing filter routine that can implement buterworth filters @@ -32,8 +32,7 @@ that. The method to change the expected sample interval has some sanity checks to reduce, but not eliminate the possibility of mistakes that will create unstable filters. */ -class Butterworth -{ +class Butterworth { public: /*! \brief Default constructor. @@ -73,9 +72,9 @@ class Butterworth implementation. That matters only if you want to compare what we did here to the original seismic unix code. - Note the iir filter coefficiets are always derived from the poles and Frequencies - so this constructor is just an alternate way to define the filter without the abstraction - of number of poles. + Note the iir filter coefficiets are always derived from the poles and + Frequencies so this constructor is just an alternate way to define the filter + without the abstraction of number of poles. \param zerophase when true use a zerophase filter. When false defines a one pass minimum phase filter. @@ -98,11 +97,10 @@ class Butterworth \param sample_interval is the expected data sample interval */ Butterworth(const bool zerophase, const bool enable_lo, const bool enable_hi, - const double fstoplo, const double astoplo, - const double fpasslo, const double apasslo, - const double fpasshi, const double apasshi, - const double fstophi, const double astophi, - const double sample_interval); + const double fstoplo, const double astoplo, const double fpasslo, + const double apasslo, const double fpasshi, const double apasshi, + const double fstophi, const double astophi, + const double sample_interval); /*! Construct using tagged valus created from a Metadata container. This behaves exactly like the fully parameterized contructor except it @@ -110,11 +108,11 @@ class Butterworth are identical to the argument names defined above. The best guidance for using this constuctor is to look a the comments in the default parameter file.*/ - Butterworth(const mspass::utility::Metadata& md); + Butterworth(const mspass::utility::Metadata &md); /*! \brief Construct by defining corner frequencies and number of npoles - Butterworth filters can also be defind by a corner frequency and number of poles. - In fact, only the nondimensional form of these parameters are stored as + Butterworth filters can also be defind by a corner frequency and number of + poles. In fact, only the nondimensional form of these parameters are stored as private attributes to define the filter. \param zerophase when true use a zerophase filter. When false defines a one pass minimum phase filter. @@ -130,13 +128,12 @@ class Butterworth */ Butterworth(const bool zerophase, const bool enable_lo, const bool enable_hi, - const int npolelo, const double f3dblo, - const int npolehi, const double f3dbhi, - const double sample_interval); + const int npolelo, const double f3dblo, const int npolehi, + const double f3dbhi, const double sample_interval); /*! Standard copy conststructor. */ - Butterworth(const Butterworth& parent); + Butterworth(const Butterworth &parent); /*! Standard assignment operator. */ - Butterworth& operator=(const Butterworth& parent); + Butterworth &operator=(const Butterworth &parent); /*! \brief Return the impulse response. The response of a linear filter like the butterworth filter can always @@ -176,7 +173,7 @@ class Butterworth \exception throws a MsPASSError if the hi corner is inconsistent with the sample rate of d */ - void apply(mspass::seismic::CoreTimeSeries& d); + void apply(mspass::seismic::CoreTimeSeries &d); /*! \brief Apply the filter to a CoreTimeSeries object. This method alters the data vector inside d in place and changes no @@ -199,7 +196,7 @@ class Butterworth \exception none, but callers should consider checking for errors posted to elog */ - void apply(mspass::seismic::TimeSeries& d); + void apply(mspass::seismic::TimeSeries &d); /*! \brief Filter a raw vector of data. Use this method to apply the filter to a raw vector of data. The @@ -211,7 +208,7 @@ class Butterworth \param d is the data to be filtered (note the data are altered in place) */ - void apply(std::vector& d); + void apply(std::vector &d); /*! \brief Apply the filter to a CoreSeismogram object. This method alters the data vector inside d in place and changes no @@ -234,7 +231,7 @@ class Butterworth \exception throws a MsPASSError if the hi corner is inconsistent with the sample rate of d */ - void apply(mspass::seismic::CoreSeismogram& d); + void apply(mspass::seismic::CoreSeismogram &d); /*! \brief Apply the filter to a CoreTimeSeries object. This method alters the data vector inside d in place and changes no @@ -257,7 +254,7 @@ class Butterworth \exception none, but callers should consider checking for errors posted to elog */ - void apply(mspass::seismic::Seismogram& d); + void apply(mspass::seismic::Seismogram &d); /*! \brief Return the response of the filter in the frequency domain. The impulse response of any linear system can always be characterized by @@ -274,7 +271,8 @@ class Butterworth fft in the gnu scientific library that definitely does that). */ - mspass::algorithms::deconvolution::ComplexArray transfer_function(const int n); + mspass::algorithms::deconvolution::ComplexArray + transfer_function(const int n); /*! \brief set the sample interval assumed for input data. This function can be used when running with raw data vectors if the sample @@ -292,30 +290,23 @@ class Butterworth \param dtnew is the new sample interval to set for the operator. */ - void change_dt(const double dtnew) - { - this->f3db_lo *= (dtnew/(this->dt)); - this->f3db_hi *= (dtnew/(this->dt)); - this->dt=dtnew; + void change_dt(const double dtnew) { + this->f3db_lo *= (dtnew / (this->dt)); + this->f3db_hi *= (dtnew / (this->dt)); + this->dt = dtnew; }; /*! Return the low frequency 3db corner (in Hz).*/ - double low_corner() const - { - return f3db_lo/dt; - }; + double low_corner() const { return f3db_lo / dt; }; /*! Return the high frequency 3db corner (in Hz).*/ - double high_corner() const - { - return f3db_hi/dt; - }; + double high_corner() const { return f3db_hi / dt; }; /*! Return the number of poles defining the highpass (lowcut) element of the filter.*/ - int npoles_low() const {return npoles_lo;}; + int npoles_low() const { return npoles_lo; }; /*! Return the number of poles defining the lowpass (highcut) element of the filter.*/ - int npoles_high() const {return npoles_hi;}; + int npoles_high() const { return npoles_hi; }; /*! Return the current operator sample interval.*/ - double current_dt()const {return dt;}; + double current_dt() const { return dt; }; /*! Return a string defining the type of operator this filter defines. Currently can be one of the following: bandpass, lowpass, or highpass. It is possible to construct a band reject filter with the right @@ -325,18 +316,14 @@ class Butterworth (the hi and lo concepts described above) and guesses the filter type. If both are off it returns "Undefined". */ - std::string filter_type() const - { - if(use_lo) - { - if(use_hi) + std::string filter_type() const { + if (use_lo) { + if (use_hi) return std::string("bandpass"); else return std::string("highpass"); - } - else - { - if(use_hi) + } else { + if (use_hi) return std::string("lowpass"); else return std::string("Undefined"); @@ -344,12 +331,10 @@ class Butterworth }; /*! Return true if the filter is defined as a zero phase filter. Returns false if it is minimum phase. */ - bool is_zerophase() const - { - return zerophase; - }; + bool is_zerophase() const { return zerophase; }; + private: - bool use_lo,use_hi; + bool use_lo, use_hi; bool zerophase; /* bfdesign sets npoles and 3db points based on specified band properties. @@ -362,24 +347,25 @@ class Butterworth /* These three functions are nearly exact copies of seismic unix functions with a couple of differences: 1. All floats in the su code are made double here to mesh with modern arch - 2. bfhighpass is changed to bflowpass and bflowpass is changed to bfhighcut to mesh - with the names used here more cleanly. that is, pass and cut define + 2. bfhighpass is changed to bflowpass and bflowpass is changed to bfhighcut + to mesh with the names used here more cleanly. that is, pass and cut define the opposite sense of a filter and it gets very confusing when the terms get mixed up in the same set of code. This way low always means the low end of the pass band and high is the high end of the pass band. */ - void bfdesign (double fpass, double apass, double fstop, double astop, - int *npoles, double *f3db); - void bfhighcut (int npoles, double f3db, int n, double p[], double q[]); - void bflowcut (int npoles, double f3db, int n, double p[], double q[]); + void bfdesign(double fpass, double apass, double fstop, double astop, + int *npoles, double *f3db); + void bfhighcut(int npoles, double f3db, int n, double p[], double q[]); + void bflowcut(int npoles, double f3db, int n, double p[], double q[]); /* These internal methods use internal dt value to call bfdesign with nondimensional frequencies. Hence they should always be called after - a change in dt. They are also handy to contain common code for constructors. */ - void set_lo(const double fstop, const double fpass, - const double astop, const double apass); - void set_hi(const double fstop, const double fpass, - const double astop, const double apass); + a change in dt. They are also handy to contain common code for constructors. +*/ + void set_lo(const double fstop, const double fpass, const double astop, + const double apass); + void set_hi(const double fstop, const double fpass, const double astop, + const double apass); }; -} // namespace end +} // namespace mspass::algorithms #endif diff --git a/cxx/include/mspass/algorithms/Taper.h b/cxx/include/mspass/algorithms/Taper.h index 0486aa309..eace23305 100644 --- a/cxx/include/mspass/algorithms/Taper.h +++ b/cxx/include/mspass/algorithms/Taper.h @@ -1,50 +1,48 @@ #ifndef _TAPER_H_ #define _TAPER_H_ -//#include -#include +// #include #include +#include - -#include #include +#include #include -#include #include +#include - -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" -namespace mspass::algorithms{ +namespace mspass::algorithms { -class BasicTaper -{ +class BasicTaper { public: - BasicTaper() - { - head =false; + BasicTaper() { + head = false; tail = false; all = false; }; - virtual ~BasicTaper(){}; - virtual int apply(mspass::seismic::TimeSeries& d)=0; - virtual int apply(mspass::seismic::Seismogram& d)=0; - void enable_head(){head=true;}; - void disable_head(){head=false;all=false;}; - void enable_tail(){tail=true;}; - void disable_tail(){tail=false;all=false;}; - bool head_is_enabled() - { - if(head || all) - { + virtual ~BasicTaper() {}; + virtual int apply(mspass::seismic::TimeSeries &d) = 0; + virtual int apply(mspass::seismic::Seismogram &d) = 0; + void enable_head() { head = true; }; + void disable_head() { + head = false; + all = false; + }; + void enable_tail() { tail = true; }; + void disable_tail() { + tail = false; + all = false; + }; + bool head_is_enabled() { + if (head || all) { return true; } return false; }; - bool tail_is_enable() - { - if(tail || all) - { + bool tail_is_enable() { + if (tail || all) { return true; } return false; @@ -54,19 +52,20 @@ class BasicTaper throw an exception */ virtual double get_t0head() const = 0; virtual double get_t1head() const = 0; + protected: /* A taper can be head, tail, or all. For efficiency it is required implementations set these three booleans. head or tail may be true. all means a single function is needed to defne the taper. */ - bool head,tail,all; + bool head, tail, all; + private: friend class boost::serialization::access; - template - void serialize(Archive & ar, const unsigned int version) - { - ar & head; - ar & tail; - ar & all; + template + void serialize(Archive &ar, const unsigned int version) { + ar & head; + ar & tail; + ar & all; }; }; /*! \brief Used to construct an operator to apply a linear taper to either end. @@ -75,8 +74,7 @@ Linear tapers are defined here as a time spanning a ramp running from 0 to 1. Data will be zeroed on each end of a 0 mark and a linear weight applied between 0 points and 1 points. Postive ramp slope on left and negative slope ramp on right. */ -class LinearTaper : public BasicTaper -{ +class LinearTaper : public BasicTaper { public: LinearTaper(); /*! \brief primary constructor. @@ -86,21 +84,21 @@ class LinearTaper : public BasicTaper t0head and t1head and in opposite sense from t1tail to t0tail. Setting 0 value = 1 value should be used as a signal to disable. */ - LinearTaper(const double t0head,const double t1head, - const double t1tail,const double t0tail); - int apply(mspass::seismic::TimeSeries& d); - int apply(mspass::seismic::Seismogram& d); - double get_t0head()const {return t0head;}; - double get_t1head()const {return t1head;}; - double get_t0tail()const {return t0tail;}; - double get_t1tail()const {return t1tail;}; + LinearTaper(const double t0head, const double t1head, const double t1tail, + const double t0tail); + int apply(mspass::seismic::TimeSeries &d); + int apply(mspass::seismic::Seismogram &d); + double get_t0head() const { return t0head; }; + double get_t1head() const { return t1head; }; + double get_t0tail() const { return t0tail; }; + double get_t1tail() const { return t1tail; }; + private: - double t0head,t1head,t1tail,t0tail; + double t0head, t1head, t1tail, t0tail; friend class boost::serialization::access; - template - void serialize(Archive & ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); ar & t0head; ar & t1head; ar & t1tail; @@ -115,8 +113,7 @@ the right it defines the same function for the range 0 to pi. The period of the left and right operator can be different. Turn off left or right by giving illegal start and end points and the operator will silently be only one sided. */ -class CosineTaper : public BasicTaper -{ +class CosineTaper : public BasicTaper { public: CosineTaper(); /*! \brief primary constructor. @@ -125,22 +122,22 @@ class CosineTaper : public BasicTaper Zero times before t0head and after t0tail. Taper between t0head and t1head and in opposite sense from t1tail to t0tail. */ - CosineTaper(const double t0head,const double t1head, - const double t1tail,const double t0tail); + CosineTaper(const double t0head, const double t1head, const double t1tail, + const double t0tail); /* these need to post to history using new feature*/ - int apply(mspass::seismic::TimeSeries& d); - int apply(mspass::seismic::Seismogram& d); - double get_t0head() const {return t0head;}; - double get_t1head() const {return t1head;}; - double get_t0tail() const {return t0tail;}; - double get_t1tail() const {return t1tail;}; + int apply(mspass::seismic::TimeSeries &d); + int apply(mspass::seismic::Seismogram &d); + double get_t0head() const { return t0head; }; + double get_t1head() const { return t1head; }; + double get_t0tail() const { return t0tail; }; + double get_t1tail() const { return t1tail; }; + private: - double t0head,t1head,t1tail,t0tail; + double t0head, t1head, t1tail, t0tail; friend class boost::serialization::access; - template - void serialize(Archive & ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); ar & t0head; ar & t1head; ar & t1tail; @@ -152,27 +149,33 @@ class CosineTaper : public BasicTaper This method provides a simple way to build a taper from a set of uniformly spaced points. The apply methods will dogmatically only accept input data of the same length as the taper defined in the operator. */ -class VectorTaper : public BasicTaper -{ +class VectorTaper : public BasicTaper { public: VectorTaper(); VectorTaper(const std::vector taperdata); - int apply(mspass::seismic::TimeSeries& d); - int apply(mspass::seismic::Seismogram& d); - void disable(){all=false;}; - void enable(){ - if(taper.size()>0) all=true; + int apply(mspass::seismic::TimeSeries &d); + int apply(mspass::seismic::Seismogram &d); + void disable() { all = false; }; + void enable() { + if (taper.size() > 0) + all = true; + }; + std::vector get_taper() { return taper; }; + double get_t0head() const { + std::cerr << "get_t0head not implemented for VectorTaper"; + return 0.0; }; - std::vector get_taper(){return taper;}; - double get_t0head() const {std::cerr << "get_t0head not implemented for VectorTaper";return 0.0;}; - double get_t1head() const {std::cerr << "get_t1head not implemented for VectorTaper";return 0.0;}; + double get_t1head() const { + std::cerr << "get_t1head not implemented for VectorTaper"; + return 0.0; + }; + private: std::vector taper; friend class boost::serialization::access; - template - void serialize(Archive & ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); ar & taper; }; }; @@ -192,8 +195,7 @@ there is also a constructor using the base class for Taper objects. It allows custom implementations of taper beyond those associated with keywords in the definition passed to the main constructor. */ -class TopMute -{ +class TopMute { public: /*! Default constructor. Exists but the result is invalid */ TopMute(); @@ -216,23 +218,25 @@ class TopMute */ TopMute(const double t0, const double t1, const std::string type); /*! Standard copy constructor. */ - TopMute(const TopMute& parent); + TopMute(const TopMute &parent); /*! Destructor. The destructor of this class is not null. */ ~TopMute(); /*! Standard assignment operator. */ - TopMute& operator=(const TopMute& parent); + TopMute &operator=(const TopMute &parent); /*! Apply the operator to a TimeSeries object. */ - int apply(mspass::seismic::TimeSeries& d); + int apply(mspass::seismic::TimeSeries &d); /*! Apply the operator to a Seismogram object. */ - int apply(mspass::seismic::Seismogram& d); - /*! Return the start of mute taper - points with time < this number are zeroed*/ - double get_t0() const - {return taper->get_t0head();}; - /*! Return the end time of the mute taper - points after this point are unaltered by the mute.*/ - double get_t1() const - {return taper->get_t1head();}; - /*! Return a string with a name describing the form of the taper - currently returns either linear or cosine*/ + int apply(mspass::seismic::Seismogram &d); + /*! Return the start of mute taper - points with time < this number are + * zeroed*/ + double get_t0() const { return taper->get_t0head(); }; + /*! Return the end time of the mute taper - points after this point are + * unaltered by the mute.*/ + double get_t1() const { return taper->get_t1head(); }; + /*! Return a string with a name describing the form of the taper - currently + * returns either linear or cosine*/ std::string taper_type() const; + private: /* We use a shared_ptr to the base class. That allows inheritance to handle the actual form - a classic oop use of a base class. the shared_ptr @@ -256,5 +260,5 @@ class TopMute }; */ }; -} // End namespace +} // namespace mspass::algorithms #endif // End guard diff --git a/cxx/include/mspass/algorithms/TimeWindow.h b/cxx/include/mspass/algorithms/TimeWindow.h index 4a48d6243..73739ff5c 100644 --- a/cxx/include/mspass/algorithms/TimeWindow.h +++ b/cxx/include/mspass/algorithms/TimeWindow.h @@ -1,75 +1,72 @@ #ifndef _TIMEWINDOW_H_ #define _TIMEWINDOW_H_ -namespace mspass::algorithms -{ +namespace mspass::algorithms { /*! \brief Defines a time window. Time windows are a common concept in time series analysis and seismology in particular. The object definition here has no concept of a time -standard. It simply defines an interval in terms of a pair of -real numbers. +standard. It simply defines an interval in terms of a pair of +real numbers. **/ -class TimeWindow -{ +class TimeWindow { public: -/*! -// Start time of the window. -**/ + /*! + // Start time of the window. + **/ double start; -/*! -// End time of the window. -**/ - double end; -/*! -// Default constructor. -**/ - TimeWindow(){start=0.0;end=1.0e99;}; -/*! -// Parameterized constructor. -//\param ts - start time -//\param te - end time -**/ - TimeWindow(const double ts,const double te){start=ts;end=te;}; - TimeWindow(const TimeWindow& parent) - { - start=parent.start; - end=parent.end; + /*! + // End time of the window. + **/ + double end; + /*! + // Default constructor. + **/ + TimeWindow() { + start = 0.0; + end = 1.0e99; + }; + /*! + // Parameterized constructor. + //\param ts - start time + //\param te - end time + **/ + TimeWindow(const double ts, const double te) { + start = ts; + end = te; + }; + TimeWindow(const TimeWindow &parent) { + start = parent.start; + end = parent.end; } - TimeWindow& operator=(const TimeWindow& parent) - { - if(&parent!=this) - { - start=parent.start; - end=parent.end; + TimeWindow &operator=(const TimeWindow &parent) { + if (&parent != this) { + start = parent.start; + end = parent.end; } return *this; } -/*! -// Returns a new time window translated by tshift argument. -**/ - TimeWindow shift(const double tshift) const - { + /*! + // Returns a new time window translated by tshift argument. + **/ + TimeWindow shift(const double tshift) const { TimeWindow newwindow(*this); - newwindow.start+=tshift; + newwindow.start += tshift; newwindow.end += tshift; - return(newwindow); + return (newwindow); } -/*! -// Returns the window length -**/ - double length() - { - return(end-start); - }; + /*! + // Returns the window length + **/ + double length() { return (end - start); }; }; /* This strange looking function is a C++ function object. -// It is used in the STL container called a set used for gaps below. +// It is used in the STL container called a set used for gaps below. // This function is used as the comparison function for ordering // the elements of the set. It makes TimeWindows indexed by // intervals similar to thw way Datascope uses time:endtime -// Be aware, however, that for the same reason as datascope overlapping +// Be aware, however, that for the same reason as datascope overlapping // time windows will cause ambiguity in indexing times by this // method. */ @@ -81,11 +78,11 @@ class TimeWindow // determine if a time is inside a particular time window. //\author Gary L. Pavlis **/ -class TimeWindowCmp -{ +class TimeWindowCmp { public: - bool operator()(const TimeWindow ti1,const TimeWindow ti2) const - {return(ti1.end ArrivalTimeReference(mspass::seismic::Seismogram& din, - std::string key, mspass::algorithms::TimeWindow tw); -/*! \brief Extract one component from a Seismogram and create a TimeSeries object from it. +std::shared_ptr +ArrivalTimeReference(mspass::seismic::Seismogram &din, std::string key, + mspass::algorithms::TimeWindow tw); +/*! \brief Extract one component from a Seismogram and create a TimeSeries +object from it. Copies all Metadata from parent Seismogram to build a TimeSeries object. This will often leave relics of the transformation matrix @@ -157,8 +172,9 @@ std::shared_ptr ArrivalTimeReference(mspass::seismi \return TimeSeries of component requested **/ -mspass::seismic::TimeSeries ExtractComponent(const mspass::seismic::Seismogram& tcs, - const unsigned int component); +mspass::seismic::TimeSeries +ExtractComponent(const mspass::seismic::Seismogram &tcs, + const unsigned int component); /* Enemble algorithms */ /*! \brief Returns a gather of Seismograms in an arrival time reference fram. @@ -166,15 +182,18 @@ mspass::seismic::TimeSeries ExtractComponent(const mspass::seismic::Seismogram& zero is defined as an arrival time extracted from the metadata area of each member object. -\exception SeisppError for errors in extracting required information from metadata area. +\exception SeisppError for errors in extracting required information from +metadata area. \param din is input gather -\param key is the metadata key used to find the arrival time to use as a reference. -\param tw is a TimeWindow object that defines the window of data to extract around - the desired arrival time. +\param key is the metadata key used to find the arrival time to use as a +reference. +\param tw is a TimeWindow object that defines the window of data to extract +around the desired arrival time. **/ -std::shared_ptr ArrivalTimeReference - (mspass::seismic::ThreeComponentEnsemble& din, std::string key, mspass::algorithms::TimeWindow tw); +std::shared_ptr +ArrivalTimeReference(mspass::seismic::ThreeComponentEnsemble &din, + std::string key, mspass::algorithms::TimeWindow tw); /*! \brief Extract one component from a 3C ensemble. * This function creates an ensemble of TimeSeries objects that are @@ -196,8 +215,8 @@ std::shared_ptr ArrivalTimeReference input is incompatible or the component number is not 0,1, or 2. */ mspass::seismic::Ensemble ExtractComponent( - const mspass::seismic::Ensemble& d, - const unsigned int comp); + const mspass::seismic::Ensemble &d, + const unsigned int comp); /*! \brief Sparse time domain convolution. Sometimes with modeling we have an data series (d) that is sparse that we want to convolve with a wavelet to produce a simulation data @@ -212,9 +231,9 @@ for zeros in d. \param wavelet is the wavelet to be convolved with d (not sparse) \param d is the sparse data vector (dominated by zeros). */ -mspass::seismic::CoreSeismogram sparse_convolve( - const mspass::seismic::CoreTimeSeries& wavelet, - const mspass::seismic::CoreSeismogram& d); +mspass::seismic::CoreSeismogram +sparse_convolve(const mspass::seismic::CoreTimeSeries &wavelet, + const mspass::seismic::CoreSeismogram &d); /*! \brief Combine a grouped set of TimeSeries into one Seismogram. A Seismogram object is a bundle of TimeSeries objects that define a @@ -277,8 +296,8 @@ Errors from attempting to construct a Seismogram generate elog messages and kills of an output components. */ -mspass::seismic::Seismogram BundleSEEDGroup - (const std::vector& d, +mspass::seismic::Seismogram +BundleSEEDGroup(const std::vector &d, const size_t i0, const size_t iend); /*! \brief Assemble a SeismogramEnsemble from a sorted TimeSeriesEnsemble. @@ -317,8 +336,8 @@ does not have sta or chan defined (as noted above null net or loc are handled.) */ -mspass::seismic::LoggingEnsemble bundle_seed_data - (mspass::seismic::LoggingEnsemble& d); +mspass::seismic::LoggingEnsemble bundle_seed_data( + mspass::seismic::LoggingEnsemble &d); /*! \brief Sort a TimeSeriesEnsemble with a natural order with seed name codes. The seed standard tags every single miniseed record with four string keys @@ -343,8 +362,12 @@ Note this algorithm alters the ensemble it receives in place. \param d is the ensemble to be sorted. */ -void seed_ensemble_sort(mspass::seismic::LoggingEnsemble& d); -mspass::seismic::TimeSeriesWGaps splice_segments(std::vector& segments,bool save_history); -std::vector repair_overlaps(std::vector& segments); -}//End mspass::algorithms namespace encapsulation +void seed_ensemble_sort( + mspass::seismic::LoggingEnsemble &d); +mspass::seismic::TimeSeriesWGaps +splice_segments(std::vector &segments, + bool save_history); +std::vector +repair_overlaps(std::vector &segments); +} // namespace mspass::algorithms #endif diff --git a/cxx/include/mspass/algorithms/amplitudes.h b/cxx/include/mspass/algorithms/amplitudes.h index 47f16ba82..916f358d0 100644 --- a/cxx/include/mspass/algorithms/amplitudes.h +++ b/cxx/include/mspass/algorithms/amplitudes.h @@ -1,30 +1,31 @@ #ifndef _AMPLITUDES_H_ #define _AMPLITUDES_H_ -#include -#include "mspass/utility/MsPASSError.h" -#include "mspass/seismic/TimeSeries.h" -#include "mspass/seismic/Seismogram.h" +#include "mspass/algorithms/TimeWindow.h" +#include "mspass/algorithms/algorithms.h" #include "mspass/seismic/Ensemble.h" #include "mspass/seismic/PowerSpectrum.h" -#include "mspass/utility/VectorStatistics.h" +#include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" #include "mspass/utility/Metadata.h" -#include "mspass/algorithms/TimeWindow.h" -#include "mspass/algorithms/algorithms.h" -namespace mspass::algorithms::amplitudes{ -double PeakAmplitude(const mspass::seismic::CoreTimeSeries& d); -double PeakAmplitude(const mspass::seismic::CoreSeismogram& d); -double RMSAmplitude(const mspass::seismic::CoreTimeSeries& d); -double RMSAmplitude(const mspass::seismic::CoreSeismogram& d); -double PercAmplitude(const mspass::seismic::CoreTimeSeries& d,const double perf); -double PercAmplitude(const mspass::seismic::CoreSeismogram& d,const double perf); -double MADAmplitude(const mspass::seismic::CoreTimeSeries& d); -double MADAmplitude(const mspass::seismic::CoreSeismogram& d); -enum class ScalingMethod -{ - Peak, /*! Use peak amplitude method - equivalent to Linfinity norm*/ - RMS, /*! Use RMS amplitude method - L2 norm of data.*/ +#include "mspass/utility/MsPASSError.h" +#include "mspass/utility/VectorStatistics.h" +#include +namespace mspass::algorithms::amplitudes { +double PeakAmplitude(const mspass::seismic::CoreTimeSeries &d); +double PeakAmplitude(const mspass::seismic::CoreSeismogram &d); +double RMSAmplitude(const mspass::seismic::CoreTimeSeries &d); +double RMSAmplitude(const mspass::seismic::CoreSeismogram &d); +double PercAmplitude(const mspass::seismic::CoreTimeSeries &d, + const double perf); +double PercAmplitude(const mspass::seismic::CoreSeismogram &d, + const double perf); +double MADAmplitude(const mspass::seismic::CoreTimeSeries &d); +double MADAmplitude(const mspass::seismic::CoreSeismogram &d); +enum class ScalingMethod { + Peak, /*! Use peak amplitude method - equivalent to Linfinity norm*/ + RMS, /*! Use RMS amplitude method - L2 norm of data.*/ ClipPerc, /*! Use a percent clip scaling method as used in seismic unix.*/ - MAD /*! Use median absolute deviation scaling - a form of L1 norm*/ + MAD /*! Use median absolute deviation scaling - a form of L1 norm*/ }; const std::string scale_factor_key("calib"); /*! \brief Scaling function for atomic data objects in mspass. @@ -52,86 +53,86 @@ units. \return computed amplitude */ -template double scale(Tdata& d,const ScalingMethod method, - const double level, const mspass::algorithms::TimeWindow win) -{ - if((method==ScalingMethod::ClipPerc) && (level<=0.0 || level>1.0)) - throw mspass::utility::MsPASSError("scale function: illegal perf level specified for clip percentage scale - must be between 0 and 1\nData unaltered - may cause downstream problems", - mspass::utility::ErrorSeverity::Suspect); - try{ +template +double scale(Tdata &d, const ScalingMethod method, const double level, + const mspass::algorithms::TimeWindow win) { + if ((method == ScalingMethod::ClipPerc) && (level <= 0.0 || level > 1.0)) + throw mspass::utility::MsPASSError( + "scale function: illegal perf level specified for clip percentage " + "scale - must be between 0 and 1\nData unaltered - may cause " + "downstream problems", + mspass::utility::ErrorSeverity::Suspect); + try { double newcalib(1.0); /* the else condition here should perhaps generate an elog message but did not implement to allow this template to be used for CoreTimeSeries and CoreSeismogram that do not have an elog attribute.*/ - if(d.is_defined(scale_factor_key)) - { - newcalib=d.get_double(scale_factor_key); + if (d.is_defined(scale_factor_key)) { + newcalib = d.get_double(scale_factor_key); } /* Handle time windowing. Log window mismatches but silently handle cast where the window is invalid - used as a way to override any time windowing */ mspass::algorithms::TimeWindow ampwindow; - if(win.start>win.end) - { - ampwindow.start=d.t0(); - ampwindow.end=d.endtime(); - } - else if( (fabs(win.start-d.t0())/d.dt()>0.5) - || (fabs(win.end-d.endtime())/d.dt() > 0.5) ) - { + if (win.start > win.end) { + ampwindow.start = d.t0(); + ampwindow.end = d.endtime(); + } else if ((fabs(win.start - d.t0()) / d.dt() > 0.5) || + (fabs(win.end - d.endtime()) / d.dt() > 0.5)) { std::stringstream ss; - ss << "Window time range is inconsistent with input data range"<0.0) - { - dscale = level/amplitude; + if (amplitude > 0.0) { + dscale = level / amplitude; newcalib /= dscale; d *= dscale; - d.put(scale_factor_key,newcalib); - } - else - { + d.put(scale_factor_key, newcalib); + } else { std::stringstream ss; ss << "Data array is all 0s and cannot be scaled"; - d.elog.log_error("scale",ss.str(),mspass::utility::ErrorSeverity::Complaint); - /* This may not be necessary but it assures this value is always set on + d.elog.log_error("scale", ss.str(), + mspass::utility::ErrorSeverity::Complaint); + /* This may not be necessary but it assures this value is always set on return even if it means nothing*/ - d.put(scale_factor_key,newcalib); + d.put(scale_factor_key, newcalib); } return amplitude; - }catch(...){throw;}; + } catch (...) { + throw; + }; } /*! Generic function to scale ensembles. @@ -141,9 +142,9 @@ the scale function for each. The template is for member data type. \param d is the data to be scale. Works only if overloaded functions PeakAmplitude, PercAmplitude, MADAmplitude, and - RMSAmplitude are defined for ensemble members. Currently that means CoreTimeSeries and - CoreSeismogram. Note in mspass this assumes history preservation is handled - in python wrappers. + RMSAmplitude are defined for ensemble members. Currently that means +CoreTimeSeries and CoreSeismogram. Note in mspass this assumes history +preservation is handled in python wrappers. \param method sets the scaling metric defined through ScalingMethod eum class. \param level has two different contexts. For PercAmplitude it must be a a number n with 0 std::vector scale_ensemble_members(mspass::seismic::Ensemble& d, - const ScalingMethod& method, const double level, const mspass::algorithms::TimeWindow win) -{ - if((method==ScalingMethod::ClipPerc) && (level<=0.0 || level>1.0)) - throw mspass::utility::MsPASSError("scale_ensemble_members function: illegal perf level specified for clip percentage scale - must be between 0 and 1\nData unaltered - may cause downstream problems", - mspass::utility::ErrorSeverity::Suspect); - try{ +template +std::vector +scale_ensemble_members(mspass::seismic::Ensemble &d, + const ScalingMethod &method, const double level, + const mspass::algorithms::TimeWindow win) { + if ((method == ScalingMethod::ClipPerc) && (level <= 0.0 || level > 1.0)) + throw mspass::utility::MsPASSError( + "scale_ensemble_members function: illegal perf level specified for " + "clip percentage scale - must be between 0 and 1\nData unaltered - may " + "cause downstream problems", + mspass::utility::ErrorSeverity::Suspect); + try { typename std::vector::iterator dptr; std::vector amps; amps.reserve(d.member.size()); - for(dptr=d.member.begin();dptr!=d.member.end();++dptr) - { + for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) { double thisamp; - thisamp=scale(*dptr,method,level,win); + thisamp = scale(*dptr, method, level, win); amps.push_back(thisamp); } return amps; - }catch(...){throw;}; + } catch (...) { + throw; + }; } /*! Generic function to apply an ensemble average scale factor. -Sometimes we want to preserve true relative amplitudes between members of an ensemble -but we need to scale the overall data to some range (e.g order 1 for plotting). -Use this function to do that for ensembles. The scale_ensemble_members function, -in contrast, scales each member separately. +Sometimes we want to preserve true relative amplitudes between members of an +ensemble but we need to scale the overall data to some range (e.g order 1 for +plotting). Use this function to do that for ensembles. The +scale_ensemble_members function, in contrast, scales each member separately. \param d is the data to be scale. Works only if overloaded functions PeakAmplitude, PercAmplitude, MADAmplitude, and - RMSAmplitude are defined for ensemble members. Currently that means CoreTimeSeries and - CoreSeismogram. Note in mspass this assumes history preservation is handled - in python wrappers. + RMSAmplitude are defined for ensemble members. Currently that means +CoreTimeSeries and CoreSeismogram. Note in mspass this assumes history +preservation is handled in python wrappers. \param method sets the scaling metric defined through ScalingMethod eum class. \param level has two different contexts. For PercAmplitude it must be a a number n with 0 double scale_ensemble(mspass::seismic::Ensemble& d, - const ScalingMethod& method, const double level, const bool use_mean) -{ - if((method==ScalingMethod::ClipPerc) && (level<=0.0 || level>1.0)) - throw mspass::utility::MsPASSError("scale_ensemble function: illegal perf level specified for clip percentage scale - must be between 0 and 1\nData unaltered - may cause downstream problems", - mspass::utility::ErrorSeverity::Suspect); - try{ - double avgamp; //defined here because the value computed here is returned on success +template +double scale_ensemble(mspass::seismic::Ensemble &d, + const ScalingMethod &method, const double level, + const bool use_mean) { + if ((method == ScalingMethod::ClipPerc) && (level <= 0.0 || level > 1.0)) + throw mspass::utility::MsPASSError( + "scale_ensemble function: illegal perf level specified for clip " + "percentage scale - must be between 0 and 1\nData unaltered - may " + "cause downstream problems", + mspass::utility::ErrorSeverity::Suspect); + try { + double avgamp; // defined here because the value computed here is returned + // on success typename std::vector::iterator dptr; std::vector amps; amps.reserve(d.member.size()); size_t nlive(0); - for(dptr=d.member.begin();dptr!=d.member.end();++dptr) - { + for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) { double amplitude; - if(dptr->dead()) continue; - switch(method) - { - case ScalingMethod::Peak: - amplitude=PeakAmplitude(*dptr); - break; - case ScalingMethod::ClipPerc: - amplitude=PercAmplitude(*dptr,level); - break; - case ScalingMethod::MAD: - amplitude=MADAmplitude(*dptr); - break; - case ScalingMethod::RMS: - default: - amplitude=RMSAmplitude(*dptr); + if (dptr->dead()) + continue; + switch (method) { + case ScalingMethod::Peak: + amplitude = PeakAmplitude(*dptr); + break; + case ScalingMethod::ClipPerc: + amplitude = PercAmplitude(*dptr, level); + break; + case ScalingMethod::MAD: + amplitude = MADAmplitude(*dptr); + break; + case ScalingMethod::RMS: + default: + amplitude = RMSAmplitude(*dptr); }; ++nlive; amps.push_back(log(amplitude)); } /*Silently return a 0 if there are no live data members*/ - if(nlive==0) return 0.0; + if (nlive == 0) + return 0.0; mspass::utility::VectorStatistics ampstats(amps); - if(use_mean) - { - avgamp=ampstats.mean(); - } - else - { - avgamp=ampstats.median(); + if (use_mean) { + avgamp = ampstats.mean(); + } else { + avgamp = ampstats.median(); } /* restore to a value instead of natural log*/ - avgamp=exp(avgamp); - /* Silently do nothing if all the data are zero. Would be better to + avgamp = exp(avgamp); + /* Silently do nothing if all the data are zero. Would be better to log an error but use as a "Core" object doesn't contain an elog attribute*/ - if(avgamp<=0.0) - { - return 0.0; + if (avgamp <= 0.0) { + return 0.0; } - double dscale=level/avgamp; + double dscale = level / avgamp; /* Now scale the data and apply calib */ - for(dptr=d.member.begin();dptr!=d.member.end();++dptr) - { - if(dptr->live()) - { + for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) { + if (dptr->live()) { double calib; (*dptr) *= dscale; - if(dptr->is_defined(scale_factor_key)) - { - calib=dptr->get_double(scale_factor_key); - } - else - { - calib=1.0; + if (dptr->is_defined(scale_factor_key)) { + calib = dptr->get_double(scale_factor_key); + } else { + calib = 1.0; } - calib/=dscale; - dptr->put(scale_factor_key,calib); + calib /= dscale; + dptr->put(scale_factor_key, calib); } } return avgamp; - }catch(...){throw;}; + } catch (...) { + throw; + }; } /*! Convert an std::vector to a unit vector based on L2 norm.*/ -template std::vector normalize(const std::vector& d) -{ +template std::vector normalize(const std::vector &d) { size_t N = d.size(); std::vector result; result.reserve(N); - double d_nrm(0.0);; - for(size_t i=0;i -#include -#include "mspass/utility/AntelopePf.h" -#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" -#include "mspass/algorithms/deconvolution/ShapingWavelet.h" -#include "mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h" -#include "mspass/algorithms/amplitudes.h" #include "mspass/algorithms/Taper.h" #include "mspass/algorithms/TimeWindow.h" +#include "mspass/algorithms/amplitudes.h" +#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h" +#include "mspass/algorithms/deconvolution/ShapingWavelet.h" #include "mspass/seismic/PowerSpectrum.h" -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Seismogram.h" -namespace mspass::algorithms::deconvolution{ +#include "mspass/seismic/TimeSeries.h" +#include "mspass/utility/AntelopePf.h" +#include +#include +namespace mspass::algorithms::deconvolution { /*! \brief Absract base class for algorithms handling full 3C data. -*/ -class Base3CDecon -{ + */ +class Base3CDecon { public: - virtual ~Base3CDecon() {}; - /* - virtual void change_parameters(const mspass::BasicMetadata &md)=0; - virtual void loaddata(mspass::Seismogram& d,const int comp)=0; - virtual void loadwavelet(const mspass::TimeSeries& w)=0; - */ - /* \brief Return the ideal output of the deconvolution operator. + virtual ~Base3CDecon() {}; + /* + virtual void change_parameters(const mspass::BasicMetadata &md)=0; + virtual void loaddata(mspass::Seismogram& d,const int comp)=0; + virtual void loadwavelet(const mspass::TimeSeries& w)=0; + */ + /* \brief Return the ideal output of the deconvolution operator. - All deconvolution operators have a implicit or explicit ideal output - signal. e.g. for a spiking Wiener filter it is a delta function with or - without a lag. For a shaping wavelt it is the time domain version of the - wavelet. */ - virtual mspass::seismic::Seismogram process()=0; - /*! \brif Return the actual output of the deconvolution operator. + All deconvolution operators have a implicit or explicit ideal output + signal. e.g. for a spiking Wiener filter it is a delta function with or + without a lag. For a shaping wavelt it is the time domain version of the + wavelet. */ + virtual mspass::seismic::Seismogram process() = 0; + /*! \brif Return the actual output of the deconvolution operator. - The actual output is defined as w^-1*w and is compable to resolution - kernels in linear inverse theory. Although not required we would - normally expect this function to be peaked at 0. Offsets from 0 - would imply a bias. */ - virtual mspass::seismic::TimeSeries actual_output()=0; + The actual output is defined as w^-1*w and is compable to resolution + kernels in linear inverse theory. Although not required we would + normally expect this function to be peaked at 0. Offsets from 0 + would imply a bias. */ + virtual mspass::seismic::TimeSeries actual_output() = 0; - /*! \brief Return a FIR represention of the inverse filter. + /*! \brief Return a FIR represention of the inverse filter. - After any deconvolution is computed one can sometimes produce a finite - impulse response (FIR) respresentation of the inverse filter. */ - //virtual mspass::TimeSeries inverse_wavelet() = 0; - virtual mspass::seismic::TimeSeries inverse_wavelet(double) = 0; - /*! \brief Return appropriate quality measures. + After any deconvolution is computed one can sometimes produce a finite + impulse response (FIR) respresentation of the inverse filter. */ + // virtual mspass::TimeSeries inverse_wavelet() = 0; + virtual mspass::seismic::TimeSeries inverse_wavelet(double) = 0; + /*! \brief Return appropriate quality measures. - Each operator commonly has different was to measure the quality of the - result. This method should return these in a generic Metadata object. */ - virtual mspass::utility::Metadata QCMetrics()=0; + Each operator commonly has different was to measure the quality of the + result. This method should return these in a generic Metadata object. */ + virtual mspass::utility::Metadata QCMetrics() = 0; }; /* This enum is used internally to define the algorithm the processor is to run. I (glp) chose that approach over the inheritance approach used in the scalar @@ -62,10 +61,10 @@ needed with pybind11 to create wrappers for the python bindings. This enum will not be exposed to python as it is totally internal to the CNR3CDecon class. */ -enum class CNR3C_algorithms{ - generalized_water_level, - colored_noise_damping, - undefined +enum class CNR3C_algorithms { + generalized_water_level, + colored_noise_damping, + undefined }; /*! \brief Colored Noise Regularized 3C Deconvolution opertor. @@ -93,13 +92,13 @@ it would be hard to do so without causing more problems that it would solve. In MsPASS we expect to hide this a bit behind some python wrappers to create more safety mechanisms. */ -//class CNR3CDecon : public mspass::FFTDeconOperator -class CNR3CDecon : public Base3CDecon, public FFTDeconOperator -{ +// class CNR3CDecon : public mspass::FFTDeconOperator +class CNR3CDecon : public Base3CDecon, public FFTDeconOperator { public: /*! Default constructor. Puts operator in an invalid state.*/ CNR3CDecon(); - /*! \brief Construct from a tree of parameters loaded into an AntelopePf object. + /*! \brief Construct from a tree of parameters loaded into an AntelopePf + object. There number of inputs to construct this operator are quite large. An AntelelopePf is one way to store this information and the format assumed by @@ -107,12 +106,12 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator xml file, in principle, but for the present we use Antelope's pf file \ format. i.e. a typical program would read a pf file and to construct the object passed as pf. */ - CNR3CDecon(const mspass::utility::AntelopePf& pf); + CNR3CDecon(const mspass::utility::AntelopePf &pf); /*! Standard copy constructor. */ - CNR3CDecon(const CNR3CDecon& parent); + CNR3CDecon(const CNR3CDecon &parent); /*! Standard destructor. */ ~CNR3CDecon(); - CNR3CDecon& operator=(const CNR3CDecon& parent); + CNR3CDecon &operator=(const CNR3CDecon &parent); /*! \brief Change the setup of the operator on the fly. Sometimes an operator needs to have it's properties adjusted on the fly. @@ -123,7 +122,7 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator currently it is implicitly expected to be data compatible with the pf constructor. */ - void change_parameters(const mspass::utility::BasicMetadata& md); + void change_parameters(const mspass::utility::BasicMetadata &md); /*! \brief Load data with one component used as wavelet estimate. Use this method for conventional receiver function data if one of @@ -144,7 +143,8 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator that can arise. All such errors will have an invalid condition set. */ - void loaddata(mspass::seismic::Seismogram& d, const int wcomp,const bool loadnoise=false); + void loaddata(mspass::seismic::Seismogram &d, const int wcomp, + const bool loadnoise = false); /*! \brief Load data and optionally load noise. * This method must be called before running process to get a unique result. @@ -156,10 +156,12 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator \param d is the input data (see note about time span above) \param loadnoise when true the function will attempt to load data for - the noise based regularization from the noise window defined for the operator. - \exception MsPASSError may be thrown for a number of potential error conditions. + the noise based regularization from the noise window defined for the + operator. + \exception MsPASSError may be thrown for a number of potential error + conditions. */ - void loaddata(mspass::seismic::Seismogram& d, const bool loadnoise=false); + void loaddata(mspass::seismic::Seismogram &d, const bool loadnoise = false); /*! \brief Load noise data directly. This method can be used to load noise to be used to compute signal to noise @@ -170,41 +172,44 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator and parameters). Note for this constructor the actual time of the noise window passed is ignored, but the length it defines is used to define the length of the computed spectrum. It is better to have the input noise - slightly larger than the operator length to be consistent with the expectations - of the multitaper method. If the noise window is short the spectrum is - computed but will be biased to lower amplitudes because of zero padding. - That happens because the operator will not recompute Slepian tapers if the - data are short. + slightly larger than the operator length to be consistent with the + expectations of the multitaper method. If the noise window is short the + spectrum is computed but will be biased to lower amplitudes because of zero + padding. That happens because the operator will not recompute Slepian tapers + if the data are short. */ - void loadnoise_data(const mspass::seismic::Seismogram& n); + void loadnoise_data(const mspass::seismic::Seismogram &n); /*! \brief Load noise estimate directly as a PowerSpectrum object. * - The actual noise regularization is computed by this algorithm from an internally - stored PowerSpectrum object. This method allows the power spectrum to be computed - by some other method or using previously computed estimates rather than computing - it through loadnoise. For instance, one might want to use a robust - form of Welch's method to estimate preevent noise that would be immune to - spikes and sporatic noise bursts. + The actual noise regularization is computed by this algorithm from an + internally stored PowerSpectrum object. This method allows the power + spectrum to be computed by some other method or using previously computed + estimates rather than computing it through loadnoise. For instance, one + might want to use a robust form of Welch's method to estimate preevent noise + that would be immune to spikes and sporatic noise bursts. */ - void loadnoise_data(const mspass::seismic::PowerSpectrum& n); + void loadnoise_data(const mspass::seismic::PowerSpectrum &n); /*! \brief Load data defining the wavelet to use for deconvolution. - This algorithm assumes a deterministic model for deconvolution. That is, we have - an estimate of the source wavelet. In conventional receiver functions this is - the vertical or longitudinal component. In current array methods it is always some - stack (not necessarily a simple average) of vertical or longitudinal data from an ensemble. + This algorithm assumes a deterministic model for deconvolution. That is, we + have an estimate of the source wavelet. In conventional receiver functions + this is the vertical or longitudinal component. In current array methods + it is always some stack (not necessarily a simple average) of vertical or + longitudinal data from an ensemble. - It is VERY IMPORTANT to realize that loadwavelet initiates the calculation of the inverse - for the deconvolution. That allows this same processing object to be efficiently used in - array deconvolution and single station deconvolution. For single station estimates - loadwavelet should be called on each seismogram. For array methods loadwavelet should - be called once for the ensemble (common source gather) to which a wavelet is linked. - The inverse is then applied to very signal in the ensemble with process. + It is VERY IMPORTANT to realize that loadwavelet initiates the calculation + of the inverse for the deconvolution. That allows this same processing + object to be efficiently used in array deconvolution and single station + deconvolution. For single station estimates loadwavelet should be called + on each seismogram. For array methods loadwavelet should be called once for + the ensemble (common source gather) to which a wavelet is linked. The + inverse is then applied to very signal in the ensemble with process. - \param w is the wavelet. Must be in relative time with 0 set to the estimated first break time. + \param w is the wavelet. Must be in relative time with 0 set to the + estimated first break time. */ - void loadwavelet(const mspass::seismic::TimeSeries& w); + void loadwavelet(const mspass::seismic::TimeSeries &w); /*! \brief Load noise data for wavelet directly. This method can be used to load noise to be used for regularization from an @@ -215,22 +220,23 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator and parameters). Note for this constructor the actual time of the noise window passed is ignored, but the length it defines is used to define the length of the computed spectrum. It is better to have the input noise - slightly larger than the operator length to be consistent with the expectations - of the multitaper method. If the noise window is short the spectrum is - computed but will be biased to lower amplitudes because of zero padding. - That happens because the operator will not recompute Slepian tapers if the - data are short. + slightly larger than the operator length to be consistent with the + expectations of the multitaper method. If the noise window is short the + spectrum is computed but will be biased to lower amplitudes because of zero + padding. That happens because the operator will not recompute Slepian tapers + if the data are short. */ - void loadnoise_wavelet(const mspass::seismic::TimeSeries& n); - /*! \brief Load noise estimate for wavelet signal directly as a PowerSpectrum object. + void loadnoise_wavelet(const mspass::seismic::TimeSeries &n); + /*! \brief Load noise estimate for wavelet signal directly as a PowerSpectrum + object. * - The actual noise regularization is computed by this algorithm from an internally - stored PowerSpectrum object. This method allows the power spectrum to be computed - by some other method or using previously computed estimates rather than computing - it through loadnoise. + The actual noise regularization is computed by this algorithm from an + internally stored PowerSpectrum object. This method allows the power + spectrum to be computed by some other method or using previously computed + estimates rather than computing it through loadnoise. */ - void loadnoise_wavelet(const mspass::seismic::PowerSpectrum& n); + void loadnoise_wavelet(const mspass::seismic::PowerSpectrum &n); /* These same names are used in ScalarDecon but we don't inherit them here because this algorithm is 3C data centric there is a collision with the ScalarDecon api because of it. */ @@ -272,29 +278,18 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator */ mspass::utility::Metadata QCMetrics(); /*! Return the power spectrum estimate linked to the wavelet signal. */ - mspass::seismic::PowerSpectrum wavelet_noise_spectrum() - { - return psnoise; - }; + mspass::seismic::PowerSpectrum wavelet_noise_spectrum() { return psnoise; }; /*! Return the average 3C power spectrum for the latest loaded noise data. */ - mspass::seismic::PowerSpectrum data_noise_spectrum() - { - return psnoise_data; - }; + mspass::seismic::PowerSpectrum data_noise_spectrum() { return psnoise_data; }; /*! Return the power spectrum of the wavelet signal used for deconvolution. */ - mspass::seismic::PowerSpectrum wavelet_spectrum() - { - return pswavelet; - }; + mspass::seismic::PowerSpectrum wavelet_spectrum() { return pswavelet; }; /*! Return the power spectrum of the signal loaded for deconvolution. */ - mspass::seismic::PowerSpectrum data_spectrum() - { - return pssignal; - }; + mspass::seismic::PowerSpectrum data_spectrum() { return pssignal; }; + private: CNR3C_algorithms algorithm; - bool taper_data; //Set false only if none specified - double operator_dt; // Data must match this sample interval + bool taper_data; // Set false only if none specified + double operator_dt; // Data must match this sample interval /* Expected time window size in samples (computed from processing_window and operator dt)*/ int winlength; @@ -308,7 +303,7 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator mspass::algorithms::TimeWindow noise_window; /*! Operator used to compute power spectra using multitaper. Need different ones for diffent contexts to handle mixed window sizes */ - MTPowerSpectrumEngine signalengine,waveletengine; + MTPowerSpectrumEngine signalengine, waveletengine; MTPowerSpectrumEngine dnoise_engine, wnoise_engine; ShapingWavelet shapingwavelet; /* This contains the noise power spectrum to use for regularization @@ -351,7 +346,7 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator by the bandwidth estimation algorithm to define the edge of the working frequency band. */ double snr_bandwidth; - //ComplexArray winv; + // ComplexArray winv; /* winv is in FFTDeconOperator*/ ComplexArray ao_fft; mspass::algorithms::amplitudes::BandwidthData wavelet_bwd; @@ -365,13 +360,16 @@ class CNR3CDecon : public Base3CDecon, public FFTDeconOperator double signal_bandwidth_fraction[3]; double peak_snr[3]; double regularization_bandwidth_fraction; - void read_parameters(const mspass::utility::AntelopePf& pf); - int TestSeismogramInput(mspass::seismic::Seismogram& d,const int comp,const bool loaddata); + void read_parameters(const mspass::utility::AntelopePf &pf); + int TestSeismogramInput(mspass::seismic::Seismogram &d, const int comp, + const bool loaddata); void compute_gwl_inverse(); void compute_gdamp_inverse(); - mspass::seismic::PowerSpectrum ThreeCPower(const mspass::seismic::Seismogram& d); - void update_shaping_wavelet(const mspass::algorithms::amplitudes::BandwidthData& bwd); + mspass::seismic::PowerSpectrum + ThreeCPower(const mspass::seismic::Seismogram &d); + void update_shaping_wavelet( + const mspass::algorithms::amplitudes::BandwidthData &bwd); }; -} // End namespace +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/CNRDeconEngine.h b/cxx/include/mspass/algorithms/deconvolution/CNRDeconEngine.h index c73c316d5..9f339c675 100644 --- a/cxx/include/mspass/algorithms/deconvolution/CNRDeconEngine.h +++ b/cxx/include/mspass/algorithms/deconvolution/CNRDeconEngine.h @@ -5,16 +5,16 @@ #include #include -#include "mspass/utility/AntelopePf.h" -#include "mspass/algorithms/deconvolution/ShapingWavelet.h" -#include "mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h" #include "mspass/algorithms/Taper.h" +#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h" +#include "mspass/algorithms/deconvolution/ShapingWavelet.h" #include "mspass/seismic/PowerSpectrum.h" -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Seismogram.h" -#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/seismic/TimeSeries.h" +#include "mspass/utility/AntelopePf.h" -namespace mspass::algorithms::deconvolution{ +namespace mspass::algorithms::deconvolution { /* This enum is file scope to intentionally exclude it from python wrappers. It is used internally to define the algorithm the processor is to run. I (glp) chose that approach over the inheritance approach used in the scalar @@ -24,12 +24,8 @@ the parameter setup as opposed to having to select the right symbolic name to construct. Anyway, this enum defines algorithms that can be chosen for processing. */ -enum class CNR3C_algorithms{ - generalized_water_level, - colored_noise_damping -}; -class CNRDeconEngine : public FFTDeconOperator -{ +enum class CNR3C_algorithms { generalized_water_level, colored_noise_damping }; +class CNRDeconEngine : public FFTDeconOperator { public: CNRDeconEngine(); /* design note - delete when finished. @@ -46,147 +42,147 @@ class CNRDeconEngine : public FFTDeconOperator grungy work to assure all that gets done correction is handled in python. */ - /*! Construct from AntelopePf container. + /*! Construct from AntelopePf container. - This is currently the only valid constructor for this object. - The operator requires a fair number of parameters for construction - that include secondary groups of parameters in a hierarchy. - An AntelopePf is one way to handle that with a human readable form. - An alternative is yaml but that is not currently supported as - an external format. The AntelopePf object API is a generic - way to handle parameter data hierarchies that is in MsPASS. - Most user's will interact with the object only indirectly - via python wrappers. All a user needs to do is define all the - required key-value pairs and get the obscure pf format correct. - To use this constructor one passes an instance of the object - of type AntelopePf that is normally constructed by reading a - text file with the "pf" format or retrieved from another - AntelopePf with the get_branch method. - */ - CNRDeconEngine(const mspass::utility::AntelopePf& pf); - CNRDeconEngine(const CNRDeconEngine& parent); - void initialize_inverse_operator(const mspass::seismic::TimeSeries& wavelet, - const mspass::seismic::TimeSeries& noise_data); - void initialize_inverse_operator(const mspass::seismic::TimeSeries& wavelet, - const mspass::seismic::PowerSpectrum& noise_spectrum); - virtual ~CNRDeconEngine(){}; - mspass::seismic::Seismogram process(const mspass::seismic::Seismogram& d, - const mspass::seismic::PowerSpectrum& psnoise, - const double fl, const double fh); - double get_operator_dt() const - { - return this->operator_dt; - } + This is currently the only valid constructor for this object. + The operator requires a fair number of parameters for construction + that include secondary groups of parameters in a hierarchy. + An AntelopePf is one way to handle that with a human readable form. + An alternative is yaml but that is not currently supported as + an external format. The AntelopePf object API is a generic + way to handle parameter data hierarchies that is in MsPASS. + Most user's will interact with the object only indirectly + via python wrappers. All a user needs to do is define all the + required key-value pairs and get the obscure pf format correct. + To use this constructor one passes an instance of the object + of type AntelopePf that is normally constructed by reading a + text file with the "pf" format or retrieved from another + AntelopePf with the get_branch method. + */ + CNRDeconEngine(const mspass::utility::AntelopePf &pf); + CNRDeconEngine(const CNRDeconEngine &parent); + void + initialize_inverse_operator(const mspass::seismic::TimeSeries &wavelet, + const mspass::seismic::TimeSeries &noise_data); + void initialize_inverse_operator( + const mspass::seismic::TimeSeries &wavelet, + const mspass::seismic::PowerSpectrum &noise_spectrum); + virtual ~CNRDeconEngine() {}; + mspass::seismic::Seismogram + process(const mspass::seismic::Seismogram &d, + const mspass::seismic::PowerSpectrum &psnoise, const double fl, + const double fh); + double get_operator_dt() const { return this->operator_dt; } /*! Compute noise spectrum using internal Multitaper operator. Normally used for noise spectrum but can be used for signal. Necessary to assure consistent scaling between signal and noise spectrum estimators. */ - mspass::seismic::PowerSpectrum compute_noise_spectrum( - const mspass::seismic::TimeSeries& d2use); + mspass::seismic::PowerSpectrum + compute_noise_spectrum(const mspass::seismic::TimeSeries &d2use); /*! Compute noise spectrum from three component data. - Overloaded version returns average power spectrum of all three components. Measure - of overall 3c power comparable to scalar signal power. Could do - sum of component spectra but that would cause a scaling problem for - use in regularization. For most data this will tend to be dominated by - the component with the highest noise level*/ - mspass::seismic::PowerSpectrum compute_noise_spectrum( - const mspass::seismic::Seismogram& d2use); + Overloaded version returns average power spectrum of all three components. + Measure of overall 3c power comparable to scalar signal power. Could do sum + of component spectra but that would cause a scaling problem for use in + regularization. For most data this will tend to be dominated by the component + with the highest noise level*/ + mspass::seismic::PowerSpectrum + compute_noise_spectrum(const mspass::seismic::Seismogram &d2use); mspass::seismic::TimeSeries ideal_output(); - mspass::seismic::TimeSeries actual_output( - const mspass::seismic::TimeSeries& wavelet); - mspass::seismic::TimeSeries inverse_wavelet( - const mspass::seismic::TimeSeries& wavelet, const double t0shift); + mspass::seismic::TimeSeries + actual_output(const mspass::seismic::TimeSeries &wavelet); + mspass::seismic::TimeSeries + inverse_wavelet(const mspass::seismic::TimeSeries &wavelet, + const double t0shift); mspass::utility::Metadata QCMetrics(); - CNRDeconEngine& operator=(const CNRDeconEngine& parent); + CNRDeconEngine &operator=(const CNRDeconEngine &parent); private: - CNR3C_algorithms algorithm; - /* For the colored noise damping algorithm the damper is frequency dependent. - The same issue in water level that requires a floor on the water level - applies to damping. We use noise_floor to create a lower bound on - damper values. Note the damping constant at each frequency is - damp*noise except where noise is below noise_floor defined relative to - maximum noise value where it is set to n_peak*noise_floor*damp. */ - double damp; - double noise_floor; - /* SNR bandbwidth estimates count frequencies with snr above this value */ - double band_snr_floor; - double operator_dt; // Data must match this sample interval - int shaping_wavelet_number_poles; - mspass::algorithms::deconvolution::ShapingWavelet shapingwavelet; - /* Expected time window size in samples. When signal lengths - match this value the slepian tapers are not recomputed. When there - is a mismatch it will change. That means this can change dynamically - when run on multiple data objects. */ - int winlength; - mspass::algorithms::deconvolution::MTPowerSpectrumEngine signal_engine; - mspass::algorithms::deconvolution::MTPowerSpectrumEngine noise_engine; + CNR3C_algorithms algorithm; + /* For the colored noise damping algorithm the damper is frequency dependent. + The same issue in water level that requires a floor on the water level + applies to damping. We use noise_floor to create a lower bound on + damper values. Note the damping constant at each frequency is + damp*noise except where noise is below noise_floor defined relative to + maximum noise value where it is set to n_peak*noise_floor*damp. */ + double damp; + double noise_floor; + /* SNR bandbwidth estimates count frequencies with snr above this value */ + double band_snr_floor; + double operator_dt; // Data must match this sample interval + int shaping_wavelet_number_poles; + mspass::algorithms::deconvolution::ShapingWavelet shapingwavelet; + /* Expected time window size in samples. When signal lengths + match this value the slepian tapers are not recomputed. When there + is a mismatch it will change. That means this can change dynamically + when run on multiple data objects. */ + int winlength; + mspass::algorithms::deconvolution::MTPowerSpectrumEngine signal_engine; + mspass::algorithms::deconvolution::MTPowerSpectrumEngine noise_engine; - /* This algorithm uses a mix of damping and water level. Above this floor, - which acts a bit like a water level, no regularization is done. If - snr is less than this value we regularize with damp*noise_amplitude. - Note the noise_floor parameter puts a lower bound on the frequency dependent - regularization. If noise amplitude (not power) is less than noise_floor - the floor is set like a water level as noise_max*noise_level.*/ - double snr_regularization_floor; - /* These are QC metrics computed by process method. Saved to allow them - to be use in QCmetrics method. */ - double regularization_bandwidth_fraction; - double peak_snr[3]; - double signal_bandwidth_fraction[3]; - mspass::algorithms::deconvolution::ComplexArray winv; - /* This is the lag from sample 0 for the time defines as 0 for the - wavelet used to compute the inverse. It is needed to resolve time - in the actual_output method.*/ - int winv_t0_lag; - /*** Private methods *****/ - void update_shaping_wavelet(const double fl, const double fh); - /* These are two algorithms for computing inverse operator in the frequency domain*/ - void compute_winv(const mspass::seismic::TimeSeries& wavelet, - const mspass::seismic::PowerSpectrum& psnoise); - void compute_gwl_inverse(const mspass::seismic::TimeSeries& wavelet, - const mspass::seismic::PowerSpectrum& psnoise); - void compute_gdamp_inverse(const mspass::seismic::TimeSeries& wavelet, - const mspass::seismic::PowerSpectrum& psnoise); + /* This algorithm uses a mix of damping and water level. Above this floor, + which acts a bit like a water level, no regularization is done. If + snr is less than this value we regularize with damp*noise_amplitude. + Note the noise_floor parameter puts a lower bound on the frequency dependent + regularization. If noise amplitude (not power) is less than noise_floor + the floor is set like a water level as noise_max*noise_level.*/ + double snr_regularization_floor; + /* These are QC metrics computed by process method. Saved to allow them + to be use in QCmetrics method. */ + double regularization_bandwidth_fraction; + double peak_snr[3]; + double signal_bandwidth_fraction[3]; + mspass::algorithms::deconvolution::ComplexArray winv; + /* This is the lag from sample 0 for the time defines as 0 for the + wavelet used to compute the inverse. It is needed to resolve time + in the actual_output method.*/ + int winv_t0_lag; + /*** Private methods *****/ + void update_shaping_wavelet(const double fl, const double fh); + /* These are two algorithms for computing inverse operator in the frequency + * domain*/ + void compute_winv(const mspass::seismic::TimeSeries &wavelet, + const mspass::seismic::PowerSpectrum &psnoise); + void compute_gwl_inverse(const mspass::seismic::TimeSeries &wavelet, + const mspass::seismic::PowerSpectrum &psnoise); + void compute_gdamp_inverse(const mspass::seismic::TimeSeries &wavelet, + const mspass::seismic::PowerSpectrum &psnoise); friend boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - //std::cout <<"Entered serialize function"<(*this); - //std::cout << "Serializing first group of simple parameters"< + void serialize(Archive &ar, const unsigned int version) { + // std::cout <<"Entered serialize function"<(*this); + // std::cout << "Serializing first group of simple parameters"< -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include -#define REAL(z,i) ((z)[2*(i)]) -#define IMAG(z,i) ((z)[2*(i)+1]) -namespace mspass::algorithms::deconvolution{ +#define REAL(z, i) ((z)[2 * (i)]) +#define IMAG(z, i) ((z)[2 * (i) + 1]) +namespace mspass::algorithms::deconvolution { typedef std::complex Complex64; typedef std::complex Complex32; typedef struct FortranComplex32 { - float real; - float imag; + float real; + float imag; } FortranComplex32; typedef struct FortranComplex64 { - double real; - double imag; + double real; + double imag; } FortranComplex64; /* needed for boost serialization */ -template -void serialize(Archive & ar, FortranComplex64 &z, const unsigned int version) -{ - ar & z.real; - ar & z.imag; +template +void serialize(Archive &ar, FortranComplex64 &z, const unsigned int version) { + ar & z.real; + ar & z.imag; } - /* \brief Interfacing object to ease conversion between FORTRAN and C++ complex. */ -class ComplexArray -{ +class ComplexArray { public: - /*! Empty constructor. */ - ComplexArray(); - /*! Construct from stl vector container of complex. */ - ComplexArray(std::vector &d); - /*! Similar for 32 bit version */ - ComplexArray(std::vector &d); - /*! Construct from a FORTRAN data array. + /*! Empty constructor. */ + ComplexArray(); + /*! Construct from stl vector container of complex. */ + ComplexArray(std::vector &d); + /*! Similar for 32 bit version */ + ComplexArray(std::vector &d); + /*! Construct from a FORTRAN data array. - Fortran stores complex numbers in a mulitplexed array - structure (real(1), imag(1), real(2), imag(2), etc.). - The constructors below provide a mechanism for building - this object from various permutations of this. - \param nsamp is the number of elements in the C vector - \param d is the pointer to the first compoment of the - fortran vector. + Fortran stores complex numbers in a mulitplexed array + structure (real(1), imag(1), real(2), imag(2), etc.). + The constructors below provide a mechanism for building + this object from various permutations of this. + \param nsamp is the number of elements in the C vector + \param d is the pointer to the first compoment of the + fortran vector. + */ + ComplexArray(int nsamp, FortranComplex32 *d); + ComplexArray(int nsamp, FortranComplex64 *d); + ComplexArray(int nsamp, float *d); + ComplexArray(int nsamp, double *d); + ComplexArray(int nsamp); + /*! Construct from different length of vector, adds zoeros to it + And construct a constant arrays */ - ComplexArray(int nsamp, FortranComplex32 *d); - ComplexArray(int nsamp, FortranComplex64 *d); - ComplexArray(int nsamp, float *d); - ComplexArray(int nsamp, double *d); - ComplexArray(int nsamp); - /*! Construct from different length of vector, adds zoeros to it - And construct a constant arrays - */ - template ComplexArray(int nsamp, std::vector d); - template ComplexArray(int nsamp, T d); + template ComplexArray(int nsamp, std::vector d); + template ComplexArray(int nsamp, T d); - /*! Construct from magnitude and phase arrays.*/ - ComplexArray(std::vector mag,std::vector phase); + /*! Construct from magnitude and phase arrays.*/ + ComplexArray(std::vector mag, std::vector phase); - /* These will need to be implemented. Likely cannot - depend on the compiler to generate them correctly */ - ComplexArray(const ComplexArray &parent); - ComplexArray& operator=(const ComplexArray &parent); - ~ComplexArray(); + /* These will need to be implemented. Likely cannot + depend on the compiler to generate them correctly */ + ComplexArray(const ComplexArray &parent); + ComplexArray &operator=(const ComplexArray &parent); + ~ComplexArray(); - /* These are kind of the inverse of the constructor. - Independent of what the internal representation is they - will return useful interface representations. */ - /*! Return a pointer to a fortran array containing - the data vector. + /* These are kind of the inverse of the constructor. + Independent of what the internal representation is they + will return useful interface representations. */ + /*! Return a pointer to a fortran array containing + the data vector. - The array linked to the returned pointer should be - created with the new operator and the caller should - be sure to use delete [] to free this memory when - finished. */ - //template T *FortranData(); - /* This is same for what I think fortran calls - double complex */ -// double *FortranData(); - /* C representation. This can be templated easily. - See below. The syntax is weird and should probably - be wrapped with a typedef */ - //template std::vector > CPPData(); + The array linked to the returned pointer should be + created with the new operator and the caller should + be sure to use delete [] to free this memory when + finished. */ + // template T *FortranData(); + /* This is same for what I think fortran calls + double complex */ + // double *FortranData(); + /* C representation. This can be templated easily. + See below. The syntax is weird and should probably + be wrapped with a typedef */ + // template std::vector > CPPData(); + /* Operators are the most important elements of this + thing to make life easier. */ + /*! Index operator. + Cannot make it work by getting the address from reference. + Have to call the ptr() function to get the address. + \param sample is the sample number to return. + \return contents of vector at position sample. + */ + Complex64 operator[](int sample); + double *ptr(); + double *ptr(int sample); + ComplexArray &operator+=(const ComplexArray &other) noexcept(false); + ComplexArray &operator-=(const ComplexArray &other) noexcept(false); + /* This actually is like .* in matlab - sample by sample multiply not + a dot product */ + ComplexArray &operator*=(const ComplexArray &other) noexcept(false); + /* This is like *= but complex divide element by element */ + ComplexArray &operator/=(const ComplexArray &other) noexcept(false); + const ComplexArray operator+(const ComplexArray &other) const noexcept(false); + // template ComplexArray operator +(const vector &other); + // template ComplexArray operator +(const T &other); + const ComplexArray operator-(const ComplexArray &other) const noexcept(false); + // template ComplexArray operator -(const vector &other); + // template ComplexArray operator -(const T &other); + const ComplexArray operator*(const ComplexArray &other) const noexcept(false); + const ComplexArray operator/(const ComplexArray &other) const noexcept(false); + /*! product of complex and real vectors */ + // template ComplexArray operator *(const vector &other); + // template friend ComplexArray operator *(const vector &lhs,const + // ComplexArray &rhs); + /*! product of complex and a number */ + // template ComplexArray operator *(const T &other); + // template friend ComplexArray operator *(const T &lhs,const + // ComplexArray &rhs); + /*! Change vector to complex conjugates. */ + void conj(); + /* Return stl vector of amplitude spectrum. */ + std::vector abs() const; + /* Return rms value. */ + double rms() const; + /* Return 2-norm value. */ + double norm2() const; + /* Return stl vector of phase */ + std::vector phase() const; + /* Return size of the array*/ + int size() const; - /* Operators are the most important elements of this - thing to make life easier. */ - /*! Index operator. - Cannot make it work by getting the address from reference. - Have to call the ptr() function to get the address. - \param sample is the sample number to return. - \return contents of vector at position sample. - */ - Complex64 operator[](int sample); - double *ptr(); - double *ptr(int sample); - ComplexArray& operator +=(const ComplexArray& other) noexcept(false); - ComplexArray& operator -=(const ComplexArray& other) noexcept(false); - /* This actually is like .* in matlab - sample by sample multiply not - a dot product */ - ComplexArray& operator *= (const ComplexArray& other)noexcept(false); - /* This is like *= but complex divide element by element */ - ComplexArray& operator /= (const ComplexArray& other)noexcept(false); - const ComplexArray operator +(const ComplexArray& other)const noexcept(false); - //template ComplexArray operator +(const vector &other); - //template ComplexArray operator +(const T &other); - const ComplexArray operator -(const ComplexArray& other)const noexcept(false); - //template ComplexArray operator -(const vector &other); - //template ComplexArray operator -(const T &other); - const ComplexArray operator *(const ComplexArray& other)const noexcept(false); - const ComplexArray operator /(const ComplexArray& other)const noexcept(false); - /*! product of complex and real vectors */ - //template ComplexArray operator *(const vector &other); - //template friend ComplexArray operator *(const vector &lhs,const ComplexArray &rhs); - /*! product of complex and a number */ - //template ComplexArray operator *(const T &other); - //template friend ComplexArray operator *(const T &lhs,const ComplexArray &rhs); - /*! Change vector to complex conjugates. */ - void conj(); - /* Return stl vector of amplitude spectrum. */ - std::vector abs() const; - /* Return rms value. */ - double rms() const; - /* Return 2-norm value. */ - double norm2() const; - /* Return stl vector of phase */ - std::vector phase() const; - /* Return size of the array*/ - int size() const; private: - /* Here is an implementation detail. There are three ways - I can think to do this. First, we could internally store - data as fortran array of 32 bit floats. That is probably - the best because we can use BLAS routines (if you haven't - heard of this - likely - I need to educate you.) to do - most of the numerics fast. Second, we could use stl - vector container of std::complex. The third is excessively - messy but technically feasible - I would not recommend it. - That is, one could store pointers to either representation - and internally convert back and forth. Ugly and dangerous - I think. + /* Here is an implementation detail. There are three ways + I can think to do this. First, we could internally store + data as fortran array of 32 bit floats. That is probably + the best because we can use BLAS routines (if you haven't + heard of this - likely - I need to educate you.) to do + most of the numerics fast. Second, we could use stl + vector container of std::complex. The third is excessively + messy but technically feasible - I would not recommend it. + That is, one could store pointers to either representation + and internally convert back and forth. Ugly and dangerous + I think. - I suggest we store a FORTRAN 32 bit form since that is - what standard numeric libraries (e.g. most fft routines) - use. */ - /*I decided to use 64 bit, since the GSL's fft routine is using that.*/ - /* Changed from raw pointer to shared_ptr by glp - Dec 2024 */ - //FortranComplex64 *data; - std::shared_ptr data; - int nsamp; - friend boost::serialization::access; - template - void save(Archive& ar, const unsigned int version) const - { - std::vector dv; - dv.reserve(this->nsamp); - for(auto i=0;idata[i]); - ar & nsamp; - ar & dv; - } - template - void load(Archive &ar, const unsigned int version) - { - ar & this->nsamp; - std::vector dv; - dv.reserve(this->nsamp); - ar & dv; - this->data = std::shared_ptr - (new FortranComplex64[this->nsamp]); - for(auto i=0;insamp;++i) this->data[i] = dv[i]; - } - BOOST_SERIALIZATION_SPLIT_MEMBER() + I suggest we store a FORTRAN 32 bit form since that is + what standard numeric libraries (e.g. most fft routines) + use. */ + /*I decided to use 64 bit, since the GSL's fft routine is using that.*/ + /* Changed from raw pointer to shared_ptr by glp - Dec 2024 */ + // FortranComplex64 *data; + std::shared_ptr data; + int nsamp; + friend boost::serialization::access; + template + void save(Archive &ar, const unsigned int version) const { + std::vector dv; + dv.reserve(this->nsamp); + for (auto i = 0; i < nsamp; ++i) + dv.push_back(this->data[i]); + ar & nsamp; + ar & dv; + } + template void load(Archive &ar, const unsigned int version) { + ar &this->nsamp; + std::vector dv; + dv.reserve(this->nsamp); + ar & dv; + this->data = + std::shared_ptr(new FortranComplex64[this->nsamp]); + for (auto i = 0; i < this->nsamp; ++i) + this->data[i] = dv[i]; + } + BOOST_SERIALIZATION_SPLIT_MEMBER() }; /* This would normally be in the .h file and since I don't think you've used templates worth showing you how it would work. */ @@ -210,42 +208,33 @@ template T* ComplexArray::FortranData() } */ -template ComplexArray::ComplexArray(int n, std::vector d) -{ - nsamp=n; - if(nsamp>d.size()) - { - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; idata[i].real=d[i]; - this->data[i].imag=0.0; - } - for(std::size_t i=d.size(); idata[i].real=0.0; - this->data[i].imag=0.0; - } +template ComplexArray::ComplexArray(int n, std::vector d) { + nsamp = n; + if (nsamp > d.size()) { + data = std::shared_ptr(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < d.size(); i++) { + this->data[i].real = d[i]; + this->data[i].imag = 0.0; } - else - { - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; idata[i].real=d[i]; - this->data[i].imag=0.0; - } + for (std::size_t i = d.size(); i < nsamp; i++) { + this->data[i].real = 0.0; + this->data[i].imag = 0.0; } -} -template ComplexArray::ComplexArray(int n, T d) -{ - nsamp=n; + } else { data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; idata[i].real=d; - this->data[i].imag=0.0; + for (std::size_t i = 0; i < nsamp; i++) { + this->data[i].real = d[i]; + this->data[i].imag = 0.0; } + } +} +template ComplexArray::ComplexArray(int n, T d) { + nsamp = n; + data = std::shared_ptr(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + this->data[i].real = d; + this->data[i].imag = 0.0; + } } /* template ComplexArray ComplexArray::operator +(const vector &other) @@ -309,7 +298,8 @@ template ComplexArray ComplexArray::operator *(const vector &other) } return result; } -template ComplexArray operator *(const vector& lhs,const ComplexArray& rhs) +template ComplexArray operator *(const vector& lhs,const +ComplexArray& rhs) { return rhs*lhs; } @@ -328,5 +318,5 @@ template ComplexArray operator *(const T& lhs,const ComplexArray& rhs) return rhs*lhs; } */ -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/FFTDeconOperator.h b/cxx/include/mspass/algorithms/deconvolution/FFTDeconOperator.h index 3a3651019..5437a2401 100644 --- a/cxx/include/mspass/algorithms/deconvolution/FFTDeconOperator.h +++ b/cxx/include/mspass/algorithms/deconvolution/FFTDeconOperator.h @@ -1,15 +1,15 @@ #ifndef __FFT_DECON_OPERATOR_H__ #define __FFT_DECON_OPERATOR_H__ -#include -#include -#include -#include -#include -#include "mspass/utility/Metadata.h" -#include "mspass/seismic/CoreTimeSeries.h" #include "mspass/algorithms/TimeWindow.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" -namespace mspass::algorithms::deconvolution{ +#include "mspass/seismic/CoreTimeSeries.h" +#include "mspass/utility/Metadata.h" +#include +#include +#include +#include +#include +namespace mspass::algorithms::deconvolution { /*! \brief Object to hold components needed in all fft based decon algorithms. The fft based algorithms implemented here us the GNU Scientific Library @@ -17,74 +17,67 @@ prime factorization fft algorithm. Those methods require initialization given length of the fft to load and store the factorization data. This object holds these for all such methods and recomputes them only when needed for efficiency. */ -class FFTDeconOperator -{ +class FFTDeconOperator { public: - FFTDeconOperator(); - FFTDeconOperator(const mspass::utility::Metadata& md); - FFTDeconOperator(const FFTDeconOperator& parent); - ~FFTDeconOperator(); - FFTDeconOperator& operator=(const FFTDeconOperator& parent); - void changeparameter(const mspass::utility::Metadata& md); - void change_size(const int nfft_new); - void change_shift(const int shift) { - sample_shift=shift; - }; - int get_size(){return nfft;}; - int get_shift(){return sample_shift;}; - int operator_size() { - return static_cast(nfft); - }; - int operator_shift() { - return sample_shift; - }; - double df(const double dt){ - double period; - period=static_cast(nfft)*dt; - return 1.0/period; - }; - /*! \brief Return inverse wavelet for Fourier methods. + FFTDeconOperator(); + FFTDeconOperator(const mspass::utility::Metadata &md); + FFTDeconOperator(const FFTDeconOperator &parent); + ~FFTDeconOperator(); + FFTDeconOperator &operator=(const FFTDeconOperator &parent); + void changeparameter(const mspass::utility::Metadata &md); + void change_size(const int nfft_new); + void change_shift(const int shift) { sample_shift = shift; }; + int get_size() { return nfft; }; + int get_shift() { return sample_shift; }; + int operator_size() { return static_cast(nfft); }; + int operator_shift() { return sample_shift; }; + double df(const double dt) { + double period; + period = static_cast(nfft) * dt; + return 1.0 / period; + }; + /*! \brief Return inverse wavelet for Fourier methods. - This is a helper to be used by the inverse_wavelet method for all - Fourier based deconvolution methods. It avoids repetitious code that - would be required otherwise. inverse_wavelet methods are only - wrappers for this generic method. See documentation for inverse_wavelet - for description of tshift and t0parent. */ - mspass::seismic::CoreTimeSeries FourierInverse(const ComplexArray& winv, const ComplexArray& sw, - const double dt, const double t0parent); + This is a helper to be used by the inverse_wavelet method for all + Fourier based deconvolution methods. It avoids repetitious code that + would be required otherwise. inverse_wavelet methods are only + wrappers for this generic method. See documentation for inverse_wavelet + for description of tshift and t0parent. */ + mspass::seismic::CoreTimeSeries FourierInverse(const ComplexArray &winv, + const ComplexArray &sw, + const double dt, + const double t0parent); protected: - int nfft; - int sample_shift; - gsl_fft_complex_wavetable *wavetable; - gsl_fft_complex_workspace *workspace; - ComplexArray winv; + int nfft; + int sample_shift; + gsl_fft_complex_wavetable *wavetable; + gsl_fft_complex_workspace *workspace; + ComplexArray winv; + private: - friend boost::serialization::access; - template - void save(Archive& ar, const unsigned int version) const - { - //std::cout << "Entered FFTDecon serialization save function"< - void load(Archive &ar, const unsigned int version) - { - //std::cout << "Entered FFTDecon serializaton load function" << std::endl; - ar & this->nfft; - ar & this->sample_shift; - //std::cout << "Loading winv vector" << std::endl; - ar & winv; - //std::cout << "Creating wavetable " << std::endl; - this->wavetable = gsl_fft_complex_wavetable_alloc (this->nfft); - //std::cout << "Creating workspace" << std::endl; - this->workspace = gsl_fft_complex_workspace_alloc (this->nfft); - //std::cout << "Exiting load" << std::endl; - } - BOOST_SERIALIZATION_SPLIT_MEMBER() + friend boost::serialization::access; + template + void save(Archive &ar, const unsigned int version) const { + // std::cout << "Entered FFTDecon serialization save function"< void load(Archive &ar, const unsigned int version) { + // std::cout << "Entered FFTDecon serializaton load function" << std::endl; + ar &this->nfft; + ar &this->sample_shift; + // std::cout << "Loading winv vector" << std::endl; + ar & winv; + // std::cout << "Creating wavetable " << std::endl; + this->wavetable = gsl_fft_complex_wavetable_alloc(this->nfft); + // std::cout << "Creating workspace" << std::endl; + this->workspace = gsl_fft_complex_workspace_alloc(this->nfft); + // std::cout << "Exiting load" << std::endl; + } + BOOST_SERIALIZATION_SPLIT_MEMBER() }; /* This helper is best referenced here */ @@ -101,7 +94,7 @@ class FFTDeconOperator (Warning - this is C convention sample number) */ -std::vector circular_shift(const std::vector& d,const int i0); +std::vector circular_shift(const std::vector &d, const int i0); /*! Derive fft length from a time window. All deconvlution methods using an fft need to define nfft based on the @@ -115,13 +108,14 @@ the same name with a time window and sample interval argument. This procedure extracts these using three parameter keys to extract the real numbers form md: deconvolution_data_window_start, decon_window_end, and target dt. */ -int ComputeFFTLength(const mspass::utility::Metadata& md); +int ComputeFFTLength(const mspass::utility::Metadata &md); /*! Returns next power of 2 larger than n. * * Some FFT implementations require the size of the input data vector be a power - * of 2. This routine can be used to define a buffer size satisfying that constraint.*/ + * of 2. This routine can be used to define a buffer size satisfying that + * constraint.*/ extern "C" { - unsigned int nextPowerOf2(unsigned int n); -} +unsigned int nextPowerOf2(unsigned int n); } +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/GeneralIterDecon.h b/cxx/include/mspass/algorithms/deconvolution/GeneralIterDecon.h index 7aae0b898..2a55094aa 100644 --- a/cxx/include/mspass/algorithms/deconvolution/GeneralIterDecon.h +++ b/cxx/include/mspass/algorithms/deconvolution/GeneralIterDecon.h @@ -1,44 +1,43 @@ #ifndef __SIIMPLE_GENERAL_ITER_DECON__ #define __SIIMPLE_GENERAL_ITER_DECON__ -#include +#include "mspass/algorithms/TimeWindow.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" -#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/FFTDeconOperator.h" -#include "mspass/algorithms/TimeWindow.h" -#include "mspass/utility/dmatrix.h" -#include "mspass/utility/Metadata.h" -#include "mspass/utility/AntelopePf.h" +#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/seismic/CoreTimeSeries.h" #include "mspass/seismic/Seismogram.h" -namespace mspass::algorithms::deconvolution{ +#include "mspass/utility/AntelopePf.h" +#include "mspass/utility/Metadata.h" +#include "mspass/utility/dmatrix.h" +#include +namespace mspass::algorithms::deconvolution { /* Wang's original version allowed XCORR here - dropped for now as this code assumes the decon produces a zero phase ideal output while in every case the original XCORR iterative method uses the raw wavelet and cross correlation. */ -enum IterDeconType {WATER_LEVEL,LEAST_SQ,MULTI_TAPER}; -class ThreeCSpike -{ +enum IterDeconType { WATER_LEVEL, LEAST_SQ, MULTI_TAPER }; +class ThreeCSpike { public: - /*! The column position where this spike should be placed in parent - three component dmatrix where time is the column field */ - int col; - /*! This holds the amplitude of this spike as three components */ - double u[3]; - /*! L2 norm of u savd for efficiency to avoid repeated calculations */ - double amp; - /* This is the primary constructor for oneo of thise. In the iterative - sequnce of generalized iterative decon we find the column with maximum - amplitude and point this constructor at the right column. The - parent dmatrix is then updated subtracting the inverse wavelet at - this lag. */ - ThreeCSpike(mspass::utility::dmatrix& d, int k); - /*! Copy consructor. Could be defaulted, I think, but best to - create it explicitly. */ - ThreeCSpike(const ThreeCSpike& parent); - /*! Makign sure operator= is also essential be sure STL containers - work correctly */ - ThreeCSpike& operator=(const ThreeCSpike& parent); + /*! The column position where this spike should be placed in parent + three component dmatrix where time is the column field */ + int col; + /*! This holds the amplitude of this spike as three components */ + double u[3]; + /*! L2 norm of u savd for efficiency to avoid repeated calculations */ + double amp; + /* This is the primary constructor for oneo of thise. In the iterative + sequnce of generalized iterative decon we find the column with maximum + amplitude and point this constructor at the right column. The + parent dmatrix is then updated subtracting the inverse wavelet at + this lag. */ + ThreeCSpike(mspass::utility::dmatrix &d, int k); + /*! Copy consructor. Could be defaulted, I think, but best to + create it explicitly. */ + ThreeCSpike(const ThreeCSpike &parent); + /*! Makign sure operator= is also essential be sure STL containers + work correctly */ + ThreeCSpike &operator=(const ThreeCSpike &parent); }; /*! \brief Implements the Generalized Iterative Method of Wang and Pavlis. @@ -64,195 +63,185 @@ the assure consistency they need to call loadnoise before load OR call only the load method that includes a signal and noise window. */ -class GeneralIterDecon: public ScalarDecon -{ +class GeneralIterDecon : public ScalarDecon { public: - /*! \brief Create an initialize an operator for subseqquent processing. + /*! \brief Create an initialize an operator for subseqquent processing. - This is the primary constructor for this object. The PfStyleMetadata - object is expected to contain a suite of parameters used to define - how this operator will behave along with what inverse filter is to - be constructed ans applied before the iterative algorithm is applied. - The pf is required to have multiple Arr sections the components. */ - GeneralIterDecon(mspass::utility::AntelopePf &md); - GeneralIterDecon(const GeneralIterDecon &parent); - void changeparameter(const mspass::utility::Metadata &md) { - this->preprocessor->changeparameter(md); - }; - int load(const mspass::seismic::CoreSeismogram &d, mspass::algorithms::TimeWindow dwin); - int loadnoise(const mspass::seismic::CoreSeismogram &d, mspass::algorithms::TimeWindow nwin); - /*! \brief Load all needed data and process. + This is the primary constructor for this object. The PfStyleMetadata + object is expected to contain a suite of parameters used to define + how this operator will behave along with what inverse filter is to + be constructed ans applied before the iterative algorithm is applied. + The pf is required to have multiple Arr sections the components. */ + GeneralIterDecon(mspass::utility::AntelopePf &md); + GeneralIterDecon(const GeneralIterDecon &parent); + void changeparameter(const mspass::utility::Metadata &md) { + this->preprocessor->changeparameter(md); + }; + int load(const mspass::seismic::CoreSeismogram &d, + mspass::algorithms::TimeWindow dwin); + int loadnoise(const mspass::seismic::CoreSeismogram &d, + mspass::algorithms::TimeWindow nwin); + /*! \brief Load all needed data and process. - This method is little more than a call to loadnoise followed - immediately by a call to load that is assumed to initiate the - computation. */ - int load(const mspass::seismic::CoreSeismogram &d, mspass::algorithms::TimeWindow dwin, mspass::algorithms::TimeWindow nwin); - void process(); - ~GeneralIterDecon(); - mspass::seismic::CoreSeismogram getresult(); - mspass::seismic::CoreTimeSeries ideal_output() { - return this->preprocessor->ideal_output(); - }; - mspass::seismic::CoreTimeSeries actual_output() { - return this->preprocessor->actual_output(); - }; - mspass::seismic::CoreTimeSeries inverse_wavelet() { - return this->preprocessor->inverse_wavelet(); - }; - mspass::seismic::CoreTimeSeries inverse_wavelet(double t0parent) { - return this->preprocessor->inverse_wavelet(t0parent); - }; - mspass::utility::Metadata QCMetrics(); -private: - /* These are data at different stages of process. d_all is the - largest signal window that is assumed to have been initialized by the - load method for this object. d_decon is the - result of apply the preprocessor (signal processing) deconvolution to - all three components. d_decon is computed from d, which is a windowed - version of the input data received by the load method. It have a - time duration less than or at least equal to that of d_all. - r is the residual, which is accumulated during the iterative method. - The time duraction of r is the same as d. It is initalized by - convolving the inverse filter with d_all. - n is the noise data. It should normally be at least as long a d_all*/ - mspass::seismic::CoreSeismogram d_all,d_decon,r,n; - /* We save the set of data lengths for clarity and a minor bit efficiency. - ndwin is the number of samples in d_all and r. - nnwin is the number of samples in n. - nfft is the size of d_decon */ - int ndwin, nnwin,nfft; - /* this is the time shift in sample applied by preprocessor*/ - int time_shift; - /* Save the TimeWindow objects hat define the extent of d_all, d_decon, - and n. Some things need at least some of these downstream */ - mspass::algorithms::TimeWindow dwin, nwin, fftwin; - /*! For preprocessor algorithms that are scalare we specify which channel - is used to define the noise for regularization */ - int noise_component; - /*! The algorithm accumulates spikes in this linked list. To create - the result as a normal time series this is converted to a conventional - form in the getresult method */ - std::list spikes; - /*! Penalty function weights. + This method is little more than a call to loadnoise followed + immediately by a call to load that is assumed to initiate the + computation. */ + int load(const mspass::seismic::CoreSeismogram &d, + mspass::algorithms::TimeWindow dwin, + mspass::algorithms::TimeWindow nwin); + void process(); + ~GeneralIterDecon(); + mspass::seismic::CoreSeismogram getresult(); + mspass::seismic::CoreTimeSeries ideal_output() { + return this->preprocessor->ideal_output(); + }; + mspass::seismic::CoreTimeSeries actual_output() { + return this->preprocessor->actual_output(); + }; + mspass::seismic::CoreTimeSeries inverse_wavelet() { + return this->preprocessor->inverse_wavelet(); + }; + mspass::seismic::CoreTimeSeries inverse_wavelet(double t0parent) { + return this->preprocessor->inverse_wavelet(t0parent); + }; + mspass::utility::Metadata QCMetrics(); - This algorithm uses a penalty function that downweights a range of lags - around the time of each spike subtracted in the iteration. This stl - vector contains the accumulated weighting function at the end of the - iteration. It is used for QC */ - std::vector lag_weights; - /* This vector contains the function time shifted and added to lag_weights - vector after each iteration. */ - std::vector wtf; - int nwtf; //size of wtf - cached because wtf is inside the deepest loop +private: + /* These are data at different stages of process. d_all is the + largest signal window that is assumed to have been initialized by the + load method for this object. d_decon is the + result of apply the preprocessor (signal processing) deconvolution to + all three components. d_decon is computed from d, which is a windowed + version of the input data received by the load method. It have a + time duration less than or at least equal to that of d_all. + r is the residual, which is accumulated during the iterative method. + The time duraction of r is the same as d. It is initalized by + convolving the inverse filter with d_all. + n is the noise data. It should normally be at least as long a d_all*/ + mspass::seismic::CoreSeismogram d_all, d_decon, r, n; + /* We save the set of data lengths for clarity and a minor bit efficiency. + ndwin is the number of samples in d_all and r. + nnwin is the number of samples in n. + nfft is the size of d_decon */ + int ndwin, nnwin, nfft; + /* this is the time shift in sample applied by preprocessor*/ + int time_shift; + /* Save the TimeWindow objects hat define the extent of d_all, d_decon, + and n. Some things need at least some of these downstream */ + mspass::algorithms::TimeWindow dwin, nwin, fftwin; + /*! For preprocessor algorithms that are scalare we specify which channel + is used to define the noise for regularization */ + int noise_component; + /*! The algorithm accumulates spikes in this linked list. To create + the result as a normal time series this is converted to a conventional + form in the getresult method */ + std::list spikes; + /*! Penalty function weights. - /* This is a pointer to the BasicDeconOperator class used for preprocessing - Classic use of inheritance to simplify the api. */ - ScalarDecon *preprocessor; + This algorithm uses a penalty function that downweights a range of lags + around the time of each spike subtracted in the iteration. This stl + vector contains the accumulated weighting function at the end of the + iteration. It is used for QC */ + std::vector lag_weights; + /* This vector contains the function time shifted and added to lag_weights + vector after each iteration. */ + std::vector wtf; + int nwtf; // size of wtf - cached because wtf is inside the deepest loop - /* This defines a shaping wavelet applied to output. The inverse filter - preprocessing algorithm is assumed to have it's own embedded shaping wavelet. - i.e. this is the wavelet convolved with the output spike sequence. */ - ShapingWavelet shapingwavelet; - /* This parameter is set in the constructor. It would normally be half the length - of the fir representation of the inverse wavelet. (winv_fir below)*/ - int wavelet_pad; - /*! \brief Shorted inverse wavelet used for iteration. + /* This is a pointer to the BasicDeconOperator class used for preprocessing + Classic use of inheritance to simplify the api. */ + ScalarDecon *preprocessor; - The iteration is done here in the time domain. The wavelet returned by - the preprocessor algorithm will commonly have lots of zeros or small numbers - outside a central area. The constructor to needs to define how that - long wavelet is shortened to build this one. winv_fir is the inverse - wavelet vector and actual_o_fir is the ideal output fir filter vector. - */ - std::vector winv_fir, actual_o_fir; - int actual_o_0; //offset from sample zero for zero lag position - IterDeconType decon_type; - /* This is called by the constructor to create the wtf penalty function */ - void construct_weight_penalty_function(const mspass::utility::Metadata& md); - /*! Subtract current spike signal from data. + /* This defines a shaping wavelet applied to output. The inverse filter + preprocessing algorithm is assumed to have it's own embedded shaping wavelet. + i.e. this is the wavelet convolved with the output spike sequence. */ + ShapingWavelet shapingwavelet; + /* This parameter is set in the constructor. It would normally be half the + length of the fir representation of the inverse wavelet. (winv_fir below)*/ + int wavelet_pad; + /*! \brief Shorted inverse wavelet used for iteration. - \param spk - vector amplitude and lag of spike - subtract ideal - output at this lag. - */ - void update_residual_matrix(ThreeCSpike spk); - /*! Updates the lag weight vector. + The iteration is done here in the time domain. The wavelet returned by + the preprocessor algorithm will commonly have lots of zeros or small numbers + outside a central area. The constructor to needs to define how that + long wavelet is shortened to build this one. winv_fir is the inverse + wavelet vector and actual_o_fir is the ideal output fir filter vector. + */ + std::vector winv_fir, actual_o_fir; + int actual_o_0; // offset from sample zero for zero lag position + IterDeconType decon_type; + /* This is called by the constructor to create the wtf penalty function */ + void construct_weight_penalty_function(const mspass::utility::Metadata &md); + /*! Subtract current spike signal from data. - This implementation has an enhanced time weighting function - beyond that given in Wang and Pavlis's paper. This private method - updates the weight vector using the lag position (in samples) of the - current spike. */ - void update_lag_weights(int col); - /*! This private method is called after load noise to se the quantity - resid_linf_floor = convergence criteria on amplitude. That paramters is - computed from sorting the filtered, preevent noise and setting the - threshold from a probability level. Returns the computed floor but - also sets resid_linf_floor in this base object */ - double compute_resid_linf_floor(); - /*! Apply all convergence tests. Returns false to terminate the iteration - loop. This puts all such calculations in one place. */ - bool has_not_converged(); - /* These are convergence attributes. lw_inf indicates Linf norm of - lag_weight array, lw_l2 is L2 metric of lag_weight, resid_inf is Linf - norm of residual vector, and resid_l2 is L2 of resid matrix. prev - modifier means the size of that quantity in the previous iteration. initial - means initial value at the top of the loop.*/ - double lw_linf_initial, lw_linf_prev; - double lw_l2_initial, lw_l2_prev; - double resid_linf_initial, resid_linf_prev; - double resid_l2_initial, resid_l2_prev; - /* These are convergence paramwters for the different tests */ - int iter_count, iter_max; // actual iteration count and ceiling to break loop - /*lw metrics are scaled with range of 0 to 1. l2 gets scaled by number of - points and so can use a similar absolute scale. */ - double lw_linf_floor, lw_l2_floor; - /* We use a probability level to define the floor Linf of the residual - matrix. For L2 we use the conventional fractional improvment metric. */ - double resid_linf_prob, resid_linf_floor; - double resid_l2_tol; + \param spk - vector amplitude and lag of spike - subtract ideal + output at this lag. + */ + void update_residual_matrix(ThreeCSpike spk); + /*! Updates the lag weight vector. + This implementation has an enhanced time weighting function + beyond that given in Wang and Pavlis's paper. This private method + updates the weight vector using the lag position (in samples) of the + current spike. */ + void update_lag_weights(int col); + /*! This private method is called after load noise to se the quantity + resid_linf_floor = convergence criteria on amplitude. That paramters is + computed from sorting the filtered, preevent noise and setting the + threshold from a probability level. Returns the computed floor but + also sets resid_linf_floor in this base object */ + double compute_resid_linf_floor(); + /*! Apply all convergence tests. Returns false to terminate the iteration + loop. This puts all such calculations in one place. */ + bool has_not_converged(); + /* These are convergence attributes. lw_inf indicates Linf norm of + lag_weight array, lw_l2 is L2 metric of lag_weight, resid_inf is Linf + norm of residual vector, and resid_l2 is L2 of resid matrix. prev + modifier means the size of that quantity in the previous iteration. initial + means initial value at the top of the loop.*/ + double lw_linf_initial, lw_linf_prev; + double lw_l2_initial, lw_l2_prev; + double resid_linf_initial, resid_linf_prev; + double resid_l2_initial, resid_l2_prev; + /* These are convergence paramwters for the different tests */ + int iter_count, iter_max; // actual iteration count and ceiling to break loop + /*lw metrics are scaled with range of 0 to 1. l2 gets scaled by number of + points and so can use a similar absolute scale. */ + double lw_linf_floor, lw_l2_floor; + /* We use a probability level to define the floor Linf of the residual + matrix. For L2 we use the conventional fractional improvment metric. */ + double resid_linf_prob, resid_linf_floor; + double resid_l2_tol; - /* DEBUG attributes and methods. These should be deleted after testing. */ - std::vector lw_linf_history,lw_l2_history,resid_l2_history,resid_linf_history; - void print_convergence_history(std::ostream& ofs) - { - ofs << "lw_inf lw_l2 resid_l2, resid_linf"< lw_linf_history, lw_l2_history, resid_l2_history, + resid_linf_history; + void print_convergence_history(std::ostream &ofs) { + ofs << "lw_inf lw_l2 resid_l2, resid_linf" << std::endl; + for (int i = 0; i < iter_count; ++i) { + if (i < lw_linf_history.size()) { + ofs << lw_linf_history[i] << " "; + } else { + ofs << "xxxx "; + } + if (i < lw_l2_history.size()) { + ofs << lw_l2_history[i] << " "; + } else { + ofs << "xxxx "; + } + if (i < resid_linf_history.size()) { + ofs << resid_linf_history[i] << " "; + } else { + ofs << "xxxx "; + } + if (i < resid_l2_history.size()) { + ofs << resid_l2_history[i] << " "; + } else { + ofs << "xxxx "; + } + ofs << std::endl; } + } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/LeastSquareDecon.h b/cxx/include/mspass/algorithms/deconvolution/LeastSquareDecon.h index d731ecd4d..5368eda9f 100644 --- a/cxx/include/mspass/algorithms/deconvolution/LeastSquareDecon.h +++ b/cxx/include/mspass/algorithms/deconvolution/LeastSquareDecon.h @@ -1,84 +1,81 @@ #ifndef __SIMPLE_LEAST_SQUARE_DECON_H__ #define __SIMPLE_LEAST_SQUARE_DECON_H__ -#include -#include -#include -#include -#include "mspass/utility/Metadata.h" -#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/ShapingWavelet.h" #include "mspass/seismic/CoreTimeSeries.h" -namespace mspass::algorithms::deconvolution{ -class LeastSquareDecon: public FFTDeconOperator, public ScalarDecon -{ +#include "mspass/utility/Metadata.h" +#include +#include +#include +#include +namespace mspass::algorithms::deconvolution { +class LeastSquareDecon : public FFTDeconOperator, public ScalarDecon { public: - LeastSquareDecon() : FFTDeconOperator(),ScalarDecon() - { - damp=1.0; - }; - LeastSquareDecon(const LeastSquareDecon &parent); - LeastSquareDecon(const mspass::utility::Metadata &md); - LeastSquareDecon(const mspass::utility::Metadata &md,const std::vector &wavelet, - const std::vector &data); - void changeparameter(const mspass::utility::Metadata &md); - void process(); - /*! \brief Return the actual output of the deconvolution operator. + LeastSquareDecon() : FFTDeconOperator(), ScalarDecon() { damp = 1.0; }; + LeastSquareDecon(const LeastSquareDecon &parent); + LeastSquareDecon(const mspass::utility::Metadata &md); + LeastSquareDecon(const mspass::utility::Metadata &md, + const std::vector &wavelet, + const std::vector &data); + void changeparameter(const mspass::utility::Metadata &md); + void process(); + /*! \brief Return the actual output of the deconvolution operator. + + The actual output is defined as w^-1*w and is compable to resolution + kernels in linear inverse theory. Although not required we would + normally expect this function to be peaked at 0. Offsets from 0 + would imply a bias. */ + mspass::seismic::CoreTimeSeries actual_output(); + /*! \brief Return a FIR respresentation of the inverse filter. - The actual output is defined as w^-1*w and is compable to resolution - kernels in linear inverse theory. Although not required we would - normally expect this function to be peaked at 0. Offsets from 0 - would imply a bias. */ - mspass::seismic::CoreTimeSeries actual_output(); - /*! \brief Return a FIR respresentation of the inverse filter. + An inverse filter has an impulse response. For some wavelets this + can be respresented by a FIR filter with finite numbers of coefficients. + Since this is a Fourier method the best we can do is return the inverse + fft of the regularized operator. The output usually needs to be + phase shifted to be most useful. For typical seismic source wavelets + that are approximately minimum phase the shift can be small, but for + zero phase input it should be approximately half the window size. + This method also has an optional argument for t0parent. Because + this processor was written to be agnostic about a time standard + it implicitly assumes time 0 is sample 0 of the input waveforms. + If the original data have a nonzero start time this should be + passed as t0parent or the output will contain a time shift of t0parent. + Note that tshift and t0parent do very different things. tshift + is used to apply circular phase shift to the output (e.g. a shift + of 10 samples causes the last 10 samples in the wavelet to be wrapped + to the first 10 samples). t0parent only changes the time standard + so the output has t0 -= parent.t0. - An inverse filter has an impulse response. For some wavelets this - can be respresented by a FIR filter with finite numbers of coefficients. - Since this is a Fourier method the best we can do is return the inverse - fft of the regularized operator. The output usually needs to be - phase shifted to be most useful. For typical seismic source wavelets - that are approximately minimum phase the shift can be small, but for - zero phase input it should be approximately half the window size. - This method also has an optional argument for t0parent. Because - this processor was written to be agnostic about a time standard - it implicitly assumes time 0 is sample 0 of the input waveforms. - If the original data have a nonzero start time this should be - passed as t0parent or the output will contain a time shift of t0parent. - Note that tshift and t0parent do very different things. tshift - is used to apply circular phase shift to the output (e.g. a shift - of 10 samples causes the last 10 samples in the wavelet to be wrapped - to the first 10 samples). t0parent only changes the time standard - so the output has t0 -= parent.t0. + The returned wavelet is always time shifted by nfft/2. - The returned wavelet is always time shifted by nfft/2. + \param t0parent - time zero of parent seismograms (see above). - \param t0parent - time zero of parent seismograms (see above). + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent = 0.0); + /*! \brief Return default FIR represesentation of the inverse filter. - */ - mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent=0.0); - /*! \brief Return default FIR represesentation of the inverse filter. + This is an overloaded version of the parameterized method. It is + equivalent to this->inverse_wavelet(0.0,0.0); + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(); + /*! \brief Return appropriate quality measures. - This is an overloaded version of the parameterized method. It is - equivalent to this->inverse_wavelet(0.0,0.0); - */ - mspass::seismic::CoreTimeSeries inverse_wavelet() ; - /*! \brief Return appropriate quality measures. + Each operator commonly has different was to measure the quality of the + result. This method should return these in a generic Metadata object. */ + mspass::utility::Metadata QCMetrics(); - Each operator commonly has different was to measure the quality of the - result. This method should return these in a generic Metadata object. */ - mspass::utility::Metadata QCMetrics(); private: - int read_metadata(const mspass::utility::Metadata &md); - int apply(); - double damp; - friend boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & boost::serialization::base_object(*this); - ar & damp; - } + int read_metadata(const mspass::utility::Metadata &md); + int apply(); + double damp; + friend boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); + ar &boost::serialization::base_object(*this); + ar & damp; + } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h b/cxx/include/mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h index 6a75d1019..554f43221 100644 --- a/cxx/include/mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h +++ b/cxx/include/mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h @@ -1,17 +1,17 @@ #ifndef __MTPOWERSPECTRUM_ENGINE_H__ -#define __MTPOWERSPECTRUM_ENGINE_H__ +#define __MTPOWERSPECTRUM_ENGINE_H__ -#include -#include -#include -#include -#include -#include +#include "mspass/seismic/PowerSpectrum.h" #include "mspass/seismic/TimeSeries.h" #include "mspass/utility/dmatrix.h" -#include "mspass/seismic/PowerSpectrum.h" +#include +#include +#include +#include +#include +#include -namespace mspass::algorithms::deconvolution{ +namespace mspass::algorithms::deconvolution { /*! \brief Multittaper power spectral estimator. The multitaper method uses averages of spectra windowed by Slepian functions. @@ -26,10 +26,10 @@ This class uses the apply model for processing. It accepts raw vector or TimeSeries data. The former assumes the sample interval is 1 while the second scales the spectrum to have units of 1/Hz. */ -class MTPowerSpectrumEngine -{ +class MTPowerSpectrumEngine { public: - /*! Default constructor. Do not use as it produces a null object that is no functional.*/ + /*! Default constructor. Do not use as it produces a null object that is no + * functional.*/ MTPowerSpectrumEngine(); /*! \brief construct with full definition. @@ -48,14 +48,14 @@ class MTPowerSpectrumEngine to compute frequency bin size from fft length. */ MTPowerSpectrumEngine(const int winsize, const double tbp, const int ntapers, - const int nfftin=-1,const double dtin=1.0); + const int nfftin = -1, const double dtin = 1.0); /*! Standard copy constructor*/ - MTPowerSpectrumEngine(const MTPowerSpectrumEngine& parent); + MTPowerSpectrumEngine(const MTPowerSpectrumEngine &parent); /*! Destructor. Not trivial as it has to delete the fft workspace and cached tapers. */ ~MTPowerSpectrumEngine(); /*! Standard assignment operator. */ - MTPowerSpectrumEngine& operator=(const MTPowerSpectrumEngine& parent); + MTPowerSpectrumEngine &operator=(const MTPowerSpectrumEngine &parent); /*! \process a TimeSeries. This is one of two methods for applying the multiaper algorithm to data. @@ -70,7 +70,7 @@ class MTPowerSpectrumEngine \param parent is the data to process \return vector containing estimated power spwecrum */ - mspass::seismic::PowerSpectrum apply(const mspass::seismic::TimeSeries& d); + mspass::seismic::PowerSpectrum apply(const mspass::seismic::TimeSeries &d); /*! \brief Low level processing of vector of data. This is lower level function that processes a raw vector of data. Since @@ -81,37 +81,29 @@ class MTPowerSpectrumEngine returns power spectral density assuming a sample rate of 1. i.e. it scales to correct for the gsl fft scaling by of the forward transform by N. - \param d is the vector of data to process. d.size() must this->taperlen() value. + \param d is the vector of data to process. d.size() must this->taperlen() + value. \return vector containing estimated power spectrum (usual convention with 0 containing 0 frequency value) \exception throw a MsPASSError if the size of d does not match operator length */ - std::vector apply(const std::vector& d); + std::vector apply(const std::vector &d); /*! Return the frquency bin size defined for this operator. */ - double df() const {return deltaf;}; + double df() const { return deltaf; }; /*! Return and std::vector of all frequencies for spectral estimates this operator computes. */ std::vector frequencies(); /*! Retrieve the taper length.*/ - int taper_length() const - { - return taperlen; - }; + int taper_length() const { return taperlen; }; /*! Retrieve time-bandwidth product.*/ - double time_bandwidth_product() const - { - return tbp; - }; + double time_bandwidth_product() const { return tbp; }; /*! Return number of tapers used by this engine. */ - int number_tapers() const - { - return ntapers; - }; + int number_tapers() const { return ntapers; }; /*! Return size of fft used by this operator - usually not the same as taper length.*/ - int fftsize() const {return nfft;}; + int fftsize() const { return nfft; }; /*! Retrieve the internally cached required data sample interval. */ - double dt(){return operator_dt;}; + double dt() { return operator_dt; }; /*! \brief Putter equivalent of df. The computation of the Rayleigh bin size is complicated a bit by the folding @@ -131,17 +123,16 @@ class MTPowerSpectrumEngine \return computed df */ - double set_df(double dt) - { + double set_df(double dt) { this->operator_dt = dt; int this_nf = this->nf(); - double fny = 1.0/(2.0*dt); - this->deltaf = fny/static_cast(this_nf-1); + double fny = 1.0 / (2.0 * dt); + this->deltaf = fny / static_cast(this_nf - 1); return deltaf; }; - /*! Return tne number of frequency bins in estimates the operator will compute. */ - int nf() - { + /*! Return tne number of frequency bins in estimates the operator will + * compute. */ + int nf() { /* this simple formula depends upon integer truncation when used with nfft as an odd number. For reference, this is what prieto uses in the python multitaper package: @@ -151,8 +142,9 @@ class MTPowerSpectrumEngine nf = int((nfft+1)/2) they will yield the same result but this is simpler and faster */ - return (this->nfft)/2 + 1; + return (this->nfft) / 2 + 1; }; + private: int taperlen; int ntapers; @@ -165,20 +157,17 @@ class MTPowerSpectrumEngine gsl_fft_complex_wavetable *wavetable; gsl_fft_complex_workspace *workspace; friend boost::serialization::access; - template - void save(Archive& ar, const unsigned int version) const - { - ar & taperlen; - ar & ntapers; - ar & nfft; - ar & tbp; - ar & operator_dt; - ar & tapers; - ar & deltaf; + template + void save(Archive &ar, const unsigned int version) const { + ar & taperlen; + ar & ntapers; + ar & nfft; + ar & tbp; + ar & operator_dt; + ar & tapers; + ar & deltaf; } - template - void load(Archive &ar, const unsigned int version) - { + template void load(Archive &ar, const unsigned int version) { ar & taperlen; ar & ntapers; ar & nfft; @@ -186,10 +175,10 @@ class MTPowerSpectrumEngine ar & operator_dt; ar & tapers; ar & deltaf; - this->wavetable = gsl_fft_complex_wavetable_alloc (this->nfft); - this->workspace = gsl_fft_complex_workspace_alloc (this->nfft); + this->wavetable = gsl_fft_complex_wavetable_alloc(this->nfft); + this->workspace = gsl_fft_complex_workspace_alloc(this->nfft); } BOOST_SERIALIZATION_SPLIT_MEMBER() }; -} //namespace ed +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/MultiTaperSpecDivDecon.h b/cxx/include/mspass/algorithms/deconvolution/MultiTaperSpecDivDecon.h index e5cf92d5a..5a0b744dd 100644 --- a/cxx/include/mspass/algorithms/deconvolution/MultiTaperSpecDivDecon.h +++ b/cxx/include/mspass/algorithms/deconvolution/MultiTaperSpecDivDecon.h @@ -1,151 +1,149 @@ #ifndef __PAVLIS_MULTITAPER_DECON_H__ #define __PAVLIS_MULTITAPER_DECON_H__ -#include -#include -#include -#include -#include -#include "mspass/utility/Metadata.h" -#include "mspass/utility/dmatrix.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" -#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/ShapingWavelet.h" #include "mspass/seismic/CoreTimeSeries.h" -namespace mspass::algorithms::deconvolution{ -class MultiTaperSpecDivDecon: public FFTDeconOperator, public ScalarDecon -{ +#include "mspass/utility/Metadata.h" +#include "mspass/utility/dmatrix.h" +#include +#include +#include +#include +#include +namespace mspass::algorithms::deconvolution { +class MultiTaperSpecDivDecon : public FFTDeconOperator, public ScalarDecon { public: - /*! Default constructor. Do not use - only for declarations */ - MultiTaperSpecDivDecon() : FFTDeconOperator(),ScalarDecon(){}; - MultiTaperSpecDivDecon(const mspass::utility::Metadata &md,const std::vector &noise, - const std::vector &wavelet,const std::vector &data); - MultiTaperSpecDivDecon(const mspass::utility::Metadata &md); - MultiTaperSpecDivDecon(const MultiTaperSpecDivDecon &parent); - ~MultiTaperSpecDivDecon() {}; - void changeparameter(const mspass::utility::Metadata &md) { - this->read_metadata(md,true); - }; - /*! \brief Load a section of preevent noise. + /*! Default constructor. Do not use - only for declarations */ + MultiTaperSpecDivDecon() : FFTDeconOperator(), ScalarDecon() {}; + MultiTaperSpecDivDecon(const mspass::utility::Metadata &md, + const std::vector &noise, + const std::vector &wavelet, + const std::vector &data); + MultiTaperSpecDivDecon(const mspass::utility::Metadata &md); + MultiTaperSpecDivDecon(const MultiTaperSpecDivDecon &parent); + ~MultiTaperSpecDivDecon() {}; + void changeparameter(const mspass::utility::Metadata &md) { + this->read_metadata(md, true); + }; + /*! \brief Load a section of preevent noise. + + The multitaper algorithm requires a preevent noise it uses to compute a + frequency dependent regularization. This method loads the noise data + but does not initiate a computation. It should be called before calling + the ScalarDecon::load method which will initiate a computation of the + result. */ + int loadnoise(const std::vector &noise); + /*! \brief load all data components. - The multitaper algorithm requires a preevent noise it uses to compute a - frequency dependent regularization. This method loads the noise data - but does not initiate a computation. It should be called before calling - the ScalarDecon::load method which will initiate a computation of the - result. */ - int loadnoise(const std::vector &noise); - /*! \brief load all data components. + This method should be called immediately befor process. It loads the + wavelet and data to which teh deconvolution is to be applied. - This method should be called immediately befor process. It loads the - wavelet and data to which teh deconvolution is to be applied. + \param w is expected to contain the wavelet data + \paarm e is the data to be deconvolved with w. + \param n is the vector of noise used for regularization in this method..*/ + int load(const std::vector &w, const std::vector &d, + const std::vector &n); + void process(); + /*! \brif Return the actual output of the deconvolution operator. - \param w is expected to contain the wavelet data - \paarm e is the data to be deconvolved with w. - \param n is the vector of noise used for regularization in this method..*/ - int load(const std::vector& w, const std::vector& d, - const std::vector& n); - void process(); - /*! \brif Return the actual output of the deconvolution operator. + The actual output is defined as w^-1*w and is compable to resolution + kernels in linear inverse theory. Although not required we would + normally expect this function to be peaked at 0. Offsets from 0 + would imply a bias. */ + mspass::seismic::CoreTimeSeries actual_output(); + /*! \brief Return a FIR respresentation of the inverse filter. - The actual output is defined as w^-1*w and is compable to resolution - kernels in linear inverse theory. Although not required we would - normally expect this function to be peaked at 0. Offsets from 0 - would imply a bias. */ - mspass::seismic::CoreTimeSeries actual_output(); - /*! \brief Return a FIR respresentation of the inverse filter. + An inverse filter has an impulse response. For some wavelets this + can be respresented by a FIR filter with finite numbers of coefficients. + Since this is a Fourier method the best we can do is return the inverse + fft of the regularized operator. The output usually needs to be + phase shifted to be most useful. For typical seismic source wavelets + that are approximately minimum phase the shift can be small, but for + zero phase input it should be approximately half the window size. + This method also has an optional argument for t0parent. Because + this processor was written to be agnostic about a time standard + it implicitly assumes time 0 is sample 0 of the input waveforms. + If the original data have a nonzero start time this should be + passed as t0parent or the output will contain a time shift of t0parent. + Note that tshift and t0parent do very different things. tshift + is used to apply circular phase shift to the output (e.g. a shift + of 10 samples causes the last 10 samples in the wavelet to be wrapped + to the first 10 samples). t0parent only changes the time standard + so the output has t0 -= parent.t0. - An inverse filter has an impulse response. For some wavelets this - can be respresented by a FIR filter with finite numbers of coefficients. - Since this is a Fourier method the best we can do is return the inverse - fft of the regularized operator. The output usually needs to be - phase shifted to be most useful. For typical seismic source wavelets - that are approximately minimum phase the shift can be small, but for - zero phase input it should be approximately half the window size. - This method also has an optional argument for t0parent. Because - this processor was written to be agnostic about a time standard - it implicitly assumes time 0 is sample 0 of the input waveforms. - If the original data have a nonzero start time this should be - passed as t0parent or the output will contain a time shift of t0parent. - Note that tshift and t0parent do very different things. tshift - is used to apply circular phase shift to the output (e.g. a shift - of 10 samples causes the last 10 samples in the wavelet to be wrapped - to the first 10 samples). t0parent only changes the time standard - so the output has t0 -= parent.t0. + Output wavelet is always circular shifted with 0 lag at center. - Output wavelet is always circular shifted with 0 lag at center. + \param t0parent - time zero of parent seismograms (see above). - \param t0parent - time zero of parent seismograms (see above). + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent = 0.0); + /*! \brief Return default FIR represesentation of the inverse filter. - */ - mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent=0.0); - /*! \brief Return default FIR represesentation of the inverse filter. + This is an overloaded version of the parameterized method. It is + equivalent to this->inverse_wavelet(0.0,0.0); + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(); + /*! Return the ensemble for number of tapers inverse wavelets. */ + std::vector + all_inverse_wavelets(const double t0parent = 0.0); + /*! Return the ensemble of rf estimates */ + std::vector + all_rfestimates(const double t0parent = 0.0); + /*! Return the ensemble of actual outputs */ + std::vector + all_actual_outputs(const double t0parent = 0.0); + /*! \brief Return appropriate quality measures. - This is an overloaded version of the parameterized method. It is - equivalent to this->inverse_wavelet(0.0,0.0); - */ - mspass::seismic::CoreTimeSeries inverse_wavelet() ; - /*! Return the ensemble for number of tapers inverse wavelets. */ - std::vector all_inverse_wavelets(const double t0parent=0.0); - /*! Return the ensemble of rf estimates */ - std::vector all_rfestimates(const double t0parent=0.0); - /*! Return the ensemble of actual outputs */ - std::vector all_actual_outputs(const double t0parent=0.0); - /*! \brief Return appropriate quality measures. + Each operator commonly has different was to measure the quality of the + result. This method should return these in a generic Metadata object. */ + mspass::utility::Metadata QCMetrics(); + int get_taperlen() { return taperlen; }; + int get_number_tapers() { return nseq; }; + double get_time_bandwidth_product() { return nw; }; - Each operator commonly has different was to measure the quality of the - result. This method should return these in a generic Metadata object. */ - mspass::utility::Metadata QCMetrics(); - int get_taperlen() { - return taperlen; - }; - int get_number_tapers() { - return nseq; - }; - double get_time_bandwidth_product() { - return nw; - }; private: - std::vector noise; - double nw,damp; - int nseq; // number of tapers - unsigned int taperlen; - mspass::utility::dmatrix tapers; - /* With this algorithm we have to keep frequeny domain - * representations of the inverse for each taper. The - * xcor version of this alorithm doesn't need that because - * the averaging is different. */ - std::vector winv_taper; - /* We also cache the actual output fft because the cost is - * small compared to need to recompute it when requested. - * This is a feature added for the GID method that adds - * an inefficiency for straight application */ - std::vector ao_fft; - /* This contains the rf estimates from each of the tapers. They - are averaged to produce final result, but we keep them for the - option of bootstrap errors. */ - std::vector rfestimates; - /* Private methods */ - /* Returns a tapered data in container of ComplexArray objects*/ - std::vector taper_data(const std::vector& signal); - /*! Private method called by constructors to load parameters. */ - int read_metadata(const mspass::utility::Metadata &md,bool refresh); - int apply(); - friend boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & boost::serialization::base_object(*this); - ar & noise; - ar & nw; - ar & damp; - ar & nseq; - ar & taperlen; - ar & tapers; - ar & winv_taper; - ar & ao_fft; - ar & rfestimates; - } + std::vector noise; + double nw, damp; + int nseq; // number of tapers + unsigned int taperlen; + mspass::utility::dmatrix tapers; + /* With this algorithm we have to keep frequeny domain + * representations of the inverse for each taper. The + * xcor version of this alorithm doesn't need that because + * the averaging is different. */ + std::vector winv_taper; + /* We also cache the actual output fft because the cost is + * small compared to need to recompute it when requested. + * This is a feature added for the GID method that adds + * an inefficiency for straight application */ + std::vector ao_fft; + /* This contains the rf estimates from each of the tapers. They + are averaged to produce final result, but we keep them for the + option of bootstrap errors. */ + std::vector rfestimates; + /* Private methods */ + /* Returns a tapered data in container of ComplexArray objects*/ + std::vector taper_data(const std::vector &signal); + /*! Private method called by constructors to load parameters. */ + int read_metadata(const mspass::utility::Metadata &md, bool refresh); + int apply(); + friend boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); + ar &boost::serialization::base_object(*this); + ar & noise; + ar & nw; + ar & damp; + ar & nseq; + ar & taperlen; + ar & tapers; + ar & winv_taper; + ar & ao_fft; + ar & rfestimates; + } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/MultiTaperXcorDecon.h b/cxx/include/mspass/algorithms/deconvolution/MultiTaperXcorDecon.h index 9247da3e1..59cfc9dd2 100644 --- a/cxx/include/mspass/algorithms/deconvolution/MultiTaperXcorDecon.h +++ b/cxx/include/mspass/algorithms/deconvolution/MultiTaperXcorDecon.h @@ -1,133 +1,128 @@ #ifndef __SIMPLE_MULTITAPER_DECON_H__ #define __SIMPLE_MULTITAPER_DECON_H__ -#include +#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/ScalarDecon.h" +#include "mspass/algorithms/deconvolution/ShapingWavelet.h" +#include "mspass/seismic/CoreTimeSeries.h" +#include "mspass/utility/Metadata.h" +#include "mspass/utility/dmatrix.h" #include #include #include #include -#include "mspass/utility/Metadata.h" -#include "mspass/utility/dmatrix.h" -#include "mspass/seismic/CoreTimeSeries.h" -#include "mspass/algorithms/deconvolution/ScalarDecon.h" -#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" -#include "mspass/algorithms/deconvolution/ShapingWavelet.h" -namespace mspass::algorithms::deconvolution{ -class MultiTaperXcorDecon: public FFTDeconOperator, public ScalarDecon -{ +#include +namespace mspass::algorithms::deconvolution { +class MultiTaperXcorDecon : public FFTDeconOperator, public ScalarDecon { public: - /*! Default constructor. Do not use - only for declarations */ - MultiTaperXcorDecon() : FFTDeconOperator(), ScalarDecon(){}; - MultiTaperXcorDecon(const mspass::utility::Metadata &md,const std::vector &noise, - const std::vector &wavelet,const std::vector &data); - MultiTaperXcorDecon(const mspass::utility::Metadata &md); - MultiTaperXcorDecon(const MultiTaperXcorDecon &parent); - ~MultiTaperXcorDecon() {}; - void changeparameter(const mspass::utility::Metadata &md) { - this->read_metadata(md,true); - }; - /*! \brief Load a section of preevent noise. + /*! Default constructor. Do not use - only for declarations */ + MultiTaperXcorDecon() : FFTDeconOperator(), ScalarDecon() {}; + MultiTaperXcorDecon(const mspass::utility::Metadata &md, + const std::vector &noise, + const std::vector &wavelet, + const std::vector &data); + MultiTaperXcorDecon(const mspass::utility::Metadata &md); + MultiTaperXcorDecon(const MultiTaperXcorDecon &parent); + ~MultiTaperXcorDecon() {}; + void changeparameter(const mspass::utility::Metadata &md) { + this->read_metadata(md, true); + }; + /*! \brief Load a section of preevent noise. + + The multitaper algorithm requires a preevent noise it uses to compute a + frequency dependent regularization. This method loads the noise data + but does not initiate a computation. It should be called before calling + the ScalarDecon::load method which will initiate a computation of the + result. */ + int loadnoise(const std::vector &noise); + /*! \brief load all data components. - The multitaper algorithm requires a preevent noise it uses to compute a - frequency dependent regularization. This method loads the noise data - but does not initiate a computation. It should be called before calling - the ScalarDecon::load method which will initiate a computation of the - result. */ - int loadnoise(const std::vector &noise); - /*! \brief load all data components. + This method should be called immediately befor process. It loads the + wavelet and data to which teh deconvolution is to be applied. - This method should be called immediately befor process. It loads the - wavelet and data to which teh deconvolution is to be applied. + \param w is expected to contain the wavelet data + \paarm e is the data to be deconvolved with w. + \param n is the vector of noise used for regularization in this method..*/ + int load(const std::vector &w, const std::vector &d, + const std::vector &n); + void process(); + /*! \brif Return the actual output of the deconvolution operator. - \param w is expected to contain the wavelet data - \paarm e is the data to be deconvolved with w. - \param n is the vector of noise used for regularization in this method..*/ - int load(const std::vector& w, const std::vector& d, - const std::vector& n); - void process(); - /*! \brif Return the actual output of the deconvolution operator. + The actual output is defined as w^-1*w and is compable to resolution + kernels in linear inverse theory. Although not required we would + normally expect this function to be peaked at 0. Offsets from 0 + would imply a bias. */ + mspass::seismic::CoreTimeSeries actual_output(); + /*! \brief Return a FIR respresentation of the inverse filter. - The actual output is defined as w^-1*w and is compable to resolution - kernels in linear inverse theory. Although not required we would - normally expect this function to be peaked at 0. Offsets from 0 - would imply a bias. */ - mspass::seismic::CoreTimeSeries actual_output(); - /*! \brief Return a FIR respresentation of the inverse filter. + An inverse filter has an impulse response. For some wavelets this + can be respresented by a FIR filter with finite numbers of coefficients. + Since this is a Fourier method the best we can do is return the inverse + fft of the regularized operator. The output usually needs to be + phase shifted to be most useful. For typical seismic source wavelets + that are approximately minimum phase the shift can be small, but for + zero phase input it should be approximately half the window size. + This method also has an optional argument for t0parent. Because + this processor was written to be agnostic about a time standard + it implicitly assumes time 0 is sample 0 of the input waveforms. + If the original data have a nonzero start time this should be + passed as t0parent or the output will contain a time shift of t0parent. + Note that tshift and t0parent do very different things. tshift + is used to apply circular phase shift to the output (e.g. a shift + of 10 samples causes the last 10 samples in the wavelet to be wrapped + to the first 10 samples). t0parent only changes the time standard + so the output has t0 -= parent.t0. - An inverse filter has an impulse response. For some wavelets this - can be respresented by a FIR filter with finite numbers of coefficients. - Since this is a Fourier method the best we can do is return the inverse - fft of the regularized operator. The output usually needs to be - phase shifted to be most useful. For typical seismic source wavelets - that are approximately minimum phase the shift can be small, but for - zero phase input it should be approximately half the window size. - This method also has an optional argument for t0parent. Because - this processor was written to be agnostic about a time standard - it implicitly assumes time 0 is sample 0 of the input waveforms. - If the original data have a nonzero start time this should be - passed as t0parent or the output will contain a time shift of t0parent. - Note that tshift and t0parent do very different things. tshift - is used to apply circular phase shift to the output (e.g. a shift - of 10 samples causes the last 10 samples in the wavelet to be wrapped - to the first 10 samples). t0parent only changes the time standard - so the output has t0 -= parent.t0. + Output wavelet is always circular shifted with 0 lag at center. - Output wavelet is always circular shifted with 0 lag at center. + \param t0parent - time zero of parent seismograms (see above). - \param t0parent - time zero of parent seismograms (see above). + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent = 0.0); + /*! \brief Return default FIR represesentation of the inverse filter. - */ - mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent=0.0); - /*! \brief Return default FIR represesentation of the inverse filter. + This is an overloaded version of the parameterized method. It is + equivalent to this->inverse_wavelet(0.0,0.0); + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(); + /*! \brief Return appropriate quality measures. - This is an overloaded version of the parameterized method. It is - equivalent to this->inverse_wavelet(0.0,0.0); - */ - mspass::seismic::CoreTimeSeries inverse_wavelet() ; - /*! \brief Return appropriate quality measures. + Each operator commonly has different was to measure the quality of the + result. This method should return these in a generic Metadata object. */ + mspass::utility::Metadata QCMetrics(); + int get_taperlen() { return taperlen; }; + int get_number_tapers() { return nseq; }; + double get_time_bandwidth_product() { return nw; }; - Each operator commonly has different was to measure the quality of the - result. This method should return these in a generic Metadata object. */ - mspass::utility::Metadata QCMetrics(); - int get_taperlen() { - return taperlen; - }; - int get_number_tapers() { - return nseq; - }; - double get_time_bandwidth_product() { - return nw; - }; private: - /* noise data vector */ - std::vector noise; - double nw,damp; - int nseq; // number of tapers - unsigned int taperlen; - mspass::utility::dmatrix tapers; - /* We also cache the actual output fft because the cost is - * small compared to need to recompute it when requested. - * This is a feature added for the GID method that adds - * an inefficiency for straight application */ - ComplexArray ao_fft; - /*! Private method called by constructors to load parameters. */ - int read_metadata(const mspass::utility::Metadata &md,bool refresh); - /* Returns a tapered data in container of ComplexArray objects*/ - std::vector taper_data(const std::vector& signal); - int apply(); - friend boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & boost::serialization::base_object(*this); - ar & noise; - ar & nw; - ar & damp; - ar & nseq; - ar & taperlen; - ar & tapers; - ar & ao_fft; - } + /* noise data vector */ + std::vector noise; + double nw, damp; + int nseq; // number of tapers + unsigned int taperlen; + mspass::utility::dmatrix tapers; + /* We also cache the actual output fft because the cost is + * small compared to need to recompute it when requested. + * This is a feature added for the GID method that adds + * an inefficiency for straight application */ + ComplexArray ao_fft; + /*! Private method called by constructors to load parameters. */ + int read_metadata(const mspass::utility::Metadata &md, bool refresh); + /* Returns a tapered data in container of ComplexArray objects*/ + std::vector taper_data(const std::vector &signal); + int apply(); + friend boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); + ar &boost::serialization::base_object(*this); + ar & noise; + ar & nw; + ar & damp; + ar & nseq; + ar & taperlen; + ar & tapers; + ar & ao_fft; + } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/ScalarDecon.h b/cxx/include/mspass/algorithms/deconvolution/ScalarDecon.h index cbd756333..e48634a18 100644 --- a/cxx/include/mspass/algorithms/deconvolution/ScalarDecon.h +++ b/cxx/include/mspass/algorithms/deconvolution/ScalarDecon.h @@ -1,18 +1,19 @@ #ifndef __SIMPLE_DECON_H__ #define __SIMPLE_DECON_H__ -#include -#include "mspass/utility/Metadata.h" #include "mspass/algorithms/deconvolution/BasicDeconOperator.h" #include "mspass/algorithms/deconvolution/ShapingWavelet.h" #include "mspass/seismic/CoreTimeSeries.h" +#include "mspass/utility/Metadata.h" +#include #include #include #include -namespace mspass::algorithms::deconvolution{ -/*! \brief Base class decon operator for single station 3C decon (receiver functions). +namespace mspass::algorithms::deconvolution { +/*! \brief Base class decon operator for single station 3C decon (receiver +functions). A class of algorithms exist for computing so called receiver functions. Simple for this application means a method that is applied to a single @@ -25,92 +26,87 @@ to allow application of different conventional methods as the first step in the generalized iterative method. The generalized method can select one of the children of this base class. */ -class ScalarDecon: public BasicDeconOperator -{ +class ScalarDecon : public BasicDeconOperator { public: - ScalarDecon():shapingwavelet() {}; - ScalarDecon(const mspass::utility::Metadata& md); - ScalarDecon(const std::vector& d, const std::vector& w); - ScalarDecon(const ScalarDecon& parent); - /*! \brief Load all data required for decon. + ScalarDecon() : shapingwavelet() {}; + ScalarDecon(const mspass::utility::Metadata &md); + ScalarDecon(const std::vector &d, const std::vector &w); + ScalarDecon(const ScalarDecon &parent); + /*! \brief Load all data required for decon. + + This method loads both the data vector and wavelet estimates as + simple std::vectors. Timing must be maintained externally. This or + the pair of methods loaddata and loadwavelet must be called before process. + This method has a slight advantage in efficiency over successive calls to + loaddata and loadwavelet for several reasons, but the difference is small. - This method loads both the data vector and wavelet estimates as - simple std::vectors. Timing must be maintained externally. This or - the pair of methods loaddata and loadwavelet must be called before process. - This method has a slight advantage in efficiency over successive calls to - loaddata and loadwavelet for several reasons, but the difference is small. + \return always returns 0 + */ + int load(const std::vector &wavelet, const std::vector &data); + /*! Load only the data vector.*/ + int loaddata(const std::vector &data); + /*! Load only the wavelet estimate.*/ + int loadwavelet(const std::vector &wavelet); + virtual void process() = 0; + ~ScalarDecon() {}; + ScalarDecon &operator=(const ScalarDecon &parent); + std::vector getresult() { return result; }; + /* This method does nothing, but needs to be defined to avoid + * gcc compile errors in programs using children of this class.*/ + void changeparameter(const mspass::utility::Metadata &md); + /*! Change the shaping wavelet that will be applied to output. + * + The suite of algorithms here use the concept of a shaping wavelet + thoughout. The shaping wavelet for most applications should have + a zero phase impulse response. This method changes the + wavelet set with the operator. */ + void change_shaping_wavelet(const ShapingWavelet &nsw); + /*! getter for ShapingWavelet stored with the operator. */ + ShapingWavelet get_shaping_wavelet() const { return this->shapingwavelet; }; + /* \brief Return the ideal output of the deconvolution operator. - \return always returns 0 - */ - int load(const std::vector &wavelet,const std::vector &data); - /*! Load only the data vector.*/ - int loaddata(const std::vector &data); - /*! Load only the wavelet estimate.*/ - int loadwavelet(const std::vector &wavelet); - virtual void process()=0; - ~ScalarDecon() {}; - ScalarDecon& operator=(const ScalarDecon& parent); - std::vector getresult() { - return result; - }; - /* This method does nothing, but needs to be defined to avoid - * gcc compile errors in programs using children of this class.*/ - void changeparameter(const mspass::utility::Metadata &md); - /*! Change the shaping wavelet that will be applied to output. - * - The suite of algorithms here use the concept of a shaping wavelet - thoughout. The shaping wavelet for most applications should have - a zero phase impulse response. This method changes the - wavelet set with the operator. */ - void change_shaping_wavelet(const ShapingWavelet& nsw); - /*! getter for ShapingWavelet stored with the operator. */ - ShapingWavelet get_shaping_wavelet() const - { - return this->shapingwavelet; - }; - /* \brief Return the ideal output of the deconvolution operator. + All deconvolution operators have a implicit or explicit ideal output + signal. e.g. for a spiking Wiener filter it is a delta function with or + without a lag. For a shaping wavelt it is the time domain version of the + wavelet. */ + mspass::seismic::CoreTimeSeries ideal_output() { + return this->shapingwavelet.impulse_response(); + }; + /*! \brief Return the actual output of the deconvolution operator. - All deconvolution operators have a implicit or explicit ideal output - signal. e.g. for a spiking Wiener filter it is a delta function with or - without a lag. For a shaping wavelt it is the time domain version of the - wavelet. */ - mspass::seismic::CoreTimeSeries ideal_output() { - return this->shapingwavelet.impulse_response(); - }; - /*! \brief Return the actual output of the deconvolution operator. + The actual output is defined as w^-1*w and is compable to resolution + kernels in linear inverse theory. Although not required we would + normally expect this function to be peaked at 0. Offsets from 0 + would imply a bias. */ + virtual mspass::seismic::CoreTimeSeries actual_output() = 0; - The actual output is defined as w^-1*w and is compable to resolution - kernels in linear inverse theory. Although not required we would - normally expect this function to be peaked at 0. Offsets from 0 - would imply a bias. */ - virtual mspass::seismic::CoreTimeSeries actual_output()=0; + /*! \brief Return a FIR represention of the inverse filter. - /*! \brief Return a FIR represention of the inverse filter. + After any deconvolution is computed one can sometimes produce a finite + impulse response (FIR) respresentation of the inverse filter. */ + virtual mspass::seismic::CoreTimeSeries inverse_wavelet() = 0; + virtual mspass::seismic::CoreTimeSeries inverse_wavelet(double) = 0; + /*! \brief Return appropriate quality measures. - After any deconvolution is computed one can sometimes produce a finite - impulse response (FIR) respresentation of the inverse filter. */ - virtual mspass::seismic::CoreTimeSeries inverse_wavelet() = 0; - virtual mspass::seismic::CoreTimeSeries inverse_wavelet(double) = 0; - /*! \brief Return appropriate quality measures. + Each operator commonly has different was to measure the quality of the + result. This method should return these in a generic Metadata object. */ + virtual mspass::utility::Metadata QCMetrics() = 0; - Each operator commonly has different was to measure the quality of the - result. This method should return these in a generic Metadata object. */ - virtual mspass::utility::Metadata QCMetrics()=0; protected: - std::vector data; - std::vector wavelet; - std::vector result; - ShapingWavelet shapingwavelet; + std::vector data; + std::vector wavelet; + std::vector result; + ShapingWavelet shapingwavelet; + private: friend boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) - { - ar & data; - ar & wavelet; - ar & result; - ar & shapingwavelet; + template + void serialize(Archive &ar, const unsigned int version) { + ar & data; + ar & wavelet; + ar & result; + ar & shapingwavelet; } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/ShapingWavelet.h b/cxx/include/mspass/algorithms/deconvolution/ShapingWavelet.h index 9fe93bb1f..80e938d5d 100644 --- a/cxx/include/mspass/algorithms/deconvolution/ShapingWavelet.h +++ b/cxx/include/mspass/algorithms/deconvolution/ShapingWavelet.h @@ -1,113 +1,101 @@ #ifndef __SHAPING_WAVELET_H__ #define __SHAPING_WAVELET_H__ -#include "mspass/utility/Metadata.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" #include "mspass/seismic/CoreTimeSeries.h" +#include "mspass/utility/Metadata.h" #include #include #include -namespace mspass::algorithms::deconvolution{ +namespace mspass::algorithms::deconvolution { /*! \brief Frequency domain shaping wavelet. Frequency domain based deconvolution methods all use a shaping wavelet for output to avoid ringing. Frequency domain deconvolution methods in this Library -contain an instance of this object. In all cases it is hidden behind the interface. -A complexity, however, is that all frequency domain methods will -call the Metadata driven constructor. +contain an instance of this object. In all cases it is hidden behind the +interface. A complexity, however, is that all frequency domain methods will call +the Metadata driven constructor. This version currently allows three shaping wavelets: Gaussin, Ricker, and Slepian0. The first two are standard. The last is novel and theoretically can produce an actual output with the smalle posible sidebands*/ -class ShapingWavelet -{ +class ShapingWavelet { public: - ShapingWavelet() { - dt=-1; - df=-1; - }; - /*! \brief Construct using a limited set of analytic forms for the wavelet. - * - This constructor is used to create a ricker or gaussian shaping wavelet - with parameters defined by parameters passed through the Metadata object. + ShapingWavelet() { + dt = -1; + df = -1; + }; + /*! \brief Construct using a limited set of analytic forms for the wavelet. + * + This constructor is used to create a ricker or gaussian shaping wavelet + with parameters defined by parameters passed through the Metadata object. + + \param md - Metadata object with parameters specifying the wavelet. + \npts - length of signal to be generated which is the same as the fft + size for real valued signals. If set 0 (the default) the constructor + will attempt to get npts from md using the keyword "operator_nfft". + */ + ShapingWavelet(const mspass::utility::Metadata &md, int npts = 0); + /* \brief Use a wavelet defined by a TimeSeries object. + * + This constructor uses the data stored in a TimeSeries object to define + the shaping wavelet. Note to assure output is properly time aligned + this signal is forced to be symmetric around relative time 0. That is + necessary because of the way this, like every fft we are aware of, handles + phase. Hence, the user should assure zero time is an appropriate + zero reference (e.g. a zero phase filter should the dominant peak at 0 + time.) If the input data size is smaller than the buffer size + specified the buffer is zero padded. - \param md - Metadata object with parameters specifying the wavelet. - \npts - length of signal to be generated which is the same as the fft - size for real valued signals. If set 0 (the default) the constructor - will attempt to get npts from md using the keyword "operator_nfft". + \param w - TimeSeries specifying wavelet. Note dt will be extracted + and stored in this object. + \param nfft - buffer size = fft length for frequency domain representation + of the wavelet. If ns of d is less than nfft or the time range defined + by d is does not exceed -(nfft/2)*dt to (nfft2)*dt the result will + be sero padded before computing the fft. if nfft is 0 the d.ns will + set the fft length. */ - ShapingWavelet(const mspass::utility::Metadata& md, int npts=0); - /* \brief Use a wavelet defined by a TimeSeries object. - * - This constructor uses the data stored in a TimeSeries object to define - the shaping wavelet. Note to assure output is properly time aligned - this signal is forced to be symmetric around relative time 0. That is - necessary because of the way this, like every fft we are aware of, handles - phase. Hence, the user should assure zero time is an appropriate - zero reference (e.g. a zero phase filter should the dominant peak at 0 - time.) If the input data size is smaller than the buffer size - specified the buffer is zero padded. + ShapingWavelet(mspass::seismic::CoreTimeSeries d, int nfft = 0); + /*! Construct a Ricker wavelet shaping filter with peak frequency fpeak. */ + ShapingWavelet(const double fpeak, const double dtin, const int n); + /*! Construct a zero phase Butterworth filter wavelet. - \param w - TimeSeries specifying wavelet. Note dt will be extracted - and stored in this object. - \param nfft - buffer size = fft length for frequency domain representation - of the wavelet. If ns of d is less than nfft or the time range defined - by d is does not exceed -(nfft/2)*dt to (nfft2)*dt the result will - be sero padded before computing the fft. if nfft is 0 the d.ns will - set the fft length. - */ - ShapingWavelet(mspass::seismic::CoreTimeSeries d,int nfft=0); - /*! Construct a Ricker wavelet shaping filter with peak frequency fpeak. */ - ShapingWavelet(const double fpeak,const double dtin, const int n); - /*! Construct a zero phase Butterworth filter wavelet. + This is the recommended constructor to use for adjustable bandwidth shaping. + It is the default for CNR3CDecon. + \param npolelo is the number of poles for the low corner + \param f3dblo is the 3db point for the low corner of the passband + \param nplolehi is the number of poles for the upper corner filter + \param f3dbhi is the 3db point for the high corner of the passband. + */ + ShapingWavelet(const int npolelo, const double f3dblo, const int npolehi, + const double f3dbhi, const double dtin, const int n); + ShapingWavelet(const ShapingWavelet &parent); + ShapingWavelet &operator=(const ShapingWavelet &parent); + /*! Return a pointer to the shaping wavelet this object defines in + * the frequency domain. */ + ComplexArray *wavelet() { return &w; }; + /*! Return the impulse response of the shaping filter. Expect the + * result to be symmetric about 0 (i.e. output spans nfft/2 to nfft/2.*/ + mspass::seismic::CoreTimeSeries impulse_response(); + double freq_bin_size() { return df; }; + double sample_interval() { return dt; }; + std::string type() { return wavelet_name; }; + int size() const { return w.size(); }; - This is the recommended constructor to use for adjustable bandwidth shaping. - It is the default for CNR3CDecon. - \param npolelo is the number of poles for the low corner - \param f3dblo is the 3db point for the low corner of the passband - \param nplolehi is the number of poles for the upper corner filter - \param f3dbhi is the 3db point for the high corner of the passband. - */ - ShapingWavelet(const int npolelo, const double f3dblo, - const int npolehi, const double f3dbhi, - const double dtin, const int n); - ShapingWavelet(const ShapingWavelet& parent); - ShapingWavelet& operator=(const ShapingWavelet& parent); - /*! Return a pointer to the shaping wavelet this object defines in - * the frequency domain. */ - ComplexArray *wavelet() { - return &w; - }; - /*! Return the impulse response of the shaping filter. Expect the - * result to be symmetric about 0 (i.e. output spans nfft/2 to nfft/2.*/ - mspass::seismic::CoreTimeSeries impulse_response(); - double freq_bin_size() { - return df; - }; - double sample_interval() { - return dt; - }; - std::string type() { - return wavelet_name; - }; - int size()const{ - return w.size(); - }; private: - int nfft; - /*! Frequency domain form of the shaping wavelet. */ - ComplexArray w; - double dt,df; - std::string wavelet_name; - friend boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) - { - ar & nfft; - ar & dt; - ar & df; - ar & wavelet_name; - ar & w; - } + int nfft; + /*! Frequency domain form of the shaping wavelet. */ + ComplexArray w; + double dt, df; + std::string wavelet_name; + friend boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar & nfft; + ar & dt; + ar & df; + ar & wavelet_name; + ar & w; + } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/WaterLevelDecon.h b/cxx/include/mspass/algorithms/deconvolution/WaterLevelDecon.h index f6bc2b8bd..2a5c830cf 100644 --- a/cxx/include/mspass/algorithms/deconvolution/WaterLevelDecon.h +++ b/cxx/include/mspass/algorithms/deconvolution/WaterLevelDecon.h @@ -1,90 +1,90 @@ #ifndef __SIMPLE_WATER_LEVEL_DECON_H__ #define __SIMPLE_WATER_LEVEL_DECON_H__ -#include -#include -#include -#include -#include "mspass/utility/Metadata.h" -#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/ScalarDecon.h" #include "mspass/algorithms/deconvolution/ShapingWavelet.h" #include "mspass/seismic/CoreTimeSeries.h" -namespace mspass::algorithms::deconvolution{ -class WaterLevelDecon : public FFTDeconOperator, public ScalarDecon -{ +#include "mspass/utility/Metadata.h" +#include +#include +#include +#include +namespace mspass::algorithms::deconvolution { +class WaterLevelDecon : public FFTDeconOperator, public ScalarDecon { public: - WaterLevelDecon() : FFTDeconOperator(),ScalarDecon() - { - this->wlv=0.1; - this->regularization_fraction=0.0; - }; - WaterLevelDecon(const WaterLevelDecon &parent); - WaterLevelDecon(const mspass::utility::Metadata &md); - WaterLevelDecon(const mspass::utility::Metadata &md,const std::vector &wavelet,const std::vector &data); - void changeparameter(const mspass::utility::Metadata &md); - void process(); - /*! \brief Return the actual output of the deconvolution operator. + WaterLevelDecon() : FFTDeconOperator(), ScalarDecon() { + this->wlv = 0.1; + this->regularization_fraction = 0.0; + }; + WaterLevelDecon(const WaterLevelDecon &parent); + WaterLevelDecon(const mspass::utility::Metadata &md); + WaterLevelDecon(const mspass::utility::Metadata &md, + const std::vector &wavelet, + const std::vector &data); + void changeparameter(const mspass::utility::Metadata &md); + void process(); + /*! \brief Return the actual output of the deconvolution operator. + + The actual output is defined as w^-1*w and is compable to resolution + kernels in linear inverse theory. Although not required we would + normally expect this function to be peaked at 0. Offsets from 0 + would imply a bias. */ + mspass::seismic::CoreTimeSeries actual_output(); + /*! \brief Return a FIR respresentation of the inverse filter. - The actual output is defined as w^-1*w and is compable to resolution - kernels in linear inverse theory. Although not required we would - normally expect this function to be peaked at 0. Offsets from 0 - would imply a bias. */ - mspass::seismic::CoreTimeSeries actual_output(); - /*! \brief Return a FIR respresentation of the inverse filter. + An inverse filter has an impulse response. For some wavelets this + can be respresented by a FIR filter with finite numbers of coefficients. + Since this is a Fourier method the best we can do is return the inverse + fft of the regularized operator. The output usually needs to be + phase shifted to be most useful. For typical seismic source wavelets + that are approximately minimum phase the shift can be small, but for + zero phase input it should be approximately half the window size. + This method also has an optional argument for t0parent. Because + this processor was written to be agnostic about a time standard + it implicitly assumes time 0 is sample 0 of the input waveforms. + If the original data have a nonzero start time this should be + passed as t0parent or the output will contain a time shift of t0parent. + Note that tshift and t0parent do very different things. tshift + is used to apply circular phase shift to the output (e.g. a shift + of 10 samples causes the last 10 samples in the wavelet to be wrapped + to the first 10 samples). t0parent only changes the time standard + so the output has t0 -= parent.t0. - An inverse filter has an impulse response. For some wavelets this - can be respresented by a FIR filter with finite numbers of coefficients. - Since this is a Fourier method the best we can do is return the inverse - fft of the regularized operator. The output usually needs to be - phase shifted to be most useful. For typical seismic source wavelets - that are approximately minimum phase the shift can be small, but for - zero phase input it should be approximately half the window size. - This method also has an optional argument for t0parent. Because - this processor was written to be agnostic about a time standard - it implicitly assumes time 0 is sample 0 of the input waveforms. - If the original data have a nonzero start time this should be - passed as t0parent or the output will contain a time shift of t0parent. - Note that tshift and t0parent do very different things. tshift - is used to apply circular phase shift to the output (e.g. a shift - of 10 samples causes the last 10 samples in the wavelet to be wrapped - to the first 10 samples). t0parent only changes the time standard - so the output has t0 -= parent.t0. + Output wavelet is always circular shifted with 0 lag at center. - Output wavelet is always circular shifted with 0 lag at center. + \param t0parent - time zero of parent seismograms (see above). - \param t0parent - time zero of parent seismograms (see above). + \throw SeisppError is thrown if the tshift value is more than + half the length of the data (nfft*dt). Reason is justified above. + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent = 0.0); + /*! \brief Return default FIR represesentation of the inverse filter. - \throw SeisppError is thrown if the tshift value is more than - half the length of the data (nfft*dt). Reason is justified above. - */ - mspass::seismic::CoreTimeSeries inverse_wavelet(const double t0parent=0.0); - /*! \brief Return default FIR represesentation of the inverse filter. + This is an overloaded version of the parameterized method. It is + equivalent to this->inverse_wavelet(0.0,0.0); + */ + mspass::seismic::CoreTimeSeries inverse_wavelet(); + /*! \brief Return appropriate quality measures. - This is an overloaded version of the parameterized method. It is - equivalent to this->inverse_wavelet(0.0,0.0); - */ - mspass::seismic::CoreTimeSeries inverse_wavelet() ; - /*! \brief Return appropriate quality measures. + Each operator commonly has different was to measure the quality of the + result. This method should return these in a generic Metadata object. */ + mspass::utility::Metadata QCMetrics(); - Each operator commonly has different was to measure the quality of the - result. This method should return these in a generic Metadata object. */ - mspass::utility::Metadata QCMetrics(); private: - int read_metadata(const mspass::utility::Metadata &md); - int apply(); - double wlv; - /* QC metrics. */ - /* This is the fraction of frequencies below the water level */ - double regularization_fraction; - friend boost::serialization::access; - template - void serialize(Archive &ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & boost::serialization::base_object(*this); - ar & wlv; - ar & regularization_fraction; - } + int read_metadata(const mspass::utility::Metadata &md); + int apply(); + double wlv; + /* QC metrics. */ + /* This is the fraction of frequencies below the water level */ + double regularization_fraction; + friend boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); + ar &boost::serialization::base_object(*this); + ar & wlv; + ar & regularization_fraction; + } }; -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/common_multitaper.h b/cxx/include/mspass/algorithms/deconvolution/common_multitaper.h index 2b5845b5f..b497c2f0a 100644 --- a/cxx/include/mspass/algorithms/deconvolution/common_multitaper.h +++ b/cxx/include/mspass/algorithms/deconvolution/common_multitaper.h @@ -2,7 +2,7 @@ #ifndef _COMMON_MULTITAPER_H_ #define _COMMON_MULTITAPER_H_ #include "mspass/utility/Metadata.h" -namespace mspass::algorithms::deconvolution{ -int ComputeTaperLength(const mspass::utility::Metadata& md); +namespace mspass::algorithms::deconvolution { +int ComputeTaperLength(const mspass::utility::Metadata &md); } #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/dpss.h b/cxx/include/mspass/algorithms/deconvolution/dpss.h index ce9e52fd8..ace345c9a 100644 --- a/cxx/include/mspass/algorithms/deconvolution/dpss.h +++ b/cxx/include/mspass/algorithms/deconvolution/dpss.h @@ -2,36 +2,40 @@ #define __DPSS_H__ #include #include -namespace mspass::algorithms::deconvolution{ +namespace mspass::algorithms::deconvolution { -//Error classes for LAPACK, and a general error +// Error classes for LAPACK, and a general error class ERR { public: - ERR() {}; - ERR(const char *msg); - void getmsg(char *errmsg); - const char *getmsg(); + ERR() {}; + ERR(const char *msg); + void getmsg(char *errmsg); + const char *getmsg(); + protected: - char msg[30]; + char msg[30]; }; class LAPACK_ERROR : public ERR { public: - LAPACK_ERROR() {}; - LAPACK_ERROR(const char *errmsg); + LAPACK_ERROR() {}; + LAPACK_ERROR(const char *errmsg); }; -void compute_energy_concentrations(double *h, int n, double NW, double *lambda, int nseq); +void compute_energy_concentrations(double *h, int n, double NW, double *lambda, + int nseq); -void eig_iit(int n, double *D, double *E, int il, int iu, double *eig_val, double *eig_vec, int vec_length); +void eig_iit(int n, double *D, double *E, int il, int iu, double *eig_val, + double *eig_vec, int vec_length); -//normalizes a vector h +// normalizes a vector h void normalize_vec(double *h, int n); -//polarizes the sequences +// polarizes the sequences void polarize_dpss(double *h, int n, int iseq); -//Reduces the problem using simple even/odd splitting (exploiting double symmetry) +// Reduces the problem using simple even/odd splitting (exploiting double +// symmetry) void dpss_calc(int n, double NW, int seql, int sequ, double *h); -} +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/algorithms/deconvolution/wavelet.h b/cxx/include/mspass/algorithms/deconvolution/wavelet.h index 3e079a403..8903f281e 100644 --- a/cxx/include/mspass/algorithms/deconvolution/wavelet.h +++ b/cxx/include/mspass/algorithms/deconvolution/wavelet.h @@ -1,6 +1,6 @@ #ifndef __DECON_H__ #define __DECON_H__ -namespace mspass::algorithms::deconvolution{ +namespace mspass::algorithms::deconvolution { double *rickerwavelet(float fpeak, float dt, int n); double *gaussian(float sigma, float dt, int n); /*! \brief Creates frequency domain window as a 0th order slepian wavelet. @@ -35,6 +35,6 @@ time series length. function in the frequency domain BUT will nearly always need to undergo a circular shift to put the peak at 0. NOT done here to avoid confusion. */ -double *slepian0(double tbp,int n); -} +double *slepian0(double tbp, int n); +} // namespace mspass::algorithms::deconvolution #endif diff --git a/cxx/include/mspass/io/fileio.h b/cxx/include/mspass/io/fileio.h index 575965055..4cef89fec 100644 --- a/cxx/include/mspass/io/fileio.h +++ b/cxx/include/mspass/io/fileio.h @@ -1,10 +1,10 @@ #ifndef _MSEED_INDEX_H_ #define _MSEED_INDEX_H_ -#include -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Seismogram.h" -namespace mspass::io -{ +#include "mspass/seismic/TimeSeries.h" +#include "mspass/seismic/Ensemble.h" +#include +namespace mspass::io { /*! \brief Fast file writer for native TimeSeries save to a file. When saving data to a file system there is no standard way to do so we @@ -34,8 +34,8 @@ common io related issues. Caller should always include the call to this function in a try block. */ -long int fwrite_to_file(mspass::seismic::TimeSeries& d, - const std::string dir,const std::string dfile); +long int fwrite_to_file(mspass::seismic::TimeSeries &d, const std::string dir, + const std::string dfile); /*! \brief Fast file writer for native Seismogram save to a file. When saving data to a file system there is no standard way to do so we @@ -68,21 +68,21 @@ common io related issues. Caller should always include the call to this function in a try block. */ -long int fwrite_to_file(mspass::seismic::Seismogram& d, - const std::string dir,const std::string dfile); - +long int fwrite_to_file(mspass::seismic::Seismogram &d, const std::string dir, + const std::string dfile); + /*! \brief Fast file writer for native Ensemble save to a file. When saving data to a file system there is no standard way to do so we know of that is faster than the low level C fwrite function. This function -uses fwrite to write ONLY the sample data of all TimeSeries objects in the -input Ensemble object to a file specified by a directory (dir) -and leaf file name (dfile). It can do so because the std::vector container is -required by the standard to define a contiguous block of memory. This function -is expected to be used in MsPASS only under the hood of the python database -writer for native saves. This function works for Ensemble objects -it this form only because the dmatrix container puts all the sample data for -the 3xnpts matrix in a contiguous block of memory fetched interally with the +uses fwrite to write ONLY the sample data of all TimeSeries objects in the +input Ensemble object to a file specified by a directory (dir) +and leaf file name (dfile). It can do so because the std::vector container is +required by the standard to define a contiguous block of memory. This function +is expected to be used in MsPASS only under the hood of the python database +writer for native saves. This function works for Ensemble objects +it this form only because the dmatrix container puts all the sample data for +the 3xnpts matrix in a contiguous block of memory fetched interally with the get_address method. The function constructs a unix path file name as dir+"/"+dfile. If that @@ -104,21 +104,22 @@ common io related issues. Caller should always include the call to this function in a try block. */ -std::vector fwrite_to_file(mspass::seismic::LoggingEnsemble& d, - const std::string dir,const std::string dfile); +std::vector +fwrite_to_file(mspass::seismic::LoggingEnsemble &d, + const std::string dir, const std::string dfile); /*! \brief Fast file writer for native Ensemble save to a file. When saving data to a file system there is no standard way to do so we know of that is faster than the low level C fwrite function. This function -uses fwrite to write ONLY the sample data of all Seismogram objects in the -input Ensemble object to a file specified by a directory (dir) -and leaf file name (dfile). It can do so because the std::vector container is -required by the standard to define a contiguous block of memory. This function -is expected to be used in MsPASS only under the hood of the python database -writer for native saves. This function works for Ensemble objects -it this form only because the dmatrix container puts all the sample data for -the 3xnpts matrix in a contiguous block of memory fetched interally with the +uses fwrite to write ONLY the sample data of all Seismogram objects in the +input Ensemble object to a file specified by a directory (dir) +and leaf file name (dfile). It can do so because the std::vector container is +required by the standard to define a contiguous block of memory. This function +is expected to be used in MsPASS only under the hood of the python database +writer for native saves. This function works for Ensemble objects +it this form only because the dmatrix container puts all the sample data for +the 3xnpts matrix in a contiguous block of memory fetched interally with the get_address method. The function constructs a unix path file name as dir+"/"+dfile. If that @@ -140,8 +141,9 @@ common io related issues. Caller should always include the call to this function in a try block. */ -std::vector fwrite_to_file(mspass::seismic::LoggingEnsemble& d, - const std::string dir,const std::string dfile); +std::vector +fwrite_to_file(mspass::seismic::LoggingEnsemble &d, + const std::string dir, const std::string dfile); /*! \brief Use C fread to read sample data from a file. @@ -167,13 +169,13 @@ value as a short read will not cause an error to be thrown. The value return should be 3 * d.npts(). \exception This function may throw a MsPASSError exception if anything -goes wrong in the read process (open failure, seek fails, fread fails completely). -If that happens the data result should be killed as the sample contents are -guaranteed to be invalid. +goes wrong in the read process (open failure, seek fails, fread fails +completely). If that happens the data result should be killed as the sample +contents are guaranteed to be invalid. */ -size_t fread_from_file(mspass::seismic::Seismogram& d,const std::string dir, const std::string dfile, - const long int foff); +size_t fread_from_file(mspass::seismic::Seismogram &d, const std::string dir, + const std::string dfile, const long int foff); /*! \brief Use C fread to read sample data from a file. @@ -199,13 +201,13 @@ value as a short read will not cause an error to be thrown. The value return should be d.npts(). \exception This function may throw a MsPASSError exception if anything -goes wrong in the read process (open failure, seek fails, fread fails completely). -If that happens the data result should be killed as the sample contents are -guaranteed to be invalid. +goes wrong in the read process (open failure, seek fails, fread fails +completely). If that happens the data result should be killed as the sample +contents are guaranteed to be invalid. */ -size_t fread_from_file(mspass::seismic::TimeSeries& d,const std::string dir, const std::string dfile, - const long int foff); +size_t fread_from_file(mspass::seismic::TimeSeries &d, const std::string dir, + const std::string dfile, const long int foff); /*! \brief Use C fread to read multiple TimeSeries from a file. @@ -213,32 +215,34 @@ This low level function is used in the file based reader of mspass to speed up python readers. It is intrinsically dangerous because it assumes the data object has a preconstructed size sufficient to hold the data loaded with the low-level C fread function. -The reader assumes the input has been initialized on construction or -with set_npts to the size matching the data file contents. If there is +The reader assumes the input has been initialized on construction or +with set_npts to the size matching the data file contents. If there is a mismatch the results are unpredictable. -\param Ensemble object to hold the sample data to be read from the - files. Note that this function acts like a subroutine with entire purpose being - to fill the data array of this object. +\param Ensemble object to hold the sample data to be read from the + files. Note that this function acts like a subroutine with entire purpose +being to fill the data array of this object. \param dir is the directory name to use for file name (no trailing slash) \param dfile is the leaf file name to be openned. -\param foffs is a vector of the number of bytes to seek for first byte of all +\param foffs is a vector of the number of bytes to seek for first byte of all TimeSeries objects to be read in the given file. -\param indexes is a vector of indexes of TimeSeries objects to be read to the +\param indexes is a vector of indexes of TimeSeries objects to be read to the ensemble. \param length is the size of the ensemble. It is used to resize the ensemble. -\return total number of samples read for all TimeSeries objects. Note caller -should test this value as a short read will not cause an error to be thrown. +\return total number of samples read for all TimeSeries objects. Note caller +should test this value as a short read will not cause an error to be thrown. \exception This function may throw a MsPASSError exception if anything -goes wrong in the read process (open failure, seek fails, fread fails completely). -If that happens the data result should be killed as the sample contents are -guaranteed to be invalid. +goes wrong in the read process (open failure, seek fails, fread fails +completely). If that happens the data result should be killed as the sample +contents are guaranteed to be invalid. */ -size_t fread_from_file(mspass::seismic::LoggingEnsemble &de, - const std::string dir, const std::string dfile, std::vector indexes); +size_t fread_from_file( + mspass::seismic::LoggingEnsemble &de, + const std::string dir, const std::string dfile, + std::vector indexes); /*! \brief Use C fread to read multiple Seismogram from a file. @@ -246,32 +250,34 @@ This low level function is used in the file based reader of mspass to speed up python readers. It is intrinsically dangerous because it assumes the data object has a preconstructed size sufficient to hold the data loaded with the low-level C fread function. -The reader assumes the input has been initialized on construction or -with set_npts to the size matching the data file contents. If there is +The reader assumes the input has been initialized on construction or +with set_npts to the size matching the data file contents. If there is a mismatch the results are unpredictable. -\param Ensemble object to hold the sample data to be read from the - files. Note that this function acts like a subroutine with entire purpose being - to fill the data array of this object. +\param Ensemble object to hold the sample data to be read from the + files. Note that this function acts like a subroutine with entire purpose +being to fill the data array of this object. \param dir is the directory name to use for file name (no trailing slash) \param dfile is the leaf file name to be openned. -\param foffs is a vector of the number of bytes to seek for first byte of all +\param foffs is a vector of the number of bytes to seek for first byte of all Seismogram objects to be read in the given file. -\param indexes is a vector of indexes of Seismogram objects to be read to the +\param indexes is a vector of indexes of Seismogram objects to be read to the ensemble. \param length is the size of the ensemble. It is used to resize the ensemble. -\return total number of samples read for all Seismogram objects. Note caller -should test this value as a short read will not cause an error to be thrown. +\return total number of samples read for all Seismogram objects. Note caller +should test this value as a short read will not cause an error to be thrown. \exception This function may throw a MsPASSError exception if anything -goes wrong in the read process (open failure, seek fails, fread fails completely). -If that happens the data result should be killed as the sample contents are -guaranteed to be invalid. +goes wrong in the read process (open failure, seek fails, fread fails +completely). If that happens the data result should be killed as the sample +contents are guaranteed to be invalid. */ -size_t fread_from_file(mspass::seismic::LoggingEnsemble &de, - const std::string dir, const std::string dfile, std::vector indexes); +size_t fread_from_file( + mspass::seismic::LoggingEnsemble &de, + const std::string dir, const std::string dfile, + std::vector indexes); -} +} // namespace mspass::io #endif diff --git a/cxx/include/mspass/io/mseed_index.h b/cxx/include/mspass/io/mseed_index.h index 436f66394..65aa5c847 100644 --- a/cxx/include/mspass/io/mseed_index.h +++ b/cxx/include/mspass/io/mseed_index.h @@ -1,13 +1,11 @@ #ifndef _MSEED_INDEX_H_ #define _MSEED_INDEX_H_ -#include -#include #include "mspass/utility/ErrorLogger.h" -namespace mspass::io -{ +#include +#include +namespace mspass::io { -class mseed_index -{ +class mseed_index { public: std::string net; std::string sta; @@ -22,50 +20,47 @@ class mseed_index double last_packet_time; /* These aren't really essential because the compiler should automatically generate them, but better to be explicit since the std::vector demands them*/ - mseed_index() - { - net=""; - sta=""; - loc=""; - chan=""; - foff=0; + mseed_index() { + net = ""; + sta = ""; + loc = ""; + chan = ""; + foff = 0; nbytes = 0; npts = 0; samprate = 0.0; starttime = 0.0; endtime = 0.0; - last_packet_time=0.0; + last_packet_time = 0.0; }; - mseed_index(const mseed_index& parent) : net(parent.net),sta(parent.sta), - loc(parent.loc),chan(parent.chan) - { - foff=parent.foff; - nbytes=parent.nbytes; - npts=parent.npts; - samprate=parent.samprate; - starttime=parent.starttime; - endtime=parent.endtime; - last_packet_time=parent.last_packet_time; + mseed_index(const mseed_index &parent) + : net(parent.net), sta(parent.sta), loc(parent.loc), chan(parent.chan) { + foff = parent.foff; + nbytes = parent.nbytes; + npts = parent.npts; + samprate = parent.samprate; + starttime = parent.starttime; + endtime = parent.endtime; + last_packet_time = parent.last_packet_time; }; - mseed_index& operator=(const mseed_index& parent) - { - if(&parent != this) - { - net=parent.net; - sta=parent.sta; - loc=parent.loc; - chan=parent.chan; - foff=parent.foff; - npts=parent.npts; - nbytes=parent.nbytes; - samprate=parent.samprate; - starttime=parent.starttime; - endtime=parent.endtime; - last_packet_time=parent.last_packet_time; + mseed_index &operator=(const mseed_index &parent) { + if (&parent != this) { + net = parent.net; + sta = parent.sta; + loc = parent.loc; + chan = parent.chan; + foff = parent.foff; + npts = parent.npts; + nbytes = parent.nbytes; + samprate = parent.samprate; + starttime = parent.starttime; + endtime = parent.endtime; + last_packet_time = parent.last_packet_time; } return *this; }; - friend std::ostringstream& operator<< (std::ostringstream& ss,const mseed_index& ind); + friend std::ostringstream &operator<<(std::ostringstream &ss, + const mseed_index &ind); }; /*! \brief Construct an index for a miniseed file. @@ -96,16 +91,16 @@ of interest only if something breaks. logged. That is, at present if this parameter is true any time the logic detects a time tear it is logged in the returned error log as an informational log message. If false only reading errors for things like garbled miniseed - packets are logged. + packets are logged. \param return is an std::pair. "First" contains a vector of objects called mseed_index that contain the basic information defining an index for inputfile. See class description of mseed_index for more details. "second" contains an ErrorLogger objects. Caller should test that the contents are empty and if not save the error log or print it. */ -std::pair,mspass::utility::ErrorLogger> - mseed_file_indexer(const std::string inputfile, const bool segment_timetears, - const bool Verbose); +std::pair, mspass::utility::ErrorLogger> +mseed_file_indexer(const std::string inputfile, const bool segment_timetears, + const bool Verbose); -} // end namespace +} // namespace mspass::io #endif diff --git a/cxx/include/mspass/mspass.h b/cxx/include/mspass/mspass.h index edb5899ee..fde9889e0 100644 --- a/cxx/include/mspass/mspass.h +++ b/cxx/include/mspass/mspass.h @@ -5,18 +5,18 @@ #include "mspass/utility/ErrorLogger.h" #include "mspass/utility/Metadata.h" #include "mspass/utility/MsPASSError.h" -//#include "mspass/utility/MsPASSMetadata.h" +// #include "mspass/utility/MsPASSMetadata.h" +#include "mspass/seismic/BasicTimeSeries.h" #include "mspass/utility/SphericalCoordinate.h" #include "mspass/utility/dmatrix.h" #include "mspass/utility/utility.h" -#include "mspass/seismic/BasicTimeSeries.h" -//#include "mspass/seismic/DataGap.h" +// #include "mspass/seismic/DataGap.h" #include "mspass/seismic/Ensemble.h" -//#include "mspass/seismic/MPSeismogram.h" +// #include "mspass/seismic/MPSeismogram.h" #include "mspass/seismic/Seismogram.h" -//#include "mspass/seismic/SeismogramWGaps.h" +// #include "mspass/seismic/SeismogramWGaps.h" #include "mspass/seismic/SlownessVector.h" #include "mspass/seismic/TimeSeries.h" -//#include "mspass/seismic/TimeSeriesWGaps.h" -#include "mspass/seismic/TimeWindow.h" +// #include "mspass/seismic/TimeSeriesWGaps.h" #include "mspass/io/mseed_index.h" +#include "mspass/seismic/TimeWindow.h" diff --git a/cxx/include/mspass/seismic/BasicSpectrum.h b/cxx/include/mspass/seismic/BasicSpectrum.h index f696e1e70..49d3ec412 100644 --- a/cxx/include/mspass/seismic/BasicSpectrum.h +++ b/cxx/include/mspass/seismic/BasicSpectrum.h @@ -1,9 +1,9 @@ #ifndef _BASIC_SPECTRUM_H_ #define _BASIC_SPECTRUM_H_ -#include -#include #include "mspass/utility/MsPASSError.h" -namespace mspass::seismic{ +#include +#include +namespace mspass::seismic { /*! Base class for family of data objects created by Fourier transforms. There are a range of algorithms used in seismology that center on the use @@ -16,60 +16,57 @@ common concpets shared by this base class. Classic use of inheritance to avoid redundant code. */ -class BasicSpectrum -{ +class BasicSpectrum { public: /*! Default constructor. sets frequency interval to 1 and f0 to 0 */ - BasicSpectrum(){dfval=1.0;f0val=0.0;}; + BasicSpectrum() { + dfval = 1.0; + f0val = 0.0; + }; /*! Parameterized constructor. \param dfin frequency bin size - \param f0in frequency of first component of data vector of regular frequency grid. + \param f0in frequency of first component of data vector of regular frequency + grid. \param dtin parent sample interval of spectrum estimate. \param npts_in number of actual samples of parent time series (may not be the same as spectrum link when zero padding is used. subclasses should provide a way to handle zero padding */ - BasicSpectrum(const double dfin, - const double f0in, - const double dtin, - const int npts_in) - { - is_live=false; - dfval=dfin; - f0val=f0in; - parent_dt=dtin; - parent_npts=npts_in; + BasicSpectrum(const double dfin, const double f0in, const double dtin, + const int npts_in) { + is_live = false; + dfval = dfin; + f0val = f0in; + parent_dt = dtin; + parent_npts = npts_in; }; /*! Standard copy constructor */ - BasicSpectrum(const BasicSpectrum& parent) - { - is_live=parent.is_live; - dfval=parent.dfval; - f0val=parent.f0val; - parent_dt=parent.parent_dt; - parent_npts=parent.parent_npts; + BasicSpectrum(const BasicSpectrum &parent) { + is_live = parent.is_live; + dfval = parent.dfval; + f0val = parent.f0val; + parent_dt = parent.parent_dt; + parent_npts = parent.parent_npts; }; - BasicSpectrum& operator=(const BasicSpectrum& parent) - { - if(this!=(&parent)) - { - is_live=parent.is_live; - dfval=parent.dfval; - f0val=parent.f0val; - parent_dt=parent.parent_dt; - parent_npts=parent.parent_npts; + BasicSpectrum &operator=(const BasicSpectrum &parent) { + if (this != (&parent)) { + is_live = parent.is_live; + dfval = parent.dfval; + f0val = parent.f0val; + parent_dt = parent.parent_dt; + parent_npts = parent.parent_npts; } return *this; }; /*~ Destructor.*/ - virtual ~BasicSpectrum(){}; + virtual ~BasicSpectrum() {}; /*! Test live condition of the data. Returns true if the data are marked as being good. This method is part of a four methods for handling the concept of "live"==gppd versus "dead" == bad. The concept was borrowed from seismic reflection processing. */ - bool live()const{return is_live;}; + bool live() const { return is_live; }; /*! Test live condition of the data. Returns true if the data are marked as being bad This method is part of @@ -77,35 +74,36 @@ class BasicSpectrum The concept was borrowed from seismic reflection processing. This method is the negation of the live method. */ - bool dead()const{return !is_live;}; + bool dead() const { return !is_live; }; /*! Mark this datum bad. Returns true if the data are marked as being good. This method is part of a four methods for handling the concept of "live"==gppd versus "dead" == bad. The concept was borrowed from seismic reflection processing. */ - void kill(){is_live=false;}; + void kill() { is_live = false; }; /*! Mark this datum good.. Returns true if the data are marked as being good. This method is part of a four methods for handling the concept of "live"==gppd versus "dead" == bad. The concept was borrowed from seismic reflection processing. */ - void set_live(){is_live=true;}; + void set_live() { is_live = true; }; /*! Return the (fixed) frequemcy bin size.*/ - double df() const{return this->dfval;}; + double df() const { return this->dfval; }; /*! Return the frequency of the first (0) component of the spectrum vector. This value is normally 0 but the api allows it to be nonzero. That is useful for windowing to store only data in a limited passband. */ - double f0() const{return this->f0val;}; - /*! \brief Return the original sample interval of data used to generate spectrum. + double f0() const { return this->f0val; }; + /*! \brief Return the original sample interval of data used to generate + spectrum. When zero padding is used the original sample interval of data cannot be known without additional data. In this implementation we require the user to store that information with a protected attribute within the object. This retrieves that stored value. */ - double dt() const{return this->parent_dt;}; + double dt() const { return this->parent_dt; }; /*! \brief Return the Rayleigh bin size for this spectrum. The Rayleigh bin size of a spectrum is 1/T where T is the length of the @@ -113,48 +111,42 @@ class BasicSpectrum Note the rayleigh and df methods will return the same number only when a spectrum is computed with no zero padding. */ - double rayleigh() const{ - return 1.0/(this->parent_dt*static_cast(this->parent_npts)); + double rayleigh() const { + return 1.0 / (this->parent_dt * static_cast(this->parent_npts)); }; - /*! Return number of points in parent time series. Of use mostly internally.*/ - int timeseries_npts() const - { - return parent_npts; - } + /*! Return number of points in parent time series. Of use mostly + * internally.*/ + int timeseries_npts() const { return parent_npts; } /*! Return the integer sample number of the closest sample to the specified frequency. Uses rounding. Will throw a MsPASSError object if the specified frequency is not within the range of the data. */ - int sample_number(const double f) const - { - int itest=static_cast( round( (f-f0val)/dfval ) ); - if(itest<0) - { + int sample_number(const double f) const { + int itest = static_cast(round((f - f0val) / dfval)); + if (itest < 0) { throw mspass::utility::MsPASSError( - "BasicSpectrum::sample_number: f must be positive or greater than f0 if f0!=0", - mspass::utility::ErrorSeverity::Fatal); - } - else if(itest>=this->nf()) - { + "BasicSpectrum::sample_number: f must be positive or greater than " + "f0 if f0!=0", + mspass::utility::ErrorSeverity::Fatal); + } else if (itest >= this->nf()) { throw mspass::utility::MsPASSError( - "BasicSpectrum::sample_number: f received exceeds the length of stored vector", - mspass::utility::ErrorSeverity::Fatal); - } - else - { + "BasicSpectrum::sample_number: f received exceeds the length of " + "stored vector", + mspass::utility::ErrorSeverity::Fatal); + } else { return itest; } }; /*! Setter for the frequency bin interval - use with caution. */ - void set_df(const double dfin){dfval=dfin;}; + void set_df(const double dfin) { dfval = dfin; }; /*! Setter for the initial frequency value. */ - void set_f0(const double f0in){f0val=f0in;}; + void set_f0(const double f0in) { f0val = f0in; }; /*! Setter for internally stored parent data sample interval. */ - void set_dt(const double dtin){parent_dt=dtin;}; - /*! Setter for internal parent number of data points (need by rayleigh method). - Note one should only use this in constructors and when creating an instance - from pieces.*/ - void set_npts(const int npts_in){parent_npts=npts_in;}; + void set_dt(const double dtin) { parent_dt = dtin; }; + /*! Setter for internal parent number of data points (need by rayleigh + method). Note one should only use this in constructors and when creating an + instance from pieces.*/ + void set_npts(const int npts_in) { parent_npts = npts_in; }; /*! Return an std::vector container containing the frequency of each sample in the spectrum vector. Commonly necessary for plotting. Made virtual because nf method needs to be virtual.*/ @@ -168,6 +160,7 @@ class BasicSpectrum and the number of points is not the full fft output this needs to be handled differently. */ virtual double Nyquist() const = 0; + protected: double dfval; double f0val; @@ -175,5 +168,5 @@ class BasicSpectrum double parent_npts; bool is_live; }; -} //End namspace +} // namespace mspass::seismic #endif diff --git a/cxx/include/mspass/seismic/BasicTimeSeries.h b/cxx/include/mspass/seismic/BasicTimeSeries.h index 2b5907142..b4004aed8 100644 --- a/cxx/include/mspass/seismic/BasicTimeSeries.h +++ b/cxx/include/mspass/seismic/BasicTimeSeries.h @@ -1,9 +1,9 @@ #ifndef _BASICTIMESERIES_H_ #define _BASICTIMESERIES_H_ -#include #include #include -namespace mspass::seismic{ +#include +namespace mspass::seismic { /*! \brief Type of time standard for time series data. Time series data have two common standards. Absolute time means the @@ -12,8 +12,9 @@ reference. An example is an arrival time reference frame where all data are set with time zero defined by a set of arrival time picks. **/ enum class TimeReferenceType { - UTC, /*!< Use an absolute (usually UTC) time base - previously absolute in SEISPP. */ - Relative /*! Time is relative to some other standard like shot time */ + UTC, /*!< Use an absolute (usually UTC) time base - previously absolute in + SEISPP. */ + Relative /*! Time is relative to some other standard like shot time */ }; /*! \brief Base class for time series objects. @@ -31,287 +32,257 @@ interface to python with pybind11. \author Gary L. Pavlis **/ -class BasicTimeSeries -{ +class BasicTimeSeries { public: + /*! + Default constructor. Does essentially nothing since a BasicTimeSeries + object has no data. Does initialize data to avoid run time checkers + bitching about unitialized data, but values are meaningless when this + constructor is called. + **/ + BasicTimeSeries(); + /*! + Standard copy constructor. + **/ + BasicTimeSeries(const BasicTimeSeries &); + /*! \brief Virtual destructor. -/*! -Default constructor. Does essentially nothing since a BasicTimeSeries -object has no data. Does initialize data to avoid run time checkers -bitching about unitialized data, but values are meaningless when this -constructor is called. -**/ - BasicTimeSeries(); -/*! -Standard copy constructor. -**/ - BasicTimeSeries(const BasicTimeSeries&); -/*! \brief Virtual destructor. - - A base class with virtual members like this requires this - incantation to avoid some odd conflicts. This particular one - was added to make the boost::serialization code work properly. - The geeky details for why this is necessary can be found in - Scott Meyers book "Effective C++" */ - virtual ~BasicTimeSeries(){}; -/*! -Get the time of sample i. -It is common to need to ask for the time of a given sample. -This standardizes this common operation in an obvious way. -//\param i - sample number to compute time for. -**/ - double time(const int i)const - { - return(mt0+mdt*static_cast(i)); - }; -/*! -Inverse of time function. That is, it returns the integer position -of a given time t within a time series. The returned number is -not tested for validity compared to the data range. This is the -callers responsibility as this is a common error condition that -should not require the overhead of an exception. -**/ - int sample_number(double t)const - { - return(round((t-mt0)/mdt)); - }; -/*! -Returns the end time (time associated with last data sample) -of this data object. -**/ - double endtime()const noexcept - { - return(mt0 + (static_cast(nsamp) - 1.0)*mdt); - }; -/*! Return true if a time shift has been applied to the data. - * Never true if data were never in an absolute time frame (i.e.UTC)*/ - bool shifted() const - { - return t0shift_is_valid; - }; -/*! Return the current value of the t0shift attribute - argument to restore to UTC */ - double get_t0shift() const - { - return this->t0shift; + A base class with virtual members like this requires this + incantation to avoid some odd conflicts. This particular one + was added to make the boost::serialization code work properly. + The geeky details for why this is necessary can be found in + Scott Meyers book "Effective C++" */ + virtual ~BasicTimeSeries() {}; + /*! + Get the time of sample i. + It is common to need to ask for the time of a given sample. + This standardizes this common operation in an obvious way. + //\param i - sample number to compute time for. + **/ + double time(const int i) const { + return (mt0 + mdt * static_cast(i)); }; -/*! Return the reference time. + /*! + Inverse of time function. That is, it returns the integer position + of a given time t within a time series. The returned number is + not tested for validity compared to the data range. This is the + callers responsibility as this is a common error condition that + should not require the overhead of an exception. + **/ + int sample_number(double t) const { return (round((t - mt0) / mdt)); }; + /*! + Returns the end time (time associated with last data sample) + of this data object. + **/ + double endtime() const noexcept { + return (mt0 + (static_cast(nsamp) - 1.0) * mdt); + }; + /*! Return true if a time shift has been applied to the data. + * Never true if data were never in an absolute time frame (i.e.UTC)*/ + bool shifted() const { return t0shift_is_valid; }; + /*! Return the current value of the t0shift attribute - argument to restore to + * UTC */ + double get_t0shift() const { return this->t0shift; }; + /*! Return the reference time. - We distinguish relative and UTC time by a time shift constant - stored with the object. This returns the time shift to return - data to an epoch time. + We distinguish relative and UTC time by a time shift constant + stored with the object. This returns the time shift to return + data to an epoch time. - \throw SeisppError object if the request is not rational. That is this - request only makes sense if the data began with an absolute time and was - converted with the ator method. Some cross checks are made for consistency - that can throw an error in this condition. */ + \throw SeisppError object if the request is not rational. That is this + request only makes sense if the data began with an absolute time and was + converted with the ator method. Some cross checks are made for consistency + that can throw an error in this condition. */ double time_reference() const; -/*! \brief Force a t0 shift value on data. - * - * This is largely an interface routine for constructors that need to - * handle data in relative time that are derived from an absolute - * base. It can also be used to fake processing routines that demand - * data be in absolute time when the original data were not. It was - * added for MsPASS to support reads and writes to MongoDB where we - * want to be able to read and write data that had been previously - * time shifted (e.g. ArrivalTimeReference). - * - * \param t is the time shift to force - * */ - void force_t0_shift(const double t) - { - this->t0shift=t; - t0shift_is_valid=true; - }; -/*! -Absolute to relative time conversion. -Sometimes we want to convert data from absolute time (epoch times) -to a relative time standard. Examples are conversions to travel -time using an event origin time or shifting to an arrival time -reference frame. This operation simply switches the tref -variable and alters t0 by tshift. -\param tshift - time shift applied to data before switching data to relative time mode. -**/ - virtual void ator(const double tshift); -/*! Relative to absolute time conversion. - Sometimes we want to convert data from relative time to - to an UTC time standard. An example would be converting - segy shot data to something that could be processed like earthquake - data in a css3.0 database. + /*! \brief Force a t0 shift value on data. + * + * This is largely an interface routine for constructors that need to + * handle data in relative time that are derived from an absolute + * base. It can also be used to fake processing routines that demand + * data be in absolute time when the original data were not. It was + * added for MsPASS to support reads and writes to MongoDB where we + * want to be able to read and write data that had been previously + * time shifted (e.g. ArrivalTimeReference). + * + * \param t is the time shift to force + * */ + void force_t0_shift(const double t) { + this->t0shift = t; + t0shift_is_valid = true; + }; + /*! + Absolute to relative time conversion. + Sometimes we want to convert data from absolute time (epoch times) + to a relative time standard. Examples are conversions to travel + time using an event origin time or shifting to an arrival time + reference frame. This operation simply switches the tref + variable and alters t0 by tshift. + \param tshift - time shift applied to data before switching data to relative + time mode. + **/ + virtual void ator(const double tshift); + /*! Relative to absolute time conversion. + Sometimes we want to convert data from relative time to + to an UTC time standard. An example would be converting + segy shot data to something that could be processed like earthquake + data in a css3.0 database. - This method returns data previously converted to relative back to UTC using the - internally stored time shift attribute. */ + This method returns data previously converted to relative back to UTC using + the internally stored time shift attribute. */ virtual void rtoa(); -/*! Shift the reference time. + /*! Shift the reference time. - Sometimes we need to shift the reference time t0. An example is a moveout correction. - This method shifts the reference time by dt. Note a positive dt means data aligned to - zero will be shifted left because relative time is t-t0. - */ + Sometimes we need to shift the reference time t0. An example is a moveout + correction. This method shifts the reference time by dt. Note a positive + dt means data aligned to zero will be shifted left because relative time is + t-t0. + */ virtual void shift(const double dt); - /*! Returns true of data are marked valid (live). */ - bool live()const{return this->mlive;}; - /*! Return true if the data have been marked bad (killed) - inverse of live()*/ - bool dead()const{return !(this->mlive);}; - /*! Mark these data bad. */ - void kill(){this->mlive=false;}; - /*! Inverse of kill - marks data live overriding anything set before. Use to - resurrect data improperly killed (useful only for interactive editing) or - creating data pieces outside constructors. */ - void set_live(){this->mlive=true;}; - /*! Return the data sample interval. */ - double dt()const {return this->mdt;}; - /*! Test if the time standard is UTC. */ - bool time_is_UTC()const - { - if(tref==TimeReferenceType::UTC) - return true; - else - return false; - }; - bool time_is_relative()const - { - if(tref==TimeReferenceType::Relative) - return true; - else - return false; - }; - TimeReferenceType timetype()const - { - return this->tref; - } - /*! Return sample rate. */ - double samprate()const - { - return 1.0/mdt; - } - /*! Return the number of points in the data series. */ - size_t npts()const {return nsamp;}; - /*! Return time of first data sample. An epoch time or relative time depending - on TimeReferenceType private variable tref*/ - double t0()const {return this->mt0;}; - /*! Return a vector with the time of each sample. + /*! Returns true of data are marked valid (live). */ + bool live() const { return this->mlive; }; + /*! Return true if the data have been marked bad (killed) - inverse of + * live()*/ + bool dead() const { return !(this->mlive); }; + /*! Mark these data bad. */ + void kill() { this->mlive = false; }; + /*! Inverse of kill - marks data live overriding anything set before. Use to + resurrect data improperly killed (useful only for interactive editing) or + creating data pieces outside constructors. */ + void set_live() { this->mlive = true; }; + /*! Return the data sample interval. */ + double dt() const { return this->mdt; }; + /*! Test if the time standard is UTC. */ + bool time_is_UTC() const { + if (tref == TimeReferenceType::UTC) + return true; + else + return false; + }; + bool time_is_relative() const { + if (tref == TimeReferenceType::Relative) + return true; + else + return false; + }; + TimeReferenceType timetype() const { return this->tref; } + /*! Return sample rate. */ + double samprate() const { return 1.0 / mdt; } + /*! Return the number of points in the data series. */ + size_t npts() const { return nsamp; }; + /*! Return time of first data sample. An epoch time or relative time + depending on TimeReferenceType private variable tref*/ + double t0() const { return this->mt0; }; + /*! Return a vector with the time of each sample. - There are times when it is desirable to have a parallel vector to sample - data that contains the actual times of each sample rather than using the - implicit time t0+n*dt. A case in point is graphics where time is - the x axis in a standard plot. This function returns - an nsamp length std::vector of the time values of each sample. - Note this is a base class method that makes sense only in subclasses - that have data in a sample vector. + There are times when it is desirable to have a parallel vector to sample + data that contains the actual times of each sample rather than using the + implicit time t0+n*dt. A case in point is graphics where time is + the x axis in a standard plot. This function returns + an nsamp length std::vector of the time values of each sample. + Note this is a base class method that makes sense only in subclasses + that have data in a sample vector. */ std::vector time_axis() const; - /*! \brief Set the sample interval. + /*! \brief Set the sample interval. - This is a simple setter for the sample interval attribute. It is virtual - because children may want to do more than just set the attribute in this - base class. In MsPASS that means keeping the Metadata attributes that - define sample interval in sync with the data. Aliases further complicate - that issue so it is not trivial. Other data objects that could be - derived form this base could have similar issues. + This is a simple setter for the sample interval attribute. It is virtual + because children may want to do more than just set the attribute in this + base class. In MsPASS that means keeping the Metadata attributes that + define sample interval in sync with the data. Aliases further complicate + that issue so it is not trivial. Other data objects that could be + derived form this base could have similar issues. - \param sample_interval is the new data sample interval to be used. - */ - virtual void set_dt(const double sample_interval) - { - mdt=sample_interval; - }; - /*! \brief Set the number of samples attribute for data. + \param sample_interval is the new data sample interval to be used. + */ + virtual void set_dt(const double sample_interval) { mdt = sample_interval; }; + /*! \brief Set the number of samples attribute for data. - This is a simple setter for the number of samples attribute. It is virtual - because children may want to do more than just set the attribute in this - base class. In MsPASS that means keeping the Metadata attributes that - define the number of points in sync with the data. Aliases further complicate - that issue so it is not trivial. Other data objects that could be - derived form this base could have similar issues. + This is a simple setter for the number of samples attribute. It is virtual + because children may want to do more than just set the attribute in this + base class. In MsPASS that means keeping the Metadata attributes that + define the number of points in sync with the data. Aliases further + complicate that issue so it is not trivial. Other data objects that could be + derived form this base could have similar issues. - \param npts is the new number of points to set. - */ - virtual void set_npts(const size_t npts) - { - nsamp=npts; - }; - /*! \brief Set the data start time. + \param npts is the new number of points to set. + */ + virtual void set_npts(const size_t npts) { nsamp = npts; }; + /*! \brief Set the data start time. - This is a simple setter for the start time attribute. It is virtual - because children may want to do more than just set the attribute in this - base class. In MsPASS that means keeping the Metadata attributes that - define t0 in sync with the data. Aliases further complicate - that issue so it is not trivial. Other data objects that could be - derived form this base could have similar issues. + This is a simple setter for the start time attribute. It is virtual + because children may want to do more than just set the attribute in this + base class. In MsPASS that means keeping the Metadata attributes that + define t0 in sync with the data. Aliases further complicate + that issue so it is not trivial. Other data objects that could be + derived form this base could have similar issues. - \param t0in is the new data sample interval to be used. - */ - virtual void set_t0(const double t0in) - { - mt0=t0in; - }; - /*! \brief Force the time standard. + \param t0in is the new data sample interval to be used. + */ + virtual void set_t0(const double t0in) { mt0 = t0in; }; + /*! \brief Force the time standard. - Time series data may have multiple concepts of what the time standard is. - In MsPASS the allowed values currently are UTC and relative defined by - the TimeReferenceType enum class. Other implementations might need to use - some other standard (e.g. raw gps time is not utc). This method allows - forcing a standard. It is not recommended for normal use, but only as a - brutal solution for assembling a data object outside normal constructors. +Time series data may have multiple concepts of what the time standard is. + In MsPASS the allowed values currently are UTC and relative defined by + the TimeReferenceType enum class. Other implementations might need to use + some other standard (e.g. raw gps time is not utc). This method allows + forcing a standard. It is not recommended for normal use, but only as a + brutal solution for assembling a data object outside normal constructors. - \param newtref is the new time standard to set for these data. - */ - void set_tref(const TimeReferenceType newtref) - { - tref=newtref; - }; -/*! Standard assignment operator. */ - BasicTimeSeries& operator=(const BasicTimeSeries& parent); + \param newtref is the new time standard to set for these data. + */ + void set_tref(const TimeReferenceType newtref) { tref = newtref; }; + /*! Standard assignment operator. */ + BasicTimeSeries &operator=(const BasicTimeSeries &parent); protected: - /*! - Boolean defining if a data object has valid data or is to be ignored. - Data processing often requires data to be marked bad but keep the original - data around in case an error was made. This boolean allows this capability. - **/ - bool mlive; - /*! - Sample interval. - **/ - double mdt; - /*! - Data start time. That is the time of the first sample of data. - **/ - double mt0; - /*! - Number of data samples in this data object. - **/ - size_t nsamp; - /*! - Time reference standard for this data object. Defined by enum Time_Reference - this currently is only one of two things. When set as "UTC" the time - standard is an epoch time. When set as "relative" time has no relationship - to any external standard but are relative to some arbitrary reference that must - ascertained by the algorithm by some other means (in seispp this is normally - done through a metadata object). A classic example is multichannel data where - channels have a time relative to a shot time. - **/ - TimeReferenceType tref; - /* We actually test for t0shift two ways. If this is true we always accept it. - * If false we check for nonzero t0shift and override if necessary. - * */ - bool t0shift_is_valid; - /*When ator or rtoa are called this variable defines the conversion back - * and forth. The shift method should be used to change it. */ - double t0shift; + /*! + Boolean defining if a data object has valid data or is to be ignored. + Data processing often requires data to be marked bad but keep the original + data around in case an error was made. This boolean allows this capability. + **/ + bool mlive; + /*! + Sample interval. + **/ + double mdt; + /*! + Data start time. That is the time of the first sample of data. + **/ + double mt0; + /*! + Number of data samples in this data object. + **/ + size_t nsamp; + /*! + Time reference standard for this data object. Defined by enum Time_Reference + this currently is only one of two things. When set as "UTC" the time + standard is an epoch time. When set as "relative" time has no relationship + to any external standard but are relative to some arbitrary reference that + must ascertained by the algorithm by some other means (in seispp this is + normally done through a metadata object). A classic example is multichannel + data where channels have a time relative to a shot time. + **/ + TimeReferenceType tref; + /* We actually test for t0shift two ways. If this is true we always accept + * it. If false we check for nonzero t0shift and override if necessary. + * */ + bool t0shift_is_valid; + /*When ator or rtoa are called this variable defines the conversion back + * and forth. The shift method should be used to change it. */ + double t0shift; + private: - friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { - ar & mlive; - ar & mdt; - ar & nsamp; - ar & mt0; - ar & tref; - ar & t0shift_is_valid; - ar & t0shift; - }; + friend boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar & mlive; + ar & mdt; + ar & nsamp; + ar & mt0; + ar & tref; + ar & t0shift_is_valid; + ar & t0shift; + }; }; -} -#endif // End guard +} // namespace mspass::seismic +#endif // End guard diff --git a/cxx/include/mspass/seismic/CoreSeismogram.h b/cxx/include/mspass/seismic/CoreSeismogram.h index 6d92fb3b0..99a467647 100644 --- a/cxx/include/mspass/seismic/CoreSeismogram.h +++ b/cxx/include/mspass/seismic/CoreSeismogram.h @@ -1,15 +1,15 @@ #ifndef _MSPASS_CORESEISMOGRAM_H_ #define _MSPASS_CORESEISMOGRAM_H_ -#include -#include -#include "mspass/utility/Metadata.h" -#include "mspass/utility/dmatrix.h" #include "mspass/seismic/BasicTimeSeries.h" #include "mspass/seismic/CoreTimeSeries.h" -#include "mspass/utility/SphericalCoordinate.h" #include "mspass/seismic/SlownessVector.h" -//#include "mspass/seismic/Ensemble.h" -namespace mspass::seismic{ +#include "mspass/utility/Metadata.h" +#include "mspass/utility/SphericalCoordinate.h" +#include "mspass/utility/dmatrix.h" +#include +#include +// #include "mspass/seismic/Ensemble.h" +namespace mspass::seismic { /* A Seismogram is viewed as a special collection of Time Series type data that is essentially a special version of a vector time series. @@ -23,7 +23,6 @@ MsPASS to add functionality or aliased to Seismogram to simplify the naming as done, for example, with std::basic_string made equivalent to std::string. */ - /*! \brief Vector (three-component) seismogram data object. A three-component seismogram is a common concept in seismology. The concept @@ -36,350 +35,359 @@ done, for example, with std::basic_string made equivalent to std::string. through inheritance of a Metadata object. \author Gary L. Pavlis **/ -class CoreSeismogram : public mspass::seismic::BasicTimeSeries , public mspass::utility::Metadata -{ +class CoreSeismogram : public mspass::seismic::BasicTimeSeries, + public mspass::utility::Metadata { public: - /*! - Holds the actual data. - -Matrix is 3xns. Thus the rows are the component number - and columns define time position. Note there is a redundancy in - these definitions that must be watched if you manipulate the - contents of this matrix. That is, BasicTimeSeries defines ns, but - the u matrix has it's own internal size definitions. Currently no - tests are done to validate this consistency. All constructors handle - this, but again because u is public be very careful in altering u. -**/ - mspass::utility::dmatrix u; - -/*! - Default constructor. - -Sets ns to zero and builds an empty data matrix. The live variable -in BasicTimeSeries is also set false. -**/ - CoreSeismogram(); -/*! - Simplest parameterized constructor. - -Initializes data and sets aside memory for - matrix of size 3xnsamples. The data matrix is not initialized - and the object is marked as not live. -\param nsamples number of samples expected for holding data. -**/ - CoreSeismogram(const size_t nsamples); -/*! - Construct a three component seismogram from three TimeSeries objects. - - A three component seismogram is commonly assembled from individual - single channel components. This constructor does the process taking - reasonable care to deal with (potentially) irregular start and end - times of the individual components. If the start and end times are - all the same it uses a simple copy operation. Otherwise it runs a - more complicated (read much slower) algorithm that handles the ragged - start and stop times by adding a marked gap. - - If start or end times are not constant the algorithm shortens the output - to the latest start time and earliest end time respectively. - - Note this constructor requires variables hang and vang, which are - orientation angles defined in the CSS3.0 schema (NOT spherical - coordinates by the way), by set for each component. This is used to - construct the transformation matrix for the object that allows, - for example, removing raw data orientation errors using rotate_to_standard. - The constructor will throw an exception if any component does not have - these attributes set in their Metadata area. - -\exception SeisppError exception can be throw for a variety of serious - problems. -\param ts vector of 3 TimeSeries objects to be used to assemble - this Seismogram. Input vector order could be - arbitrary because a transformation matrix is computed, but for - efficiency standard order (E,N,Z) is advised. -\param component_to_clone the auxiliary parameters (Metadata and - BasicTimeSeries common parameters) - from one of the components is cloned to assure common required - parameters are copied to this object. This argument controls which - of the three components passed through ts is used. Default is 0. - -**/ - CoreSeismogram(const std::vector& ts, - const unsigned int component_to_clone=0); -/*! \brief Construct from Metadata definition that includes data path. - * - A Metadata object is sufficiently general that it can contain enough - information to contruct an object from attributes contained in it. - This constuctor uses that approach, with the actual loading of data - being an option (on by default). In mspass this is constructor is - used to load data with Metadata constructed from MongoDB and then - using the path created from two parameters (dir and dfile used as - in css3.0 wfdisc) to read data. The API is general but the - implementation in mspass is very rigid. It blindly assumes the - data being read are binary doubles in the right byte order and - ordered in the native order for dmatrix (Fortran order). i.e. - the constuctor does a raw fread of ns*3 doubles into the internal - array used in the dmatrix implementation. - - A second element of the Metadata that is special for MsPASS is the - handling of the transformation matrix by this constructor. In MsPASS - the transformation matrix is stored as a python object in MongoDB. - This constructor aims to fetch that entity with the key 'tmatrix'. - To be more robust and simpler to use with data not loaded from mongodb - we default tmatrix to assume the data are in standard coordinates. That is, - if the key tmatrix is not defined in Metadata passed as arg0, the - constructor assumes it should set the transformation matrix to an identity. - Use set_transformation_matrix if that assumption is wrong for your data. - - \param md is the Metadata used for the construction. - - \param load_data if true (default) a file name is constructed from - dir+"/"+dfile, the file is openned, fseek is called to foff, - data are read with fread, and the file is closed. If false a dmatrix - for u is still created of size 3xns, but the matrix is only initialized - to all zeros. - - \exception Will throw a MsPASSError if required metadata are missing. - */ - CoreSeismogram(const mspass::utility::Metadata& md,const bool load_data=true); -/*! - Standard copy constructor. -**/ - - CoreSeismogram(const CoreSeismogram&); - /* These overload virtual methods in BasicTimeSeries. */ - /*! \brief Set the sample interval. - - This method is complicated by the need to sync the changed value with - Metadata. That is further complicated by the need to support aliases - for the keys used to defined dt in Metadata. That is handled by - first setting the internal dt value and then going through a fixed list - of valid alias keys for dt. Any that exist are changed. If - none were previously defined the unique name (see documentation) is - added to Metadata. - - \param sample_interval is the new data sample interval to be used. - */ - void set_dt(const double sample_interval); - /*! \brief Set the number of samples attribute for data. - - This method is complicated by the need to sync the changed value with - Metadata. That is further complicated by the need to support aliases - for the keys used to defined npts in Metadata. That is handled by - first setting the internal npts value (actually ns) and then going through a fixed list - of valid alias keys for npts. Any that exist are changed. If - none were previously defined the unique name (see documentation) is - added to Metadata. - - This attribute has an additional complication compared to other setter - that are overrides from BasicTimeSeries. That is, the number of points - define the data buffer size to hold the sample data. To guarantee - the buffer size and the internal remain consistent this method clears - any existing content of the dmatrix u and initializes the 3xnpts matrix to 0s. - Note this means if one is using this to assemble a data object in pieces - you MUST call this method before loading any data or it will be cleared - and you will mysteriously find the data are all zeros. - - \param npts is the new number of points to set. - */ - void set_npts(const size_t npts); - /*! \brief Sync the number of samples attribute with actual data size. - - This method syncs the npts attribute with the actual size of the dmatrix u. - It also syncs aliases in the same way as the set_npts method. - - */ - void sync_npts(); - /*! \brief Set the data start time. - - This method is complicated by the need to sync the changed value with - Metadata. That is further complicated by the need to support aliases - for the keys used to defined npts in Metadata. That is handled by - first setting the internal t0 value and then going through a fixed list - of valid alias keys for it. Any that exist are changed. If - none were previously defined the unique name (see documentation) is - added to Metadata. - - This is a dangerous method to use on real data as it can mess up the time - if not handled correctly. It should be used only when that sharp knife is - needed such as in assembling data outside of constructors in a test program. - - \param t0in is the new data sample interval to be used. - */ - void set_t0(const double t0in); -/*! - Standard assignment operator. -**/ - CoreSeismogram& operator= (const CoreSeismogram&); - /*! \brief Summation operator. - - Summing data from signals of irregular length requires handling potential - mismatches in size and overlap. This behaves the way a += operator should - logically behave in that situation. That is, because the lhs is where - the sum is being accumulated, the size is always controlled by the left hand - side of the operator. Any portions of the right hand side that are outside - the t0 to endtime() of the left hand side are silently discarded. If the - start time of the right hand side is greater than t0 or the endtime is less - than endtime of the lhs there will be discontinuties in the sum there - the ends of the rhs are inside the range of the lhs. - - \param d is other signal to add to this. - \exception MsPASSError can be thrown if lhs and rhs do not have matching - time standards. - **/ - CoreSeismogram& operator+=(const CoreSeismogram& d); - /*! Addition operator. - - This operator is implemented in a standard way utilizing operator+=. - For data with irregular start and end times that has an important - consequence; the operator is not communative. i.e given x an y - z=x+y will not yield the same result as z=y+x. - */ - const CoreSeismogram operator+(const CoreSeismogram& other) const; - /*! Multiply data by a scalar. */ - CoreSeismogram& operator*=(const double); - /*! \brief Subtraction operator. - - Differencing data from signals of irregular length requires handling potential - mismatches in size and overlap. This behaves the way a -= operator should - logically behave in that situation. That is, because the lhs is where - the sum is being accumulated, the size is always controlled by the left hand - side of the operator. Any portions of the right hand side that are outside - the t0 to endtime() of the left hand side are silently discarded. If the - start time of the right hand side is greater than t0 or the endtime is less - than endtime of the lhs there will be discontinuties in the sum there - the ends of the rhs are inside the range of the lhs. - - \param d is other signal to subract from this. - \exception MsPASSError can be thrown if lhs and rhs do not have matching - time standards. - **/ - CoreSeismogram& operator-=(const CoreSeismogram& d); - /*! Subtraction operator. - - This operator is implemented in a standard way utilizing operator-=. - For data with irregular start and end times that has an important - consequence; the operator is not communative. i.e given x an y - z=x-y will not yield the same result as z=-(y-x). - */ - const CoreSeismogram operator-(const CoreSeismogram& other) const; -/*! - Extract a sample from data vector. - - A sample in this context means a three-vector at a requested - sample index. Range checking is implicit because - of the internal use of the dmatrix to store the samples of - data. This operator is an alternative to extracting samples - through indexing of the internal dmatrix u that holds the data. - -\param sample is the sample number requested (must be in range or an exception will be thrown) - -\exception MsPASSError if the requested sample is outside - the range of the data. Note this includes an implicit "outside" - defined when the contents are marked dead. - Note the code does this by catching an error thrown by dmatrix - in this situation, printing the error message from the dmatrix - object, and then throwing a new SeisppError with a shorter - message. -\return std::vector containing a 3 vector of the samples at requested sample number - -\param sample is the integer sample number of data desired. -**/ - std::vector operator[](const int sample)const; -/*! \brief Overloaded version of operator[] for time. - -Sometimes it is useful to ask for data at a specified time without worrying -about the time conversion. This simplifies that process. It is still subject -to an exception if the the time requested is outside the data range. - -\param time is the time of the requested sample -\return 3 vector of data samples at requested time -\exception MsPASSError will be thrown if the time is outside the data range. - -*/ - std::vector operator[](const double time)const; -/*! Standard destructor. */ - virtual ~CoreSeismogram(){}; -/*! - Apply inverse transformation matrix to return data to cardinal direction components. - - It is frequently necessary to make certain a set of three component data are oriented - to the standard reference frame (EW, NS, Vertical). This function does this. - For efficiency it checks the components_are_cardinal variable and does nothing if - it is set true. Otherwise, it applies the inverse transformation and then sets this variable true. - Note even if the current transformation matrix is not orthogonal it will be put back into - cardinal coordinates. - \exception SeisppError thrown if the an inversion of the transformation matrix is required and that - matrix is singular. This can happen if the transformation matrix is incorrectly defined or the - actual data are coplanar. -**/ - void rotate_to_standard(); - // This overloaded pair do the same thing for a vector - // specified as a unit vector nu or as spherical coordinate angles -/*! - Rotate data using a P wave type coordinate definition. - - In seismology the longitudinal motion direction of a P wave defines a direction - in space. This method rotates the data into a coordinate system defined by a - direction passed through the argument. The data are rotated such that x1 becomes - the transverse component, x2 becomes radial, and x3 becomes longitudinal. In the - special case for a vector pointing in the x3 direction the data are not altered. - The transformation matrix is effectively the matrix product of two coordinate rotations: - (1) rotation around x3 by angle phi and (2) rotation around x1 by theta. - -The sense of this transformation is confusing because of a difference in -convention between spherical coordinates and standard earth coordinates. -In particular, orientation on the earth uses a convention with x2 being -the x2 axis and bearings are relative to that with a standard azimuth -measured clockwise from north. Spherical coordinate angle phi (used here) -is measured counterclockwise relative to the x1 axis, which is east in -standard earth coordinates. This transformation is computed using a phi -angle. To use this then to compute a transformation to standard ray -coordinates with x2 pointing in the direction of wavefront advance, -phi should be set to pi/2-azimuth which gives the phi angle needed to rotate -x2 to radial. This is extremely confusing because in spherical coordinates -it would be more intuitive to rotate x1 to radial, but this is NOT the -convention used here. In general to use this feature the best way to avoid -this confusion is to use the PMHalfSpaceModel procedure to compute a -SphericalCoordinate object consistent with given propagation direction -defined by a slowness vector. Alternatively, use the free_surface_transformation -method defined below. - -A VERY IMPORTANT thing to recognize about this tranformation is it will -always yield a result relative to cardinal coordinates. i.e. if the data -had been previously rotated or were not originally in ENZ form they -will be first transformed to ENZ before actually performing this -transformation. Use the transform or horizontal rotation method to -create cummulative transformations. - -\param sc defines final x3 direction (longitudinal) in a spherical coordinate structure. -**/ - void rotate(mspass::utility::SphericalCoordinate& sc); - -/*! - Rotate data using a P wave type coordinate definition. - - In seismology the longitudinal motion direction of a P wave defines a direction - in space. This method rotates the data into a coordinate system defined by a - direction passed through the argument. The data are rotated such that x1 becomes - the transverse component, x2 becomes radial, and x3 becomes longitudinal. In the - special case for a vector pointing in the x3 direction the data are not altered. - - This method effectively turns nu into a SphericalCoordinate object and calles the - related rotate method that has a SphericalCoordinate object as an argument. The - potential confusion of orientation is not as extreme here. After the transformation - x3prime will point in the direction of nu, x2 will be in the x3-x3prime plane (rotation by - theta) and orthogonal to x3prime, and x1 will be horizontal and perpendicular to x2prime - and x3prime. - -A VERY IMPORTANT thing to recognize about this tranformation is it will -always yield a result relative to cardinal coordinates. i.e. if the data -had been previously rotated or were not originally in ENZ form they -will be first transformed to ENZ before actually performing this -transformation. Use the transform or horizontal rotation method to - -\param nu defines direction of x3 direction (longitudinal) as a unit vector with three components. -**/ - void rotate(const double nu[3]); + /*! + Holds the actual data. + + Matrix is 3xns. Thus the rows are the component number + and columns define time position. Note there is a redundancy in + these definitions that must be watched if you manipulate the + contents of this matrix. That is, BasicTimeSeries defines ns, but + the u matrix has it's own internal size definitions. Currently no + tests are done to validate this consistency. All constructors handle + this, but again because u is public be very careful in altering u. + **/ + mspass::utility::dmatrix u; + + /*! + Default constructor. + + Sets ns to zero and builds an empty data matrix. The live variable + in BasicTimeSeries is also set false. + **/ + CoreSeismogram(); + /*! + Simplest parameterized constructor. + + Initializes data and sets aside memory for + matrix of size 3xnsamples. The data matrix is not initialized + and the object is marked as not live. + \param nsamples number of samples expected for holding data. + **/ + CoreSeismogram(const size_t nsamples); + /*! + Construct a three component seismogram from three TimeSeries objects. + + A three component seismogram is commonly assembled from individual + single channel components. This constructor does the process taking + reasonable care to deal with (potentially) irregular start and end + times of the individual components. If the start and end times are + all the same it uses a simple copy operation. Otherwise it runs a + more complicated (read much slower) algorithm that handles the ragged + start and stop times by adding a marked gap. + + If start or end times are not constant the algorithm shortens the output + to the latest start time and earliest end time respectively. + + Note this constructor requires variables hang and vang, which are + orientation angles defined in the CSS3.0 schema (NOT spherical + coordinates by the way), by set for each component. This is used to + construct the transformation matrix for the object that allows, + for example, removing raw data orientation errors using rotate_to_standard. + The constructor will throw an exception if any component does not have + these attributes set in their Metadata area. + + \exception SeisppError exception can be throw for a variety of serious + problems. + \param ts vector of 3 TimeSeries objects to be used to assemble + this Seismogram. Input vector order could be + arbitrary because a transformation matrix is computed, but for + efficiency standard order (E,N,Z) is advised. + \param component_to_clone the auxiliary parameters (Metadata and + BasicTimeSeries common parameters) + from one of the components is cloned to assure common required + parameters are copied to this object. This argument controls which + of the three components passed through ts is used. Default is 0. + + **/ + CoreSeismogram(const std::vector &ts, + const unsigned int component_to_clone = 0); + /*! \brief Construct from Metadata definition that includes data path. + * + A Metadata object is sufficiently general that it can contain enough + information to contruct an object from attributes contained in it. + This constuctor uses that approach, with the actual loading of data + being an option (on by default). In mspass this is constructor is + used to load data with Metadata constructed from MongoDB and then + using the path created from two parameters (dir and dfile used as + in css3.0 wfdisc) to read data. The API is general but the + implementation in mspass is very rigid. It blindly assumes the + data being read are binary doubles in the right byte order and + ordered in the native order for dmatrix (Fortran order). i.e. + the constuctor does a raw fread of ns*3 doubles into the internal + array used in the dmatrix implementation. + + A second element of the Metadata that is special for MsPASS is the + handling of the transformation matrix by this constructor. In MsPASS + the transformation matrix is stored as a python object in MongoDB. + This constructor aims to fetch that entity with the key 'tmatrix'. + To be more robust and simpler to use with data not loaded from mongodb + we default tmatrix to assume the data are in standard coordinates. That is, + if the key tmatrix is not defined in Metadata passed as arg0, the + constructor assumes it should set the transformation matrix to an identity. + Use set_transformation_matrix if that assumption is wrong for your data. + + \param md is the Metadata used for the construction. + + \param load_data if true (default) a file name is constructed from + dir+"/"+dfile, the file is openned, fseek is called to foff, + data are read with fread, and the file is closed. If false a dmatrix + for u is still created of size 3xns, but the matrix is only initialized + to all zeros. + + \exception Will throw a MsPASSError if required metadata are missing. + */ + CoreSeismogram(const mspass::utility::Metadata &md, + const bool load_data = true); + /*! + Standard copy constructor. + **/ + + CoreSeismogram(const CoreSeismogram &); + /* These overload virtual methods in BasicTimeSeries. */ + /*! \brief Set the sample interval. + + This method is complicated by the need to sync the changed value with + Metadata. That is further complicated by the need to support aliases + for the keys used to defined dt in Metadata. That is handled by + first setting the internal dt value and then going through a fixed list + of valid alias keys for dt. Any that exist are changed. If + none were previously defined the unique name (see documentation) is + added to Metadata. + + \param sample_interval is the new data sample interval to be used. + */ + void set_dt(const double sample_interval); + /*! \brief Set the number of samples attribute for data. + + This method is complicated by the need to sync the changed value with + Metadata. That is further complicated by the need to support aliases + for the keys used to defined npts in Metadata. That is handled by + first setting the internal npts value (actually ns) and then going through a + fixed list of valid alias keys for npts. Any that exist are changed. If + none were previously defined the unique name (see documentation) is + added to Metadata. + + This attribute has an additional complication compared to other setter + that are overrides from BasicTimeSeries. That is, the number of points + define the data buffer size to hold the sample data. To guarantee + the buffer size and the internal remain consistent this method clears + any existing content of the dmatrix u and initializes the 3xnpts matrix to 0s. + Note this means if one is using this to assemble a data object in pieces + you MUST call this method before loading any data or it will be cleared + and you will mysteriously find the data are all zeros. + + \param npts is the new number of points to set. + */ + void set_npts(const size_t npts); + /*! \brief Sync the number of samples attribute with actual data size. + + This method syncs the npts attribute with the actual size of the dmatrix u. + It also syncs aliases in the same way as the set_npts method. + + */ + void sync_npts(); + /*! \brief Set the data start time. + + This method is complicated by the need to sync the changed value with + Metadata. That is further complicated by the need to support aliases + for the keys used to defined npts in Metadata. That is handled by + first setting the internal t0 value and then going through a fixed list + of valid alias keys for it. Any that exist are changed. If + none were previously defined the unique name (see documentation) is + added to Metadata. + + This is a dangerous method to use on real data as it can mess up the time + if not handled correctly. It should be used only when that sharp knife is + needed such as in assembling data outside of constructors in a test program. + + \param t0in is the new data sample interval to be used. + */ + void set_t0(const double t0in); + /*! + Standard assignment operator. + **/ + CoreSeismogram &operator=(const CoreSeismogram &); + /*! \brief Summation operator. + + Summing data from signals of irregular length requires handling potential + mismatches in size and overlap. This behaves the way a += operator should + logically behave in that situation. That is, because the lhs is where + the sum is being accumulated, the size is always controlled by the left hand + side of the operator. Any portions of the right hand side that are outside + the t0 to endtime() of the left hand side are silently discarded. If the + start time of the right hand side is greater than t0 or the endtime is less + than endtime of the lhs there will be discontinuties in the sum there + the ends of the rhs are inside the range of the lhs. + + \param d is other signal to add to this. + \exception MsPASSError can be thrown if lhs and rhs do not have matching + time standards. + **/ + CoreSeismogram &operator+=(const CoreSeismogram &d); + /*! Addition operator. + + This operator is implemented in a standard way utilizing operator+=. + For data with irregular start and end times that has an important + consequence; the operator is not communative. i.e given x an y + z=x+y will not yield the same result as z=y+x. + */ + const CoreSeismogram operator+(const CoreSeismogram &other) const; + /*! Multiply data by a scalar. */ + CoreSeismogram &operator*=(const double); + /*! \brief Subtraction operator. + + Differencing data from signals of irregular length requires handling potential + mismatches in size and overlap. This behaves the way a -= operator should + logically behave in that situation. That is, because the lhs is where + the sum is being accumulated, the size is always controlled by the left hand + side of the operator. Any portions of the right hand side that are outside + the t0 to endtime() of the left hand side are silently discarded. If the + start time of the right hand side is greater than t0 or the endtime is less + than endtime of the lhs there will be discontinuties in the sum there + the ends of the rhs are inside the range of the lhs. + + \param d is other signal to subract from this. + \exception MsPASSError can be thrown if lhs and rhs do not have matching + time standards. + **/ + CoreSeismogram &operator-=(const CoreSeismogram &d); + /*! Subtraction operator. + + This operator is implemented in a standard way utilizing operator-=. + For data with irregular start and end times that has an important + consequence; the operator is not communative. i.e given x an y + z=x-y will not yield the same result as z=-(y-x). + */ + const CoreSeismogram operator-(const CoreSeismogram &other) const; + /*! + Extract a sample from data vector. + + A sample in this context means a three-vector at a requested + sample index. Range checking is implicit because + of the internal use of the dmatrix to store the samples of + data. This operator is an alternative to extracting samples + through indexing of the internal dmatrix u that holds the data. + + \param sample is the sample number requested (must be in range or an exception + will be thrown) + + \exception MsPASSError if the requested sample is outside + the range of the data. Note this includes an implicit "outside" + defined when the contents are marked dead. + Note the code does this by catching an error thrown by dmatrix + in this situation, printing the error message from the dmatrix + object, and then throwing a new SeisppError with a shorter + message. + \return std::vector containing a 3 vector of the samples at requested sample + number + + \param sample is the integer sample number of data desired. + **/ + std::vector operator[](const int sample) const; + /*! \brief Overloaded version of operator[] for time. + + Sometimes it is useful to ask for data at a specified time without worrying + about the time conversion. This simplifies that process. It is still + subject to an exception if the the time requested is outside the data range. + + \param time is the time of the requested sample + \return 3 vector of data samples at requested time + \exception MsPASSError will be thrown if the time is outside the data range. + + */ + std::vector operator[](const double time) const; + /*! Standard destructor. */ + virtual ~CoreSeismogram() {}; + /*! + Apply inverse transformation matrix to return data to cardinal direction + components. + + It is frequently necessary to make certain a set of three component data are + oriented to the standard reference frame (EW, NS, Vertical). This function + does this. For efficiency it checks the components_are_cardinal variable and + does nothing if it is set true. Otherwise, it applies the inverse + transformation and then sets this variable true. Note even if the current + transformation matrix is not orthogonal it will be put back into cardinal + coordinates. + \exception SeisppError thrown if the an inversion of the transformation + matrix is required and that matrix is singular. This can happen if the + transformation matrix is incorrectly defined or the actual data are coplanar. + **/ + void rotate_to_standard(); + // This overloaded pair do the same thing for a vector + // specified as a unit vector nu or as spherical coordinate angles + /*! + Rotate data using a P wave type coordinate definition. + + In seismology the longitudinal motion direction of a P wave defines a + direction in space. This method rotates the data into a coordinate system + defined by a direction passed through the argument. The data are rotated such + that x1 becomes the transverse component, x2 becomes radial, and x3 becomes + longitudinal. In the special case for a vector pointing in the x3 direction + the data are not altered. The transformation matrix is effectively the matrix + product of two coordinate rotations: (1) rotation around x3 by angle phi and + (2) rotation around x1 by theta. + + The sense of this transformation is confusing because of a difference in + convention between spherical coordinates and standard earth coordinates. + In particular, orientation on the earth uses a convention with x2 being + the x2 axis and bearings are relative to that with a standard azimuth + measured clockwise from north. Spherical coordinate angle phi (used here) + is measured counterclockwise relative to the x1 axis, which is east in + standard earth coordinates. This transformation is computed using a phi + angle. To use this then to compute a transformation to standard ray + coordinates with x2 pointing in the direction of wavefront advance, + phi should be set to pi/2-azimuth which gives the phi angle needed to rotate + x2 to radial. This is extremely confusing because in spherical coordinates + it would be more intuitive to rotate x1 to radial, but this is NOT the + convention used here. In general to use this feature the best way to avoid + this confusion is to use the PMHalfSpaceModel procedure to compute a + SphericalCoordinate object consistent with given propagation direction + defined by a slowness vector. Alternatively, use the + free_surface_transformation method defined below. + + A VERY IMPORTANT thing to recognize about this tranformation is it will + always yield a result relative to cardinal coordinates. i.e. if the data + had been previously rotated or were not originally in ENZ form they + will be first transformed to ENZ before actually performing this + transformation. Use the transform or horizontal rotation method to + create cummulative transformations. + + \param sc defines final x3 direction (longitudinal) in a spherical coordinate + structure. + **/ + void rotate(mspass::utility::SphericalCoordinate &sc); + + /*! + Rotate data using a P wave type coordinate definition. + + In seismology the longitudinal motion direction of a P wave defines a + direction in space. This method rotates the data into a coordinate system + defined by a direction passed through the argument. The data are rotated such + that x1 becomes the transverse component, x2 becomes radial, and x3 becomes + longitudinal. In the special case for a vector pointing in the x3 direction + the data are not altered. + + This method effectively turns nu into a SphericalCoordinate object and calles + the related rotate method that has a SphericalCoordinate object as an + argument. The potential confusion of orientation is not as extreme here. + After the transformation x3prime will point in the direction of nu, x2 will be + in the x3-x3prime plane (rotation by theta) and orthogonal to x3prime, and x1 + will be horizontal and perpendicular to x2prime and x3prime. + + A VERY IMPORTANT thing to recognize about this tranformation is it will + always yield a result relative to cardinal coordinates. i.e. if the data + had been previously rotated or were not originally in ENZ form they + will be first transformed to ENZ before actually performing this + transformation. Use the transform or horizontal rotation method to + + \param nu defines direction of x3 direction (longitudinal) as a unit vector + with three components. + **/ + void rotate(const double nu[3]); /*! \brief Rotate horizontals by a simple angle in degrees. A common transformation in 3C processing is a rotation of the @@ -397,130 +405,133 @@ transformation. Use the transform or horizontal rotation method to direction (in radians). */ void rotate(const double phi); -/*! - Applies an arbitrary transformation matrix to the data. - i.e. after calling this method the data will have been multiplied by the matrix a - and the transformation matrix will be updated. The later allows cascaded - transformations to data. - -\param a is a C style 3x3 matrix. -**/ - void transform(const double a[3][3]); -/*! - Computes and applies the Kennett [1991] free surface transformation matrix. - - Kennett [1991] gives the form for a free surface transformation operator - that reduces to a nonorthogonal transformation matrix when the wavefield is - not evanescent. On output x1 will be transverse, x2 will be SV (radial), - and x3 will be longitudinal. - -\param u slowness vector off the incident wavefield -\param vp0 Surface P wave velocity -\param vs0 Surface S wave velocity. -**/ - void free_surface_transformation(const mspass::seismic::SlownessVector u, const double vp0, const double vs0); -/*! Return current transformation matrix. - -The transformation matrix is maintained internally in this object. -Transformations like rotations and the transform method can change make -this matrix not an identity matrix. It should always be an identity -matrix when the coordinates are cardinal (i.e. ENZ). - -\return 3x3 transformation matrix. -*/ - mspass::utility::dmatrix get_transformation_matrix() const - { - mspass::utility::dmatrix result(3,3); - for(int i=0;i<3;++i) - for(int j=0;j<3;++j) result(i,j)=tmatrix[i][j]; - return result; + /*! + Applies an arbitrary transformation matrix to the data. + i.e. after calling this method the data will have been multiplied by the + matrix a and the transformation matrix will be updated. The later allows + cascaded transformations to data. + + \param a is a C style 3x3 matrix. + **/ + void transform(const double a[3][3]); + /*! + Computes and applies the Kennett [1991] free surface transformation matrix. + + Kennett [1991] gives the form for a free surface transformation operator + that reduces to a nonorthogonal transformation matrix when the wavefield is + not evanescent. On output x1 will be transverse, x2 will be SV (radial), + and x3 will be longitudinal. + + \param u slowness vector off the incident wavefield + \param vp0 Surface P wave velocity + \param vs0 Surface S wave velocity. + **/ + void free_surface_transformation(const mspass::seismic::SlownessVector u, + const double vp0, const double vs0); + /*! Return current transformation matrix. + + The transformation matrix is maintained internally in this object. + Transformations like rotations and the transform method can change make + this matrix not an identity matrix. It should always be an identity + matrix when the coordinates are cardinal (i.e. ENZ). + + \return 3x3 transformation matrix. + */ + mspass::utility::dmatrix get_transformation_matrix() const { + mspass::utility::dmatrix result(3, 3); + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + result(i, j) = tmatrix[i][j]; + return result; }; -/*! \brief Define the transformaton matrix. - * - Occasionally we need to set the transformation matrix manually. - The type example is input with a format where the component - directions are embedded. We use a dmatrix as it is more - easily wrapped for python than the raw C 2D array which - really doesn't translate well between the languages. - - \param A is the 3X3 matrix copied to the internal transformation - matrix array. - - \return true if the given transformation matrix is an identity - meaning components_are_cardinal gets set true. - false if the test for an identity matrix fails. - \exception Will throw a MsPASSError if the input matrix is - not 3x3. - */ - bool set_transformation_matrix(const mspass::utility::dmatrix& A); -/*! \brief Define the transformaton matrix with a C style 3x3 matrix. - - \param a is a C style 3x3 matrix. - - \return true if the given transformation matrix is an identity - meaning components_are_cardinal gets set true. - false if the test for an identity matrix fails. - \exception Will throw a MsPASSError if the input matrix is - not 3x3. - */ + /*! \brief Define the transformaton matrix. + * + Occasionally we need to set the transformation matrix manually. + The type example is input with a format where the component + directions are embedded. We use a dmatrix as it is more + easily wrapped for python than the raw C 2D array which + really doesn't translate well between the languages. + + \param A is the 3X3 matrix copied to the internal transformation + matrix array. + + \return true if the given transformation matrix is an identity + meaning components_are_cardinal gets set true. + false if the test for an identity matrix fails. + \exception Will throw a MsPASSError if the input matrix is + not 3x3. + */ + bool set_transformation_matrix(const mspass::utility::dmatrix &A); + /*! \brief Define the transformaton matrix with a C style 3x3 matrix. + + \param a is a C style 3x3 matrix. + + \return true if the given transformation matrix is an identity + meaning components_are_cardinal gets set true. + false if the test for an identity matrix fails. + \exception Will throw a MsPASSError if the input matrix is + not 3x3. + */ bool set_transformation_matrix(const double a[3][3]); -/*! \brief Define the transformaton matrix with a python object. + /*! \brief Define the transformaton matrix with a python object. - \param a is a python object of 9 elements with types of dmatrix, numpy array, or list. + \param a is a python object of 9 elements with types of dmatrix, numpy array, + or list. - \return true if the given transformation matrix is an identity - meaning components_are_cardinal gets set true. - false if the test for an identity matrix fails. - \exception Will throw a MsPASSError if the input type or dimension is not recognized. - */ + \return true if the given transformation matrix is an identity + meaning components_are_cardinal gets set true. + false if the test for an identity matrix fails. + \exception Will throw a MsPASSError if the input type or dimension is not + recognized. + */ bool set_transformation_matrix(pybind11::object a); -/*! Returns true of components are cardinal. */ - bool cardinal()const {return components_are_cardinal;}; -/*! Return true if the components are orthogonal. */ - bool orthogonal()const {return components_are_orthogonal;}; -/*! -Returns the end time (time associated with last data sample) -of this data object. -**/ - double endtime()const noexcept - { - return(mt0+mdt*static_cast(u.columns()-1)); + /*! Returns true of components are cardinal. */ + bool cardinal() const { return components_are_cardinal; }; + /*! Return true if the components are orthogonal. */ + bool orthogonal() const { return components_are_orthogonal; }; + /*! + Returns the end time (time associated with last data sample) + of this data object. + **/ + double endtime() const noexcept { + return (mt0 + mdt * static_cast(u.columns() - 1)); }; protected: - /*! - Defines if the contents of this object are components of an orthogonal basis. - - Most raw 3c seismic data use orthogonal components, but this is not universal. - Furthermore, some transformations (e.g. the free surface transformation operator) - define transformations to basis sets that are not orthogonal. Because - detecting orthogonality from a transformation is a nontrivial thing - (rounding error is the complication) this is made a part of the object to - simplify a number of algorithms. - **/ - bool components_are_orthogonal; - /*! - Defines of the contents of the object are in Earth cardinal coordinates. - - Cardinal means the cardinal directions at a point on the earth. That is, - x1 is positive east, x2 is positive north, and x3 is positive up. - Like the components_are_orthogonal variable the purpose of this variable is - to simplify common tests for properties of a given data series. - **/ - bool components_are_cardinal; // true if x1=e, x2=n, x3=up - /*! - Transformation matrix. - - This is a 3x3 transformation that defines how the data in this object is - produced from cardinal coordinates. That is, if u is the contents of this - object the data in cardinal directions can be produced by tmatrix^-1 * u. - **/ - double tmatrix[3][3]; + /*! + Defines if the contents of this object are components of an orthogonal basis. + + Most raw 3c seismic data use orthogonal components, but this is not + universal. Furthermore, some transformations (e.g. the free surface + transformation operator) define transformations to basis sets that are not + orthogonal. Because detecting orthogonality from a transformation is a + nontrivial thing (rounding error is the complication) this is made a part of + the object to simplify a number of algorithms. + **/ + bool components_are_orthogonal; + /*! + Defines of the contents of the object are in Earth cardinal coordinates. + + Cardinal means the cardinal directions at a point on the earth. That is, + x1 is positive east, x2 is positive north, and x3 is positive up. + Like the components_are_orthogonal variable the purpose of this variable is + to simplify common tests for properties of a given data series. + **/ + bool components_are_cardinal; // true if x1=e, x2=n, x3=up + /*! + Transformation matrix. + + This is a 3x3 transformation that defines how the data in this object is + produced from cardinal coordinates. That is, if u is the contents of this + object the data in cardinal directions can be produced by tmatrix^-1 * u. + **/ + double tmatrix[3][3]; + private: /* This is used internally when necessary to test if a computed or input matrix is an identity matrix. */ bool tmatrix_is_cardinal(); }; -} //end mspass::seismic namespace enscapsulation -#endif // End guard +} // namespace mspass::seismic +#endif // End guard diff --git a/cxx/include/mspass/seismic/CoreTimeSeries.h b/cxx/include/mspass/seismic/CoreTimeSeries.h index 64118e462..b96b2dd25 100644 --- a/cxx/include/mspass/seismic/CoreTimeSeries.h +++ b/cxx/include/mspass/seismic/CoreTimeSeries.h @@ -1,10 +1,10 @@ #ifndef _MSPASS_CORETIMESERIES_H_ #define _MSPASS_CORETIMESERIES_H_ -#include #include "mspass/seismic/BasicTimeSeries.h" #include "mspass/utility/Metadata.h" +#include -namespace mspass::seismic{ +namespace mspass::seismic { /*! \brief Scalar time series data object. This data object extends BasicTimeSeries mainly by adding a vector of @@ -13,189 +13,189 @@ that aren't essential to define the data object, but which are necessary for some algorithms. \author Gary L. Pavlis **/ -class CoreTimeSeries: public mspass::seismic::BasicTimeSeries , - public mspass::utility::Metadata -{ +class CoreTimeSeries : public mspass::seismic::BasicTimeSeries, + public mspass::utility::Metadata { public: -/*! -Actual data stored as an STL vector container. -Note the STL guarantees the data elements of vector container are -contiguous in memory like FORTRAN vectors. As a result things -like the BLAS can be used with data object by using a syntax -like this: if d is a CoreTimeSeries object, the address of the first sample of -the data is &(d.s[0]). -**/ - std::vectors; -/*! -Default constructor. Initializes object data to zeros and sets the -initial STL vector size to 0 length. -**/ - CoreTimeSeries(); -/*! -Similar to the default constructor but creates a vector of data -with nsin samples and initializes all samples to 0.0. -This vector can safely be accessed with the vector index -operator (i.e. operator []). A corollary is that push_back -or push_front applied to this vector will alter it's length -so use this only if the size of the data to fill the object is -already known. -**/ - CoreTimeSeries(const size_t nsin); - virtual ~CoreTimeSeries(){}; -/*! Partially construct from components. - -There are times one wants to use the Metadata area as a template to -flesh out a CoreTimeSeries as what might be called skin and bones: skin is -Metadata and bones as BasicTimeSeries data. This constructor initializes -those two base classes but does not fully a valid data vector. It only -attempts to fetch the number of points expected for the data vector using -the npts metadata (integer) key (i.e. it sets npts to md.get_int("npts")). -It then creates the data vector of that length and initialzies it to all zeros.*/ - CoreTimeSeries(const BasicTimeSeries& bts,const Metadata& md); -/*! -Standard copy constructor. -**/ - CoreTimeSeries(const CoreTimeSeries&); - /* These overload virtual methods in BasicTimeSeries. */ - /*! \brief Set the sample interval. + /*! + Actual data stored as an STL vector container. + Note the STL guarantees the data elements of vector container are + contiguous in memory like FORTRAN vectors. As a result things + like the BLAS can be used with data object by using a syntax + like this: if d is a CoreTimeSeries object, the address of the first sample of + the data is &(d.s[0]). + **/ + std::vector s; + /*! + Default constructor. Initializes object data to zeros and sets the + initial STL vector size to 0 length. + **/ + CoreTimeSeries(); + /*! + Similar to the default constructor but creates a vector of data + with nsin samples and initializes all samples to 0.0. + This vector can safely be accessed with the vector index + operator (i.e. operator []). A corollary is that push_back + or push_front applied to this vector will alter it's length + so use this only if the size of the data to fill the object is + already known. + **/ + CoreTimeSeries(const size_t nsin); + virtual ~CoreTimeSeries() {}; + /*! Partially construct from components. + + There are times one wants to use the Metadata area as a template to + flesh out a CoreTimeSeries as what might be called skin and bones: skin is + Metadata and bones as BasicTimeSeries data. This constructor initializes + those two base classes but does not fully a valid data vector. It only + attempts to fetch the number of points expected for the data vector using + the npts metadata (integer) key (i.e. it sets npts to md.get_int("npts")). + It then creates the data vector of that length and initialzies it to all + zeros.*/ + CoreTimeSeries(const BasicTimeSeries &bts, const Metadata &md); + /*! + Standard copy constructor. + **/ + CoreTimeSeries(const CoreTimeSeries &); + /* These overload virtual methods in BasicTimeSeries. */ + /*! \brief Set the sample interval. + +This method is complicated by the need to sync the changed value with + Metadata. That is further complicated by the need to support aliases + for the keys used to defined dt in Metadata. That is handled by + first setting the internal dt value and then going through a fixed list + of valid alias keys for dt. Any that exist are changed. If + none were previously defined the unique name (see documentation) is + added to Metadata. + + \param sample_interval is the new data sample interval to be used. + */ + void set_dt(const double sample_interval); + /*! \brief Set the number of samples attribute for data. This method is complicated by the need to sync the changed value with - Metadata. That is further complicated by the need to support aliases - for the keys used to defined dt in Metadata. That is handled by - first setting the internal dt value and then going through a fixed list - of valid alias keys for dt. Any that exist are changed. If - none were previously defined the unique name (see documentation) is - added to Metadata. - - \param sample_interval is the new data sample interval to be used. - */ - void set_dt(const double sample_interval); - /*! \brief Set the number of samples attribute for data. - - This method is complicated by the need to sync the changed value with - Metadata. That is further complicated by the need to support aliases - for the keys used to defined npts in Metadata. That is handled by - first setting the internal npts value (actually ns) and then going through a fixed list - of valid alias keys for npts. Any that exist are changed. If - none were previously defined the unique name (see documentation) is - added to Metadata. - - This attribute has an additional complication compared to other setter - that are overrides from BasicTimeSeries. That is, the number of points - define the data buffer size to hold the sample data. To guarantee - the buffer size and the internal remain consistent this method clears - any existing content of the vector s and initializes npts points to 0.0. - Note this means if one is using this to assemble a data object in pieces - you MUST call this method before loading any data or it will be cleared - and you will mysteriously find the data are all zeros. - - \param npts is the new number of points to set. - */ - void set_npts(const size_t npts); - /*! \brief Sync the number of samples attribute with actual data size. - - This method syncs the npts attribute with the actual size of the vector s. - It also syncs aliases in the same way as the set_npts method. - - */ - void sync_npts(); - /*! \brief Set the data start time. - - This method is complicated by the need to sync the changed value with - Metadata. That is further complicated by the need to support aliases - for the keys used to defined npts in Metadata. That is handled by - first setting the internal t0 value and then going through a fixed list - of valid alias keys for it. Any that exist are changed. If - none were previously defined the unique name (see documentation) is - added to Metadata. - - This is a dangerous method to use on real data as it can mess up the time - if not handled correctly. It should be used only when that sharp knife is - needed such as in assembling data outside of constructors in a test program. - - \param t0in is the new data sample interval to be used. - */ - void set_t0(const double t0in); -/* Depricated in 2020 API change - no longer needed with new BasicTimeSeries. -Returns the end time (time associated with last data sample) -of this data object. - -*/ -/* - double endtime()const noexcept - { - return(mt0+mdt*static_cast(s.size()-1)); - }; -*/ -/*! -Standard assignment operator. -**/ - CoreTimeSeries& operator=(const CoreTimeSeries& parent); -/*! \brief Summation operator. - -Summing data from signals of irregular length requires handling potential -mismatches in size and overlap. This behaves the way a += operator should -logically behave in that situation. That is, because the lhs is where -the sum is being accumulated, the size is always controlled by the left hand -side of the operator. Any portions of the right hand side that are outside -the t0 to endtime() of the left hand side are silently discarded. If the -start time of the right hand side is greater than t0 or the endtime is less -than endtime of the lhs there will be discontinuties in the sum there -the ends of the rhs are inside the range of the lhs. - -\param d is other signal to add to this. -\exception MsPASSError can be thrown if lhs and rhs do not have matching -time standards. -**/ - CoreTimeSeries& operator+=(const CoreTimeSeries& d); - /*! Addition operator. - - This operator is implemented in a standard way utilizing operator+=. - For data with irregular start and end times that has an important - consequence; the operator is not communative. i.e given x an y - z=x+y will not yield the same result as z=y+x. - */ - const CoreTimeSeries operator+(const CoreTimeSeries& other) const; - -/*! Multiply data by a scalar. */ - CoreTimeSeries& operator*=(const double); - /*! \brief Subtraction operator. - - Differencing data from signals of irregular length requires handling potential - mismatches in size and overlap. This behaves the way a -= operator should - logically behave in that situation. That is, because the lhs is where - the sum is being accumulated, the size is always controlled by the left hand - side of the operator. Any portions of the right hand side that are outside - the t0 to endtime() of the left hand side are silently discarded. If the - start time of the right hand side is greater than t0 or the endtime is less - than endtime of the lhs there will be discontinuties in the sum there - the ends of the rhs are inside the range of the lhs. - - \param d is other signal to subract from this. - \exception MsPASSError can be thrown if lhs and rhs do not have matching - time standards. - **/ - CoreTimeSeries& operator-=(const CoreTimeSeries& d); - /*! Subtraction operator. - - This operator is implemented in a standard way utilizing operator-=. - For data with irregular start and end times that has an important - consequence; the operator is not communative. i.e given x an y - z=x-y will not yield the same result as z=-(y-x). - */ - const CoreTimeSeries operator-(const CoreTimeSeries& other) const; -/*! -Extract a sample from data vector with range checking. -Because the data vector is public in this interface -this operator is simply an alterative interface to this->s[sample]. - -\exception SeisppError exception if the requested sample is outside - the range of the data. Note this includes an implicit "outside" - defined when the contents are marked dead. - -\param sample is the integer sample number of data desired. -**/ - double operator[](size_t const sample) const; + Metadata. That is further complicated by the need to support aliases + for the keys used to defined npts in Metadata. That is handled by + first setting the internal npts value (actually ns) and then going through a + fixed list of valid alias keys for npts. Any that exist are changed. If + none were previously defined the unique name (see documentation) is + added to Metadata. + + This attribute has an additional complication compared to other setter + that are overrides from BasicTimeSeries. That is, the number of points + define the data buffer size to hold the sample data. To guarantee + the buffer size and the internal remain consistent this method clears + any existing content of the vector s and initializes npts points to 0.0. + Note this means if one is using this to assemble a data object in pieces + you MUST call this method before loading any data or it will be cleared + and you will mysteriously find the data are all zeros. + + \param npts is the new number of points to set. + */ + void set_npts(const size_t npts); + /*! \brief Sync the number of samples attribute with actual data size. + + This method syncs the npts attribute with the actual size of the vector s. + It also syncs aliases in the same way as the set_npts method. + + */ + void sync_npts(); + /*! \brief Set the data start time. + + This method is complicated by the need to sync the changed value with + Metadata. That is further complicated by the need to support aliases + for the keys used to defined npts in Metadata. That is handled by + first setting the internal t0 value and then going through a fixed list + of valid alias keys for it. Any that exist are changed. If + none were previously defined the unique name (see documentation) is + added to Metadata. + + This is a dangerous method to use on real data as it can mess up the time + if not handled correctly. It should be used only when that sharp knife is + needed such as in assembling data outside of constructors in a test program. + + \param t0in is the new data sample interval to be used. + */ + void set_t0(const double t0in); + /* Depricated in 2020 API change - no longer needed with new BasicTimeSeries. + Returns the end time (time associated with last data sample) + of this data object. + + */ + /* + double endtime()const noexcept + { + return(mt0+mdt*static_cast(s.size()-1)); + }; + */ + /*! + Standard assignment operator. + **/ + CoreTimeSeries &operator=(const CoreTimeSeries &parent); + /*! \brief Summation operator. + + Summing data from signals of irregular length requires handling potential + mismatches in size and overlap. This behaves the way a += operator should + logically behave in that situation. That is, because the lhs is where + the sum is being accumulated, the size is always controlled by the left hand + side of the operator. Any portions of the right hand side that are outside + the t0 to endtime() of the left hand side are silently discarded. If the + start time of the right hand side is greater than t0 or the endtime is less + than endtime of the lhs there will be discontinuties in the sum there + the ends of the rhs are inside the range of the lhs. + + \param d is other signal to add to this. + \exception MsPASSError can be thrown if lhs and rhs do not have matching + time standards. + **/ + CoreTimeSeries &operator+=(const CoreTimeSeries &d); + /*! Addition operator. + + This operator is implemented in a standard way utilizing operator+=. + For data with irregular start and end times that has an important + consequence; the operator is not communative. i.e given x an y + z=x+y will not yield the same result as z=y+x. + */ + const CoreTimeSeries operator+(const CoreTimeSeries &other) const; + + /*! Multiply data by a scalar. */ + CoreTimeSeries &operator*=(const double); + /*! \brief Subtraction operator. + + Differencing data from signals of irregular length requires handling potential + mismatches in size and overlap. This behaves the way a -= operator should + logically behave in that situation. That is, because the lhs is where + the sum is being accumulated, the size is always controlled by the left hand + side of the operator. Any portions of the right hand side that are outside + the t0 to endtime() of the left hand side are silently discarded. If the + start time of the right hand side is greater than t0 or the endtime is less + than endtime of the lhs there will be discontinuties in the sum there + the ends of the rhs are inside the range of the lhs. + + \param d is other signal to subract from this. + \exception MsPASSError can be thrown if lhs and rhs do not have matching + time standards. + **/ + CoreTimeSeries &operator-=(const CoreTimeSeries &d); + /*! Subtraction operator. + + This operator is implemented in a standard way utilizing operator-=. + For data with irregular start and end times that has an important + consequence; the operator is not communative. i.e given x an y + z=x-y will not yield the same result as z=-(y-x). + */ + const CoreTimeSeries operator-(const CoreTimeSeries &other) const; + /*! + Extract a sample from data vector with range checking. + Because the data vector is public in this interface + this operator is simply an alterative interface to this->s[sample]. + + \exception SeisppError exception if the requested sample is outside + the range of the data. Note this includes an implicit "outside" + defined when the contents are marked dead. + + \param sample is the integer sample number of data desired. + **/ + double operator[](size_t const sample) const; }; -} // End mspass::seismic namespace -#endif //end guard +} // namespace mspass::seismic +#endif // end guard diff --git a/cxx/include/mspass/seismic/DataGap.h b/cxx/include/mspass/seismic/DataGap.h index 5ffb5d3c0..3a7073533 100644 --- a/cxx/include/mspass/seismic/DataGap.h +++ b/cxx/include/mspass/seismic/DataGap.h @@ -1,10 +1,10 @@ #ifndef _MSPASS_SEISMIC_DATAGAP_H_ #define _MSPASS_SEISMIC_DATAGAP_H_ -#include -#include #include "mspass/algorithms/TimeWindow.h" +#include +#include -namespace mspass::seismic{ +namespace mspass::seismic { /*! \brief Function object used for weak comparison to order TimeWindow objects. // TimeWindow objects are used, among other things, to define real @@ -13,97 +13,101 @@ namespace mspass::seismic{ // determine if a time is inside a particular time window. //\author Gary L. Pavlis **/ -class TimeWindowCmp -{ +class TimeWindowCmp { public: - bool operator()(const mspass::algorithms::TimeWindow ti1, - const mspass::algorithms::TimeWindow ti2) const - {return(ti1.end& twlist); - DataGap(const DataGap& parent):gaps(parent.gaps){}; - virtual ~DataGap(){}; -/*! -Checks if data at time ttest is a gap or valid data. -This function is like the overloaded version with an int argument except -it uses a time instead of sample number for the query. -\param ttest - time to be tested. -**/ - bool is_gap(const double ttest); //query by time -/*! -Checks if a given data segment has a gap. -For efficiency it is often useful to ask if a whole segment of data is -free of gaps. Most time series algorithms cannot process through data -gaps so normal practice would be to drop data with any gaps in a -requested time segment. -\return true if time segment has any data gaps -\param twin time window of data to test defined by a TimeWindow object -**/ - bool has_gap(const mspass::algorithms::TimeWindow twin); -/*! -Global test to see if data has any gaps defined. -Gap processing is expensive and we need this simple method to -test to see if the associated object has any gaps defined. -\return true if the associated object has any gaps defined. -**/ - bool has_gap(){return(!gaps.empty());}; -/*! -Adds a gap to the gap definitions for this data object. -Sometimes an algorithm detects or needs to create a gap (e.g. a mute, -or a constructor). -This function provides a common mechanism to define such a gap in the data. -**/ - void add_gap(const mspass::algorithms::TimeWindow tw); -/*! Getter returns a list of TimeWindows defining a set of gaps. */ - std::list get_gaps() const; - /*! \brief Clear gaps. + /*! Default construtor. Does nothing but create empty gap container. */ + DataGap() {}; + /*! Construct with an initial list of TimeWindows defining gaps. */ + DataGap(const std::list &twlist); + DataGap(const DataGap &parent) : gaps(parent.gaps) {}; + virtual ~DataGap() {}; + /*! + Checks if data at time ttest is a gap or valid data. + This function is like the overloaded version with an int argument except + it uses a time instead of sample number for the query. + \param ttest - time to be tested. + **/ + bool is_gap(const double ttest); // query by time + /*! + Checks if a given data segment has a gap. + For efficiency it is often useful to ask if a whole segment of data is + free of gaps. Most time series algorithms cannot process through data + gaps so normal practice would be to drop data with any gaps in a + requested time segment. + \return true if time segment has any data gaps + \param twin time window of data to test defined by a TimeWindow object + **/ + bool has_gap(const mspass::algorithms::TimeWindow twin); + /*! + Global test to see if data has any gaps defined. + Gap processing is expensive and we need this simple method to + test to see if the associated object has any gaps defined. + \return true if the associated object has any gaps defined. + **/ + bool has_gap() { return (!gaps.empty()); }; + /*! + Adds a gap to the gap definitions for this data object. + Sometimes an algorithm detects or needs to create a gap (e.g. a mute, + or a constructor). + This function provides a common mechanism to define such a gap in the data. + **/ + void add_gap(const mspass::algorithms::TimeWindow tw); + /*! Getter returns a list of TimeWindows defining a set of gaps. */ + std::list get_gaps() const; + /*! \brief Clear gaps. + + It is sometimes necessary to clear gap definitions. + This is particularly important when a descendent of this class + is cloned and then morphed into something else. + This method clears the entire content. This class assumes + gaps are an immutable property of recorded data. A subclass + could be used to add that functionality. + */ + void clear_gaps() { + if (!gaps.empty()) + gaps.clear(); + }; + /*! Return number of defined gaps. */ + int number_gaps() const { return gaps.size(); }; + /*! Return the subset of gaps within a specified time interval. + * + * \param tw TimeWindow defining range to be returned. Note + * overlaps with the edge will be returned with range outside the + * range defined by tw. If the tw is larger than the range of + * the current content returns a copy of itself. + */ + DataGap subset(const mspass::algorithms::TimeWindow tw) const; + /*! Shift the times of all gaps by a value. + * + When used with a TimeSeries or Seismogram the concept of UTC + versus relative time requires shifting the time origin. + This method should be used in that context or any other context + where the data origin is shifted. The number passed as shift + is subtracted from all the window start and end times that define + data gaps. + */ + void translate_origin(double time_of_new_origin); + /*! Standard assignment operator.*/ + DataGap &operator=(const DataGap &parent); + /*! Add contents of another DataGap container to this one. */ + DataGap &operator+=(const DataGap &other); - It is sometimes necessary to clear gap definitions. - This is particularly important when a descendent of this class - is cloned and then morphed into something else. - This method clears the entire content. This class assumes - gaps are an immutable property of recorded data. A subclass - could be used to add that functionality. - */ - void clear_gaps(){if(!gaps.empty())gaps.clear();}; - /*! Return number of defined gaps. */ - int number_gaps() const{return gaps.size();}; - /*! Return the subset of gaps within a specified time interval. - * - * \param tw TimeWindow defining range to be returned. Note - * overlaps with the edge will be returned with range outside the - * range defined by tw. If the tw is larger than the range of - * the current content returns a copy of itself. - */ - DataGap subset(const mspass::algorithms::TimeWindow tw) const; - /*! Shift the times of all gaps by a value. - * - When used with a TimeSeries or Seismogram the concept of UTC - versus relative time requires shifting the time origin. - This method should be used in that context or any other context - where the data origin is shifted. The number passed as shift - is subtracted from all the window start and end times that define - data gaps. - */ - void translate_origin(double time_of_new_origin); - /*! Standard assignment operator.*/ - DataGap& operator=(const DataGap& parent); - /*! Add contents of another DataGap container to this one. */ - DataGap& operator+=(const DataGap& other); protected: /*! \brief Holds data gap definitions. We use an STL set object to define data gaps for any time series object derived from this base class. The set is keyed by a TimeWindow which allows a simple, fast way to define a time range with invalid data. */ - std::set gaps; + std::set + gaps; }; -} // End mspass::seismic namespace -#endif //end guard +} // namespace mspass::seismic +#endif // end guard diff --git a/cxx/include/mspass/seismic/Ensemble.h b/cxx/include/mspass/seismic/Ensemble.h index 2a5b22031..9badd3081 100644 --- a/cxx/include/mspass/seismic/Ensemble.h +++ b/cxx/include/mspass/seismic/Ensemble.h @@ -1,13 +1,12 @@ #ifndef _MSPASS_ENSEMBLE_H_ #define _MSPASS_ENSEMBLE_H_ -#include -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" #include "mspass/utility/memory_constants.h" +#include -namespace mspass::seismic{ -template class Ensemble : public mspass::utility::Metadata -{ +namespace mspass::seismic { +template class Ensemble : public mspass::utility::Metadata { public: /*! \brief Container holding data objects. @@ -20,7 +19,7 @@ template class Ensemble : public mspass::utility::Metadata */ std::vector member; /*! Default constructor. */ - Ensemble(){}; + Ensemble() {}; /*! \brief Reserve space but build empty ensemble. Often we know what the size of an ensemble is before we start @@ -29,22 +28,22 @@ template class Ensemble : public mspass::utility::Metadata \param - expected number of members. */ - Ensemble(const size_t n){member.reserve(n);}; + Ensemble(const size_t n) { member.reserve(n); }; /*! Partial constructor to clone metadata and set aside n slots but no data*/ - Ensemble(const mspass::utility::Metadata& md,const size_t n) : mspass::utility::Metadata(md) - { - member.reserve(n); + Ensemble(const mspass::utility::Metadata &md, const size_t n) + : mspass::utility::Metadata(md) { + member.reserve(n); }; /*! Standard copy constructor. */ - Ensemble(const Ensemble& parent) : mspass::utility::Metadata(dynamic_cast(parent)), - member(parent.member){}; + Ensemble(const Ensemble &parent) + : mspass::utility::Metadata( + dynamic_cast(parent)), + member(parent.member) {}; /*! Standard assignment operator. */ - Ensemble& operator=(const Ensemble& parent) - { - if(this!=(&parent)) - { + Ensemble &operator=(const Ensemble &parent) { + if (this != (&parent)) { this->Metadata::operator=(parent); - member=parent.member; + member = parent.member; } return *this; }; @@ -59,10 +58,11 @@ template class Ensemble : public mspass::utility::Metadata \param n is the member to be extracted and returned. */ - Tdata& operator[](const size_t n) const - try{ - return(this->member[n]); - }catch(...){throw;}; + Tdata &operator[](const size_t n) const try { + return (this->member[n]); + } catch (...) { + throw; + }; /*! \brief updates ensemble header (Metadata). Sometime it is helpful to change a group of header attributes stored @@ -74,12 +74,13 @@ template class Ensemble : public mspass::utility::Metadata \param newmd contains new Metadata to use for updates. */ - void update_metadata(const mspass::utility::Metadata& newmd) - try{ + void update_metadata(const mspass::utility::Metadata &newmd) try { mspass::utility::Metadata *md; - md=dynamic_cast(this); + md = dynamic_cast(this); (*md) += newmd; - }catch(...){throw;}; + } catch (...) { + throw; + }; /*! \brief copy ensemble metadata to all members. An ensemble has global metadata, but each member is required to have @@ -88,37 +89,31 @@ template class Ensemble : public mspass::utility::Metadata previous key:value pairs in a member that are also present in the ensemble metadata. */ - void sync_metadata() - { - size_t i; - for(i=0;imember.size();++i) - { - mspass::utility::Metadata *mdmember=&(this->member[i]); - (*mdmember)+=dynamic_cast(*this); - } + void sync_metadata() { + size_t i; + for (i = 0; i < this->member.size(); ++i) { + mspass::utility::Metadata *mdmember = &(this->member[i]); + (*mdmember) += dynamic_cast(*this); + } }; /*! \brief copy ensemble metadata to all members except for the ones excluded. - */ - void sync_metadata(std::vector exclude) - { + */ + void sync_metadata(std::vector exclude) { mspass::utility::Metadata sync_md(*this); - for (size_t i = 0; i < exclude.size(); i++) - { - if(sync_md.is_defined(exclude[i])) { - sync_md.erase(exclude[i]); + for (size_t i = 0; i < exclude.size(); i++) { + if (sync_md.is_defined(exclude[i])) { + sync_md.erase(exclude[i]); } } - for (size_t i = 0; i < this->member.size(); ++i) - { + for (size_t i = 0; i < this->member.size(); ++i) { mspass::utility::Metadata *mdmember = &(this->member[i]); (*mdmember) += sync_md; } }; - }; - -/*! \brief Template class that extends Ensemble to include an error log and live tests. +/*! \brief Template class that extends Ensemble to include an error log and live +tests. This class extends the Ensemble class defined in MsPASS for bundling collections of data that can be pushed to an std::vector container. The @@ -137,17 +132,13 @@ algorithms that emit an ensemble (regardless of inputs). The live/dead tests and error logger make the handlers consistent with the atomic objects of mspass. */ -template class LoggingEnsemble : public Ensemble -{ +template class LoggingEnsemble : public Ensemble { public: /*! Standard mspass container for holding error logs. */ mspass::utility::ErrorLogger elog; /*! Default constructor. Initializes all pieces with default constructor and marks the ensemble dead.*/ - LoggingEnsemble(): Ensemble(), elog() - { - ensemble_is_live=false; - }; + LoggingEnsemble() : Ensemble(), elog() { ensemble_is_live = false; }; /* The next two constructors are used to provide a parallel qpi to CoreEnsemble - there might be another way to do this in the C++ code or @@ -160,9 +151,8 @@ template class LoggingEnsemble : public Ensemble ensemble, like the default constructor, is marked initially as dead. The user must call the set_live method of this ensemble after loading data or downstream processors may drop the data. */ - LoggingEnsemble(const size_t n) : Ensemble(n),elog() - { - ensemble_is_live=false; + LoggingEnsemble(const size_t n) : Ensemble(n), elog() { + ensemble_is_live = false; } /*! Construct a framework for the ensemble with metadata. @@ -170,12 +160,11 @@ template class LoggingEnsemble : public Ensemble and reserves n slots to be added. Be warned it marks the ensemble dead. Once valid data are loaded the user should call the set_live method for the ensemble to prevent the data from being ignored downstream.*/ - LoggingEnsemble(const mspass::utility::Metadata& md,const size_t n) - : Ensemble(md,n),elog() - { + LoggingEnsemble(const mspass::utility::Metadata &md, const size_t n) + : Ensemble(md, n), elog() { /* Might be advised to set this true, but since an object created by this method has only slots but no data validate would return false.*/ - ensemble_is_live=false; + ensemble_is_live = false; } /*! Construct from all pieces. @@ -183,29 +172,27 @@ template class LoggingEnsemble : public Ensemble construct for the pickle interface. It is unlikely to be of interest in a C++ application. Calls reserve only for member vector but but does not insert data - detail of the pickle implementation*/ - LoggingEnsemble(const mspass::utility::Metadata& md, - const mspass::utility::ErrorLogger& elogin, const size_t ndata) - : Ensemble(md,ndata),elog(elogin) - { - }; + LoggingEnsemble(const mspass::utility::Metadata &md, + const mspass::utility::ErrorLogger &elogin, + const size_t ndata) + : Ensemble(md, ndata), elog(elogin) {}; /*! Standard copy constructor. */ - LoggingEnsemble(const LoggingEnsemble& parent) - : Ensemble(parent),elog(parent.elog) - { - ensemble_is_live=parent.ensemble_is_live; + LoggingEnsemble(const LoggingEnsemble &parent) + : Ensemble(parent), elog(parent.elog) { + ensemble_is_live = parent.ensemble_is_live; + }; + /*! Clone from a base class Ensemble. Initializes error null and sets live. + */ + LoggingEnsemble(const Ensemble &parent) : Ensemble(parent), elog() { + ensemble_is_live = true; }; - /*! Clone from a base class Ensemble. Initializes error null and sets live. */ - LoggingEnsemble(const Ensemble& parent) - : Ensemble(parent),elog() - { - ensemble_is_live=true; - }; /*! Markt the entire ensemble bad. */ - void kill(){ensemble_is_live=false;}; + void kill() { ensemble_is_live = false; }; /*! Getter to test if the ensemble has any valid data. */ - bool live() const {return ensemble_is_live;}; - /*! Complement to live method - returns true if there are no valid data members. */ - bool dead() const {return !ensemble_is_live;}; + bool live() const { return ensemble_is_live; }; + /*! Complement to live method - returns true if there are no valid data + * members. */ + bool dead() const { return !ensemble_is_live; }; /*! Check to see if ensemble has any live data. In processing one can occasionally (not rare but not common either) @@ -221,56 +208,52 @@ template class LoggingEnsemble : public Ensemble validate method. If validate is false it refuses to set the state live and returns false. If validate returns true it will return true. */ - bool set_live(){ - if(this->validate()) - { - ensemble_is_live=true; + bool set_live() { + if (this->validate()) { + ensemble_is_live = true; return true; - } - else + } else return false; }; /*! Standard assignment operator. */ - LoggingEnsemble& operator=(const LoggingEnsemble& parent) - { - if(&parent != this) - { + LoggingEnsemble &operator=(const LoggingEnsemble &parent) { + if (&parent != this) { this->mspass::utility::Metadata::operator=(parent); this->member.reserve(parent.member.size()); - this->member=parent.member; - elog=parent.elog; - ensemble_is_live=parent.ensemble_is_live; + this->member = parent.member; + elog = parent.elog; + ensemble_is_live = parent.ensemble_is_live; } return *this; }; - size_t memory_use() const - { + size_t memory_use() const { size_t memuse; memuse = sizeof(*this); - for(auto p=this->member.begin();p!=this->member.end();++p) - { + for (auto p = this->member.begin(); p != this->member.end(); ++p) { memuse += p->memory_use(); } /* Account for ensemble metadata */ /* We can only estimate the size of the Metadata container. These constants are defined in memory_constants.h */ - memuse += mspass::utility::memory_constants::MD_AVERAGE_SIZE*this->md.size(); - memuse += mspass::utility::memory_constants::KEY_AVERAGE_SIZE*this->changed_or_set.size(); + memuse += + mspass::utility::memory_constants::MD_AVERAGE_SIZE * this->md.size(); + memuse += mspass::utility::memory_constants::KEY_AVERAGE_SIZE * + this->changed_or_set.size(); /* Account for error log size */ - memuse += mspass::utility::memory_constants::ELOG_AVERAGE_SIZE*this->elog.size(); + memuse += mspass::utility::memory_constants::ELOG_AVERAGE_SIZE * + this->elog.size(); return memuse; }; -private: +private: bool ensemble_is_live; }; -template bool LoggingEnsemble::validate() -{ - for(auto dptr=this->member.begin();dptr!=this->member.end();++dptr) - { - if(dptr->live()) return true; +template bool LoggingEnsemble::validate() { + for (auto dptr = this->member.begin(); dptr != this->member.end(); ++dptr) { + if (dptr->live()) + return true; } return false; } @@ -301,7 +284,8 @@ instead in such a situation. */ /* Disable temporarily - needs a revision to match new history approach template - size_t set_inputs(ProcessingHistoryRecord& rec, const mspass::seismic::Ensemble& d) + size_t set_inputs(ProcessingHistoryRecord& rec, const +mspass::seismic::Ensemble& d) { try{ for(size_t i=0;i }catch(...){throw;}; }; */ -} // End mspass::seismic namespace encapsulation -#endif // End guard +} // namespace mspass::seismic +#endif // End guard diff --git a/cxx/include/mspass/seismic/PowerSpectrum.h b/cxx/include/mspass/seismic/PowerSpectrum.h index bc58c69c2..46d0ccaf6 100644 --- a/cxx/include/mspass/seismic/PowerSpectrum.h +++ b/cxx/include/mspass/seismic/PowerSpectrum.h @@ -1,16 +1,14 @@ #ifndef _POWER_SPECTRUM_H_ #define _POWER_SPECTRUM_H_ -#include +#include "mspass/seismic/BasicSpectrum.h" +#include "mspass/utility/ErrorLogger.h" #include "mspass/utility/Metadata.h" #include "mspass/utility/MsPASSError.h" -#include "mspass/utility/ErrorLogger.h" -#include "mspass/seismic/BasicSpectrum.h" -namespace mspass::seismic -{ +#include +namespace mspass::seismic { /*! Class defining the concept of a power psectrum. */ class PowerSpectrum : public mspass::seismic::BasicSpectrum, - public mspass::utility::Metadata -{ + public mspass::utility::Metadata { public: /*! Descriptive name assigned by creator of algorithm used to generate it. @@ -44,15 +42,12 @@ class PowerSpectrum : public mspass::seismic::BasicSpectrum, this estimate. */ - template PowerSpectrum(const mspass::utility::Metadata& md, - const std::vector& d, - const double dfin, - const std::string nm, - const double f0in, - const double dtin, - const int npts_in); - PowerSpectrum(const PowerSpectrum& parent); - PowerSpectrum& operator=(const PowerSpectrum& parent); + template + PowerSpectrum(const mspass::utility::Metadata &md, const std::vector &d, + const double dfin, const std::string nm, const double f0in, + const double dtin, const int npts_in); + PowerSpectrum(const PowerSpectrum &parent); + PowerSpectrum &operator=(const PowerSpectrum &parent); /*! \brief Standard accumulation operator. Sometimes we need to sum power spectra. Type examplel would be @@ -62,7 +57,7 @@ class PowerSpectrum : public mspass::seismic::BasicSpectrum, \exception will throw a MsPaSSError if the left and right side are not equal length of have different f0 of df values.*/ - PowerSpectrum& operator+=(const PowerSpectrum& other); + PowerSpectrum &operator+=(const PowerSpectrum &other); /*! \brief Compute amplitude spectrum from power spectrum. The amplitude spectrum is sqrt of the power values. This is a @@ -80,25 +75,25 @@ class PowerSpectrum : public mspass::seismic::BasicSpectrum, */ double power(const double f) const; - double frequency(const int sample_number) const - { + double frequency(const int sample_number) const { const std::string base_error("PowerSpectrum::frequency: "); - if(sample_number<0) throw mspass::utility::MsPASSError(base_error - + "Sample number parameter passed cannot be negative"); - if(sample_number>=(this->nf())) throw mspass::utility::MsPASSError(base_error - + "Sample number parameter passed xceeds range of spectrum array"); - return this->f0()+sample_number*this->df(); + if (sample_number < 0) + throw mspass::utility::MsPASSError( + base_error + "Sample number parameter passed cannot be negative"); + if (sample_number >= (this->nf())) + throw mspass::utility::MsPASSError( + base_error + + "Sample number parameter passed xceeds range of spectrum array"); + return this->f0() + sample_number * this->df(); }; std::vector frequencies() const; /* \brief Return number of frequencies in estimate (size of spectrum vector). Users should be aware that when zero padding is used the size of the spectrum vector will be larger than half the number of time series samples.*/ - size_t nf()const{return spectrum.size();}; + size_t nf() const { return spectrum.size(); }; /*! Return the nyquist frequency for this estimate. */ - double Nyquist() const { - return 1.0/(2.0*this->parent_dt); - }; + double Nyquist() const { return 1.0 / (2.0 * this->parent_dt); }; }; /*! Fully parameterized constructor template. @@ -106,18 +101,18 @@ See class definition of PowerSpectrum for usage. Template to allow data vector of multiple types. */ -template PowerSpectrum::PowerSpectrum(const mspass::utility::Metadata& md, - const std::vector& d,const double dfin,const std::string nm, - const double f0in, const double dtin, const int npts_in) - : BasicSpectrum(dfin,f0in,dtin,npts_in), - mspass::utility::Metadata(md), - elog() -{ - spectrum_type=nm; +template +PowerSpectrum::PowerSpectrum(const mspass::utility::Metadata &md, + const std::vector &d, const double dfin, + const std::string nm, const double f0in, + const double dtin, const int npts_in) + : BasicSpectrum(dfin, f0in, dtin, npts_in), mspass::utility::Metadata(md), + elog() { + spectrum_type = nm; spectrum.reserve(d.size()); - for(size_t k=0;k(d[k])); this->set_live(); }; -} //end namespace +} // namespace mspass::seismic #endif diff --git a/cxx/include/mspass/seismic/Seismogram.h b/cxx/include/mspass/seismic/Seismogram.h index 9908bacea..a4a4cf4b8 100644 --- a/cxx/include/mspass/seismic/Seismogram.h +++ b/cxx/include/mspass/seismic/Seismogram.h @@ -1,21 +1,22 @@ #ifndef _SEISMOGRAM_H_ #define _SEISMOGRAM_H_ #include "mspass/seismic/CoreSeismogram.h" -#include "mspass/utility/ProcessingHistory.h" #include "mspass/utility/ErrorLogger.h" +#include "mspass/utility/ProcessingHistory.h" -namespace mspass::seismic{ +namespace mspass::seismic { /*! \brief Implemntation of Seismogram for MsPASS. This is the working version of a three-component seismogram object used in the MsPASS framework. It extends CoreSeismogram by adding ProcessingHistory. */ class Seismogram : public mspass::seismic::CoreSeismogram, - public mspass::utility::ProcessingHistory -{ + public mspass::utility::ProcessingHistory { public: /*! Default constructor. Only runs subclass default constructors. */ - Seismogram() : mspass::seismic::CoreSeismogram(),mspass::utility::ProcessingHistory(){}; + Seismogram() + : mspass::seismic::CoreSeismogram(), + mspass::utility::ProcessingHistory() {}; /*! Bare bones constructor allocates space and little else. Sometimes it is helpful to construct a skeleton that can be fleshed out @@ -49,7 +50,7 @@ class Seismogram : public mspass::seismic::CoreSeismogram, \param d is the data to be copied to create the new Seismogram object */ - Seismogram(const mspass::seismic::CoreSeismogram& d); + Seismogram(const mspass::seismic::CoreSeismogram &d); /*! Contruct from a core seismogram and initialize history as origin. This constructor is a variant of a similar one built only from a @@ -67,7 +68,7 @@ class Seismogram : public mspass::seismic::CoreSeismogram, \param is core data to be cloned \param alg is the algorithm name to set for the origin history record. */ - Seismogram(const mspass::seismic::CoreSeismogram& d, const std::string alg); + Seismogram(const mspass::seismic::CoreSeismogram &d, const std::string alg); /*! \brief Partial constructor from base classes. This is a partial constructor useful sometimes for building a Seismogram @@ -79,9 +80,8 @@ class Seismogram : public mspass::seismic::CoreSeismogram, \param bts is the BasicTimeSeries that overrides any md data. \param md is the Metadata cloned to make the partially constructed object. */ - Seismogram(const mspass::seismic::BasicTimeSeries& bts, - const mspass::utility::Metadata& md); - + Seismogram(const mspass::seismic::BasicTimeSeries &bts, + const mspass::utility::Metadata &md); /*! \brief Construct from all pieces. @@ -101,10 +101,11 @@ mess with). tm is also a dmatrix representation the tmatrix stored internally as a 2d C array, but we use the dmatrix to mesh with serialization. */ - Seismogram(const mspass::seismic::BasicTimeSeries& b, const mspass::utility::Metadata& m, - const mspass::utility::ProcessingHistory& his, - const bool card, const bool ortho, - const mspass::utility::dmatrix& tm, const mspass::utility::dmatrix& uin); + Seismogram(const mspass::seismic::BasicTimeSeries &b, + const mspass::utility::Metadata &m, + const mspass::utility::ProcessingHistory &his, const bool card, + const bool ortho, const mspass::utility::dmatrix &tm, + const mspass::utility::dmatrix &uin); /*! Constructor driven by a Metadata object. The flexibilityof Metadata makes it helpful at times to build a Seismogram @@ -124,56 +125,57 @@ with serialization. \param readername is the algorithm name assigned to the top level history record. Defaults to "load3C" */ - Seismogram(const Metadata& md,const std::string jobname=std::string("test"), - const std::string jobid=std::string("UNDEFINED"), - const std::string readername=std::string("load3C"), - const std::string algid=std::string("0")); - -/*! \brief Construct from Metadata definition that includes data path. - * - A Metadata object is sufficiently general that it can contain enough - information to contruct an object from attributes contained in it. - This constuctor uses that approach, with the actual loading of data - being an option (on by default). In mspass this is constructor is - used to load data with Metadata constructed from MongoDB and then - using the path created from two parameters (dir and dfile used as - in css3.0 wfdisc) to read data. The API is general but the - implementation in mspass is very rigid. It blindly assumes the - data being read are binary doubles in the right byte order and - ordered in the native order for dmatrix (Fortran order). i.e. - the constuctor does a raw fread of ns*3 doubles into the internal - array used in the dmatrix implementation. - - A second element of the Metadata that is special for MsPASS is the - handling of the transformation matrix by this constructor. In MsPASS - the transformation matrix is stored as a python object in MongoDB. - This constructor aims to fetch that entity with the key 'tmatrix'. - To be more robust and simpler to use with data not loaded from mongodb - we default tmatrix to assume the data are in standard coordinates. That is, - if the key tmatrix is not defined in Metadata passed as arg0, the - constructor assumes it should set the transformation matrix to an identity. - Use set_transformation_matrix if that assumption is wrong for your data. - - \param md is the Metadata used for the construction. - - \param load_data if true (default) a file name is constructed from - dir+"/"+dfile, the file is openned, fseek is called to foff, - data are read with fread, and the file is closed. If false a dmatrix - for u is still created of size 3xns, but the matrix is only initialized - to all zeros. - - \exception Will throw a MsPASSError if required metadata are missing. - */ - Seismogram(const Metadata& md, bool load_data) - : mspass::seismic::CoreSeismogram(md,load_data), mspass::utility::ProcessingHistory() - {}; + Seismogram(const Metadata &md, + const std::string jobname = std::string("test"), + const std::string jobid = std::string("UNDEFINED"), + const std::string readername = std::string("load3C"), + const std::string algid = std::string("0")); + + /*! \brief Construct from Metadata definition that includes data path. + * + A Metadata object is sufficiently general that it can contain enough + information to contruct an object from attributes contained in it. + This constuctor uses that approach, with the actual loading of data + being an option (on by default). In mspass this is constructor is + used to load data with Metadata constructed from MongoDB and then + using the path created from two parameters (dir and dfile used as + in css3.0 wfdisc) to read data. The API is general but the + implementation in mspass is very rigid. It blindly assumes the + data being read are binary doubles in the right byte order and + ordered in the native order for dmatrix (Fortran order). i.e. + the constuctor does a raw fread of ns*3 doubles into the internal + array used in the dmatrix implementation. + + A second element of the Metadata that is special for MsPASS is the + handling of the transformation matrix by this constructor. In MsPASS + the transformation matrix is stored as a python object in MongoDB. + This constructor aims to fetch that entity with the key 'tmatrix'. + To be more robust and simpler to use with data not loaded from mongodb + we default tmatrix to assume the data are in standard coordinates. That is, + if the key tmatrix is not defined in Metadata passed as arg0, the + constructor assumes it should set the transformation matrix to an identity. + Use set_transformation_matrix if that assumption is wrong for your data. + + \param md is the Metadata used for the construction. + + \param load_data if true (default) a file name is constructed from + dir+"/"+dfile, the file is openned, fseek is called to foff, + data are read with fread, and the file is closed. If false a dmatrix + for u is still created of size 3xns, but the matrix is only initialized + to all zeros. + + \exception Will throw a MsPASSError if required metadata are missing. + */ + Seismogram(const Metadata &md, bool load_data) + : mspass::seismic::CoreSeismogram(md, load_data), + mspass::utility::ProcessingHistory() {}; /*! Standard copy constructor. */ - Seismogram(const Seismogram& parent) - : mspass::seismic::CoreSeismogram(parent), mspass::utility::ProcessingHistory(parent) - {}; - virtual ~Seismogram(){}; + Seismogram(const Seismogram &parent) + : mspass::seismic::CoreSeismogram(parent), + mspass::utility::ProcessingHistory(parent) {}; + virtual ~Seismogram() {}; /*! Standard assignment operator. */ - Seismogram& operator=(const Seismogram& parent); + Seismogram &operator=(const Seismogram &parent); /*! \brief Load just the ProcessingHistory data from another data source. Some algorithms don't handle processing history. In those situations it @@ -182,7 +184,7 @@ with serialization. \param h is the ProcessingHistory data to copy into this Seismogram. */ - void load_history(const mspass::utility::ProcessingHistory& h); + void load_history(const mspass::utility::ProcessingHistory &h); /*! Return an estimate of the memmory use by the data in this object. Memory consumed by a Seismogram object is needed to implement the @@ -194,5 +196,5 @@ with serialization. */ size_t memory_use() const; }; -}//END mspass::seismic namespace +} // namespace mspass::seismic #endif diff --git a/cxx/include/mspass/seismic/SeismogramWGaps.h b/cxx/include/mspass/seismic/SeismogramWGaps.h index c3715389a..151d18f77 100644 --- a/cxx/include/mspass/seismic/SeismogramWGaps.h +++ b/cxx/include/mspass/seismic/SeismogramWGaps.h @@ -1,11 +1,11 @@ #ifndef _MSPASS_SEISMIC_SEISWGAPS_H_ #define _MSPASS_SEISMIC_SEISWGAPS_H_ -#include "mspass/seismic/Seismogram.h" #include "mspass/seismic/DataGap.h" +#include "mspass/seismic/Seismogram.h" -namespace mspass::seismic{ -class SeismogramWGaps : public mspass::seismic::Seismogram, public mspass::seismic::DataGap -{ +namespace mspass::seismic { +class SeismogramWGaps : public mspass::seismic::Seismogram, + public mspass::seismic::DataGap { public: /*! \brief Constructor. @@ -13,13 +13,13 @@ class SeismogramWGaps : public mspass::seismic::Seismogram, public mspass::seism set gap is an issue. */ SeismogramWGaps(); /*! Copy constructor. */ - SeismogramWGaps(const SeismogramWGaps& parent); - SeismogramWGaps& operator=(const SeismogramWGaps& parent); + SeismogramWGaps(const SeismogramWGaps &parent); + SeismogramWGaps &operator=(const SeismogramWGaps &parent); ~SeismogramWGaps(); -/*! Force all data inside data gaps to zero. -**/ + /*! Force all data inside data gaps to zero. + **/ void zero_gaps(); }; -} //end mspass::seismic namespace -#endif //end guard +} // namespace mspass::seismic +#endif // end guard diff --git a/cxx/include/mspass/seismic/SlownessVector.h b/cxx/include/mspass/seismic/SlownessVector.h index 31bad77e9..abd151c4a 100644 --- a/cxx/include/mspass/seismic/SlownessVector.h +++ b/cxx/include/mspass/seismic/SlownessVector.h @@ -1,92 +1,90 @@ #ifndef _SLOWNESS_H_ #define _SLOWNESS_H_ -#include #include #include +#include -namespace mspass::seismic -{ -/*! \brief Slowness vector object. +namespace mspass::seismic { +/*! \brief Slowness vector object. Slowness vectors are a seismology concept used to describe wave propagation. A slowness vector points in the direction of propagation of a wave with a - magnitude equal to the slowness (1/velocity) of propagation. + magnitude equal to the slowness (1/velocity) of propagation. \author Gary L. Pavlis **/ -class SlownessVector -{ +class SlownessVector { public: -/*! - East-west component of slowness vector. -**/ - double ux; -/*! - North-south component of slowness vector. -**/ - double uy; -/*! - Default constructor. -**/ - SlownessVector(); -/*! \brief Fully parameterized constructor. + /*! + East-west component of slowness vector. + **/ + double ux; + /*! + North-south component of slowness vector. + **/ + double uy; + /*! + Default constructor. + **/ + SlownessVector(); + /*! \brief Fully parameterized constructor. -A slowness vector is defined by it's components. There is one ambiguity, however, -with a zero slowness vector. That is, normally direction of propagation is -inferred from the vector azimuth. A zero slowness vector has physical significance -(normal incidence) but presents and ambiguity in this regard. We use a defaulted -az0 parameter to specify the azimuth that should be used if the magnitude of -slowness vector is 0. + A slowness vector is defined by it's components. There is one ambiguity, + however, with a zero slowness vector. That is, normally direction of + propagation is inferred from the vector azimuth. A zero slowness vector has + physical significance (normal incidence) but presents and ambiguity in this + regard. We use a defaulted az0 parameter to specify the azimuth that should + be used if the magnitude of slowness vector is 0. + + \param ux0 - set x (EW) component to this value. + \param uy0 - set y (NS) component to this value. + \param az0 - use this as azimuth (radians) if this is a zero slowness vector + (default 0.0) + */ + SlownessVector(const double ux0, const double uy0, const double az0 = 0.0); + /*! + Copy constructor. + **/ + SlownessVector(const SlownessVector &); + /*! + Computes the magntitude of the slowness vector. + Value returned is in units of seconds/kilometer. + **/ + double mag() const noexcept; + /*! + Returns the propagation direction defined by a slowness vector. + Azimuth is a direction clockwise from north in the standard geographic + convention. Value returned is in radians. + **/ + double azimuth() const noexcept; + /*! + Returns the back azimuth direction defined by a slowness vector. + A back azimuth is 180 degrees away from the direction of propagation and + points along the great circle path directed back to the source point + from a given position. The value returned is in radians. + **/ + double baz() const noexcept; + /*! \brief Standard assignment operator. */ + SlownessVector &operator=(const SlownessVector &parent); + /* \brief Standard accumulation operator. */ + SlownessVector &operator+=(const SlownessVector &other); + /* \brief Standard subtract from operator. */ + SlownessVector &operator-=(const SlownessVector &other); + /* \brief Standard addition operator. */ + const SlownessVector operator+(const SlownessVector &other) const; + /* \brief Standard subraction operator. */ + const SlownessVector operator-(const SlownessVector &other) const; -\param ux0 - set x (EW) component to this value. -\param uy0 - set y (NS) component to this value. -\param az0 - use this as azimuth (radians) if this is a zero slowness vector - (default 0.0) -*/ - SlownessVector(const double ux0, const double uy0, const double az0=0.0); -/*! - Copy constructor. -**/ - SlownessVector(const SlownessVector&); -/*! - Computes the magntitude of the slowness vector. - Value returned is in units of seconds/kilometer. -**/ - double mag() const noexcept; -/*! - Returns the propagation direction defined by a slowness vector. - Azimuth is a direction clockwise from north in the standard geographic - convention. Value returned is in radians. -**/ - double azimuth() const noexcept; -/*! - Returns the back azimuth direction defined by a slowness vector. - A back azimuth is 180 degrees away from the direction of propagation and - points along the great circle path directed back to the source point - from a given position. The value returned is in radians. -**/ - double baz() const noexcept; -/*! \brief Standard assignment operator. */ - SlownessVector& operator=(const SlownessVector& parent); -/* \brief Standard accumulation operator. */ - SlownessVector& operator+=(const SlownessVector& other); -/* \brief Standard subtract from operator. */ - SlownessVector& operator-=(const SlownessVector& other); -/* \brief Standard addition operator. */ - const SlownessVector operator+(const SlownessVector& other) const; -/* \brief Standard subraction operator. */ - const SlownessVector operator-(const SlownessVector& other) const; private: - double azimuth0; - friend class boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { - ar & ux; - ar & uy; - ar & azimuth0; - }; + double azimuth0; + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar & ux; + ar & uy; + ar & azimuth0; + }; }; -} // End mspass::seismic namespace declaration +} // namespace mspass::seismic #endif diff --git a/cxx/include/mspass/seismic/TimeSeries.h b/cxx/include/mspass/seismic/TimeSeries.h index fbe1a8847..7b8b4b448 100644 --- a/cxx/include/mspass/seismic/TimeSeries.h +++ b/cxx/include/mspass/seismic/TimeSeries.h @@ -1,23 +1,22 @@ #ifndef _TIMESERIES_H_ #define _TIMESERIES_H_ #include "mspass/seismic/CoreTimeSeries.h" -#include "mspass/utility/ProcessingHistory.h" #include "mspass/utility/ErrorLogger.h" -namespace mspass::seismic{ - /*! \brief Implemntation of TimeSeries for MsPASS. +#include "mspass/utility/ProcessingHistory.h" +namespace mspass::seismic { +/*! \brief Implemntation of TimeSeries for MsPASS. - This is the working version of a three-component TimeSeries object used - in the MsPASS framework. It extends CoreTimeSeries by adding - common MsPASS components (ProcessingHistory). It may evolve with additional - special features. */ +This is the working version of a three-component TimeSeries object used +in the MsPASS framework. It extends CoreTimeSeries by adding +common MsPASS components (ProcessingHistory). It may evolve with additional +special features. */ class TimeSeries : public mspass::seismic::CoreTimeSeries, - public mspass::utility::ProcessingHistory -{ + public mspass::utility::ProcessingHistory { public: /*! Default constructor. Only runs subclass default constructors. */ - TimeSeries() : mspass::seismic::CoreTimeSeries(), - mspass::utility::ProcessingHistory() - {}; + TimeSeries() + : mspass::seismic::CoreTimeSeries(), + mspass::utility::ProcessingHistory() {}; /*! Bare bones constructor allocates space and little else. Sometimes it is helpful to construct a skeleton that can be fleshed out @@ -34,26 +33,26 @@ class TimeSeries : public mspass::seismic::CoreTimeSeries, \param nsamples is the number of samples needed for storing the data vector. */ - TimeSeries(const size_t nsamples) : mspass::seismic::CoreTimeSeries(nsamples), - mspass::utility::ProcessingHistory() - {}; + TimeSeries(const size_t nsamples) + : mspass::seismic::CoreTimeSeries(nsamples), + mspass::utility::ProcessingHistory() {}; /*! Partially construct from components. There are times one wants to use the Metadata area as a template to - flesh out a CoreTimeSeries as what might be called skin and bones: skin is - Metadata and bones as BasicTimeSeries data. This constructor initializes - those two base classes but does not fully a valid data vector. It only - attempts to fetch the number of points expected for the data vector using - the npts metadata (integer) key (i.e. it sets npts to md.get_int("npts")). - It then creates the data vector of that length and initialzies it to all zeros. + flesh out a CoreTimeSeries as what might be called skin and bones: skin + is Metadata and bones as BasicTimeSeries data. This constructor + initializes those two base classes but does not fully a valid data vector. + It only attempts to fetch the number of points expected for the data vector + using the npts metadata (integer) key (i.e. it sets npts to + md.get_int("npts")). It then creates the data vector of that length and + initialzies it to all zeros. This constructor is largely a wrapper for the CoreTimeSeries version with the same signature but it also initalizes the ProcessingHistory to null (calling the default constructor)*/ - TimeSeries(const BasicTimeSeries& bts,const Metadata& md) - : mspass::seismic::CoreTimeSeries(bts,md), - mspass::utility::ProcessingHistory() - {}; + TimeSeries(const BasicTimeSeries &bts, const Metadata &md) + : mspass::seismic::CoreTimeSeries(bts, md), + mspass::utility::ProcessingHistory() {}; /*! Partially construct from Metadata alone. This constructor is useful for interaction with MongoDB where the @@ -76,7 +75,7 @@ class TimeSeries : public mspass::seismic::CoreTimeSeries, not defined. */ - TimeSeries(const Metadata& md); + TimeSeries(const Metadata &md); /*! \brief Construct from lower level CoreTimeSeries. @@ -93,8 +92,9 @@ class TimeSeries : public mspass::seismic::CoreTimeSeries, \param d is the data to be copied to create the new TimeSeries object. Note that means a deep copy wherein the data vector is copied. */ - TimeSeries(const mspass::seismic::CoreTimeSeries& d) - : mspass::seismic::CoreTimeSeries(d),mspass::utility::ProcessingHistory(){}; + TimeSeries(const mspass::seismic::CoreTimeSeries &d) + : mspass::seismic::CoreTimeSeries(d), + mspass::utility::ProcessingHistory() {}; /*! Contruct from a core time series and initialize history as origin. This constructor is a variant of a similar one built only from a @@ -112,40 +112,42 @@ class TimeSeries : public mspass::seismic::CoreTimeSeries, \param is core data to be cloned \param alg is the algorithm name to set for the origin history record. */ - TimeSeries(const mspass::seismic::CoreTimeSeries& d, const std::string alg); -/*! Special constructor for pickle interface. - -The pickle interface required by spark presented problems for MsPASS. The -complicated data objects of TimeSeries and TimeSeries have to be serialized -in pieces. This constructor is only used in the function called -indirectly by pickle.load. It essentially contains a TimeSeries dismembered -into the pieces that can be the serialized independently. The -parameters are each associated with one of those required pieces and -are simply copied to build a valid TimeSeries object in the pickle.load -function */ - TimeSeries(const mspass::seismic::BasicTimeSeries& b,const mspass::utility::Metadata& m, - const mspass::utility::ProcessingHistory& mcts, const std::vector& d); + TimeSeries(const mspass::seismic::CoreTimeSeries &d, const std::string alg); + /*! Special constructor for pickle interface. + + The pickle interface required by spark presented problems for MsPASS. The + complicated data objects of TimeSeries and TimeSeries have to be serialized + in pieces. This constructor is only used in the function called + indirectly by pickle.load. It essentially contains a TimeSeries dismembered + into the pieces that can be the serialized independently. The + parameters are each associated with one of those required pieces and + are simply copied to build a valid TimeSeries object in the pickle.load + function */ + TimeSeries(const mspass::seismic::BasicTimeSeries &b, + const mspass::utility::Metadata &m, + const mspass::utility::ProcessingHistory &mcts, + const std::vector &d); /*! Standard copy constructor. */ - TimeSeries(const TimeSeries& parent) - : mspass::seismic::CoreTimeSeries(parent), mspass::utility::ProcessingHistory(parent){}; + TimeSeries(const TimeSeries &parent) + : mspass::seismic::CoreTimeSeries(parent), + mspass::utility::ProcessingHistory(parent) {}; /*! Standard assignment operator. */ - TimeSeries& operator=(const TimeSeries& parent); - TimeSeries& operator+=(const TimeSeries& d) - { - dynamic_cast(*this)+=dynamic_cast(d); - return(*this); + TimeSeries &operator=(const TimeSeries &parent); + TimeSeries &operator+=(const TimeSeries &d) { + dynamic_cast(*this) += + dynamic_cast(d); + return (*this); }; - TimeSeries& operator*=(const double scale) - { - dynamic_cast(*this) *= scale; + TimeSeries &operator*=(const double scale) { + dynamic_cast(*this) *= scale; return *this; }; - TimeSeries& operator-=(const TimeSeries& d) - { - dynamic_cast(*this)-=dynamic_cast(d); - return(*this); + TimeSeries &operator-=(const TimeSeries &d) { + dynamic_cast(*this) -= + dynamic_cast(d); + return (*this); }; - void load_history(const mspass::utility::ProcessingHistory& h); + void load_history(const mspass::utility::ProcessingHistory &h); /*! Return an estimate of the memmory use by the data in this object. Memory consumed by a TimeSeries object is needed to implement the @@ -157,5 +159,5 @@ function */ */ size_t memory_use() const; }; -}//END mspass::seismic namespace +} // namespace mspass::seismic #endif diff --git a/cxx/include/mspass/seismic/TimeSeriesWGaps.h b/cxx/include/mspass/seismic/TimeSeriesWGaps.h index 3e0d449b1..726c372b5 100644 --- a/cxx/include/mspass/seismic/TimeSeriesWGaps.h +++ b/cxx/include/mspass/seismic/TimeSeriesWGaps.h @@ -1,30 +1,30 @@ #ifndef _MSPASS_SEISMIC_TSWGAPS_H_ #define _MSPASS_SEISMIC_TSWGAPS_H_ -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/DataGap.h" -namespace mspass::seismic{ -class TimeSeriesWGaps : public TimeSeries, public DataGap -{ +#include "mspass/seismic/TimeSeries.h" +namespace mspass::seismic { +class TimeSeriesWGaps : public TimeSeries, public DataGap { public: /*! \brief Constructor. Will need a set of conastructors. Requires some thought as how to set gap is an issue. */ - TimeSeriesWGaps():TimeSeries(),DataGap(){}; + TimeSeriesWGaps() : TimeSeries(), DataGap() {}; /*! Partial copy constructor from a plain TimeSeries. Sometimes we need to build the skeleton of a gappy TimeSeries from a regular TimeSeries. This does that and builds makes a copy of the TimeSeries and creates an empty container that defines the gaps. */ - TimeSeriesWGaps(const TimeSeries& parent) : TimeSeries(parent),DataGap(){}; + TimeSeriesWGaps(const TimeSeries &parent) : TimeSeries(parent), DataGap() {}; /*! Copy constructor. */ - TimeSeriesWGaps(const TimeSeriesWGaps& parent) - : TimeSeries(dynamic_cast(parent)), - DataGap(dynamic_cast(parent)){};; - TimeSeriesWGaps(const TimeSeries& tsp, const DataGap& dgp) - : TimeSeries(tsp), DataGap(dgp) {}; - TimeSeriesWGaps& operator=(const TimeSeriesWGaps& parent); - virtual ~TimeSeriesWGaps(){}; + TimeSeriesWGaps(const TimeSeriesWGaps &parent) + : TimeSeries(dynamic_cast(parent)), + DataGap(dynamic_cast(parent)) {}; + ; + TimeSeriesWGaps(const TimeSeries &tsp, const DataGap &dgp) + : TimeSeries(tsp), DataGap(dgp) {}; + TimeSeriesWGaps &operator=(const TimeSeriesWGaps &parent); + virtual ~TimeSeriesWGaps() {}; /*! Absolute to relative time conversion. Sometimes we want to convert data from absolute time (epoch times) @@ -34,9 +34,10 @@ class TimeSeriesWGaps : public TimeSeries, public DataGap variable and alters t0 by tshift. This method overrides the one in BasicTimeSeries to correctly reference gaps. - \param tshift - time shift applied to data before switching data to relative time mode. + \param tshift - time shift applied to data before switching data to relative + time mode. **/ - void ator(const double tshift); + void ator(const double tshift); /*! Relative to absolute time conversion. Sometimes we want to convert data from relative time to to an absolute time standard. An example would be converting @@ -45,34 +46,39 @@ class TimeSeriesWGaps : public TimeSeries, public DataGap This operation simply switches the tref variable and alters t0 by tshift. - This method overrides the one in BasicTimeSeries to correctly reference gaps. - \param tshift - time shift applied to data before switching data to absolute time mode. + This method overrides the one in BasicTimeSeries to correctly reference + gaps. + \param tshift - time shift applied to data before switching data to absolute + time mode. - NOTE: This method is maintained only for backward compatibility. May be depricated - in favor of method that uses internally stored private shift variable. + NOTE: This method is maintained only for backward compatibility. May be + depricated in favor of method that uses internally stored private shift + variable. **/ - void rtoa(const double tshift); + void rtoa(const double tshift); /*! Relative to absolute time conversion. Sometimes we want to convert data from relative time to to an absolute time standard. An example would be converting segy shot data to something that could be processed like earthquake data in a css3.0 database. - This method returns data previously converted to relative back to absolute using the - internally stored time shift attribute. + This method returns data previously converted to relative back to absolute + using the internally stored time shift attribute. - This method overrides the one in BasicTimeSeries to correctly reference gaps.*/ - void rtoa(); + This method overrides the one in BasicTimeSeries to correctly reference + gaps.*/ + void rtoa(); /*! Shift the reference time. - Sometimes we need to shift the reference time t0. An example is a moveout correction. - This method shifts the reference time by dt. Note a positive dt means data aligned to - zero will be shifted left because relative time is t-t0. + Sometimes we need to shift the reference time t0. An example is a moveout + correction. This method shifts the reference time by dt. Note a positive + dt means data aligned to zero will be shifted left because relative time is + t-t0. */ - void shift(const double dt); -/*! Force all data inside data gaps to zero. -**/ + void shift(const double dt); + /*! Force all data inside data gaps to zero. + **/ void zero_gaps(); /*! Return an estimate of the memmory use by the data in this object. @@ -85,5 +91,5 @@ class TimeSeriesWGaps : public TimeSeries, public DataGap */ size_t memory_use() const; }; -} //end mspass::seismic namespace -#endif //end guard +} // namespace mspass::seismic +#endif // end guard diff --git a/cxx/include/mspass/seismic/keywords.h b/cxx/include/mspass/seismic/keywords.h index f0481e190..65875c601 100644 --- a/cxx/include/mspass/seismic/keywords.h +++ b/cxx/include/mspass/seismic/keywords.h @@ -13,12 +13,13 @@ each name are roughly the same as the concept line in the yaml file for the mspass schema at the time this file was created. Do not depend on an exact match, but the idea behind each phrase should not change (the concept). */ -namespace mspass::seismic{ +namespace mspass::seismic { /*! Number of data samples.*/ const std::string SEISMICMD_npts("npts"); /*! data sample interval in seconds.*/ const std::string SEISMICMD_dt("delta"); -/*! Time of first sample of data (epoch time or relative to some other time mark)*/ +/*! Time of first sample of data (epoch time or relative to some other time + * mark)*/ const std::string SEISMICMD_t0("starttime"); /*! Data sampling frequency in Hz=1/s */ const std::string SEISMICMD_sampling_rate("sampling_rate"); @@ -44,7 +45,8 @@ depth for borehole instruments) - TimeSeries*/ const std::string SEISMICMD_celev("channel_elev"); /*! Azimuth (in degree) of a seismometer component - horizontal angle*/ const std::string SEISMICMD_hang("channel_hang"); -/*! Inclination from +up (in degree) of a seismometer component - vertical angle*/ +/*! Inclination from +up (in degree) of a seismometer component - vertical + * angle*/ const std::string SEISMICMD_vang("channel_vang"); /* In MsPASS source information is retrieved from the source collection. @@ -73,17 +75,20 @@ const std::string SEISMICMD_uuid("uuid"); const std::string SEISMICMD_rawdata("rawdata"); /*! network code (net component of SEED net:sta:chan)*/ const std::string SEISMICMD_net("net"); -/*! station code assigned to a spot on Earth (sta component of SEED net:sta:chan)*/ +/*! station code assigned to a spot on Earth (sta component of SEED + * net:sta:chan)*/ const std::string SEISMICMD_sta("sta"); /*! channel name (e.g. HHZ, BHE, etc.) - normally a SEED channel code*/ const std::string SEISMICMD_chan("chan"); -/*! location code assigned to an instrument (loc component of SEED net:sta:chan)*/ +/*! location code assigned to an instrument (loc component of SEED + * net:sta:chan)*/ const std::string SEISMICMD_loc("loc"); /*! Define time reference (normally UTC or Relative)*/ const std::string SEISMICMD_time_standard("time_standard"); /*! Define the t0_shift attribute of BasicTimeSeries. */ const std::string SEISMICMD_t0_shift("starttime_shift"); -/*! Defines boolean used in BasicTimeSeries for properly handling Relative/UTC time standards*/ +/*! Defines boolean used in BasicTimeSeries for properly handling Relative/UTC + * time standards*/ const std::string SEISMICMD_utc_convertible("utc_convertible"); -} +} // namespace mspass::seismic #endif diff --git a/cxx/include/mspass/utility/AntelopePf.h b/cxx/include/mspass/utility/AntelopePf.h index 8ad6f488e..1edfeb634 100644 --- a/cxx/include/mspass/utility/AntelopePf.h +++ b/cxx/include/mspass/utility/AntelopePf.h @@ -1,19 +1,19 @@ #ifndef _ANTELOPE_PF_H_ #define _ANTELOPE_PF_H_ +#include "mspass/utility/Metadata.h" #include #include -#include "mspass/utility/Metadata.h" -namespace mspass{ -namespace utility{ +namespace mspass { +namespace utility { /*! key for accessing Tbl and Arr entries. * These are of use only when this object is converted to a Metadata object using the ConvertToMetadata method. This object holds - a representation of Tbl and Arr sections of the input pffile as - in map containers that are converted by that method and stored + a representation of Tbl and Arr sections of the input pffile as + in map containers that are converted by that method and stored with the boost::any capability. This allows them to be retrieved if - desired with these keys and the correct type specification. + desired with these keys and the correct type specification. */ const std::string pftbl_key("AntelopePfTbl"); const std::string pfarr_key("AntelopePfArr"); @@ -24,14 +24,14 @@ file in a single wrapper. The main constructor is actually act much like the Antelope pfread procedure. Internally this object does not use an antelope Pf at all directly, but it is a child of Metadata. Simple attributes -(i.e. key-value pairs) are posted directly to the Metadata +(i.e. key-value pairs) are posted directly to the Metadata associative array (map) container. Note that the parser attempts to guess the type of each value given in the obvious ways (periods imply real numbers, e or E imply real numbers, etc.) but the algorithm used may not be foolproof. The get -methods are from the Metadata object. Be warned like the Metadata +methods are from the Metadata object. Be warned like the Metadata object the type of an entry for a key can change and will be the last -one set. +one set. An Antelope Tbl in a pf file is converted to an stl list container of stl::string's that contain the input lines. This is a strong divergence from the tbl interface of Antelope, but one @@ -46,7 +46,7 @@ managed. A final note about this beast is that the entire thing was created with a tacit assumption the object itself is not huge. i.e. this implementation may not scale well if applied to very -large (millions) line pf files. This is a job for something like +large (millions) line pf files. This is a job for something like MongoDB. View this as a convenient format for building a Metadata object. Note, a code fragment to use this to create lower level metadata would go like this: @@ -55,106 +55,106 @@ metadata would go like this: \author Gary L. Pavlis, Indiana University -This version for MsPASS is derived from a similar thing called a -PfStyleMetadata object in antelope contrib. +This version for MsPASS is derived from a similar thing called a +PfStyleMetadata object in antelope contrib. */ -class AntelopePf : public Metadata -{ +class AntelopePf : public Metadata { public: - /*! Default constructor - does nothing.*/ - AntelopePf():Metadata(){}; - /*! \brief Construct from a base pf name. - - This constructor acts like the antelope pfread function for - parameter files constructed in plain C. That is, the argument is - a base name sans the .pf extension. Like antelope's pfread it - will follow the chain of directories defined by PFPATH. As with - the antelope pfread procedure the last file read takes precendence. - Note if PFPATH is not defined the path defaults to ".". - Further if pfbase begins with a slash (i.e. "/") it is assumed to be - the actual file name to read and the PFPATH feature is disabled. - - \param pfbase is a the base pf name (always adds .pf to each name in PFPATH) - - \exception - throws a MsPASSError object with an informative message if - constuctor fails for any reason. + /*! Default constructor - does nothing.*/ + AntelopePf() : Metadata() {}; + /*! \brief Construct from a base pf name. + + This constructor acts like the antelope pfread function for + parameter files constructed in plain C. That is, the argument is + a base name sans the .pf extension. Like antelope's pfread it + will follow the chain of directories defined by PFPATH. As with + the antelope pfread procedure the last file read takes precendence. + Note if PFPATH is not defined the path defaults to ".". + Further if pfbase begins with a slash (i.e. "/") it is assumed to be + the actual file name to read and the PFPATH feature is disabled. + + \param pfbase is a the base pf name (always adds .pf to each name in PFPATH) + + \exception - throws a MsPASSError object with an informative message if + constuctor fails for any reason. + */ + AntelopePf(std::string pfbase); + /*! \brief Construct from a set of text lines. + + This is a cruder constructor that could be used for small pf file. + Read the contents of a pf file into a vector of lines with each + line being one line from the pf. The constructor then sequentially parses + this series of lines as it would if reading from a file. + + \param lines is the modified version of the text pf file.*/ + AntelopePf(std::list lines); + /*! Standard copy constructor. */ + AntelopePf(const AntelopePf &parent); + /*! \brief get a Tbl component by key. + + Antelope has the idea of a tbl, which is a list of + lines that are parsed independently. This is the + get method to extract one of these by its key. + + \param key is the key for the Tbl desired. + \exception AntelopePfError will be thrown if the key + is not present. */ + std::list get_tbl(const std::string key) const; + /*! \brief used for subtrees (nested Arrs in antelope) + + This method is used for nested Arr constructs. + This returns a copy of the branch defined by key + attached to this object. The original from the parent + is retained. + + \param key is the key used to locate this branch. + \returns a copy of the contents of the branch linked to key. + + \exception AntelopePfError will be thrown if the key is not + found. */ + AntelopePf get_branch(const std::string key) const; + /*! Return a list of keys for branches (Arrs) in the pf file. */ + std::list arr_keys() const; + /*! Return a list of keys for Tbls in the pf.*/ + std::list tbl_keys() const; + /*! \brief Return an object with only simple name:value pairs. + * + The Metadata parent of this object only handles name:value pairs. + The values can, however, be any object boost::any can handle. + The documentation says that means it is copy constructable. + For now this method returns an object containin the boost::any + values. Any Arr and Tbl entries are pushed directly to the + output Metadata using boost::any and the two keys defined as + const strings at the top of this file (pftbl_key and pfarr_key). + + \return Metadata sans any Arr and Tbl entries. + */ + Metadata ConvertToMetadata(); + /*! Standard assignment operator, */ + AntelopePf &operator=(const AntelopePf &parent); + /*! \brief save result in a pf format. + + This is functionally equivalent to the Antelope pfwrite + procedure, but is a member of this object. A feature of + the current implementation is that all simply type parameters + will usually be listed twice in the output file. The reason + is that the constructor attempts to guess type, but to allow + for mistakes all simple parameters are also treated as string + variables so get methods are more robust. + + \param ofs is a std::ostream (e.g. cout) where the result + will be written in pf style. Usually should end in ".pf". */ - AntelopePf(std::string pfbase); - /*! \brief Construct from a set of text lines. - - This is a cruder constructor that could be used for small pf file. - Read the contents of a pf file into a vector of lines with each - line being one line from the pf. The constructor then sequentially parses - this series of lines as it would if reading from a file. - - \param lines is the modified version of the text pf file.*/ - AntelopePf(std::list lines); - /*! Standard copy constructor. */ - AntelopePf(const AntelopePf& parent); - /*! \brief get a Tbl component by key. - - Antelope has the idea of a tbl, which is a list of - lines that are parsed independently. This is the - get method to extract one of these by its key. - - \param key is the key for the Tbl desired. - \exception AntelopePfError will be thrown if the key - is not present. */ - std::list get_tbl(const std::string key) const; - /*! \brief used for subtrees (nested Arrs in antelope) - - This method is used for nested Arr constructs. - This returns a copy of the branch defined by key - attached to this object. The original from the parent - is retained. - - \param key is the key used to locate this branch. - \returns a copy of the contents of the branch linked to key. - - \exception AntelopePfError will be thrown if the key is not - found. */ - AntelopePf get_branch(const std::string key) const; - /*! Return a list of keys for branches (Arrs) in the pf file. */ - std::list arr_keys() const; - /*! Return a list of keys for Tbls in the pf.*/ - std::list tbl_keys() const; - /*! \brief Return an object with only simple name:value pairs. - * - The Metadata parent of this object only handles name:value pairs. - The values can, however, be any object boost::any can handle. - The documentation says that means it is copy constructable. - For now this method returns an object containin the boost::any - values. Any Arr and Tbl entries are pushed directly to the - output Metadata using boost::any and the two keys defined as - const strings at the top of this file (pftbl_key and pfarr_key). - - \return Metadata sans any Arr and Tbl entries. - */ - Metadata ConvertToMetadata(); - /*! Standard assignment operator, */ - AntelopePf& operator=(const AntelopePf& parent); - /*! \brief save result in a pf format. - - This is functionally equivalent to the Antelope pfwrite - procedure, but is a member of this object. A feature of - the current implementation is that all simply type parameters - will usually be listed twice in the output file. The reason - is that the constructor attempts to guess type, but to allow - for mistakes all simple parameters are also treated as string - variables so get methods are more robust. - - \param ofs is a std::ostream (e.g. cout) where the result - will be written in pf style. Usually should end in ".pf". - */ - void pfwrite(std::ostream& ofs); + void pfwrite(std::ostream &ofs); + private: - std::map > pftbls; - /* This is used for nested Arrs */ - std::map pfbranches; - /* This method is used to implemetn PFPATH - it is called for - each secondary file in the PFPATH chain. Returns the number of - items changed */ - int merge_pfmf(AntelopePf& m); + std::map> pftbls; + /* This is used for nested Arrs */ + std::map pfbranches; + /* This method is used to implemetn PFPATH - it is called for + each secondary file in the PFPATH chain. Returns the number of + items changed */ + int merge_pfmf(AntelopePf &m); }; /*! \brief Error class for AntelopePf object. @@ -163,42 +163,38 @@ class AntelopePf : public Metadata Note MsPASSError is a child of std::exception, so catch that to most easily fetch messages coming from this beast. */ -class AntelopePfError : public MsPASSError -{ - public: - AntelopePfError(){ - message=std::string("AntelopePfError->undefined error"); - }; - AntelopePfError(std::string mess) - { - message="AntelopePfError object message="+mess; - }; - AntelopePfError(const char *mess) - { - message=std::string("AntelopePfError object message=")+mess; - }; - void log_error(){ - std::cerr << message<undefined error"); + }; + AntelopePfError(std::string mess) { + message = "AntelopePfError object message=" + mess; + }; + AntelopePfError(const char *mess) { + message = std::string("AntelopePfError object message=") + mess; + }; + void log_error() { std::cerr << message << std::endl; }; }; /* Procedural functions using AntelopePf object */ /*! \brief Build a MetadataList using AntelopePf object. -A convenient format to define a MetadataList is an Antelope Pf file. -This procedure creates a MetadataList from an AntelopePf object -(generated from a pf file) keying on tag that defines a pf Tbl& -section. +A convenient format to define a MetadataList is an Antelope Pf file. +This procedure creates a MetadataList from an AntelopePf object +(generated from a pf file) keying on tag that defines a pf Tbl& +section. \param m is the PfStyleMetadata object where you expect to find the list. \param tag is the unique tag on the Tbl in the original Pf containing the data defining the MetadataList. */ -MetadataList get_mdlist(const mspass::utility::AntelopePf& m, const std::string tag); +MetadataList get_mdlist(const mspass::utility::AntelopePf &m, + const std::string tag); /*! Read a single pf file and return as an AntelopePf object. */ AntelopePf pfread(const std::string fname); -} // End utility namespace -} // End mspass namespace declaration +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/AttributeCrossReference.h b/cxx/include/mspass/utility/AttributeCrossReference.h index ee92966fc..b0cc6f59c 100644 --- a/cxx/include/mspass/utility/AttributeCrossReference.h +++ b/cxx/include/mspass/utility/AttributeCrossReference.h @@ -1,13 +1,12 @@ #ifndef _ATTRIBUTE_CROSS_REFERENCE_ #define _ATTRIBUTE_CROSS_REFERENCE_ -#include +#include "mspass/utility/AttributeMap.h" +#include "mspass/utility/Metadata.h" #include #include -#include "mspass/utility/Metadata.h" -#include "mspass/utility/AttributeMap.h" -namespace mspass -{ -namespace utility{ +#include +namespace mspass { +namespace utility { /*! \brief Cross reference between external and internal names. Data formats commonly have a frozen namespace with which people @@ -17,89 +16,90 @@ namespace utility{ software that loads data using an external format but wishes to use a different set of names internally. This object simplifies the task of managing the differences in internal and external names */ -class AttributeCrossReference -{ - public: - /*! Default constructor. +class AttributeCrossReference { +public: + /*! Default constructor. + + The default constructor is a pure placeholder that + does nothing. Result is a null namespace mapper.*/ + AttributeCrossReference() {}; + /*! Construct from a string. - The default constructor is a pure placeholder that - does nothing. Result is a null namespace mapper.*/ - AttributeCrossReference(){}; - /*! Construct from a string. + This constructor assumes the string variable passed is a + complete image of a set of (newline separated) lines + defining the mapping. The format of each line is + assumed to be: internal_name external_name type. + As the names imply "internal_name" is the name to use + internally and "external_name" is the foramt specific external + name. The "type" variable is generic and should be one of the + simple keywords real, int, string, or boolean. - This constructor assumes the string variable passed is a - complete image of a set of (newline separated) lines - defining the mapping. The format of each line is - assumed to be: internal_name external_name type. - As the names imply "internal_name" is the name to use - internally and "external_name" is the foramt specific external - name. The "type" variable is generic and should be one of the - simple keywords real, int, string, or boolean. + \param lines_to_parse is the (multiline) string in the + format described above. - \param lines_to_parse is the (multiline) string in the - format described above. + \exception MsPASSError will be thrown for parsing errors. */ + AttributeCrossReference(const std::string lines_to_parse); + /*! Construct from a list container. - \exception MsPASSError will be thrown for parsing errors. */ - AttributeCrossReference(const std::string lines_to_parse); - /*! Construct from a list container. + This constructor is nearly identical to the single string with + newline constructor. The list elements are expected to be + the contents of each line (newline break) for the string version. - This constructor is nearly identical to the single string with - newline constructor. The list elements are expected to be - the contents of each line (newline break) for the string version. + \lines list container with input lines in same format as that + described above for single string constructor. + \exception MsPASSError will be thrown if there are parsing errors. + */ + AttributeCrossReference(const std::list &lines); - \lines list container with input lines in same format as that - described above for single string constructor. - \exception MsPASSError will be thrown if there are parsing errors. - */ - AttributeCrossReference(const std::list& lines); + /*! Build for a set of STL containers. - /*! Build for a set of STL containers. + This is lower level constructor that effectively builds this + object from a set of components that are used to actually + implement the concept. + \param internal2external is an associative array keyed by + the internal name that defines external names linked to + each internal name. + \param mdlist is a MsPASS::MetadataList object defining the + complete internal namespace. */ + AttributeCrossReference( + const std::map internal2external, + const mspass::utility::MetadataList &mdlist); + /*! Standard copy constructor. */ + AttributeCrossReference(const AttributeCrossReference &parent); + /*! Get internal name for attribute with external name key.*/ + std::string internal(const std::string key) const; + /*! Get external name for attribute with internal name key.*/ + std::string external(const std::string key) const; + /*! Get type information for attribute with internal name key.*/ + MDtype type(const std::string key) const; + /*! Standard assignment operator. */ + AttributeCrossReference &operator=(const AttributeCrossReference &parent); + /*! Return number of entries in the cross reference map. */ + int size() const; + /*! Add a new entry to the map. - This is lower level constructor that effectively builds this - object from a set of components that are used to actually - implement the concept. - \param internal2external is an associative array keyed by - the internal name that defines external names linked to - each internal name. - \param mdlist is a MsPASS::MetadataList object defining the - complete internal namespace. */ - AttributeCrossReference(const std::map internal2external, - const mspass::utility::MetadataList& mdlist); - /*! Standard copy constructor. */ - AttributeCrossReference(const AttributeCrossReference& parent); - /*! Get internal name for attribute with external name key.*/ - std::string internal(const std::string key) const; - /*! Get external name for attribute with internal name key.*/ - std::string external(const std::string key) const; - /*! Get type information for attribute with internal name key.*/ - MDtype type(const std::string key) const; - /*! Standard assignment operator. */ - AttributeCrossReference& operator=(const AttributeCrossReference& parent); - /*! Return number of entries in the cross reference map. */ - int size() const; - /*! Add a new entry to the map. + This method is used to extend the namespace. - This method is used to extend the namespace. + \param intern is the internal name + \param ext is the external name to be added. */ + void put(const std::string intern, const std::string ext); + /*! Return the set of internal names defined by this object. - \param intern is the internal name - \param ext is the external name to be added. */ - void put(const std::string intern, const std::string ext); - /*! Return the set of internal names defined by this object. + Returns an std::set container of strings that are the internal + names defined by this object. */ + std::set internal_names() const; + /*! Return the set of external names defined by this object. - Returns an std::set container of strings that are the internal - names defined by this object. */ - std::set internal_names() const; - /*! Return the set of external names defined by this object. + Returns an std::set container of strings that are the external + names defined by this object. */ + std::set external_names() const; - Returns an std::set container of strings that are the external - names defined by this object. */ - std::set external_names() const; - private: - std::map itoe; - std::map etoi; - /* keyed by internal names. Get type of this attribute*/ - std::map imdtypemap; +private: + std::map itoe; + std::map etoi; + /* keyed by internal names. Get type of this attribute*/ + std::map imdtypemap; }; -} // end utility namespace -} // end mspass namespace encapsulation +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/AttributeMap.h b/cxx/include/mspass/utility/AttributeMap.h index 028dd6d50..5e74051a1 100644 --- a/cxx/include/mspass/utility/AttributeMap.h +++ b/cxx/include/mspass/utility/AttributeMap.h @@ -1,14 +1,14 @@ #ifndef _ATTRIBUTE_MAP_H_ #define _ATTRIBUTE_MAP_H_ -#include -#include -#include -#include "mspass/utility/Metadata.h" #include "mspass/utility/AntelopePf.h" -namespace mspass -{ -namespace utility{ -/*! \brief Defines properties of a database attribute and link to internal names. +#include "mspass/utility/Metadata.h" +#include +#include +#include +namespace mspass { +namespace utility { +/*! \brief Defines properties of a database attribute and link to internal +names. * * This object is used to define the relationship between a parameter stored * externally (originally conceived as a database attribute general enough for @@ -20,215 +20,219 @@ namespace utility{ could have been called a struct since all the attributes are public. */ -class AttributeProperties -{ +class AttributeProperties { public: - /*! Name for this parameter as the attribute name in some database table.*/ - std::string db_attribute_name; - /*! \brief Database table name from which this attribute was loaded. - * Relational databases always share attribute names with multiple tables. - * This is a fundamental axiom in relational database theory. Because - * an attribute of the same name may or may not be related in different tables - * it is necessary to define the parent table to avoid ambiguity. - * Note that in MsPASS a common convention is that a db_attribute_name may - * contain the parent table implicity (e.g. wfdisc.sta) but code using this - * object should not assume this. User should assume the correct table name - * is stored here. */ - std::string db_table_name; - /*! Internal name used to reference this parameter.*/ - std::string internal_name; - /*! Type of this parameter.*/ - MDtype mdt; - /*! \brief Defines if parameter is a key in it's parent table. - * Keys in a relational database are special attributes and it is useful - * to tag a parameter as being a key or not a key. When true this attribute - * indicates a parameter is a key for db_table_name. */ - bool is_key; - /*! Default constructor. Creates a null object with MTtype set to invalid. - * This constructor exists to avoid automatic construction with no parameters, - * but it should not be used. */ - AttributeProperties(); - /*! \brief Create using a string description. - * The standard construction of an associative array uses this constructor driven - * by a series of input lines. The input line that is parsed must contain these - * member attributes in the following order: - * -# internal_name - * -# db_attribute_name - * -# db_table_name - * -# mdt - * - * The mdt field must be one of "REAL", "real", "STRING", "string", "INT", "int", - * "BOOL", "BOOLEAN", "bool", or "boolean". - * Any other field with cause this constructor to throw an exception. - * \exception MetadataError is thrown for parsing errors. - */ - AttributeProperties(const std::string); - /*! \brief Standard copy constructor. */ - AttributeProperties(const AttributeProperties&); - /*! \brief Standard assignment operator. */ - AttributeProperties& operator=(const AttributeProperties&); - /*! \brief Returns full name. + /*! Name for this parameter as the attribute name in some database table.*/ + std::string db_attribute_name; + /*! \brief Database table name from which this attribute was loaded. + * Relational databases always share attribute names with multiple tables. + * This is a fundamental axiom in relational database theory. Because + * an attribute of the same name may or may not be related in different tables + * it is necessary to define the parent table to avoid ambiguity. + * Note that in MsPASS a common convention is that a db_attribute_name may + * contain the parent table implicity (e.g. wfdisc.sta) but code using this + * object should not assume this. User should assume the correct table name + * is stored here. */ + std::string db_table_name; + /*! Internal name used to reference this parameter.*/ + std::string internal_name; + /*! Type of this parameter.*/ + MDtype mdt; + /*! \brief Defines if parameter is a key in it's parent table. + * Keys in a relational database are special attributes and it is useful + * to tag a parameter as being a key or not a key. When true this attribute + * indicates a parameter is a key for db_table_name. */ + bool is_key; + /*! Default constructor. Creates a null object with MTtype set to invalid. + * This constructor exists to avoid automatic construction with no + * parameters, but it should not be used. */ + AttributeProperties(); + /*! \brief Create using a string description. + * The standard construction of an associative array uses this constructor + * driven by a series of input lines. The input line that is parsed must + * contain these member attributes in the following order: + * -# internal_name + * -# db_attribute_name + * -# db_table_name + * -# mdt + * + * The mdt field must be one of "REAL", "real", "STRING", "string", "INT", + * "int", "BOOL", "BOOLEAN", "bool", or "boolean". Any other field with cause + * this constructor to throw an exception. + * \exception MetadataError is thrown for parsing errors. + */ + AttributeProperties(const std::string); + /*! \brief Standard copy constructor. */ + AttributeProperties(const AttributeProperties &); + /*! \brief Standard assignment operator. */ + AttributeProperties &operator=(const AttributeProperties &); + /*! \brief Returns full name. - Database attribute names can and often do occur in multiple tables. This method - returns a full name that is used to uniquely define that attribute in a particular - table. Although this could be generalized, for the present this is always returned - in the form used by Antelope/datascope: that is the string is if the form table.attribute. - */ - std::string fully_qualified_name() const; - friend std::ostream& operator<<(std::ostream& ofs, const AttributeProperties& d); + Database attribute names can and often do occur in multiple tables. This + method returns a full name that is used to uniquely define that attribute in a + particular table. Although this could be generalized, for the present this is + always returned in the form used by Antelope/datascope: that is the string is + if the form table.attribute. + */ + std::string fully_qualified_name() const; + friend std::ostream &operator<<(std::ostream &ofs, + const AttributeProperties &d); }; /*! \brief An object to map internal and external attribute names. -* -* This object is used to link a set of internally defined parameters tagged with -* a name to an external name convention. The working model for external names is -* attribute names defined in a relational database schema, but the concept -* involved is more general. That is, the intent of this interface is a general -* way to between one set of parameter names and another. This could be used, -* for example, to map between header variable names in SEGY or SAC and some -* internal name convention. The relation of the map defined by this object is -* implicitly assumed to be one-to-one because of the use of the STL map to -* define the relationship. Because the map is keyed by the internal name -* lookup is also intended only for finding the external names associated with a -* particular internal parameter. The primary use of this object in the MsPASS -* library is to define a global mapping operator for a particular database -* schema. That is, the most common construct is to build this object -* early on using a call like: AttributeMap("css3.0"). -* -*/ -class AttributeMap -{ + * + * This object is used to link a set of internally defined parameters tagged + * with a name to an external name convention. The working model for external + * names is attribute names defined in a relational database schema, but the + * concept involved is more general. That is, the intent of this interface is a + * general way to between one set of parameter names and another. This could be + * used, for example, to map between header variable names in SEGY or SAC and + * some internal name convention. The relation of the map defined by this + * object is implicitly assumed to be one-to-one because of the use of the STL + * map to define the relationship. Because the map is keyed by the internal + * name lookup is also intended only for finding the external names associated + * with a particular internal parameter. The primary use of this object in the + * MsPASS library is to define a global mapping operator for a particular + * database schema. That is, the most common construct is to build this object + * early on using a call like: AttributeMap("css3.0"). + * + */ +class AttributeMap { public: - /*! \brief Maps internal to external names. - * This object is little more than a wrapper around this Standard Template - * library map container. The map is keyed by the internal name used to - * for a particular parameter. Each internal name known to the object will - * have an AttributeProperties associated with it through this map - * (same as an associative array for perl and tcl geeks). The user will - * need to be familiar with the STL map container to deal with this object - * correctly. I made an intentional design decision to not hide this - * beast behind the interface because the STL has become a standardized - * component of C++. I took the attitude that STL would outlast my interface - * definition and cost of hiding this was too high in computational burden. - * The key thing a user must know is the proper way to retrieve an element from - * a map and handle the possibility that the requested item is not known to - * the map. Consult the web or the source code for libMsPASS if you don't - * know how to do this. */ - std::map attributes; - /*! \brief Default constructor. - * The default assumes the css3.0 schema and will load the name definitions - * defined for that schema. */ - AttributeMap(); - /*! \brief Create mapping for a specified namespace tag (usually a schema name). - * - * This is the normal constructor for this object. A one word tag is used to - * define a particular title to a namespace mapping. Normally this is a - * database schema name like css3.0 or Trace4.0, but the interface allows it - * to be anything. For example, although it isn't currently defined one could - * easily create a "SacHeader" definition that defined mapping between SAC - * header fields and an internal name convention. The interface simply assumes - * this keyword can be used to establish a mechanism for creating this beast - * through an unspecified mechanism. i.e. the interface is blind to the - * details and assumes what you want is to know how to map between A and B - * and someone else worried about the format for doing this already. In the - * current implementation we use an Antelope parameter file to create this - * object, but this interface does not depend upon that choice. - * - * \param tag name tag used to define this map (usually a schema name). - */ - AttributeMap(const std::string tag); - /*! Standard copy constructor.*/ - AttributeMap(const AttributeMap& am0); - /*! Standard assignment operator.*/ - AttributeMap& operator=(const AttributeMap& am0); - /*! Fetch attribute properties by internal name key. + /*! \brief Maps internal to external names. + * This object is little more than a wrapper around this Standard Template + * library map container. The map is keyed by the internal name used to + * for a particular parameter. Each internal name known to the object will + * have an AttributeProperties associated with it through this map + * (same as an associative array for perl and tcl geeks). The user will + * need to be familiar with the STL map container to deal with this object + * correctly. I made an intentional design decision to not hide this + * beast behind the interface because the STL has become a standardized + * component of C++. I took the attitude that STL would outlast my interface + * definition and cost of hiding this was too high in computational burden. + * The key thing a user must know is the proper way to retrieve an element + * from a map and handle the possibility that the requested item is not known + * to the map. Consult the web or the source code for libMsPASS if you don't + * know how to do this. */ + std::map attributes; + /*! \brief Default constructor. + * The default assumes the css3.0 schema and will load the name definitions + * defined for that schema. */ + AttributeMap(); + /*! \brief Create mapping for a specified namespace tag (usually a schema + * name). + * + * This is the normal constructor for this object. A one word tag is used to + * define a particular title to a namespace mapping. Normally this is a + * database schema name like css3.0 or Trace4.0, but the interface allows it + * to be anything. For example, although it isn't currently defined one could + * easily create a "SacHeader" definition that defined mapping between SAC + * header fields and an internal name convention. The interface simply + * assumes this keyword can be used to establish a mechanism for creating this + * beast through an unspecified mechanism. i.e. the interface is blind to the + * details and assumes what you want is to know how to map between A and B + * and someone else worried about the format for doing this already. In the + * current implementation we use an Antelope parameter file to create this + * object, but this interface does not depend upon that choice. + * + * \param tag name tag used to define this map (usually a schema name). + */ + AttributeMap(const std::string tag); + /*! Standard copy constructor.*/ + AttributeMap(const AttributeMap &am0); + /*! Standard assignment operator.*/ + AttributeMap &operator=(const AttributeMap &am0); + /*! Fetch attribute properties by internal name key. + + This is the more conventional interface and the bombproof version to fetch + AttributeProperties using a specified key. It is safe because if the key + does not match the map an exception will be thrown. - This is the more conventional interface and the bombproof version to fetch - AttributeProperties using a specified key. It is safe because if the key - does not match the map an exception will be thrown. + \param key is the internal name for which properties are requested. + \return AttributeProperties that are defined for that name key. + \exception MsPASSError thrown if the key is not found. + */ + AttributeProperties operator[](const std::string key) const; + /*! Overloaded operator for C strings constants. */ + AttributeProperties operator[](const char *key) const; + /*! Returns a list of aliases for a key. - \param key is the internal name for which properties are requested. - \return AttributeProperties that are defined for that name key. - \exception MsPASSError thrown if the key is not found. - */ - AttributeProperties operator[](const std::string key) const; - /*! Overloaded operator for C strings constants. */ - AttributeProperties operator[](const char* key) const; - /*! Returns a list of aliases for a key. + A universal issue in a relational database interface is that an + attribute can occur in more than one table. One can give a fully + qualified name through this interface, but it is often convenient to + have a simple name (the alias) that is a shorthand for a particular + instance of that attribute in one table. Further, it is sometimes + useful to have a list of possible meanings for an alias that can + be searched in order. Thus this method returns a list of AttributeProperties + that are tied to an alias. The idea would be that the caller would + try each member of this list in order before throwing an error. + \param alias is the alias name to search. + \return STL map of AttributeProperties that are aliases for the + given keyword. The map is keyed by the table name. + This provides a clean interface for output of attributes + as it allows an output function to use an alias efficiently. + The assumption in all cases is that the alias name provides + the unique tag or an attribute. An application must avoid + modifying attributes that are part of the alias definition. + The alias name is the only one that should normally be assumed + current. + \exception MsPASSError is thrown if an attribute listed in aliases is + not defined for the AttributeMap itself. This always indicates + an error in the definition of the AttributeMap. + */ + std::map + aliases(const std::string key) const; + /*! Overload for literals. */ + std::map aliases(const char *key) const; + /*! Returns an ordered list of table names to try in extracting an alias + named. - A universal issue in a relational database interface is that an - attribute can occur in more than one table. One can give a fully - qualified name through this interface, but it is often convenient to - have a simple name (the alias) that is a shorthand for a particular - instance of that attribute in one table. Further, it is sometimes - useful to have a list of possible meanings for an alias that can - be searched in order. Thus this method returns a list of AttributeProperties - that are tied to an alias. The idea would be that the caller would - try each member of this list in order before throwing an error. - \param alias is the alias name to search. - \return STL map of AttributeProperties that are aliases for the - given keyword. The map is keyed by the table name. - This provides a clean interface for output of attributes - as it allows an output function to use an alias efficiently. - The assumption in all cases is that the alias name provides - the unique tag or an attribute. An application must avoid - modifying attributes that are part of the alias definition. - The alias name is the only one that should normally be assumed - current. - \exception MsPASSError is thrown if an attribute listed in aliases is - not defined for the AttributeMap itself. This always indicates - an error in the definition of the AttributeMap. - */ - std::map aliases(const std::string key) const; - /*! Overload for literals. */ - std::map aliases(const char *key) const; - /*! Returns an ordered list of table names to try in extracting an alias named. + Aliases present an issue on input. Because many attribute names appear in + multiple tables (an essential thing, in fact, for a relational database to + work) input of an attribute that is a generic label for such an attribute can + be problematic. This method returns an ordered list of tables that provide + guidance for extracting an attribute defined by such a generic name. The + order is very important as readers will generally need to try qualfied names + for each table in the list returned by this method. Hence the order matters + and the list should be inclusive but no longer than necessary as long + lists could generate some overead problems in some situations. - Aliases present an issue on input. Because many attribute names appear in - multiple tables (an essential thing, in fact, for a relational database to work) - input of an attribute that is a generic label for such an attribute can be - problematic. This method returns an ordered list of tables that provide - guidance for extracting an attribute defined by such a generic name. The - order is very important as readers will generally need to try qualfied names - for each table in the list returned by this method. Hence the order matters - and the list should be inclusive but no longer than necessary as long - lists could generate some overead problems in some situations. + \param key is the alias name for which this information is desired. + \return list of table names in a recommended order of access. + \exception MsPASSError will be thrown if there are inconsistencies + */ + std::list aliastables(const std::string key) const; + /*! Overload for literals*/ + std::list aliastables(const char *key) const; + /*! Check if an attribute name is an alias. - \param key is the alias name for which this information is desired. - \return list of table names in a recommended order of access. - \exception MsPASSError will be thrown if there are inconsistencies - */ - std::list aliastables(const std::string key) const; - /*! Overload for literals*/ - std::list aliastables(const char *key) const; - /*! Check if an attribute name is an alias. + For efficiency and convience it is useful to have a simple way to + ask if an attribute name is defined as an alias. This abstracts this + process. + \param key is the attribute name to be tested. + \return true if key is an alias. Otherwise return false. + */ + bool is_alias(const std::string key) const; + /*! Overloaded for string literal. */ + bool is_alias(const char *key) const; - For efficiency and convience it is useful to have a simple way to - ask if an attribute name is defined as an alias. This abstracts this - process. - \param key is the attribute name to be tested. - \return true if key is an alias. Otherwise return false. - */ - bool is_alias(const std::string key) const; - /*! Overloaded for string literal. */ - bool is_alias(const char *key) const; private: - /*! Parameter file driven constructor. - * Builds this object from an Antelope parameter file. Parses the - * parameter for for a Tbl that is processed line by line to calling - * the basic constructor for an AttributeProperties with one - * AttributeProperties object created for each entry in the associated - * Tbl. - * \param pf Parameter file object created from a pf file that is be parsed - * \param name name of Tbl to be parse. - */ - AttributeMap(const AntelopePf &pf,const std::string name); - /*! Implements aliases. - * - * The map uses an alias name as the key and the list of strings - * are keys back to the public map to AttributePropeties. */ - std::map > aliasmap; + /*! Parameter file driven constructor. + * Builds this object from an Antelope parameter file. Parses the + * parameter for for a Tbl that is processed line by line to calling + * the basic constructor for an AttributeProperties with one + * AttributeProperties object created for each entry in the associated + * Tbl. + * \param pf Parameter file object created from a pf file that is be parsed + * \param name name of Tbl to be parse. + */ + AttributeMap(const AntelopePf &pf, const std::string name); + /*! Implements aliases. + * + * The map uses an alias name as the key and the list of strings + * are keys back to the public map to AttributePropeties. */ + std::map> aliasmap; }; -} // end utility namespace -} // End namespace mspass declaration +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/BasicMetadata.h b/cxx/include/mspass/utility/BasicMetadata.h index 9e3952e62..7fd936ab7 100644 --- a/cxx/include/mspass/utility/BasicMetadata.h +++ b/cxx/include/mspass/utility/BasicMetadata.h @@ -2,28 +2,26 @@ #define _BASICMETADATA_H_ #include -namespace mspass -{ -namespace utility{ +namespace mspass { +namespace utility { /*! \brief Abstract base class for Metadata concept. A core idea in MsPASS is the idea of a generic header that allows storage and retrieval of arbitrary attributes. This base class forces support for the standard basic data types. */ -class BasicMetadata -{ +class BasicMetadata { public: - virtual ~BasicMetadata(){}; - virtual int get_int(const std::string key) const =0; - virtual double get_double(const std::string key)const =0; - virtual bool get_bool(const std::string key) const =0; - virtual std::string get_string(const std::string key)const =0; - virtual void put(const std::string key, const double val)=0; - virtual void put(const std::string key, const int val)=0; - virtual void put(const std::string key, const bool val)=0; - virtual void put(const std::string key, const std::string val)=0; + virtual ~BasicMetadata() {}; + virtual int get_int(const std::string key) const = 0; + virtual double get_double(const std::string key) const = 0; + virtual bool get_bool(const std::string key) const = 0; + virtual std::string get_string(const std::string key) const = 0; + virtual void put(const std::string key, const double val) = 0; + virtual void put(const std::string key, const int val) = 0; + virtual void put(const std::string key, const bool val) = 0; + virtual void put(const std::string key, const std::string val) = 0; }; -} // end utility namespace -}; // End mspass namespace encapsulation +} // namespace utility +}; // namespace mspass #endif diff --git a/cxx/include/mspass/utility/ErrorLogger.h b/cxx/include/mspass/utility/ErrorLogger.h index 2ad06bd4b..a5035b9dd 100644 --- a/cxx/include/mspass/utility/ErrorLogger.h +++ b/cxx/include/mspass/utility/ErrorLogger.h @@ -1,33 +1,32 @@ #ifndef _ERROR_LOGGER_H_ #define _ERROR_LOGGER_H_ -#include -#include +#include "mspass/utility/ErrorLogger.h" +#include "mspass/utility/MsPASSError.h" #include #include #include -#include #include -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/ErrorLogger.h" -namespace mspass -{ -namespace utility{ -class LogData -{ +#include +#include +#include +namespace mspass { +namespace utility { +class LogData { public: int job_id; - int p_id; // output of getpid() + int p_id; // output of getpid() std::string algorithm; mspass::utility::ErrorSeverity badness; std::string message; - LogData(){}; + LogData() {}; /*! Normal constuctor from a MsPASSError or child of same. \param jid is the value to assign to job_id. \param alg is assigned to algorithm attribute. \param merr is parsed to fill the message and severity fields. Note p_id is always fetched with the system call getpid in the constructor.*/ - LogData(const int jid, const std::string alg,const mspass::utility::MsPASSError& merr); + LogData(const int jid, const std::string alg, + const mspass::utility::MsPASSError &merr); /*! Normal constuctor from strings. \param jid is the value to assign to job_id. @@ -35,13 +34,14 @@ class LogData \param msg is the error message. \param lvl is the error severity. Note p_id is always fetched with the system call getpid in the constructor.*/ - LogData(const int jid, const std::string alg, const std::string msg, const mspass::utility::ErrorSeverity lvl); - friend std::ostream& operator<<(std::ostream&, LogData&); + LogData(const int jid, const std::string alg, const std::string msg, + const mspass::utility::ErrorSeverity lvl); + friend std::ostream &operator<<(std::ostream &, LogData &); + private: friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { + template + void serialize(Archive &ar, const unsigned int version) { ar & job_id; ar & p_id; ar & algorithm; @@ -57,17 +57,13 @@ objects (e.g. seismograms and time series objects) all use this class to log errors and mark data with ambiguous states. The log can explain why data is an invalid state, but can also contain debug information normally enabled by something like a verbose option to a program. */ -class ErrorLogger -{ +class ErrorLogger { public: - ErrorLogger(){job_id=0;}; - ErrorLogger(int job) - { - job_id=job; - }; - ErrorLogger(const ErrorLogger& parent); - void set_job_id(int jid){job_id=jid;}; - int get_job_id(){return job_id;}; + ErrorLogger() { job_id = 0; }; + ErrorLogger(int job) { job_id = job; }; + ErrorLogger(const ErrorLogger &parent); + void set_job_id(int jid) { job_id = jid; }; + int get_job_id() { return job_id; }; /*! Logs one error message. \param merr - many mspass procedures throw MsPASSError objects. @@ -75,7 +71,7 @@ class ErrorLogger \return size of error log after insertion. */ - int log_error(const mspass::utility::MsPASSError& merr); + int log_error(const mspass::utility::MsPASSError &merr); /*! Log one a message directly with a specified severity. This is a convenience overload of log_error. It splits the @@ -90,7 +86,7 @@ class ErrorLogger \return size of error log after insertion. */ int log_error(const std::string alg, const std::string mess, - const mspass::utility::ErrorSeverity level); + const mspass::utility::ErrorSeverity level); /*! \brief Log a verbose message marking it informational. @@ -100,23 +96,23 @@ class ErrorLogger the size of the log after insertion. */ int log_verbose(const std::string alg, const std::string mess); - std::list get_error_log()const{return allmessages;}; - int size()const{return allmessages.size();}; + std::list get_error_log() const { return allmessages; }; + int size() const { return allmessages.size(); }; /*! Reset error log container to make it empty. */ - void clear(){allmessages.clear();}; - ErrorLogger& operator=(const ErrorLogger& parent); + void clear() { allmessages.clear(); }; + ErrorLogger &operator=(const ErrorLogger &parent); /*! For this object + of += means add the log data from the rhs to the lhs. lhs defines the job_id. */ - ErrorLogger& operator+=(const ErrorLogger& parent); + ErrorLogger &operator+=(const ErrorLogger &parent); /*! Return an std::list container with most serious error level marked. */ - std::list worst_errors()const; + std::list worst_errors() const; + private: int job_id; std::list allmessages; friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { + template + void serialize(Archive &ar, const unsigned int version) { ar & job_id; ar & allmessages; }; @@ -134,23 +130,25 @@ it looks for Invalid or Fatal entries and returns true only if there are any entries of that level of severity. i.e. it returns false if the only errors are things like Complaint or less. */ -template bool data_are_valid(const Tdata& d) -{ - if(d.dead()) return false; +template bool data_are_valid(const Tdata &d) { + if (d.dead()) + return false; std::list welog; - welog=d.elog.worst_errors(); + welog = d.elog.worst_errors(); /*The return will be empty if there are no errors logged*/ - if(welog.size()<=0) return true; + if (welog.size() <= 0) + return true; LogData ld; /* Worst errors can return a list of multiple entries. For fatal or invalid id should never be more than one, but this is a clean way to extract the first member of the list if it isn't empty*/ - ld=*(welog.begin()); - if(ld.badness == ErrorSeverity::Fatal || ld.badness == ErrorSeverity::Invalid) - return false; + ld = *(welog.begin()); + if (ld.badness == ErrorSeverity::Fatal || + ld.badness == ErrorSeverity::Invalid) + return false; else - return true; + return true; } -} // end utility namespace -} // End mspass namespace +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/Metadata.h b/cxx/include/mspass/utility/Metadata.h index af1fdfa98..b6244cf83 100644 --- a/cxx/include/mspass/utility/Metadata.h +++ b/cxx/include/mspass/utility/Metadata.h @@ -1,40 +1,40 @@ #ifndef _METADATA_H_ #define _METADATA_H_ -#include +#include "mspass/utility/BasicMetadata.h" +#include "mspass/utility/MsPASSError.h" +#include +#include +#include +#include #include +#include #include -#include -#include -#include #include -#include -#include -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/BasicMetadata.h" +#include -namespace mspass -{ -namespace utility{ +namespace mspass { +namespace utility { /*! \brief Error thrown when get operators fail. * * This is a convenience class used to construct a more informative * set of errors when get operations fail. */ -class MetadataGetError : public MsPASSError -{ +class MetadataGetError : public MsPASSError { public: std::stringstream ss; - MetadataGetError():MsPASSError(){}; // seems necessary to not default this with gcc + MetadataGetError() + : MsPASSError() {}; // seems necessary to not default this with gcc /*! Constructor called when a key is not found in the Metadata. - * \param Texpected is the type name (return of typeid name method) trying to extract. */ - MetadataGetError(const std::string key,const char *Texpected) - { + * \param Texpected is the type name (return of typeid name method) trying to + * extract. */ + MetadataGetError(const std::string key, const char *Texpected) { std::string pretty_name(boost::core::demangle(Texpected)); - ss<<"Error trying to extract Metadata with key="<(key); + val = get(key); return val; - }catch(MetadataGetError& merr) - { - /* Try a float if that failed */ - try{ + } catch (MetadataGetError &merr) { + /* Try a float if that failed */ + try { float fval; - fval=get(key); + fval = get(key); return fval; - }catch(MetadataGetError& merr) - { - throw merr; + } catch (MetadataGetError &merr) { + throw merr; } } }; /*! Get an integer from the Metadata object. - \exception MetadataGetError if requested parameter is not found or there is a type mismatch. + \exception MetadataGetError if requested parameter is not found or there is a + type mismatch. \param key keyword associated with requested metadata member. **/ - int get_int(const std::string key) const override - { - try{ - int val; - val=get(key); - return val; - } - catch(MetadataGetError& merr) - { - try{ - long lval; - lval=get(key); - return static_cast(lval); - }catch(MetadataGetError& merr) - { - throw merr; - } + int get_int(const std::string key) const override { + try { + int val; + val = get(key); + return val; + } catch (MetadataGetError &merr) { + try { + long lval; + lval = get(key); + return static_cast(lval); + } catch (MetadataGetError &merr) { + throw merr; } + } }; /*! Get a long integer from the Metadata object. - \exception MetadataGetError if requested parameter is not found or there is a type mismatch. + \exception MetadataGetError if requested parameter is not found or there is a + type mismatch. \param key keyword associated with requested metadata member. **/ - long get_long(const std::string key) const - { - try{ - long val; - val=get(key); - return val; - } - catch(MetadataGetError& merr) - { - try{ - int ival; - ival=get(key); - return static_cast(ival); - }catch(MetadataGetError& merr) - { - throw merr; - } + long get_long(const std::string key) const { + try { + long val; + val = get(key); + return val; + } catch (MetadataGetError &merr) { + try { + int ival; + ival = get(key); + return static_cast(ival); + } catch (MetadataGetError &merr) { + throw merr; } + } }; /*! Get a string from the Metadata object. @@ -207,15 +195,18 @@ other attributes. was parsed from an Antelope Pf nested Tbl and Arrs can be extracted this way and parsed with pf routines. - \exception MetadataGetError if requested parameter is not found or there is a type mismatch. + \exception MetadataGetError if requested parameter is not found or there is a + type mismatch. \param key keyword associated with requested metadata member. **/ - std::string get_string(const std::string key) const override{ - try{ + std::string get_string(const std::string key) const override { + try { std::string val; - val=get(key); + val = get(key); return val; - }catch(...){throw;}; + } catch (...) { + throw; + }; }; /*! Get a boolean parameter from the Metadata object. @@ -225,12 +216,14 @@ other attributes. \param key keyword associated with requested metadata member. **/ - bool get_bool(const std::string key) const override{ - try{ + bool get_bool(const std::string key) const override { + try { bool val; - val=get(key); + val = get(key); return val; - }catch(...){throw;}; + } catch (...) { + throw; + }; }; /*! Generic get interface. @@ -262,13 +255,14 @@ other attributes. \exception - will throw a MetadataGetError (child of MsPASSError) for type mismatch or in an overflow or underflow condition. */ - template T get(const char *key) const - { - try{ + template T get(const char *key) const { + try { T val; - val=get(std::string(key)); + val = get(std::string(key)); return val; - }catch(...){throw;}; + } catch (...) { + throw; + }; } /*! Get the boost::any container from the Metadata object. @@ -281,81 +275,65 @@ other attributes. \exception - MetadataGetError if requested parameter is not found. */ boost::any get_any(const std::string key) const { - std::map::const_iterator iptr; - iptr=md.find(key); - if(iptr==md.end()) - { - throw MetadataGetError(key,typeid(boost::any).name()); + std::map::const_iterator iptr; + iptr = md.find(key); + if (iptr == md.end()) { + throw MetadataGetError(key, typeid(boost::any).name()); } return iptr->second; }; std::string type(const std::string key) const; - template void put(const std::string key, T val) noexcept - { - boost::any aval=val; - md[key]=aval; + template void put(const std::string key, T val) noexcept { + boost::any aval = val; + md[key] = aval; changed_or_set.insert(key); } - template void put (const char *key, T val) noexcept - { + template void put(const char *key, T val) noexcept { /* could do this as put(string(key),val) but this is so trivial duplicating - the code for the string method is more efficient than an added function call.*/ - boost::any aval=val; - md[std::string(key)]=aval; + the code for the string method is more efficient than an added function + call.*/ + boost::any aval = val; + md[std::string(key)] = aval; changed_or_set.insert(std::string(key)); } - void put(const std::string key, const double val) override - { - this->put(key,val); + void put(const std::string key, const double val) override { + this->put(key, val); }; - void put(const std::string key, const int val) override - { - this->put(key,val); + void put(const std::string key, const int val) override { + this->put(key, val); }; - void put(const std::string key, const bool val) override - { - this->put(key,val); + void put(const std::string key, const bool val) override { + this->put(key, val); }; - void put(const std::string key, const std::string val) override - { - this->put(key,val); + void put(const std::string key, const std::string val) override { + this->put(key, val); }; - /*! Put a C string literal. Requires special handling because + /*! Put a C string literal. Requires special handling because it is not copy constructable (see warning in boost docs: - https://theboostcpplibraries.com/boost.any + https://theboostcpplibraries.com/boost.any */ - void put(const char *key,const char *val) - { + void put(const char *key, const char *val) { std::string sval(val); - this->put(key,val); + this->put(key, val); } - void put(std::string key,const char *val) - { - this->put(key.c_str(),val); + void put(std::string key, const char *val) { this->put(key.c_str(), val); } + void put_object(const std::string key, const pybind11::object val) { + this->put(key, val); } - void put_object(const std::string key, const pybind11::object val) - { - this->put(key,val); - } - void put_int(const std::string key,const int val) - { - this->put(key,val); + void put_int(const std::string key, const int val) { + this->put(key, val); }; - void put_string(const std::string key,const std::string val) - { - this->put(key,val); + void put_string(const std::string key, const std::string val) { + this->put(key, val); }; - void put_bool(const std::string key,const bool val) - { - this->put(key,val); + void put_bool(const std::string key, const bool val) { + this->put(key, val); }; - void put_double(const std::string key,const double val) - { - this->put(key,val); + void put_double(const std::string key, const double val) { + this->put(key, val); }; - void put_long(const std::string key,const long val) - { - this->put(key,val); + void put_long(const std::string key, const long val) { + this->put(key, val); }; /*! Create or append to a chained string. * @@ -381,13 +359,10 @@ other attributes. and it is not of type string. */ void append_chain(const std::string key, const std::string val, - const std::string separator=std::string(":")); + const std::string separator = std::string(":")); /*! Return the keys of all altered Metadata values. */ - std::set modified() const - { - return changed_or_set; - }; + std::set modified() const { return changed_or_set; }; /*! \brief Mark all data as unmodified. * * There are situations where it is necessary to clear the @@ -398,10 +373,7 @@ other attributes. * This method clears the entire container that defines * changed data. * */ - void clear_modified() - { - changed_or_set.clear(); - }; + void clear_modified() { changed_or_set.clear(); }; /*! Return all keys without any type information. */ std::set keys() const noexcept; /*! Test if a key has an associated value. Returns true if @@ -426,9 +398,9 @@ other attributes. /*! Return the size of the internal map container. */ std::size_t size() const noexcept; /*! Return iterator to beginning of internal map container. */ - std::map::const_iterator begin() const noexcept; + std::map::const_iterator begin() const noexcept; /*! Return iterator to end of internal map container. */ - std::map::const_iterator end() const noexcept; + std::map::const_iterator end() const noexcept; /*! \brief Change the keyword to access an attribute. Sometimes it is useful to change the key used to access a particular piece @@ -456,35 +428,33 @@ other attributes. /*! Unpack serialized Metadata. * This function is the inverse of the serialize function. It recreates a - Metadata object serialized previously with the serialize function. + Metadata object serialized previously with the serialize function. \param sd is the serialized data to be unpacked \return Metadata derived from sd */ friend Metadata restore_serialized_metadata_py(const pybind11::object &sd); /*! Standard operator for overloading output to a stringstream */ - friend std::ostringstream& operator<<(std::ostringstream&, - const mspass::utility::Metadata&); + friend std::ostringstream &operator<<(std::ostringstream &, + const mspass::utility::Metadata &); + protected: - std::map md; + std::map md; /* The keys of any entry changed will be contained here. */ std::set changed_or_set; }; -template T Metadata::get(const std::string key) const -{ +template T Metadata::get(const std::string key) const { T result; - std::map::const_iterator iptr; - iptr=md.find(key); - if(iptr==md.end()) - { - throw MetadataGetError(key,typeid(T).name()); + std::map::const_iterator iptr; + iptr = md.find(key); + if (iptr == md.end()) { + throw MetadataGetError(key, typeid(T).name()); } - boost::any aval=iptr->second; - try{ - result=boost::any_cast(aval); - }catch(boost::bad_any_cast& err) - { + boost::any aval = iptr->second; + try { + result = boost::any_cast(aval); + } catch (boost::bad_any_cast &err) { const std::type_info &ti = aval.type(); - throw MetadataGetError(err.what(),key,typeid(T).name(),ti.name()); + throw MetadataGetError(err.what(), key, typeid(T).name(), ti.name()); }; return result; } @@ -506,26 +476,26 @@ However, 99% of attributes one normally wants to work with can be cast into the stock language types defined by this enum. This is derived form seispp in antelope contrib but adapted to the new form with boost::any. */ -enum class MDtype{ - Real, - Real32, - Double, - Real64, - Integer, - Int32, - Long, - Int64, - String, - Boolean, - Double_Array, - Invalid +enum class MDtype { + Real, + Real32, + Double, + Real64, + Integer, + Int32, + Long, + Int64, + String, + Boolean, + Double_Array, + Invalid }; /*! \brief Used in Metadata to defined type of Metadata associated with a given tag. **/ typedef struct Metadata_typedef { - std::string tag; /*!< Name attached to this item.*/ - MDtype mdt; /*!< Type of this item. */ + std::string tag; /*!< Name attached to this item.*/ + MDtype mdt; /*!< Type of this item. */ } Metadata_typedef; /*! Container to drive selected copies. @@ -546,20 +516,21 @@ the MetadataList at startup to define the subset. See related procedures that create one of them. (Not presently a class because the MetadataList is just a simple std::list container.) -\param mdin is the container to retrieve attributes from (commonly a dynamic_cast -from a data object). -\param mdout is the output Metadata (also commonly a dynamic_cast from a data object.) +\param mdin is the container to retrieve attributes from (commonly a +dynamic_cast from a data object). +\param mdout is the output Metadata (also commonly a dynamic_cast from a data +object.) \param mdlist is the list that defines the subset to copy from mdin to mdout. \return number of items copied -\exception will throw an MsPASSError if the input is missing one of the attributes - defined in mdlist or if there is a type mismatch. This means the copy - will be incomplete and not trusted. Handlers need to decide what to - do in this condition. +\exception will throw an MsPASSError if the input is missing one of the +attributes defined in mdlist or if there is a type mismatch. This means the +copy will be incomplete and not trusted. Handlers need to decide what to do in +this condition. */ -int copy_selected_metadata(const Metadata& mdin, Metadata& mdout, - const MetadataList& mdlist); +int copy_selected_metadata(const Metadata &mdin, Metadata &mdout, + const MetadataList &mdlist); /*! Serialize Metadata to a string. This function is needed to support pickle in the python interface. @@ -596,6 +567,6 @@ anything else. Metadata restore_serialized_metadata(const std::string); Metadata restore_serialized_metadata_py(const pybind11::object &sd); -} // end utility namespace -} //End of namespace MsPASS +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/MetadataDefinitions.h b/cxx/include/mspass/utility/MetadataDefinitions.h index 022c85ca8..2aaf9dd74 100644 --- a/cxx/include/mspass/utility/MetadataDefinitions.h +++ b/cxx/include/mspass/utility/MetadataDefinitions.h @@ -1,14 +1,14 @@ #ifndef _METADATADEFINITIONS_H_ #define _METADATADEFINITIONS_H_ +#include #include #include -namespace mspass{ -namespace utility{ -enum class MDDefFormat -{ - PF, - YAML -}; +#include +#include "mspass/utility/Metadata.h" +namespace mspass { +namespace utility { +using mspass::utility::MDtype; +enum class MDDefFormat { PF, YAML }; /*! \brief Define properties of Metadata known to mspass. @@ -22,13 +22,12 @@ by loosy goosy about types. The main use is then expected that all gets and puts to Metadata will be preceded by calls to the type method here. Based on the return the right get or put method can be called. -The overhead of creating this thing is not small. It will likely be recommended -as an initialization step for most mspass processing scripts. Ultimately it -perhaps should have a database constructor, but initially we will build it only from -data file. +The overhead of creating this thing is not small. It will likely be +recommended as an initialization step for most mspass processing scripts. +Ultimately it perhaps should have a database constructor, but initially we will +build it only from data file. */ -class MetadataDefinitions -{ +class MetadataDefinitions { public: /*! Default constructor. Loads default schema name of mspass. */ MetadataDefinitions(); @@ -48,16 +47,18 @@ class MetadataDefinitions \param mdname is the file to read \param form defines the format (limited by MDDefFormat definitions) */ - MetadataDefinitions(const std::string mdname,const mspass::utility::MDDefFormat form); + MetadataDefinitions(const std::string mdname, + const mspass::utility::MDDefFormat form); /*! Standard copy constructor. */ - MetadataDefinitions(const MetadataDefinitions& parent); + MetadataDefinitions(const MetadataDefinitions &parent); /*! Test if a key is defined either as a unique key or an alias */ bool is_defined(const std::string key) const noexcept; /*! Return a description of the concept this attribute defines. \param key is the name that defines the attribute of interest - \return a string with a terse description of the concept this attribute defines. + \return a string with a terse description of the concept this attribute + defines. */ std::string concept(const std::string key) const; /*! Get the type of an attribute. @@ -107,9 +108,10 @@ class MetadataDefinitions \param aliasname is the name of the alias for which we want the definitive key - \return std::pair with the definitive key as the first of the pair and the type - in the second field. */ - std::pair unique_name(const std::string aliasname) const; + \return std::pair with the definitive key as the first of the pair and the + type in the second field. */ + std::pair + unique_name(const std::string aliasname) const; /*! Add an alias for key. \param key is the main key for which an alias is to be defined @@ -184,13 +186,14 @@ class MetadataDefinitions related collection method that is designed to handle that. This method should normally be used only on read operations - to select the correct entry for what could otherwise be a potentially ambiguous key. + to select the correct entry for what could otherwise be a potentially + ambiguous key. \param key is the flat namespace key for which normalizing data is needed \return name for unique id for requested key. Returns an empty string if - the key is not defined as normalized. In multiple calls it is more efficient - to test for a null return and handle such entries inline instead of a double - search required if preceded by is_normalized. + the key is not defined as normalized. In multiple calls it is more + efficient to test for a null return and handle such entries inline instead of + a double search required if preceded by is_normalized. */ std::string unique_id_key(const std::string key) const; /*! \Brief return the master collection (table) for a key used as a unique id. @@ -199,13 +202,13 @@ class MetadataDefinitions that contain the data using normalization. In seismic data type examples are receiver location tables, receiver response tables, and source location data. This method should nearly always be paired with a call to unique_id_key. - The idea is to first ask for the unique_id_key and then ask what collection (table) - contains the key returned by unique_id_key. This provides a fast and + The idea is to first ask for the unique_id_key and then ask what collection + (table) contains the key returned by unique_id_key. This provides a fast and convenient lookup for normalized data. \param key is the normally the return from unique_id_key - \return string defining the collection(table) the key can be used for locating the - unique tuple/document required to access related Metadata. String will be + \return string defining the collection(table) the key can be used for locating + the unique tuple/document required to access related Metadata. String will be empty if the search fails. */ std::string collection(const std::string key) const; @@ -213,15 +216,17 @@ class MetadataDefinitions For mspass using mongodb normalization for all currently supported Metadata can be reduced to a collection(table)-attribute name pair. The unique_id_key - and collection methods can be called to obtained this information, but doing so - requires a purely duplicate (internal map container) search. - This convenience method is best used with MongoDB for efficiency. + and collection methods can be called to obtained this information, but doing + so requires a purely duplicate (internal map container) search. This + convenience method is best used with MongoDB for efficiency. \param key is the flat namespace key for which normalizing data is needed - \return an std::pair with of strings with first=collection and second=attribute name. + \return an std::pair with of strings with first=collection and + second=attribute name. */ - std::pair normalize_data(const std::string key) const; + std::pair + normalize_data(const std::string key) const; /*! \brief Apply a set of aliases to data. This method should be called in processing workflows to apply a series @@ -237,8 +242,8 @@ class MetadataDefinitions \return std::list of srings of failed changes. Callers should test the size of this return and take action if needed. */ -std::list apply_aliases(mspass::utility::Metadata& d, - const std::list aliaslist); + std::list apply_aliases(mspass::utility::Metadata &d, + const std::list aliaslist); /*! \brief Restore any aliases to unique names. Aliases are needed to support legacy packages, but can cause downstream @@ -253,9 +258,9 @@ std::list apply_aliases(mspass::utility::Metadata& d, a log entry into this returned object. Caller should test the size of the return and handle or ignore errors as appropriate. */ - void clear_aliases(mspass::utility::Metadata& d); + void clear_aliases(mspass::utility::Metadata &d); /*! Standard assignment operator. */ - MetadataDefinitions& operator=(const MetadataDefinitions& other); + MetadataDefinitions &operator=(const MetadataDefinitions &other); /*!\brief Accumulate additional definitions. Appends data in other to current. The behavior or this operator @@ -274,23 +279,24 @@ std::list apply_aliases(mspass::utility::Metadata& d, and is not expected to ever be used by processors. */ - MetadataDefinitions& operator+=(const MetadataDefinitions& other); + MetadataDefinitions &operator+=(const MetadataDefinitions &other); + private: - std::map tmap; - std::map cmap; - std::multimap aliasmap; - std::map alias_xref; + std::map tmap; + std::map cmap; + std::multimap aliasmap; + std::map alias_xref; std::set roset; /* This map is used to handle normalized data in any database. For the initial design the data could be a pair, but I make it a tuple because I can conveive extensions that would require additional information to provides - a unique index definition. e.g. the antelope indexing of sta,chan,time:endtime. + a unique index definition. e.g. the antelope indexing of + sta,chan,time:endtime. */ - std::map> unique_id_data; + std::map> unique_id_data; void pfreader(const std::string pfname); void yaml_reader(const std::string fname); - }; -} // end utility namespace -} // end mspass namespace +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/MsPASSError.h b/cxx/include/mspass/utility/MsPASSError.h index 3f8ae7c4a..342e0952d 100644 --- a/cxx/include/mspass/utility/MsPASSError.h +++ b/cxx/include/mspass/utility/MsPASSError.h @@ -1,9 +1,9 @@ #ifndef _MSPASS_ERROR_H_ #define _MSPASS_ERROR_H_ -#include #include +#include namespace mspass { -namespace utility{ +namespace utility { /*! \brief Severity code for error messages. @@ -11,18 +11,17 @@ C++11 added the construct of a enum class that we use here. It makes the enum more strongly typed to avoid collisions for these common words that would be trouble otherwise. This class is used in all MsPASSError objects. */ -enum class ErrorSeverity -{ - Fatal, - Invalid, - Suspect, - Complaint, - Debug, - Informational +enum class ErrorSeverity { + Fatal, + Invalid, + Suspect, + Complaint, + Debug, + Informational }; - -/*! Internal used to convert the keyword in a message string to an ErrorSeverity.*/ +/*! Internal used to convert the keyword in a message string to an + * ErrorSeverity.*/ mspass::utility::ErrorSeverity string2severity(const std::string howbad); /*! Inverse of string2severity*/ std::string severity2string(const mspass::utility::ErrorSeverity es); @@ -36,100 +35,95 @@ std::string severity2string(const mspass::utility::ErrorSeverity es); MsPASS error objects that are it's descendents. \author Gary L. Pavlis **/ -class MsPASSError : public std::exception -{ +class MsPASSError : public std::exception { public: - -/*! - Default constructor built inline. -**/ - MsPASSError(){ - message="MsPASS library error\n"; - badness=ErrorSeverity::Fatal; - }; -/*! \brief Construct from a std::string with badness defined by keywords in a string. - -Sometimes it is easier and more readable to use a string literal to define -the error class. This uses that approach. - -\param mess is the error message posted. -\param howbad is a string to translate to one of the allowed enum values. The - allowed values are the same as the enum defined in this file: - FATAL,Invalid,Suspect,Complaint,Debug,Informational. -**/ - MsPASSError(const std::string mess,const char *howbad){ - message=mess; - std::string s(howbad); - /* this small helper function parses s to conver to the - enum class of badness*/ - badness=string2severity(s); - }; -/*! Construct from a string with enum defining severity. - -This should be the normal form of this error object to throw. Default of -the enum allows simpler usage for most errors. - -\param mess - is the error message to be posted. -\param s is the severity enum (default Invalid). -*/ - MsPASSError(const std::string mess,const ErrorSeverity s=ErrorSeverity::Invalid) - { - message=mess; - badness=s; - }; -/*! Construct from a char * and severity enum. -**/ - MsPASSError(const char *mess,const ErrorSeverity s){ - message=std::string(mess); - badness=s; - }; -/*! - Sends error message thrown by MsPASS library functions to standard error. -**/ -void log_error(){ - std::cerr << message << std::endl; -}; -/*! Overloaded method for sending error message to other than stderr. */ -void log_error(std::ostream& ofs) -{ - ofs << message < -#include -#include -#include -#include #include #include +#include +#include +#include #include #include #include #include -namespace mspass{ -namespace utility{ +#include +#include +namespace mspass { +namespace utility { /*! \brief Lightweight data structure to completely describe an algorithm. Processing data always involves application of one or more algorithms. @@ -40,15 +40,14 @@ chain. For readers input_type is set to "NotApplicable" and output_type is to be defined for that reader. Readers may or may not have control parameters. */ -class AlgorithmDefinition -{ +class AlgorithmDefinition { public: /*! Default constructor. This consructor is realy the same as what would be automatically generated. We define it to be clear and because I think pybind11 may need this declaration to allow a python wrapper for a default constructor. */ - AlgorithmDefinition() : nm(),myid(),input_type(),output_type() {}; + AlgorithmDefinition() : nm(), myid(), input_type(), output_type() {}; /*! Primary constructor. This constructor sets the two primary attributes of this object. @@ -62,24 +61,19 @@ class AlgorithmDefinition this instance of an algorithm. */ AlgorithmDefinition(const std::string name, const std::string typin, - const std::string typout, const std::string id) - { - nm=name; - myid=id; - input_type=typin; - output_type=typout; + const std::string typout, const std::string id) { + nm = name; + myid = id; + input_type = typin; + output_type = typout; }; - AlgorithmDefinition(const AlgorithmDefinition& parent) - { - nm=parent.nm; - myid=parent.myid; - input_type=parent.input_type; - output_type=parent.output_type; - }; - std::string name() const - { - return nm; + AlgorithmDefinition(const AlgorithmDefinition &parent) { + nm = parent.nm; + myid = parent.myid; + input_type = parent.input_type; + output_type = parent.output_type; }; + std::string name() const { return nm; }; /*! \brief return the id as a string. In MsPASS the id is normally a MongoDB ObjectID string representation of @@ -87,10 +81,7 @@ class AlgorithmDefinition particular algorithm instance. If the algorithm has no parameters this string will be null. Callers should test that condition by calling the length method of std::string to verify the id is not zero length */ - std::string id() const - { - return myid; - }; + std::string id() const { return myid; }; /*! Set a new id string. The id straing is used to define a unique instance of an algorithm @@ -98,47 +89,38 @@ class AlgorithmDefinition class because it is the only attribute that should ever be changed after construction. The reason is the name and type constraints are fixed, but id defines a particular instance that may be variable. */ - void set_id(const std::string id){myid=id;}; - //void set_name(const string name){nm=name;}; - AlgorithmDefinition& operator=(const AlgorithmDefinition& parent) - { - if(this==&parent) - { - nm=parent.nm; - myid=parent.myid; - input_type=parent.input_type; - output_type=parent.output_type; + void set_id(const std::string id) { myid = id; }; + // void set_name(const string name){nm=name;}; + AlgorithmDefinition &operator=(const AlgorithmDefinition &parent) { + if (this == &parent) { + nm = parent.nm; + myid = parent.myid; + input_type = parent.input_type; + output_type = parent.output_type; } return *this; }; + private: std::string nm; std::string myid; std::string input_type; std::string output_type; friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { + template + void serialize(Archive &ar, const unsigned int version) { ar & nm; ar & myid; }; }; -class ProcessManager -{ +class ProcessManager { public: ProcessManager(); ProcessManager(std::string fname); AlgorithmDefinition algorithm(const std::string name, - const size_t instance=0) const; - std::string jobname() const - { - return jobnm; - }; - std::string jobid() const - { - return boost::uuids::to_string(job_uuid); - }; + const size_t instance = 0) const; + std::string jobname() const { return jobnm; }; + std::string jobid() const { return boost::uuids::to_string(job_uuid); }; /*! \brief Get a new UUID to define unique job run. MsPASS data objects are tagged with a UUID to properly handle @@ -150,26 +132,25 @@ class ProcessManager \return new uuid definign this job in string form. */ - std::string new_newid() - { + std::string new_newid() { boost::uuids::uuid id; - id=gen(); + id = gen(); return boost::uuids::to_string(id); } + private: std::string jobnm; boost::uuids::uuid job_uuid; boost::uuids::random_generator gen; - std::map> algs; + std::map> algs; friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { + template + void serialize(Archive &ar, const unsigned int version) { ar & jobnm; ar & job_uuid; ar & algs; }; }; -} // end utility namespace -} // end mspass namespace encapsulation +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/ProcessingHistory.h b/cxx/include/mspass/utility/ProcessingHistory.h index afd9fa85f..fee34b755 100644 --- a/cxx/include/mspass/utility/ProcessingHistory.h +++ b/cxx/include/mspass/utility/ProcessingHistory.h @@ -1,34 +1,27 @@ #ifndef _PROCESSING_HISTORY_H_ #define _PROCESSING_HISTORY_H_ -#include -#include -#include -#include +#include "mspass/utility/ErrorLogger.h" +#include +#include #include #include #include #include #include #include -#include -#include -#include "mspass/utility/ErrorLogger.h" -//#include "mspass/seismic/Ensemble.h" -namespace mspass{ -namespace utility{ +#include +#include +#include +#include +// #include "mspass/seismic/Ensemble.h" +namespace mspass { +namespace utility { /*! This enum class is used to define status of processing of a datum. We use this mechanism to help keep the history data from creating memory bloat. It is alwo helpul to build a linked list of a chain of data that have to be handled somewhat differently. See documentation for classes below for further info about how this is used */ -enum class ProcessingStatus -{ - RAW, - ORIGIN, - VOLATILE, - SAVED, - UNDEFINED -}; +enum class ProcessingStatus { RAW, ORIGIN, VOLATILE, SAVED, UNDEFINED }; /*! \brief Atomic data type definition for mspass MsPASS has the concept of atomic types. One part of that definition is that @@ -37,12 +30,7 @@ to be expanded if new types are added, but the design goal is to make extension relatively easy - add the data implementation that inherits ProcessingHistory and add an entry for that type here. */ -enum class AtomicType -{ - SEISMOGRAM, - TIMESERIES, - UNDEFINED -}; +enum class AtomicType { SEISMOGRAM, TIMESERIES, UNDEFINED }; /*! \brief Special definition of uuid for a saved record. We found in the implementation of this that an issue in the use of uuids and @@ -64,26 +52,21 @@ and the history chain will become ambiguous (two trees emerging from the same root).*/ const std::string SAVED_ID_KEY("NODEDATA_AT_SAVE"); - /*! Base class defining core concepts. */ -class BasicProcessingHistory -{ +class BasicProcessingHistory { public: - BasicProcessingHistory() - { - jid=std::string(); - jnm=std::string(); + BasicProcessingHistory() { + jid = std::string(); + jnm = std::string(); }; - virtual ~BasicProcessingHistory(){}; - BasicProcessingHistory(const std::string jobname,const std::string jobid) - { - jid=jobid; - jnm=jobname; + virtual ~BasicProcessingHistory() {}; + BasicProcessingHistory(const std::string jobname, const std::string jobid) { + jid = jobid; + jnm = jobname; }; - BasicProcessingHistory(const BasicProcessingHistory& parent) - { - jid=parent.jid; - jnm=parent.jnm; + BasicProcessingHistory(const BasicProcessingHistory &parent) { + jid = parent.jid; + jnm = parent.jnm; }; /*! Return number or processing algorithms applied to produce these data. @@ -93,45 +76,33 @@ class BasicProcessingHistory This method could be made pure, but for convenience the base class always returns 0 since it does not implement the actual history mechanism. */ - virtual size_t number_of_stages(){return 0;}; - std::string jobid() const - { - return jid; - }; - void set_jobid(const std::string& newjid) - { - jid=newjid; - }; - std::string jobname() const - { - return jnm; - }; - void set_jobname(const std::string jobname) - { - jnm=jobname; - }; - BasicProcessingHistory& operator=(const BasicProcessingHistory& parent) - { - if(this!=(&parent)) - { - jnm=parent.jnm; - jid=parent.jid; + virtual size_t number_of_stages() { return 0; }; + std::string jobid() const { return jid; }; + void set_jobid(const std::string &newjid) { jid = newjid; }; + std::string jobname() const { return jnm; }; + void set_jobname(const std::string jobname) { jnm = jobname; }; + BasicProcessingHistory &operator=(const BasicProcessingHistory &parent) { + if (this != (&parent)) { + jnm = parent.jnm; + jid = parent.jid; } return *this; } + protected: std::string jid; std::string jnm; + private: friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { - ar & jid; - ar & jnm; - }; + template + void serialize(Archive &ar, const unsigned int version) { + ar & jid; + ar & jnm; + }; }; -/*! \brief Holds properties of data used as input to algorithm that created this object. +/*! \brief Holds properties of data used as input to algorithm that created this +object. The implementation here uses a multimap to define parents of each uuid in a history chain. This class is used mainly internally for ProcessingHistory @@ -139,8 +110,7 @@ to maintain that data. It will be visible to C++ programs but will not be visible in python. One of these entries is created for each parent data used to create the current data. */ -class NodeData -{ +class NodeData { public: /*! status definition of the parent. */ mspass::utility::ProcessingStatus status; @@ -148,7 +118,8 @@ class NodeData std::string uuid; /*! This enum can be used to track changes in data type. */ mspass::utility::AtomicType type; - /*! Integer count of the number of processing steps applied to create this parent.*/ + /*! Integer count of the number of processing steps applied to create this + * parent.*/ int stage; /*! \brief Name of algorithm algorithm applied at this stage. @@ -172,22 +143,22 @@ class NodeData /* These standard elements could be defaulted, but we implement them explicitly for clarity - implemented in the cc file. */ NodeData(); - NodeData(const NodeData& parent); - NodeData& operator=(const NodeData& parent); - bool operator==(const NodeData& other); - bool operator!=(const NodeData& other); + NodeData(const NodeData &parent); + NodeData &operator=(const NodeData &parent); + bool operator==(const NodeData &other); + bool operator!=(const NodeData &other); + private: friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { - ar & status; - ar & uuid; - ar & type; - ar & stage; - ar & algorithm; - ar & algid; - }; + template + void serialize(Archive &ar, const unsigned int version) { + ar & status; + ar & uuid; + ar & type; + ar & stage; + ar & algorithm; + ar & algid; + }; }; /*! \brief Lightweight class to preserve procesing chain of atomic objects. @@ -228,22 +199,20 @@ Names used imply the following concepts: field experiment, or simulation). That tag means no prior history can be reconstructed. origin - top-level ancestor of current data. The top of a processing - chain is always tagged as an origin. A top level can also be "raw" but not necessarily. - In particular, readers that load partially processed data should mark - the data read as an origin, but not raw. - stage - all processed data objects that are volatile elements within a - workflow are defined as a stage. They are presumed to leave their - existence known only through ancestory preserved in the processing - chain. A stage becomes a potential root only when it is saved by - a writer where the writer will mark that position as a save. Considered - calling this a branch, but that doesn't capture the concept right since - we require this mechanism to correctly perserve splits into multiple - outputs. We preserve that cleanly for each data object. That is, the - implementation make it easy to reconstruct the history of a single - final data object, but reconstructing interlinks between objects in an - overall processing flow will be a challenge. That was a necessary - compomise to avoid memory bloat. The history is properly viewed as - a tree branching from a single root (the final output) to leaves that + chain is always tagged as an origin. A top level can also be "raw" but not +necessarily. In particular, readers that load partially processed data should +mark the data read as an origin, but not raw. stage - all processed data objects +that are volatile elements within a workflow are defined as a stage. They are +presumed to leave their existence known only through ancestory preserved in the +processing chain. A stage becomes a potential root only when it is saved by a +writer where the writer will mark that position as a save. Considered calling +this a branch, but that doesn't capture the concept right since we require this +mechanism to correctly perserve splits into multiple outputs. We preserve that +cleanly for each data object. That is, the implementation make it easy to +reconstruct the history of a single final data object, but reconstructing +interlinks between objects in an overall processing flow will be a challenge. +That was a necessary compomise to avoid memory bloat. The history is properly +viewed as a tree branching from a single root (the final output) to leaves that define all it's parents. The concepts of raw, origin, and stage are implemented with the @@ -254,8 +223,7 @@ Names used imply the following concepts: */ -class ProcessingHistory : public BasicProcessingHistory -{ +class ProcessingHistory : public BasicProcessingHistory { public: ErrorLogger elog; /*! Default constructor. */ @@ -265,9 +233,9 @@ class ProcessingHistory : public BasicProcessingHistory \param jobnm - set as jobname \param jid - set as jobid */ - ProcessingHistory(const std::string jobnm,const std::string jid); + ProcessingHistory(const std::string jobnm, const std::string jid); /*! Standard copy constructor. */ - ProcessingHistory(const ProcessingHistory& parent); + ProcessingHistory(const ProcessingHistory &parent); /*! Return true if the processing chain is empty. This method provides a standard test for an invalid, empty processing chain. @@ -276,15 +244,20 @@ class ProcessingHistory : public BasicProcessingHistory the chain is initialized properly with a call to set_as_origin will this method return a false. */ bool is_empty() const; - /*! Return true if the current data is in state defined as "raw" - see class description*/ - bool is_raw()const; - /*! Return true if the current data is in state defined as "origin" - see class description*/ + /*! Return true if the current data is in state defined as "raw" - see class + * description*/ + bool is_raw() const; + /*! Return true if the current data is in state defined as "origin" - see + * class description*/ bool is_origin() const; - /*! Return true if the current data is in state defined as "volatile" - see class description*/ + /*! Return true if the current data is in state defined as "volatile" - see + * class description*/ bool is_volatile() const; - /*! Return true if the current data is in state defined as "saved" - see class description*/ + /*! Return true if the current data is in state defined as "saved" - see class + * description*/ bool is_saved() const; - /*! \brief Return number of processing stages that have been applied to this object. + /*! \brief Return number of processing stages that have been applied to this + object. One might want to know how many processing steps have been previously applied to produce the current data. For linear algorithms that would be useful @@ -347,8 +320,9 @@ class ProcessingHistory : public BasicProcessingHistory to elog if the history data structures are not empty and it the clear method needs to be called internally. */ - void set_as_origin(const std::string alg,const std::string algid, - const std::string uuid,const AtomicType typ, bool define_as_raw=false); + void set_as_origin(const std::string alg, const std::string algid, + const std::string uuid, const AtomicType typ, + bool define_as_raw = false); /*! Define history chain for an algorithm with multiple inputs in an ensemble. Use this method to define the history chain for an algorithm that has @@ -396,9 +370,11 @@ class ProcessingHistory : public BasicProcessingHistory \return a string representation of the uuid of the data to which this ProcessingHistory is now attached. */ - std::string new_ensemble_process(const std::string alg,const std::string algid, - const AtomicType typ,const std::vector parents, - const bool create_newid=true); + std::string + new_ensemble_process(const std::string alg, const std::string algid, + const AtomicType typ, + const std::vector parents, + const bool create_newid = true); /*! \brief Add one datum as an input for current data. This method MUST ONLY be called after a call to new_ensemble_process in the @@ -415,7 +391,7 @@ class ProcessingHistory : public BasicProcessingHistory will be saved as the base of the input chain from data_to_add. It can be different from the type of "this". */ - void add_one_input(const ProcessingHistory& data_to_add); + void add_one_input(const ProcessingHistory &data_to_add); /*! \brief Define several data objects as inputs. This method acts like add_one_input in that it alters only the inputs @@ -424,21 +400,21 @@ class ProcessingHistory : public BasicProcessingHistory \param d is the vector of data to define as inputs */ - void add_many_inputs(const std::vector& d); + void add_many_inputs(const std::vector &d); - /*! \brief Merge the history nodes from another. + /*! \brief Merge the history nodes from another. \param data_to_add is the ProcessingHistory of the data object to be merged. */ - void merge(const ProcessingHistory& data_to_add); + void merge(const ProcessingHistory &data_to_add); /*! \brief Method to use with a spark reduce algorithm. A reduce operator in spark utilizes a binary function where two inputs are used to generate a single output object. Because the inputs could be scattered on multiple processor nodes this operation must be associative. - The new_ensemble_process method does not satisfy that constraint so this method - was necessary to handle that type of algorithm correctly. + The new_ensemble_process method does not satisfy that constraint so this + method was necessary to handle that type of algorithm correctly. The way this algorithm works is it fundamentally branches on two different cases: (1) initialization, which is detected by testing if the node data @@ -450,8 +426,8 @@ class ProcessingHistory : public BasicProcessingHistory */ - void accumulate(const std::string alg,const std::string algid, - const AtomicType typ,const ProcessingHistory& newinput); + void accumulate(const std::string alg, const std::string algid, + const AtomicType typ, const ProcessingHistory &newinput); /*! \brief Clean up inconsistent uuids that can be produced by reduce. In a spark reduce operation it is possible to create multiple uuid keys @@ -478,32 +454,32 @@ class ProcessingHistory : public BasicProcessingHistory history is empty it returns the string UNDEFINED. */ std::string clean_accumulate_uuids(); - /*! \brief Define this algorithm as a one-to-one map of same type data. - - Many algorithms define a one-to-one map where each one input data object - creates one output data object. This (overloaded) version of this method - is most appropriate when input and output are the same type and the - history chain (ProcessingHistory) is what the new algorithm will - alter to make the result when it finishes. Use the overloaded - version with a separate ProcessingHistory copy if the current object's - data are not correct. In this algorithm the chain for this algorithm - is simply appended with new definitions. - - \param alg is the algorithm names to assign to the origin node. This - would normally be name defining the algorithm that makes sense to a human. - \param algid is an id designator to uniquely define an instance of algorithm. - Note that algid must itself be a unique keyword or the history chains - will get scrambled. alg is mostly carried as baggage to make output - more easily comprehended without additional lookups. - \param typ defines the data type (C++ class) the algorithm that is generating - this data will create. - \param newstatus is how the status marking for the output. Normal (default) - would be VOLATILE. This argument was included mainly for flexibility in - case we wanted to extend the allowed entries in ProcessingStatus. - */ - std::string new_map(const std::string alg,const std::string algid, - const AtomicType typ, - const ProcessingStatus newstatus=ProcessingStatus::VOLATILE); + /*! \brief Define this algorithm as a one-to-one map of same type data. + +Many algorithms define a one-to-one map where each one input data object +creates one output data object. This (overloaded) version of this method +is most appropriate when input and output are the same type and the +history chain (ProcessingHistory) is what the new algorithm will +alter to make the result when it finishes. Use the overloaded +version with a separate ProcessingHistory copy if the current object's +data are not correct. In this algorithm the chain for this algorithm +is simply appended with new definitions. + +\param alg is the algorithm names to assign to the origin node. This + would normally be name defining the algorithm that makes sense to a human. +\param algid is an id designator to uniquely define an instance of algorithm. + Note that algid must itself be a unique keyword or the history chains + will get scrambled. alg is mostly carried as baggage to make output + more easily comprehended without additional lookups. +\param typ defines the data type (C++ class) the algorithm that is generating + this data will create. +\param newstatus is how the status marking for the output. Normal (default) + would be VOLATILE. This argument was included mainly for flexibility in + case we wanted to extend the allowed entries in ProcessingStatus. +*/ + std::string + new_map(const std::string alg, const std::string algid, const AtomicType typ, + const ProcessingStatus newstatus = ProcessingStatus::VOLATILE); /*! \brief Define this algorithm as a one-to-one map. Many algorithms define a one-to-one map where each one input data object @@ -532,10 +508,10 @@ class ProcessingHistory : public BasicProcessingHistory would be VOLATILE. This argument was included mainly for flexibility in case we wanted to extend the allowed entries in ProcessingStatus. */ - std::string new_map(const std::string alg,const std::string algid, - const AtomicType typ, - const ProcessingHistory& data_to_clone, - const ProcessingStatus newstatus=ProcessingStatus::VOLATILE); + std::string + new_map(const std::string alg, const std::string algid, const AtomicType typ, + const ProcessingHistory &data_to_clone, + const ProcessingStatus newstatus = ProcessingStatus::VOLATILE); /*! \brief Prepare the current data for saving. @@ -572,8 +548,8 @@ class ProcessingHistory : public BasicProcessingHistory an implementation detail in how this will work with MongoDB. \param typ defines the data type (C++ class) that was just saved. */ - std::string map_as_saved(const std::string alg,const std::string algid, - const AtomicType typ); + std::string map_as_saved(const std::string alg, const std::string algid, + const AtomicType typ); /*! Clear this history chain - use with caution. */ void clear(); /*! Retrieve the nodes multimap that defines the tree stucture branches. @@ -582,7 +558,7 @@ class ProcessingHistory : public BasicProcessingHistory It copies the map and then pushes the "current" contents to the map before returning the copy. This allows the data defines as current to not be pushed into the tree until they are needed. */ - std::multimap get_nodes() const; + std::multimap get_nodes() const; /*! Return the current stage count for this object. @@ -592,28 +568,18 @@ class ProcessingHistory : public BasicProcessingHistory We retain it in the API in the event we want to implement an accumulating counter. */ - int stage() const - { - return current_stage; - }; + int stage() const { return current_stage; }; /*! Return the current status definition (an enum). */ - ProcessingStatus status() const - { - return current_status; - }; + ProcessingStatus status() const { return current_status; }; /*! Return the id of this object set for this history chain. We maintain the uuid for a data object inside this class. This method fetches the string representation of the uuid of this data object. */ - std::string id() const - { - return current_id; - }; + std::string id() const { return current_id; }; /*! Return the algorithm name and id that created current node. */ - std::pair created_by() const - { - std::pair result(algorithm,algid); + std::pair created_by() const { + std::pair result(algorithm, algid); return result; } /*! Return all the attributes of current. @@ -627,15 +593,15 @@ class ProcessingHistory : public BasicProcessingHistory This creates a new uuid - how is an implementation detail but here we use boost's random number generator uuid generator that has some absurdly small - probability of generating two equal ids. It returns the string representation - of the id created. */ + probability of generating two equal ids. It returns the string + representation of the id created. */ std::string newid(); /*! Return the number of inputs used to create current data. In a number of contexts it can be useful to know the number of inputs defined for the current object. This returns that count. */ - int number_inputs()const; + int number_inputs() const; /*! Return the number of inputs defined for any data in the process chain. This overloaded version of number_inputs asks for the number of inputs @@ -644,7 +610,7 @@ class ProcessingHistory : public BasicProcessingHistory \param uuidstr is the uuid string to check in the ancestory record. */ - int number_inputs(const std::string uuidstr)const; + int number_inputs(const std::string uuidstr) const; /*! Set the uuid manually. @@ -668,19 +634,21 @@ class ProcessingHistory : public BasicProcessingHistory empty list if the key is not found. */ - std::list inputs(const std::string id_to_find) const; + std::list + inputs(const std::string id_to_find) const; /*! Assignment operator. */ - ProcessingHistory& operator=(const ProcessingHistory& parent); -/* We make this protected to simplify expected extensions. In particular, -the process of reconstructing history is a complicated process we don't -want to add as baggage to regular data. Hence, tools to reconstruct history -(provenance) are expected to extend this class. */ + ProcessingHistory &operator=(const ProcessingHistory &parent); + /* We make this protected to simplify expected extensions. In particular, + the process of reconstructing history is a complicated process we don't + want to add as baggage to regular data. Hence, tools to reconstruct history + (provenance) are expected to extend this class. */ protected: /* This map defines connections of each data object to others. Key is the uuid of a given object and the values (second) associated with that key are the inputs used to create the data defined by the key uuid */ - std::multimap nodes; + std::multimap nodes; + private: /* This set of private variables are the values of attributes for the same concepts in the NodeData struct/class. We break them out as @@ -694,20 +662,18 @@ want to add as baggage to regular data. Hence, tools to reconstruct history std::string algorithm; std::string algid; - friend boost::serialization::access; - template - void serialize(Archive& ar,const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & nodes; - ar & current_status; - ar & current_id; - ar & current_stage; - ar & mytype; - ar & algorithm; - ar & algid; - ar & elog; + template + void serialize(Archive &ar, const unsigned int version) { + ar &boost::serialization::base_object(*this); + ar & nodes; + ar & current_status; + ar & current_id; + ar & current_stage; + ar & mytype; + ar & algorithm; + ar & algid; + ar & elog; }; }; /* function prototypes of helpers */ @@ -728,11 +694,9 @@ CoreSeismogram or CoreTimeSeries. */ template - void append_input(const Tdata& d, ProcessingHistory& his) -{ - if(d.live()) - { - const ProcessingHistory *ptr=dynamic_cast(&d); +void append_input(const Tdata &d, ProcessingHistory &his) { + if (d.live()) { + const ProcessingHistory *ptr = dynamic_cast(&d); his.add_one_input(*ptr); } }; @@ -753,17 +717,17 @@ same level (stage) the order of the list will be random in algorithm and algid. \param h is the history chain to be dumped (normally a dynamic cast from a Seismogram or TimeSeries object) */ -std::list> - algorithm_history(const ProcessingHistory& h); +std::list> +algorithm_history(const ProcessingHistory &h); /*! \brief Return uuids of all data handled by a given processing algorithm that are parents of this object. This method is an extended version of algorithm_history. It returns a list of uuids matching the algorithm id passed as an argument. Note for interactive data exploration a typical usage would be to call algorithm_history -to alg and algid pair of interest and then call this method to get the uuids with -which it is associated. For linear workflows the return will be equivalent to all -inputs passed through that algorithm. For iterative algorithms the list +to alg and algid pair of interest and then call this method to get the uuids +with which it is associated. For linear workflows the return will be equivalent +to all inputs passed through that algorithm. For iterative algorithms the list can be much longer as each pass will be post new uuids for the same algorithm. @@ -774,8 +738,9 @@ algorithm. \return list of uuids handled by that instance of that algorithm. Silently returns an empty list if there is no match */ -std::list algorithm_outputs(const ProcessingHistory& h, - const std::string alg, const std::string algid); -} // end utility namespace -} // End mspass namespace +std::list algorithm_outputs(const ProcessingHistory &h, + const std::string alg, + const std::string algid); +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/SphericalCoordinate.h b/cxx/include/mspass/utility/SphericalCoordinate.h index 2377f4d42..72f8f607c 100644 --- a/cxx/include/mspass/utility/SphericalCoordinate.h +++ b/cxx/include/mspass/utility/SphericalCoordinate.h @@ -1,29 +1,32 @@ #ifndef _SPHERICALCOORDINATE_H_ #define _SPHERICALCOORDINATE_H_ namespace mspass { -namespace utility{ +namespace utility { /*! \brief Encapsulates spherical coordinates in a data structure. Spherical coordinates come up in a lot of contexts in Earth Science data processing. Note actual coodinate system can depend on context. For whole Earth models it can define global coordinates, but in three component - seismograms the normal convention of geographical coordinates is always assumed. + seismograms the normal convention of geographical coordinates is always +assumed. \author Gary L. Pavlis **/ typedef struct { -/*! - Radius from center. -**/ - double radius; -/*! - Zonal angle (from z) of spherical coordinates. Units always assumed to be radians. -**/ - double theta; -/*! - Azimuthal angle (from x) of spherical coordinates. Units always assumed to be radians. -**/ - double phi; + /*! + Radius from center. + **/ + double radius; + /*! + Zonal angle (from z) of spherical coordinates. Units always assumed to be + radians. + **/ + double theta; + /*! + Azimuthal angle (from x) of spherical coordinates. Units always assumed to + be radians. + **/ + double phi; } SphericalCoordinate; /*! Returns a SphericalCoordinate data structure equivalent to one @@ -34,11 +37,11 @@ SphericalCoordinate UnitVectorToSpherical(const double nu[3]); Returns a unit vector (vector of 3 doubles) equivalent to direction defined in sphereical coordinates. **/ -double *SphericalToUnitVector(const SphericalCoordinate& sc); +double *SphericalToUnitVector(const SphericalCoordinate &sc); /*! Convert from degrees to radians. */ double rad(const double theta_deg); /*! Convert from radians to degrees. */ double deg(const double theta_rad); -} // end utility namespace -} // end namespace mspass declaration +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/VectorStatistics.h b/cxx/include/mspass/utility/VectorStatistics.h index 6770c9df3..1a4745148 100644 --- a/cxx/include/mspass/utility/VectorStatistics.h +++ b/cxx/include/mspass/utility/VectorStatistics.h @@ -1,200 +1,192 @@ #ifndef _VECTORSTATISTICS_H_ #define _VECTORSTATISTICS_H_ -#include -#include #include "mspass/utility/MsPASSError.h" -namespace mspass -{ -namespace utility{ +#include +#include +namespace mspass { +namespace utility { -/*! \brief Generic object to compute common robust statistics from a vector container of data. +/*! \brief Generic object to compute common robust statistics from a vector +container of data. -Robust estimators commonly use statistics based on ranked data. This fits naturally with an -STL vector container that is by definition sortable by a standard method. This object -has methods that return common statistics derived from sorted vector data. */ -template class VectorStatistics -{ +Robust estimators commonly use statistics based on ranked data. This fits +naturally with an STL vector container that is by definition sortable by a +standard method. This object has methods that return common statistics derived +from sorted vector data. */ +template class VectorStatistics { public: - /*! Primary constructor. + /*! Primary constructor. + + \param din is vector container from which statistics are to be derived. + Currently assume default sort is used. Probably should have an optional order + template parameter for the container. */ + VectorStatistics(std::vector din); + /*! Construct from a C style pointer to an array of T.*/ + VectorStatistics(T *din, int n); + /*! Return median */ + T median(); + /*! Return the mean */ + T mean(); + /*! Return the lower quartile. */ + T q1_4(); + /*! Return the upper (3/4) quartile. */ + T q3_4(); + /*! Return the interquartile (q3/4 - q1/4) */ + T interquartile(); + /*! Return the median absolute distance robust measure of spread. */ + T mad(T center); + /*! Return sum of squares. */ + T ssq(); + /*! Return largest value in the data set. */ + T upper_bound(); + /*! Return smallest value in the data set. */ + T lower_bound(); + /*! Return full range of data (largest - smallest) */ + T range(); + /*! Return nth value from the sorted data that is the nth 1-quantile. + Will throw a MsPASSError if n exceed the data length. */ + T quantile(size_t n); - \param din is vector container from which statistics are to be derived. Currently - assume default sort is used. Probably should have an optional order template parameter - for the container. */ - VectorStatistics(std::vector din); - /*! Construct from a C style pointer to an array of T.*/ - VectorStatistics(T *din,int n); - /*! Return median */ - T median(); - /*! Return the mean */ - T mean(); - /*! Return the lower quartile. */ - T q1_4(); - /*! Return the upper (3/4) quartile. */ - T q3_4(); - /*! Return the interquartile (q3/4 - q1/4) */ - T interquartile(); - /*! Return the median absolute distance robust measure of spread. */ - T mad(T center); - /*! Return sum of squares. */ - T ssq(); - /*! Return largest value in the data set. */ - T upper_bound(); - /*! Return smallest value in the data set. */ - T lower_bound(); - /*! Return full range of data (largest - smallest) */ - T range(); - /*! Return nth value from the sorted data that is the nth 1-quantile. - Will throw a MsPASSError if n exceed the data length. */ - T quantile(size_t n); private: - std::vector d; + std::vector d; }; -template VectorStatistics::VectorStatistics(std::vector din) -{ - if(din.size()<=1) throw mspass::utility::MsPASSError(std::string("VectorStatistics constructor: ") - + "input vector has insufficient data to compute statistics", - mspass::utility::ErrorSeverity::Invalid); - d=din; - std::sort(d.begin(),d.end()); -} -template VectorStatistics::VectorStatistics(T *din,int n) -{ - if(n<=1) throw mspass::utility::MsPASSError(std::string("VectorStatistics constructor: ") - + "input vector has insufficient data to compute statistics", - mspass::utility::ErrorSeverity::Invalid); - d.reserve(n); - for(int i=0;i VectorStatistics::VectorStatistics(std::vector din) { + if (din.size() <= 1) + throw mspass::utility::MsPASSError( + std::string("VectorStatistics constructor: ") + + "input vector has insufficient data to compute statistics", + mspass::utility::ErrorSeverity::Invalid); + d = din; + std::sort(d.begin(), d.end()); } -template T VectorStatistics::median() -{ - int count=d.size(); - int medposition=count/2; - if(count%2) - return(d[medposition]); - else - return( (d[medposition]+d[medposition-1])/2 ); +template VectorStatistics::VectorStatistics(T *din, int n) { + if (n <= 1) + throw mspass::utility::MsPASSError( + std::string("VectorStatistics constructor: ") + + "input vector has insufficient data to compute statistics", + mspass::utility::ErrorSeverity::Invalid); + d.reserve(n); + for (int i = 0; i < n; ++i) + d.push_back(din[i]); + std::sort(d.begin(), d.end()); } -template T VectorStatistics::mean() -{ - T result; - result=0; - for(int i=0;i T VectorStatistics::median() { + int count = d.size(); + int medposition = count / 2; + if (count % 2) + return (d[medposition]); + else + return ((d[medposition] + d[medposition - 1]) / 2); } -template T VectorStatistics::q1_4() -{ - int n=d.size(); - double result; - if(n<4) - return(d[0]); - else - { - int nover4=(n-1)/4; - switch(n%4) - { - case(0): - result=static_cast(d[nover4]); - break; - case(1): - result=0.75*static_cast(d[nover4]) + 0.25*static_cast(d[nover4+1]); - break; - case(2): - result=static_cast(d[nover4]) + static_cast(d[nover4+1]); - result /= 2.0; - break; - case(3): - result=0.25*static_cast(d[nover4]) + 0.75*static_cast(d[nover4+1]); - } - } - return(static_cast(result)); +template T VectorStatistics::mean() { + T result; + result = 0; + for (int i = 0; i < d.size(); ++i) { + result += d[i]; + } + return (result / d.size()); } -template T VectorStatistics::q3_4() -{ - int n=d.size(); - double result; - if(n<4) - return(d[n-1]); - else - { - int n3_4=3*(n-1)/4; - switch(n%4) - { - case(0): - result=static_cast(d[n3_4]); - break; - case(1): - result=0.75*static_cast(d[n3_4]) + 0.25*static_cast(d[n3_4+1]); - break; - case(2): - result=static_cast(d[n3_4]) + static_cast(d[n3_4+1]); - result /= 2.0; - break; - case(3): - result=0.25*static_cast(d[n3_4]) + 0.75*static_cast(d[n3_4+1]); - } - } - return(static_cast(result)); +template T VectorStatistics::q1_4() { + int n = d.size(); + double result; + if (n < 4) + return (d[0]); + else { + int nover4 = (n - 1) / 4; + switch (n % 4) { + case (0): + result = static_cast(d[nover4]); + break; + case (1): + result = 0.75 * static_cast(d[nover4]) + + 0.25 * static_cast(d[nover4 + 1]); + break; + case (2): + result = + static_cast(d[nover4]) + static_cast(d[nover4 + 1]); + result /= 2.0; + break; + case (3): + result = 0.25 * static_cast(d[nover4]) + + 0.75 * static_cast(d[nover4 + 1]); + } + } + return (static_cast(result)); } -template T VectorStatistics::interquartile() -{ - T result; - T d1_4,d3_4; - d1_4=this->q1_4(); - d3_4=this->q3_4(); - return(d3_4 - d1_4); +template T VectorStatistics::q3_4() { + int n = d.size(); + double result; + if (n < 4) + return (d[n - 1]); + else { + int n3_4 = 3 * (n - 1) / 4; + switch (n % 4) { + case (0): + result = static_cast(d[n3_4]); + break; + case (1): + result = 0.75 * static_cast(d[n3_4]) + + 0.25 * static_cast(d[n3_4 + 1]); + break; + case (2): + result = static_cast(d[n3_4]) + static_cast(d[n3_4 + 1]); + result /= 2.0; + break; + case (3): + result = 0.25 * static_cast(d[n3_4]) + + 0.75 * static_cast(d[n3_4 + 1]); + } + } + return (static_cast(result)); } -template T VectorStatistics::mad(T center) -{ - std::vector absdiff; - int n=d.size(); - int i; - for(i=0;i result(absdiff); - return(result.median()); +template T VectorStatistics::interquartile() { + T result; + T d1_4, d3_4; + d1_4 = this->q1_4(); + d3_4 = this->q3_4(); + return (d3_4 - d1_4); } -template T VectorStatistics::ssq() -{ - T result; - int i; - for(i=0;i T VectorStatistics::mad(T center) { + std::vector absdiff; + int n = d.size(); + int i; + for (i = 0; i < n; ++i) { + T diff; + diff = d[i] - center; + if (diff < 0) + diff = -diff; + absdiff.push_back(diff); + } + VectorStatistics result(absdiff); + return (result.median()); } -template T VectorStatistics::upper_bound() -{ - return(d[d.size()-1]); +template T VectorStatistics::ssq() { + T result; + int i; + for (i = 0; i < d.size(); ++i) + result = d[i] * d[i]; + return (result); } -template T VectorStatistics::lower_bound() -{ - return(d[0]); +template T VectorStatistics::upper_bound() { + return (d[d.size() - 1]); } -template T VectorStatistics::range() -{ - T result; - result=this->upper_bound(); - result-=this->lower_bound(); - return(result); +template T VectorStatistics::lower_bound() { return (d[0]); } +template T VectorStatistics::range() { + T result; + result = this->upper_bound(); + result -= this->lower_bound(); + return (result); } -template T VectorStatistics::quantile(size_t n) -{ - if(n>=d.size()) - { - std::stringstream ss; - ss << "VectorStatistics::quantile method: asked for 1-quantile number " - << n<<" but data vecotor length is only "< T VectorStatistics::quantile(size_t n) { + if (n >= d.size()) { + std::stringstream ss; + ss << "VectorStatistics::quantile method: asked for 1-quantile number " + << n << " but data vecotor length is only " << d.size() << std::endl; + throw mspass::utility::MsPASSError(ss.str(), + mspass::utility::ErrorSeverity::Invalid); + } } -} // end utility namespace -} /* End mspass namespace encapsulation */ +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/include/mspass/utility/dmatrix.h b/cxx/include/mspass/utility/dmatrix.h index 8c2d10245..9b66229ca 100644 --- a/cxx/include/mspass/utility/dmatrix.h +++ b/cxx/include/mspass/utility/dmatrix.h @@ -1,19 +1,18 @@ #ifndef _DMATRIX_H_ #define _DMATRIX_H_ -#include #include #include +#include #include /* Either text or binary can be specified here, but we use binary - * to emphasize this class is normally serialized binary for + * to emphasize this class is normally serialized binary for * speed*/ -#include -#include -#include #include "mspass/utility/MsPASSError.h" -namespace mspass -{ -namespace utility{ +#include +#include +#include +namespace mspass { +namespace utility { //================================================================== /*! \brief special convenience class for matrix indexing errors. * @@ -23,157 +22,164 @@ namespace utility{ \author Gary L. Pavlis //================================================================== */ -class dmatrix_index_error : public MsPASSError -{ +class dmatrix_index_error : public MsPASSError { public: -/*! -Basic constructor for this error object. -\param nrmax number of rows in matrix -\param ncmax number of columns in matrix -\param ir row index requested -\param ic column index requested -*/ - dmatrix_index_error(const size_t nrmax, - const size_t ncmax, const size_t ir, const size_t ic) - { - row = ir; column=ic; nrr=nrmax; ncc=ncmax; - std::ostringstream oss; - oss << "dmatrix object: indexing error"< size() const; /*! Initialize a matrix to all zeros. */ void zero(); + protected: - std::vector ary; // initial size of container 0 - size_t length; - size_t nrr, ncc; + std::vector ary; // initial size of container 0 + size_t length; + size_t nrr, ncc; + private: - friend class boost::serialization::access; - templatevoid serialize(Archive & ar, - const unsigned int version) - { - ar & nrr & ncc & length; - ar & ary; - } + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar & nrr & ncc & length; + ar & ary; + } }; /*! \brief A vector compatible with dmatrix objects. - -A vector is a special case of a matrix with one row or column. In this + +A vector is a special case of a matrix with one row or column. In this implementation, however, it always means a column vector. Hence, it is -possible to multiply a vector x and a matrix A as Ax provided they are +possible to multiply a vector x and a matrix A as Ax provided they are compatible sizes. This differs from matlab where row and columns vectors -are sometimes used interchangably. +are sometimes used interchangably. */ -class dvector : public dmatrix -{ +class dvector : public dmatrix { public: - /*! Default constructor creates an empty vector. */ - dvector():dmatrix(){}; - /*! Create a (zero initialized) vector of length nrv. */ - dvector(size_t nrv) : dmatrix(nrv,1){}; - /*! Copy constructor. */ - dvector(const dvector& other); - /*! Standard assignment operator. */ - dvector& operator=(const dvector& other); - /*! Extract component rowindex. */ - double &operator()(size_t rowindex); - /*! Matrix vector multiple operator. - - This operator is used for constructs like y=Ax where x is a - vector and A is a matrix. y is the returned vector. - \param A - matrix on right in multiply - \param x - vector on left of multiply operation - - \return product A*x - \exception dmatrix_size_error thrown if size of A and x do not match. - */ - friend dvector operator*(const dmatrix &A, const dvector &x); + /*! Default constructor creates an empty vector. */ + dvector() : dmatrix() {}; + /*! Create a (zero initialized) vector of length nrv. */ + dvector(size_t nrv) : dmatrix(nrv, 1) {}; + /*! Copy constructor. */ + dvector(const dvector &other); + /*! Standard assignment operator. */ + dvector &operator=(const dvector &other); + /*! Extract component rowindex. */ + double &operator()(size_t rowindex); + /*! Matrix vector multiple operator. + + This operator is used for constructs like y=Ax where x is a + vector and A is a matrix. y is the returned vector. + \param A - matrix on right in multiply + \param x - vector on left of multiply operation + + \return product A*x + \exception dmatrix_size_error thrown if size of A and x do not match. + */ + friend dvector operator*(const dmatrix &A, const dvector &x); }; -} // end utility namespace +} // namespace utility } // end namespace mspass #endif diff --git a/cxx/include/mspass/utility/memory_constants.h b/cxx/include/mspass/utility/memory_constants.h index 3ce3d183c..680288980 100644 --- a/cxx/include/mspass/utility/memory_constants.h +++ b/cxx/include/mspass/utility/memory_constants.h @@ -10,14 +10,12 @@ use in those containers it will not cause issues. These constants may require tuning with experiences. Initial values set May 2023 by glp are guesses. */ -namespace mspass::utility -{ -namespace memory_constants -{ +namespace mspass::utility { +namespace memory_constants { /*! Average size of key string used for Metadata. */ const size_t KEY_AVERAGE_SIZE(8); /*! Average Metadata entry size. key-value so key size + wag of average size */ -const size_t MD_AVERAGE_SIZE(KEY_AVERAGE_SIZE+16); +const size_t MD_AVERAGE_SIZE(KEY_AVERAGE_SIZE + 16); /*! Average size of history record. History is stored as a multimap of fixed format records. The variable is that the strings in the NodeData class have variable length. This is a WAG of @@ -32,7 +30,7 @@ The dominant data for an error log entry is the posted error message. This is a power of 2 wag*/ const size_t ELOG_AVERAGE_SIZE(128); /*! Average size of a data gap set entry. */ -const size_t DATA_GAP_AVERAGE_SIZE(2*sizeof(double)+sizeof(size_t)); -} -} +const size_t DATA_GAP_AVERAGE_SIZE(2 * sizeof(double) + sizeof(size_t)); +} // namespace memory_constants +} // namespace mspass::utility #endif diff --git a/cxx/include/mspass/utility/utility.h b/cxx/include/mspass/utility/utility.h index bc6443d6e..e09bdbc2a 100644 --- a/cxx/include/mspass/utility/utility.h +++ b/cxx/include/mspass/utility/utility.h @@ -1,14 +1,15 @@ #ifndef _MSPASS_UTILITY_BASE_H_ #define _MSPASS_UTILITY_BASE_H_ -/* This header is to contain assorted useful C and C++ procedures +/* This header is to contain assorted useful C and C++ procedures appropriate for the tag utility.h. */ -#include #include -namespace mspass{ -namespace utility{ -/*! Standard method returns a string defining the top level data directory for mspass. +#include +namespace mspass { +namespace utility { +/*! Standard method returns a string defining the top level data directory for +mspass. -Programs often need a standard set of initialization files. In mspass we +Programs often need a standard set of initialization files. In mspass we group these under a "data" directory. This procedure returns the top of the chain of data directories. Note this is the top of a directory chain and most application will need to add a subdirectory. e.g. @@ -21,21 +22,21 @@ std::string data_directory(); /*! \brief Normalize rows of a matrix to unit L2 length. Sometimes it is necessary to normalize a matrix by rows or columns. -This function normalizes the rows of a matrix. +This function normalizes the rows of a matrix. \param d is the matrix to be normalized \return vector of the computed L2 norms of each row used for normalization. */ -std::vector normalize_rows(const mspass::utility::dmatrix& d); +std::vector normalize_rows(const mspass::utility::dmatrix &d); /*! \brief Normalize columns of a matrix to unit L2 length. Sometimes it is necessary to normalize a matrix by rows or columns. -This function normalizes the columns of a matrix. +This function normalizes the columns of a matrix. \param d is the matrix to be normalized \return vector of the computed L2 norms of each column used for normalization. */ -std::vector normalize_columns(const mspass::utility::dmatrix& d); -} // end utility namespace -} +std::vector normalize_columns(const mspass::utility::dmatrix &d); +} // namespace utility +} // namespace mspass #endif diff --git a/cxx/src/lib/algorithms/Butterworth.cc b/cxx/src/lib/algorithms/Butterworth.cc index 4bc40ae35..5020045e5 100644 --- a/cxx/src/lib/algorithms/Butterworth.cc +++ b/cxx/src/lib/algorithms/Butterworth.cc @@ -1,617 +1,569 @@ -#include "sstream" -#include -#include "misc/blas.h" #include "mspass/algorithms/Butterworth.h" -#include "mspass/utility/MsPASSError.h" +#include "misc/blas.h" #include "mspass/algorithms/amplitudes.h" #include "mspass/algorithms/deconvolution/FFTDeconOperator.h" -namespace mspass::algorithms -{ -using mspass::seismic::CoreTimeSeries; +#include "mspass/utility/MsPASSError.h" +#include "sstream" +#include +namespace mspass::algorithms { +using mspass::algorithms::amplitudes::normalize; using mspass::seismic::CoreSeismogram; +using mspass::seismic::CoreTimeSeries; using mspass::seismic::TimeReferenceType; +using mspass::utility::ErrorSeverity; using mspass::utility::Metadata; using mspass::utility::MsPASSError; -using mspass::utility::ErrorSeverity; -using mspass::algorithms::amplitudes::normalize; using namespace mspass::algorithms::deconvolution; using namespace std; -Butterworth::Butterworth() -{ - /* This is a translation of a minimum phase antialias filter used in - the antialias function in seismic unix. We define it as a default - because it does almost nothing - almost because it depends upon - form of antialias applied to get to current state. */ - double fnyq,fpass,apass,fstop,astop; - fnyq = 0.5; - fpass = 0.6*fnyq; - apass = 0.99; - fstop = fnyq; - astop = 0.01; - this->bfdesign(fpass,apass,fstop,astop, - &(this->npoles_hi),&(this->f3db_hi)); - dt=1.0; - use_lo=false; - use_hi=true; - f3db_lo=0.0; - npoles_lo=0; - zerophase=false; +Butterworth::Butterworth() { + /* This is a translation of a minimum phase antialias filter used in + the antialias function in seismic unix. We define it as a default + because it does almost nothing - almost because it depends upon + form of antialias applied to get to current state. */ + double fnyq, fpass, apass, fstop, astop; + fnyq = 0.5; + fpass = 0.6 * fnyq; + apass = 0.99; + fstop = fnyq; + astop = 0.01; + this->bfdesign(fpass, apass, fstop, astop, &(this->npoles_hi), + &(this->f3db_hi)); + dt = 1.0; + use_lo = false; + use_hi = true; + f3db_lo = 0.0; + npoles_lo = 0; + zerophase = false; }; -Butterworth::Butterworth(const bool zp, - const bool lcon, const bool hcon, - const double fstoplo, const double astoplo, - const double fpasslo, const double apasslo, - const double fpasshi, const double apasshi, - const double fstophi, const double astophi, - const double sample_interval) -{ - dt=sample_interval; - zerophase=zp; - use_lo=lcon; - use_hi=hcon; - this->bfdesign(fpasslo*dt,apasslo,fstoplo*dt,astoplo, - &(this->npoles_lo),&(this->f3db_lo)); - this->bfdesign(fpasshi*dt,apasshi,fstophi*dt,astophi, - &(this->npoles_hi),&(this->f3db_hi)); +Butterworth::Butterworth(const bool zp, const bool lcon, const bool hcon, + const double fstoplo, const double astoplo, + const double fpasslo, const double apasslo, + const double fpasshi, const double apasshi, + const double fstophi, const double astophi, + const double sample_interval) { + dt = sample_interval; + zerophase = zp; + use_lo = lcon; + use_hi = hcon; + this->bfdesign(fpasslo * dt, apasslo, fstoplo * dt, astoplo, + &(this->npoles_lo), &(this->f3db_lo)); + this->bfdesign(fpasshi * dt, apasshi, fstophi * dt, astophi, + &(this->npoles_hi), &(this->f3db_hi)); }; Butterworth::Butterworth(const bool zero, const bool locut, const bool hicut, - const int npolelo, const double f3dblo, - const int npolehi, const double f3dbhi, - const double sample_interval) -{ - this->dt=sample_interval; - this->zerophase=zero; - this->use_lo=locut; - this->use_hi=hicut; - this->f3db_hi=f3dbhi; - this->f3db_lo=f3dblo; - this->npoles_lo=npolelo; - this->npoles_hi=npolehi; - /* Convert the frequencies to nondimensional form */ - this->f3db_lo *= dt; - this->f3db_hi *= dt; + const int npolelo, const double f3dblo, + const int npolehi, const double f3dbhi, + const double sample_interval) { + this->dt = sample_interval; + this->zerophase = zero; + this->use_lo = locut; + this->use_hi = hicut; + this->f3db_hi = f3dbhi; + this->f3db_lo = f3dblo; + this->npoles_lo = npolelo; + this->npoles_hi = npolehi; + /* Convert the frequencies to nondimensional form */ + this->f3db_lo *= dt; + this->f3db_hi *= dt; } - /*! Construct using tagged valus created from a Metadata container*/ -Butterworth::Butterworth(const Metadata& md) -{ - string base_error("Butterworth pf constructor: "); - try{ - dt=md.get("sample_interval"); - /* We define these here even if they aren't all used for all options. - Better for tiny memory cost than declaration in each block */ - double fpass, apass, fstop, astop; - zerophase=md.get_bool("zerophase"); - /* These options simplify setup but complicate this constructor. */ - string ftype,fdmeth; - ftype=md.get_string("filter_type"); - fdmeth=md.get_string("filter_definition_method"); - if(ftype=="bandpass") - { - use_lo=true; - use_hi=true; - if(fdmeth=="corner_pole") - { - /* Note we allow flow>figh to create strongly peaked filters although - we don't scale these any any rational way */ - npoles_lo=md.get("npoles_low"); - npoles_hi=md.get("npoles_high"); - f3db_lo=md.get("corner_low"); - f3db_hi=md.get("corner_high"); - /* Stored as nondimensional internally*/ - f3db_lo *= dt; - f3db_hi *= dt; - } - else - { - /* note the set routines will test validity of input and throw - an error if the points do not define the right sense of pass versus cut*/ - fpass=md.get("fpass_low"); - apass=md.get("apass_low"); - fstop=md.get("fstop_low"); - astop=md.get("astop_low"); - this->set_lo(fpass,fstop,astop,apass); - fpass=md.get("fpass_high"); - apass=md.get("apass_high"); - fstop=md.get("fstop_high"); - astop=md.get("astop_high"); - this->set_hi(fpass,fstop,apass,astop); - } - } - else if(ftype=="lowpass") - { - /* Note the confusing naming hre - lo means lo corner not low pass*/ - use_lo=false; - use_hi=true; - /* Explicity initialization useful here */ - npoles_lo=0; - f3db_lo=0.0; - if(fdmeth=="corner_pole") - { - npoles_hi=md.get("npoles_high"); - f3db_hi=md.get("corner_high"); - f3db_hi *= dt; - } - else - { - fpass=md.get("fpass_high"); - apass=md.get("apass_high"); - fstop=md.get("fstop_high"); - astop=md.get("astop_high"); - /* note this handles inconsistencies and can throw an error */ - this->set_hi(fstop,fpass,astop,apass); - } - } - else if(ftype=="highpass") - { - use_lo=true; - use_hi=false; - /* Explicity initialization useful here */ - npoles_hi=0; - f3db_hi=0.0; - if(fdmeth=="corner_pole") - { - npoles_hi=md.get("npoles_low"); - f3db_hi=md.get("corner_low"); - f3db_hi *= dt; - } - else - { - fpass=md.get("fpass_low"); - apass=md.get("apass_low"); - fstop=md.get("fstop_low"); - astop=md.get("astop_low"); - /* note this handles inconsistencies and can throw an error */ - this->set_lo(fstop,fpass,astop,apass); - } - } - else if(ftype=="bandreject") - { - use_lo=true; - use_hi=true; - if(fdmeth=="corner_pole") - { - /* Note we allow flow>figh to create strongly peaked filters although - we don't scale these any any rational way */ - npoles_lo=md.get("npoles_low"); - npoles_hi=md.get("npoles_high"); - f3db_lo=md.get("corner_low"); - f3db_hi=md.get("corner_high"); - /* Enforce this constraint or we don't have a band reject filtr */ - if(f3db_lo("fpass_low"); - apass=md.get("apass_low"); - fstop=md.get("fstop_low"); - astop=md.get("astop_low"); - this->bfdesign(fpass*dt,apass,fstop*dt,astop,&(this->npoles_lo),&(this->f3db_lo)); - fpass=md.get("fpass_high"); - apass=md.get("apass_high"); - fstop=md.get("fstop_high"); - astop=md.get("astop_high"); - this->bfdesign(fpass*dt,apass,fstop*dt,astop,&(this->npoles_hi),&(this->f3db_hi)); - } - } - else - { - throw MsPASSError(base_error+"Unsupported filtr_type="+ftype, - ErrorSeverity::Invalid); - } - }catch(...){throw;}; +/*! Construct using tagged valus created from a Metadata container*/ +Butterworth::Butterworth(const Metadata &md) { + string base_error("Butterworth pf constructor: "); + try { + dt = md.get("sample_interval"); + /* We define these here even if they aren't all used for all options. + Better for tiny memory cost than declaration in each block */ + double fpass, apass, fstop, astop; + zerophase = md.get_bool("zerophase"); + /* These options simplify setup but complicate this constructor. */ + string ftype, fdmeth; + ftype = md.get_string("filter_type"); + fdmeth = md.get_string("filter_definition_method"); + if (ftype == "bandpass") { + use_lo = true; + use_hi = true; + if (fdmeth == "corner_pole") { + /* Note we allow flow>figh to create strongly peaked filters although + we don't scale these any any rational way */ + npoles_lo = md.get("npoles_low"); + npoles_hi = md.get("npoles_high"); + f3db_lo = md.get("corner_low"); + f3db_hi = md.get("corner_high"); + /* Stored as nondimensional internally*/ + f3db_lo *= dt; + f3db_hi *= dt; + } else { + /* note the set routines will test validity of input and throw + an error if the points do not define the right sense of pass versus + cut*/ + fpass = md.get("fpass_low"); + apass = md.get("apass_low"); + fstop = md.get("fstop_low"); + astop = md.get("astop_low"); + this->set_lo(fpass, fstop, astop, apass); + fpass = md.get("fpass_high"); + apass = md.get("apass_high"); + fstop = md.get("fstop_high"); + astop = md.get("astop_high"); + this->set_hi(fpass, fstop, apass, astop); + } + } else if (ftype == "lowpass") { + /* Note the confusing naming hre - lo means lo corner not low pass*/ + use_lo = false; + use_hi = true; + /* Explicity initialization useful here */ + npoles_lo = 0; + f3db_lo = 0.0; + if (fdmeth == "corner_pole") { + npoles_hi = md.get("npoles_high"); + f3db_hi = md.get("corner_high"); + f3db_hi *= dt; + } else { + fpass = md.get("fpass_high"); + apass = md.get("apass_high"); + fstop = md.get("fstop_high"); + astop = md.get("astop_high"); + /* note this handles inconsistencies and can throw an error */ + this->set_hi(fstop, fpass, astop, apass); + } + } else if (ftype == "highpass") { + use_lo = true; + use_hi = false; + /* Explicity initialization useful here */ + npoles_hi = 0; + f3db_hi = 0.0; + if (fdmeth == "corner_pole") { + npoles_hi = md.get("npoles_low"); + f3db_hi = md.get("corner_low"); + f3db_hi *= dt; + } else { + fpass = md.get("fpass_low"); + apass = md.get("apass_low"); + fstop = md.get("fstop_low"); + astop = md.get("astop_low"); + /* note this handles inconsistencies and can throw an error */ + this->set_lo(fstop, fpass, astop, apass); + } + } else if (ftype == "bandreject") { + use_lo = true; + use_hi = true; + if (fdmeth == "corner_pole") { + /* Note we allow flow>figh to create strongly peaked filters although + we don't scale these any any rational way */ + npoles_lo = md.get("npoles_low"); + npoles_hi = md.get("npoles_high"); + f3db_lo = md.get("corner_low"); + f3db_hi = md.get("corner_high"); + /* Enforce this constraint or we don't have a band reject filtr */ + if (f3db_lo < f3db_hi) { + throw MsPASSError(base_error + + "Illegal corner frequencies for band reject " + "filter definition" + + "For a band reject low corner must be larger " + "than high corner (pass becomes cut to reject)", + ErrorSeverity::Invalid); + } + f3db_lo *= dt; + f3db_hi *= dt; + } else { + /* We should test these values for defining band reject but bypass + these safeties for now as I don't expect this feature to get much + use. */ + fpass = md.get("fpass_low"); + apass = md.get("apass_low"); + fstop = md.get("fstop_low"); + astop = md.get("astop_low"); + this->bfdesign(fpass * dt, apass, fstop * dt, astop, &(this->npoles_lo), + &(this->f3db_lo)); + fpass = md.get("fpass_high"); + apass = md.get("apass_high"); + fstop = md.get("fstop_high"); + astop = md.get("astop_high"); + this->bfdesign(fpass * dt, apass, fstop * dt, astop, &(this->npoles_hi), + &(this->f3db_hi)); + } + } else { + throw MsPASSError(base_error + "Unsupported filtr_type=" + ftype, + ErrorSeverity::Invalid); + } + } catch (...) { + throw; + }; } -Butterworth::Butterworth(const Butterworth& parent) -{ - use_lo=parent.use_lo; - use_hi=parent.use_hi; - zerophase=parent.zerophase; - f3db_lo=parent.f3db_lo; - f3db_hi=parent.f3db_hi; - npoles_lo=parent.npoles_lo; - npoles_hi=parent.npoles_hi; - dt=parent.dt; +Butterworth::Butterworth(const Butterworth &parent) { + use_lo = parent.use_lo; + use_hi = parent.use_hi; + zerophase = parent.zerophase; + f3db_lo = parent.f3db_lo; + f3db_hi = parent.f3db_hi; + npoles_lo = parent.npoles_lo; + npoles_hi = parent.npoles_hi; + dt = parent.dt; } -Butterworth& Butterworth::operator=(const Butterworth& parent) -{ - if(this != &parent) - { - use_lo=parent.use_lo; - use_hi=parent.use_hi; - zerophase=parent.zerophase; - f3db_lo=parent.f3db_lo; - f3db_hi=parent.f3db_hi; - npoles_lo=parent.npoles_lo; - npoles_hi=parent.npoles_hi; - dt=parent.dt; - } - return *this; +Butterworth &Butterworth::operator=(const Butterworth &parent) { + if (this != &parent) { + use_lo = parent.use_lo; + use_hi = parent.use_hi; + zerophase = parent.zerophase; + f3db_lo = parent.f3db_lo; + f3db_hi = parent.f3db_hi; + npoles_lo = parent.npoles_lo; + npoles_hi = parent.npoles_hi; + dt = parent.dt; + } + return *this; } -CoreTimeSeries Butterworth::impulse_response(const int n) -{ - CoreTimeSeries result(n); - /* We use a feature that the above constructor initiallizes the buffer - to zeros so just a spike at n/2. */ - result.s[n/2]=1.0; - result.set_t0( ((double)(-n/2))*(this->dt) ); - result.set_dt(this->dt); - result.set_tref(TimeReferenceType::Relative); - result.set_live(); - this->apply(result.s); +CoreTimeSeries Butterworth::impulse_response(const int n) { + CoreTimeSeries result(n); + /* We use a feature that the above constructor initiallizes the buffer + to zeros so just a spike at n/2. */ + result.s[n / 2] = 1.0; + result.set_t0(((double)(-n / 2)) * (this->dt)); + result.set_dt(this->dt); + result.set_tref(TimeReferenceType::Relative); + result.set_live(); + this->apply(result.s); result.s = normalize(result.s); - return result; + return result; } /* Fraction of 1/dt used to cause disabling low pass (upper) corner*/ -const double FHighFloor(0.45); //90% of Nyquist -void Butterworth::apply(mspass::seismic::CoreTimeSeries& d) -{ - double d_dt=d.dt(); - if(this->dt != d_dt) - { - /* Here we throw an exception if a requested sample rate is - illegal */ - double fhtest=d_dt/(this->dt); - if(fhtest>FHighFloor) - { - stringstream ss; - ss << "Butterworth::apply: automatic dt change error"<change_dt(d_dt); - } - this->apply(d.s); +const double FHighFloor(0.45); // 90% of Nyquist +void Butterworth::apply(mspass::seismic::CoreTimeSeries &d) { + double d_dt = d.dt(); + if (this->dt != d_dt) { + /* Here we throw an exception if a requested sample rate is + illegal */ + double fhtest = d_dt / (this->dt); + if (fhtest > FHighFloor) { + stringstream ss; + ss << "Butterworth::apply: automatic dt change error" << endl + << "Current operator dt=" << this->dt << " data dt=" << d_dt << endl + << "Change would produce a corner too close to Nyquist" + << " and create an unstable filter" << endl + << "Use a different filter operator for data with this sample rate" + << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + this->change_dt(d_dt); + } + this->apply(d.s); } /* We use ErrorLogger and blunder on with TimeSeries objects instead of throwing an exception when the upper corner is bad. */ -void Butterworth::apply(mspass::seismic::TimeSeries& d) -{ - double d_dt=d.dt(); - if(this->dt != d_dt) - { - /* Here we throw an exception if a requested sample rate is - illegal */ - double fhtest=d_dt/(this->dt); - if(use_hi && (fhtest>FHighFloor)) - { - /*In this case we temporarily disable the upper corner and - cache the old dt to restore it before returning. */ - double olddt=this->dt; - double flow_old=this->f3db_lo; - use_hi=false; - this->apply(d.s); - use_hi=true; - this->dt=olddt; - this->f3db_lo=flow_old; - stringstream ss; - ss <<"Auto adjust for sample rate change error"<change_dt(d_dt); - } - } - this->apply(d.s); +void Butterworth::apply(mspass::seismic::TimeSeries &d) { + double d_dt = d.dt(); + if (this->dt != d_dt) { + /* Here we throw an exception if a requested sample rate is + illegal */ + double fhtest = d_dt / (this->dt); + if (use_hi && (fhtest > FHighFloor)) { + /*In this case we temporarily disable the upper corner and + cache the old dt to restore it before returning. */ + double olddt = this->dt; + double flow_old = this->f3db_lo; + use_hi = false; + this->apply(d.s); + use_hi = true; + this->dt = olddt; + this->f3db_lo = flow_old; + stringstream ss; + ss << "Auto adjust for sample rate change error" << endl + << "Upper corner of filter=" << this->high_corner() + << " is near or above Nyquist frequency for requested sample " + << "interval=" << this->dt << endl + << "Disabling upper corner (lowpass) and applying filter anyway" + << endl; + d.elog.log_error(string("Butterworth::apply"), ss.str(), + ErrorSeverity::Complaint); + /* With this logic we have separate return here */ + } else { + this->change_dt(d_dt); + } + } + this->apply(d.s); } /* Reverse a vector of doubles in place using a pointer algorithm for speed. */ -void reverse_vector(int nd, double *d) -{ +void reverse_vector(int nd, double *d) { double tmp; - for(auto i=0;i& d) -{ - if(use_lo) - { - this->bflowcut(npoles_hi,f3db_hi,d.size(),&(d[0]),&(d[0])); - if(zerophase) - { - reverse_vector(d.size(),&(d[0])); - this->bflowcut(npoles_hi,f3db_hi,d.size(),&(d[0]),&(d[0])); - reverse_vector(d.size(),&(d[0])); - } - } - if(use_hi) - { - this->bfhighcut(npoles_lo,f3db_lo,d.size(),&(d[0]),&(d[0])); - if(zerophase) - { - reverse_vector(d.size(),&(d[0])); - this->bfhighcut(npoles_lo,f3db_lo,d.size(),&(d[0]),&(d[0])); - reverse_vector(d.size(),&(d[0])); - } - } +void Butterworth::apply(vector &d) { + if (use_lo) { + this->bflowcut(npoles_hi, f3db_hi, d.size(), &(d[0]), &(d[0])); + if (zerophase) { + reverse_vector(d.size(), &(d[0])); + this->bflowcut(npoles_hi, f3db_hi, d.size(), &(d[0]), &(d[0])); + reverse_vector(d.size(), &(d[0])); + } + } + if (use_hi) { + this->bfhighcut(npoles_lo, f3db_lo, d.size(), &(d[0]), &(d[0])); + if (zerophase) { + reverse_vector(d.size(), &(d[0])); + this->bfhighcut(npoles_lo, f3db_lo, d.size(), &(d[0]), &(d[0])); + reverse_vector(d.size(), &(d[0])); + } + } } -void Butterworth::apply(CoreSeismogram& d) -{ - /* we could just handle a MsPASSError to take care of exceptions - thrown by apply if the sample rate is illegal, but this is more adaptable. */ - try{ - double d_dt=d.dt(); - if(this->dt != d_dt) this->change_dt(d_dt); - /* We copy each component to this buffer, filter, and then copy - back. Not as efficient as if we used a strike parameter in the filter - functions, but I did not want to rewrite those functions. */ - vector comp; - int npts=d.npts(); - comp.reserve(npts); - /* This initializes the buffer to allow us a simpler loop from 0 to 2 - using the blas function dcopy to handle the skip for a fortran style - array used for storing the 3c data in u*/ - for(auto i=0;iapply(comp); - dcopy(npts,&(comp[0]),1,d.u.get_address(k,0),3); - } - }catch(...){throw;}; +void Butterworth::apply(CoreSeismogram &d) { + /* we could just handle a MsPASSError to take care of exceptions + thrown by apply if the sample rate is illegal, but this is more adaptable. */ + try { + double d_dt = d.dt(); + if (this->dt != d_dt) + this->change_dt(d_dt); + /* We copy each component to this buffer, filter, and then copy + back. Not as efficient as if we used a strike parameter in the filter + functions, but I did not want to rewrite those functions. */ + vector comp; + int npts = d.npts(); + comp.reserve(npts); + /* This initializes the buffer to allow us a simpler loop from 0 to 2 + using the blas function dcopy to handle the skip for a fortran style + array used for storing the 3c data in u*/ + for (auto i = 0; i < npts; ++i) + comp.push_back(0.0); + for (auto k = 0; k < 3; ++k) { + dcopy(npts, d.u.get_address(k, 0), 3, &(comp[0]), 1); + this->apply(comp); + dcopy(npts, &(comp[0]), 1, d.u.get_address(k, 0), 3); + } + } catch (...) { + throw; + }; } /* The logic used here is identical to the apply method for TimeSeries to log errors and with an internal return. Difference is the need to handle 3 components. */ -void Butterworth::apply(mspass::seismic::Seismogram& d) -{ +void Butterworth::apply(mspass::seismic::Seismogram &d) { - - double d_dt=d.dt(); - if(this->dt != d_dt) - { - /* Here we throw an exception if a requested sample rate is - illegal */ - double fhtest=d_dt/(this->dt); - if(use_hi && (fhtest>FHighFloor)) - { - /*In this case we temporarily disable the upper corner and - cache the old dt to restore it before returning. */ - double olddt=this->dt; - double flow_old=this->f3db_lo; - use_hi=false; - this->apply(d); - use_hi=true; - this->dt=olddt; - this->f3db_lo=flow_old; - stringstream ss; - ss <<"Auto adjust for sample rate change error"<change_dt(d_dt); - } - } - vector comp; - int npts=d.npts(); - comp.reserve(npts); - /* This initializes the buffer to allow us a simpler loop from 0 to 2 - using the blas function dcopy to handle the skip for a fortran style - array used for storing the 3c data in u*/ - for(auto i=0;iapply(comp); - dcopy(npts,&(comp[0]),1,d.u.get_address(k,0),3); - } + double d_dt = d.dt(); + if (this->dt != d_dt) { + /* Here we throw an exception if a requested sample rate is + illegal */ + double fhtest = d_dt / (this->dt); + if (use_hi && (fhtest > FHighFloor)) { + /*In this case we temporarily disable the upper corner and + cache the old dt to restore it before returning. */ + double olddt = this->dt; + double flow_old = this->f3db_lo; + use_hi = false; + this->apply(d); + use_hi = true; + this->dt = olddt; + this->f3db_lo = flow_old; + stringstream ss; + ss << "Auto adjust for sample rate change error" << endl + << "Upper corner of filter=" << this->high_corner() + << " is near or above Nyquist frequency for requested sample " + << "interval=" << this->dt << endl + << "Disabling upper corner (lowpass) and applying filter anyway" + << endl; + d.elog.log_error(string("Butterworth::apply"), ss.str(), + ErrorSeverity::Complaint); + /* With this logic we have separate return here */ + } else { + this->change_dt(d_dt); + } + } + vector comp; + int npts = d.npts(); + comp.reserve(npts); + /* This initializes the buffer to allow us a simpler loop from 0 to 2 + using the blas function dcopy to handle the skip for a fortran style + array used for storing the 3c data in u*/ + for (auto i = 0; i < npts; ++i) + comp.push_back(0.0); + for (auto k = 0; k < 3; ++k) { + dcopy(npts, d.u.get_address(k, 0), 3, &(comp[0]), 1); + this->apply(comp); + dcopy(npts, &(comp[0]), 1, d.u.get_address(k, 0), 3); + } } -ComplexArray Butterworth::transfer_function(const int nfft) -{ - CoreTimeSeries imp=this->impulse_response(nfft); - /* the impulse response function uses a time shift and sets t0 to the - required position. A disconnect with any fft is that will introduce an - undesirable phase shift for many algorithms. We remove it here using our - circular shift function */ - int ishift=imp.sample_number(0.0); - imp.s=circular_shift(imp.s,ishift); - gsl_fft_complex_wavetable *wavetable = gsl_fft_complex_wavetable_alloc (nfft); - gsl_fft_complex_workspace *workspace = gsl_fft_complex_workspace_alloc (nfft); - ComplexArray work(nfft,imp.s); - gsl_fft_complex_forward(work.ptr(), 1, nfft, wavetable, workspace); - gsl_fft_complex_wavetable_free (wavetable); - gsl_fft_complex_workspace_free (workspace); - return work; +ComplexArray Butterworth::transfer_function(const int nfft) { + CoreTimeSeries imp = this->impulse_response(nfft); + /* the impulse response function uses a time shift and sets t0 to the + required position. A disconnect with any fft is that will introduce an + undesirable phase shift for many algorithms. We remove it here using our + circular shift function */ + int ishift = imp.sample_number(0.0); + imp.s = circular_shift(imp.s, ishift); + gsl_fft_complex_wavetable *wavetable = gsl_fft_complex_wavetable_alloc(nfft); + gsl_fft_complex_workspace *workspace = gsl_fft_complex_workspace_alloc(nfft); + ComplexArray work(nfft, imp.s); + gsl_fft_complex_forward(work.ptr(), 1, nfft, wavetable, workspace); + gsl_fft_complex_wavetable_free(wavetable); + gsl_fft_complex_workspace_free(workspace); + return work; } /* the next 3 functions are nearly idenitical to C code with the same name sans the Butterworth class tag. The only change is float was changed to double. All methods below here are private*/ -void Butterworth::bfdesign (double fpass, double apass, double fstop, double astop, - int *npoles, double *f3db) -{ - double wpass,wstop,fnpoles,w3db; +void Butterworth::bfdesign(double fpass, double apass, double fstop, + double astop, int *npoles, double *f3db) { + double wpass, wstop, fnpoles, w3db; - /* warp frequencies according to bilinear transform */ - wpass = 2.0*tan(M_PI*fpass); - wstop = 2.0*tan(M_PI*fstop); + /* warp frequencies according to bilinear transform */ + wpass = 2.0 * tan(M_PI * fpass); + wstop = 2.0 * tan(M_PI * fstop); - /* if lowpass filter, then */ - if (fstop>fpass) { - fnpoles = log((1.0/(apass*apass)-1.0)/(1.0/(astop*astop)-1.0)) - / log(pow(wpass/wstop,2.0)); - w3db = wpass/pow((1.0/(apass*apass)-1.0),0.5/fnpoles); + /* if lowpass filter, then */ + if (fstop > fpass) { + fnpoles = + log((1.0 / (apass * apass) - 1.0) / (1.0 / (astop * astop) - 1.0)) / + log(pow(wpass / wstop, 2.0)); + w3db = wpass / pow((1.0 / (apass * apass) - 1.0), 0.5 / fnpoles); - /* else, if highpass filter, then */ - } else { - fnpoles = log((1.0/(apass*apass)-1.0)/(1.0/(astop*astop)-1.0)) - / log(pow(wstop/wpass,2.0)); - w3db = wpass*pow((1.0/(apass*apass)-1.0),0.5/fnpoles); - } + /* else, if highpass filter, then */ + } else { + fnpoles = + log((1.0 / (apass * apass) - 1.0) / (1.0 / (astop * astop) - 1.0)) / + log(pow(wstop / wpass, 2.0)); + w3db = wpass * pow((1.0 / (apass * apass) - 1.0), 0.5 / fnpoles); + } - /* determine integer number of poles */ - *npoles = 1+(int)fnpoles; + /* determine integer number of poles */ + *npoles = 1 + (int)fnpoles; - /* determine (unwarped) -3 db frequency */ - *f3db = atan(0.5*w3db)/M_PI; + /* determine (unwarped) -3 db frequency */ + *f3db = atan(0.5 * w3db) / M_PI; } -void Butterworth::bflowcut (int npoles, double f3db, int n, double p[], double q[]) -{ - int jpair,j; - double r,scale,theta,a,b1,b2,pj,pjm1,pjm2,qjm1,qjm2; +void Butterworth::bflowcut(int npoles, double f3db, int n, double p[], + double q[]) { + int jpair, j; + double r, scale, theta, a, b1, b2, pj, pjm1, pjm2, qjm1, qjm2; - r = 2.0*tan(M_PI*fabs(f3db)); - if (npoles%2!=0) { - scale = r+2.0; - a = 2.0/scale; - b1 = (r-2.0)/scale; - pj = 0.0; - qjm1 = 0.0; - for (j=0; jfpass) - { - stringstream ss; - ss << base_error << "Illegal input for frequency points f_stop=" - <bfdesign(fpass*(this->dt),apass,fstop*(this->dt),astop, - &this->npoles_lo,&this->f3db_lo); + const double astop, const double apass) { + string base_error("Butterworth::set_lo: "); + if (fstop > fpass) { + stringstream ss; + ss << base_error << "Illegal input for frequency points f_stop=" << fstop + << " f_pass=" << fpass << endl + << "fstop frequency must be < fpass frequency" << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + if ((astop < 0.0) || (apass < 0.0)) { + stringstream ss; + ss << base_error << "Illegal amplitude values of astop=" << astop + << " and apass=" << apass << endl + << "Amplitudes must be nonnegative" << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + this->bfdesign(fpass * (this->dt), apass, fstop * (this->dt), astop, + &this->npoles_lo, &this->f3db_lo); }; void Butterworth::set_hi(const double fstop, const double fpass, - const double astop, const double apass) -{ - string base_error("Butterworth::set_hi: "); - if(fpass>fstop) - { - stringstream ss; - ss << base_error << "Illegal input for frequency points f_stop=" - < fpass frequency"<bfdesign(fpass*(this->dt),apass,fstop*(this->dt),astop, - &this->npoles_hi,&this->f3db_hi); + const double astop, const double apass) { + string base_error("Butterworth::set_hi: "); + if (fpass > fstop) { + stringstream ss; + ss << base_error << "Illegal input for frequency points f_stop=" << fstop + << " f_pass=" << fpass << endl + << "fstop frequency must be > fpass frequency" << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + if ((astop < 0.0) || (apass < 0.0)) { + stringstream ss; + ss << base_error << "Illegal amplitude values of astop=" << astop + << " and apass=" << apass << endl + << "Amplitudes must be nonnegative" << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + this->bfdesign(fpass * (this->dt), apass, fstop * (this->dt), astop, + &this->npoles_hi, &this->f3db_hi); }; -} // end namespace +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/Taper.cc b/cxx/src/lib/algorithms/Taper.cc index 7e99a5978..20aadcbc3 100644 --- a/cxx/src/lib/algorithms/Taper.cc +++ b/cxx/src/lib/algorithms/Taper.cc @@ -1,173 +1,158 @@ #include "mspass/algorithms/Taper.h" -namespace mspass::algorithms -{ +namespace mspass::algorithms { using namespace std; using namespace mspass::utility; using namespace mspass::seismic; -LinearTaper::LinearTaper() -{ - head=false; - tail=false; - all=false; +LinearTaper::LinearTaper() { + head = false; + tail = false; + all = false; } -LinearTaper::LinearTaper(const double t0h,const double t1h, - const double t1t,const double t0t) -{ - t0head=t0h; - t1head=t1h; - t0tail=t0t; - t1tail=t1t; - if(t1head>t0head) - head=true; +LinearTaper::LinearTaper(const double t0h, const double t1h, const double t1t, + const double t0t) { + t0head = t0h; + t1head = t1h; + t0tail = t0t; + t1tail = t1t; + if (t1head > t0head) + head = true; else - head=false; - if(t1tail=0 && is= 0 && is < d.npts()) + d.s[is] = 0.0; } - rampslope=1.0/(t1head-t0head); - for(t=t0head;t=0) - { - wt=rampslope*(t-t0head); - d.s[is]*=wt; + rampslope = 1.0 / (t1head - t0head); + for (t = t0head; t < t1head; t += d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = rampslope * (t - t0head); + d.s[is] *= wt; } } } - if(tail) - { - if(d.t0()>t0tail) - { + if (tail) { + if (d.t0() > t0tail) { stringstream ss; - ss<<"LinearTaper::apply: inconsistent tail taper parameters"<=t0tail;t-=d.dt()) - { - is=d.sample_number(t); - if(is>=0 && is= t0tail; t -= d.dt()) { + is = d.sample_number(t); + if (is >= 0 && is < d.npts()) + d.s[is] = 0.0; } - rampslope=1.0/(t0tail-t1tail); - for(t=t0tail;t>=t1tail;t-=d.dt()) - { - is=d.sample_number(t); - if(is>=0) - { - wt=rampslope*(t0tail-t); - d.s[is]*=wt; + rampslope = 1.0 / (t0tail - t1tail); + for (t = t0tail; t >= t1tail; t -= d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = rampslope * (t0tail - t); + d.s[is] *= wt; } } } return 0; } -int LinearTaper::apply( Seismogram& d) -{ +int LinearTaper::apply(Seismogram &d) { int k; double rampslope; - if(head) - { - if(d.endtime()=0 && is= 0 && is < d.npts()) { + for (k = 0; k < 3; ++k) + d.u(k, is) = 0.0; } } - rampslope=1.0/(t1head-t0head); - for(t=t0head;t=0) - { - wt=rampslope*(t-t0head); - for(k=0;k<3;++k)d.u(k,is)*=wt; + rampslope = 1.0 / (t1head - t0head); + for (t = t0head; t < t1head; t += d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = rampslope * (t - t0head); + for (k = 0; k < 3; ++k) + d.u(k, is) *= wt; } } } - if(tail) - { - if(d.t0()>t0tail) - { + if (tail) { + if (d.t0() > t0tail) { stringstream ss; - ss<<"LinearTaper::apply: inconsistent tail taper parameters"<=t0tail;t-=d.dt()) - { - is=d.sample_number(t); - if(is>=0 && is= t0tail; t -= d.dt()) { + is = d.sample_number(t); + if (is >= 0 && is < d.npts()) { + for (k = 0; k < 3; ++k) + d.u(k, is) = 0.0; } } - rampslope=1.0/(t0tail-t1tail); - for(t=t0tail;t>=t1tail;t-=d.dt()) - { - is=d.sample_number(t); - if(is>=0) - { - wt=rampslope*(t-t1tail); - for(k=0;k<3;++k)d.u(k,is)*=wt; + rampslope = 1.0 / (t0tail - t1tail); + for (t = t0tail; t >= t1tail; t -= d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = rampslope * (t - t1tail); + for (k = 0; k < 3; ++k) + d.u(k, is) *= wt; } } } @@ -176,308 +161,272 @@ int LinearTaper::apply( Seismogram& d) /* this pair of functions return weight from 0 to 1 for head and tail cosine tapers. Would make the code more efficient to put these inline, but this will be easier to maintain at a tiny cost */ -double headcos(double t0,double t1,double t) -{ +double headcos(double t0, double t1, double t) { /* No range changing necesary in this context - trust t0<=t<=t1*/ - double T=(t1-t0)*2; + double T = (t1 - t0) * 2; double x; - x=2.0*M_PI*(t-t0)/T; - x-=M_PI; - double wt=(cos(x)+1.0)/2.0; - return(wt); + x = 2.0 * M_PI * (t - t0) / T; + x -= M_PI; + double wt = (cos(x) + 1.0) / 2.0; + return (wt); } -double tailcos(double t0,double t1,double t) -{ +double tailcos(double t0, double t1, double t) { /* No range changing necesary in this context - trust t1<=t<=t0*/ - double T=(t0-t1)*2; + double T = (t0 - t1) * 2; double x; - x=2.0*M_PI*(t-t1)/T; - double wt=(cos(x)+1.0)/2.0; - return(wt); + x = 2.0 * M_PI * (t - t1) / T; + double wt = (cos(x) + 1.0) / 2.0; + return (wt); } -CosineTaper::CosineTaper() -{ - head=false; - tail=false; - all=false; +CosineTaper::CosineTaper() { + head = false; + tail = false; + all = false; } -CosineTaper::CosineTaper(const double t0h,const double t1h, - const double t1t,const double t0t) -{ - t0head=t0h; - t1head=t1h; - t0tail=t0t; - t1tail=t1t; - if(t1head>t0head) head=true; - if(t1tail t0head) + head = true; + if (t1tail < t0tail) + tail = true; + if (head && tail) + all = true; else - all=false; - if(!(head || tail)) - { + all = false; + if (!(head || tail)) { stringstream ss; - ss << "CosineTaper constructor: illegal input. No valid taper parameters"<=0 && is= 0 && is < d.npts()) + d.s[is] = 0.0; } - for(t=t0head;t=0) - { - wt=headcos(t0head,t1head,t); - d.s[is]*=wt; + for (t = t0head; t < t1head; t += d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = headcos(t0head, t1head, t); + d.s[is] *= wt; } } } - if(tail) - { - if(d.t0()>t0tail) - { + if (tail) { + if (d.t0() > t0tail) { stringstream ss; - ss<<"CosineTaper::apply: inconsistent tail taper parameters"<=t0tail;t-=d.dt()) - { - is=d.sample_number(t); - if(is>=0 && is= t0tail; t -= d.dt()) { + is = d.sample_number(t); + if (is >= 0 && is < d.npts()) { + d.s[is] = 0.0; } } - for(t=t1tail;t=0) - { - wt=tailcos(t0tail,t1tail,t); - d.s[is]*=wt; + for (t = t1tail; t < t0tail; t += d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = tailcos(t0tail, t1tail, t); + d.s[is] *= wt; } } } return 0; } -int CosineTaper::apply( Seismogram& d) -{ - if(head) - { - if(d.endtime()=0 && is= 0 && is < d.npts()) { + for (int k = 0; k < 3; ++k) + d.u(k, is) = 0.0; } } - for(t=t0head;t=0) - { - wt=headcos(t0head,t1head,t); - for(int k=0;k<3;++k)d.u(k,is)*=wt; + for (t = t0head; t < t1head; t += d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = headcos(t0head, t1head, t); + for (int k = 0; k < 3; ++k) + d.u(k, is) *= wt; } } } - if(tail) - { - if(d.t0()>t0tail) - { + if (tail) { + if (d.t0() > t0tail) { stringstream ss; - ss<<"CosineTaper::apply: inconsistent tail taper parameters"<=t0tail;t-=d.dt()) - { - is=d.sample_number(t); - if(is>=0 && is= t0tail; t -= d.dt()) { + is = d.sample_number(t); + if (is >= 0 && is < d.npts()) { + for (int k = 0; k < 3; ++k) + d.u(k, is) = 0.0; } } - for(t=t1tail;t=0) - { - wt=tailcos(t0tail,t1tail,t); - for(int k=0;k<3;++k)d.u(k,is)*=wt; + for (t = t1tail; t < t0tail; t += d.dt()) { + is = d.sample_number(t); + if (is >= 0) { + wt = tailcos(t0tail, t1tail, t); + for (int k = 0; k < 3; ++k) + d.u(k, is) *= wt; } } } return 0; } -VectorTaper::VectorTaper() -{ - head=false; - tail=false; - all=false; +VectorTaper::VectorTaper() { + head = false; + tail = false; + all = false; } -VectorTaper::VectorTaper(const vector tp) : taper(tp) -{ +VectorTaper::VectorTaper(const vector tp) : taper(tp) { /* Really only all needs to be set true here, but need to initialize others anyway as good practice.*/ - head=true; - tail=true; - all=true; + head = true; + tail = true; + all = true; } -int VectorTaper::apply( TimeSeries& d) -{ - if(all) - { - if(d.npts()!=taper.size()) - { +int VectorTaper::apply(TimeSeries &d) { + if (all) { + if (d.npts() != taper.size()) { stringstream ss; - ss<<"VectorTaper apply method: size mismatch with data"<(t0,t1,t0+999999.0,t1+999999.0); + try { + if (type == "linear") { + taper = + std::make_shared(t0, t1, t0 + 999999.0, t1 + 999999.0); taper->disable_tail(); - } - else if(type=="cosine") - { - taper=std::make_shared(t0,t1,t0+999999.0,t1+999999.0); + } else if (type == "cosine") { + taper = + std::make_shared(t0, t1, t0 + 999999.0, t1 + 999999.0); taper->disable_tail(); - } - else - { + } else { stringstream ss; - ss << base_error<<"Unrecognized type argument="<taper = parent.taper; } return *this; } -int TopMute::apply(mspass::seismic::TimeSeries& d) -{ - try{ +int TopMute::apply(mspass::seismic::TimeSeries &d) { + try { int iret; - iret=this->taper->apply(d); + iret = this->taper->apply(d); return iret; - }catch(...){throw;}; + } catch (...) { + throw; + }; } -int TopMute::apply(mspass::seismic::Seismogram& d) -{ - try{ +int TopMute::apply(mspass::seismic::Seismogram &d) { + try { int iret; - iret=this->taper->apply(d); + iret = this->taper->apply(d); return iret; - }catch(...){throw;}; + } catch (...) { + throw; + }; } /* Some sources say the approach used in the algorithm is evil, but I don't see a better solution. We use the property of dynamic_cast @@ -485,19 +434,19 @@ of a pointer returning NULL if the cast fails because the type is wrong. We then just walk through the possibilities and throw an exception if none of them work. */ -string TopMute::taper_type() const -{ +string TopMute::taper_type() const { BasicTaper *rawptr; rawptr = this->taper.get(); - if(dynamic_cast(rawptr)) + if (dynamic_cast(rawptr)) return string("linear"); - else if(dynamic_cast(rawptr)) + else if (dynamic_cast(rawptr)) return string("cosine"); else /* note there is a VectorTaper child of BasicTaper but it is not supported by TopMute - only allows linear and cosine - so if that happened somehow we throw this exception in that case too. */ - throw MsPASSError("TopMute::taper_type: Internal taper dynamic cast does not resolve; this should not happen and is a bug", - ErrorSeverity::Fatal); + throw MsPASSError("TopMute::taper_type: Internal taper dynamic cast does " + "not resolve; this should not happen and is a bug", + ErrorSeverity::Fatal); } -} // End namespace +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/agc.cc b/cxx/src/lib/algorithms/agc.cc index 56447e46c..13990623f 100644 --- a/cxx/src/lib/algorithms/agc.cc +++ b/cxx/src/lib/algorithms/agc.cc @@ -1,10 +1,9 @@ -#include +#include "mspass/algorithms/algorithms.h" +#include "mspass/seismic/Seismogram.h" #include +#include #include -#include "mspass/seismic/Seismogram.h" -#include "mspass/algorithms/algorithms.h" -namespace mspass::algorithms -{ +namespace mspass::algorithms { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; @@ -19,146 +18,125 @@ using namespace mspass::utility; gain function at the same sample rate as the original data. The original data can then be restored by scaling each vector sample by 1/gain at each sample. */ -TimeSeries agc(Seismogram& d, const double twin) -{ - try{ - /* First deal with the processing history */ - dmatrix agcdata(3,d.npts()); - double val,rms,ssq,gain,lastgain; - size_t i,k; - CoreTimeSeries gf(dynamic_cast(d), - dynamic_cast(d)); - gf.set_t0(d.t0()+gf.dt()); - gf.set_npts(d.npts()); - /* this is inefficient but needed to mesh with older push_back algorithm. */ - gf.s.clear(); - int nwin,iwagc; - nwin=round(twin/(d.dt())); - iwagc=nwin/2; - if(iwagc<=0) - { - d.elog.log_error("agc","Illegal gain time window - resolves to less than one sample", - ErrorSeverity::Invalid); - return TimeSeries(); - } - if(iwagc>d.npts()) iwagc=d.npts(); - /* First compute sum of squares in initial wondow to establish the - * initial scale */ - for(i=0,ssq=0.0;i0.0) - { - gain=1.0/sqrt(rms); - for(k=0;k<3;++k) - { - agcdata(k,0) = gain*d.u(k,0); - } - gf.s.push_back(gain); - } +TimeSeries agc(Seismogram &d, const double twin) { + try { + /* First deal with the processing history */ + dmatrix agcdata(3, d.npts()); + double val, rms, ssq, gain, lastgain; + size_t i, k; + CoreTimeSeries gf(dynamic_cast(d), + dynamic_cast(d)); + gf.set_t0(d.t0() + gf.dt()); + gf.set_npts(d.npts()); + /* this is inefficient but needed to mesh with older push_back algorithm. */ + gf.s.clear(); + int nwin, iwagc; + nwin = round(twin / (d.dt())); + iwagc = nwin / 2; + if (iwagc <= 0) { + d.elog.log_error( + "agc", "Illegal gain time window - resolves to less than one sample", + ErrorSeverity::Invalid); + return TimeSeries(); + } + if (iwagc > d.npts()) + iwagc = d.npts(); + /* First compute sum of squares in initial wondow to establish the + * initial scale */ + for (i = 0, ssq = 0.0; i < iwagc; ++i) { + for (k = 0; k < 3; ++k) { + val = d.u(k, i); + ssq += val * val; + } + } + int normalization; + normalization = 3 * iwagc; + rms = ssq / ((double)normalization); + if (rms > 0.0) { + gain = 1.0 / sqrt(rms); + for (k = 0; k < 3; ++k) { + agcdata(k, 0) = gain * d.u(k, 0); + } + gf.s.push_back(gain); + } else { + gf.s.push_back(0.0); + lastgain = 0.0; + } + for (i = 1; i <= iwagc; ++i) { + for (k = 0; k < 3; ++k) { + val = d.u(k, i + iwagc); + ssq += val * val; + ++normalization; + } + rms = ssq / ((double)normalization); + if (rms > 0.0) { + lastgain = gain; + gain = 1.0 / sqrt(rms); + } else { + if (lastgain == 0.0) + gain = 0.0; else - { - gf.s.push_back(0.0); - lastgain=0.0; - } - for(i=1;i<=iwagc;++i) - { - for(k=0;k<3;++k) - { - val=d.u(k,i+iwagc); - ssq+=val*val; - ++normalization; - } - rms=ssq/((double)normalization); - if(rms>0.0) - { - lastgain=gain; - gain=1.0/sqrt(rms); - } - else - { - if(lastgain==0.0) - gain=0.0; - else - gain=lastgain; - - } - gf.s.push_back(gain); - lastgain=gain; - for(k=0;k<3;++k) agcdata(k,i) = gain*d.u(k,i); - } - int isave; - for(i=iwagc+1,isave=iwagc+1;i0.0) - { - lastgain=gain; - gain=1.0/sqrt(rms); - } - else - { - if(lastgain==0.0) - gain=0.0; - else - gain=lastgain; - - } - gf.s.push_back(gain); - lastgain=gain; - for(k=0;k<3;++k) agcdata(k,i) = gain*d.u(k,i); - } - /* ramping off */ - for(i=isave;i0.0) - { - lastgain=gain; - gain=1.0/sqrt(rms); - } - else - { - if(lastgain==0.0) - gain=0.0; - else - gain=lastgain; - - } - gf.s.push_back(gain); - lastgain=gain; - for(k=0;k<3;++k) agcdata(k,i) = gain*d.u(k,i); - } - d.u=agcdata; - gf.set_live(); - gf.set_npts(gf.s.size()); - return gf; - }catch(...){ - string uxperr("Something threw an unexpected exception"); - d.elog.log_error("agc",uxperr,ErrorSeverity::Invalid); - /* Return an empty TimeSeries object in this case. */ - return TimeSeries(); + gain = lastgain; + } + gf.s.push_back(gain); + lastgain = gain; + for (k = 0; k < 3; ++k) + agcdata(k, i) = gain * d.u(k, i); + } + int isave; + for (i = iwagc + 1, isave = iwagc + 1; i < d.npts() - iwagc; ++i, ++isave) { + for (k = 0; k < 3; ++k) { + val = d.u(k, i + iwagc); + ssq += val * val; + val = d.u(k, i - iwagc); + ssq -= val * val; + } + rms = ssq / ((double)normalization); + if (rms > 0.0) { + lastgain = gain; + gain = 1.0 / sqrt(rms); + } else { + if (lastgain == 0.0) + gain = 0.0; + else + gain = lastgain; + } + gf.s.push_back(gain); + lastgain = gain; + for (k = 0; k < 3; ++k) + agcdata(k, i) = gain * d.u(k, i); + } + /* ramping off */ + for (i = isave; i < d.npts(); ++i) { + for (k = 0; k < 3; ++k) { + val = d.u(k, i - iwagc); + ssq -= val * val; + --normalization; + } + rms = ssq / ((double)normalization); + if (rms > 0.0) { + lastgain = gain; + gain = 1.0 / sqrt(rms); + } else { + if (lastgain == 0.0) + gain = 0.0; + else + gain = lastgain; + } + gf.s.push_back(gain); + lastgain = gain; + for (k = 0; k < 3; ++k) + agcdata(k, i) = gain * d.u(k, i); } + d.u = agcdata; + gf.set_live(); + gf.set_npts(gf.s.size()); + return gf; + } catch (...) { + string uxperr("Something threw an unexpected exception"); + d.elog.log_error("agc", uxperr, ErrorSeverity::Invalid); + /* Return an empty TimeSeries object in this case. */ + return TimeSeries(); + } } -}// End mspass namespace +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/amplitudes.cc b/cxx/src/lib/algorithms/amplitudes.cc index 6f4f57d39..27e2277a1 100644 --- a/cxx/src/lib/algorithms/amplitudes.cc +++ b/cxx/src/lib/algorithms/amplitudes.cc @@ -1,125 +1,118 @@ -#include -#include "mspass/utility/MsPASSError.h" -#include "mspass/seismic/TimeSeries.h" -#include "mspass/seismic/Seismogram.h" -#include "misc/blas.h" #include "mspass/algorithms/amplitudes.h" -namespace mspass::algorithms::amplitudes -{ +#include "misc/blas.h" +#include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" +#include "mspass/utility/MsPASSError.h" +#include +namespace mspass::algorithms::amplitudes { using namespace std; using namespace mspass::seismic; -using mspass::utility::MsPASSError; using mspass::utility::ErrorSeverity; +using mspass::utility::MsPASSError; /* Series of overloaded functions to measure peak amplitudes for different types of seismic data objects. These are used in a generic algorithm defined in seispp.h */ -double PeakAmplitude(const CoreTimeSeries& d) -{ - if(d.dead() || ((d.npts())<=0)) return(0.0); - vector work(d.s); - vector::iterator dptr,amp; - /* We want maximum absolute value of the amplitude */ - for(dptr=work.begin();dptr!=work.end();++dptr) (*dptr)=fabs(*dptr); - amp=max_element(work.begin(),work.end()); - return(*amp); +double PeakAmplitude(const CoreTimeSeries &d) { + if (d.dead() || ((d.npts()) <= 0)) + return (0.0); + vector work(d.s); + vector::iterator dptr, amp; + /* We want maximum absolute value of the amplitude */ + for (dptr = work.begin(); dptr != work.end(); ++dptr) + (*dptr) = fabs(*dptr); + amp = max_element(work.begin(), work.end()); + return (*amp); } -double PeakAmplitude(const CoreSeismogram& d) -{ - if(d.dead() || ((d.npts()<=0))) return(0.0); - // This loop could use p->ns but this more more bulletproof. - double ampval,ampvec; - double *ptr; - int j; - ampvec=0.0; - for(j=0;jampvec) ampvec=ampval; - } - return(ampvec); +double PeakAmplitude(const CoreSeismogram &d) { + if (d.dead() || ((d.npts() <= 0))) + return (0.0); + // This loop could use p->ns but this more more bulletproof. + double ampval, ampvec; + double *ptr; + int j; + ampvec = 0.0; + for (j = 0; j < d.npts(); ++j) { + ampval = 0.0; + // Pointer arithmetic a bit brutal, but done + // for speed to avoid 3 calls to operator () + ptr = d.u.get_address(0, j); + ampval = (*ptr) * (*ptr); + ++ptr; + ampval += (*ptr) * (*ptr); + ++ptr; + ampval += (*ptr) * (*ptr); + ampval = sqrt(ampval); + if (ampval > ampvec) + ampvec = ampval; + } + return (ampvec); } -double RMSAmplitude(const CoreTimeSeries& d) -{ - if(d.dead() || ((d.npts())<=0)) return(0.0); - double l2nrm=dnrm2(d.npts(),&(d.s[0]),1); - return sqrt(l2nrm*l2nrm/d.npts()); +double RMSAmplitude(const CoreTimeSeries &d) { + if (d.dead() || ((d.npts()) <= 0)) + return (0.0); + double l2nrm = dnrm2(d.npts(), &(d.s[0]), 1); + return sqrt(l2nrm * l2nrm / d.npts()); } -double RMSAmplitude(const CoreSeismogram& d) -{ - /* rms is sum of squares so rms reduces to grand sum of squares of - amplitudes on all 3 components.*/ - if(d.dead() || ((d.npts()<=0))) return(0.0); - double sumsq(0.0); - /* This depends upon implementation detail for dmatrix u where the - matrix is stored in contiguous block - beware of this implementation - detail if matrix implementation changed. */ - double *ptr; - ptr=d.u.get_address(0,0); - size_t n=3*d.npts(); - for(size_t k=0;k100.0 || perc<=0.0) - { - stringstream ss; - ss<<"PercAmplitude: received perc value="< amps; - amps=d.s; - vector::iterator ptr; - for(ptr=amps.begin();ptr!=amps.end();++ptr) *ptr = fabs(*ptr); - sort(amps.begin(),amps.end()); - size_t n=amps.size(); - size_t iperc=static_cast(percfrac*static_cast(n)); - return amps[iperc]; +double PercAmplitude(const CoreTimeSeries &d, const double perc) { + double percfrac; + if (perc > 100.0 || perc <= 0.0) { + stringstream ss; + ss << "PercAmplitude: received perc value=" << perc << endl + << "Must be a nonzero percentage from 1 to 100 or a fraction value less " + "than 1" + << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } else if (perc <= 1.0) { + percfrac = perc; + } else { + // Land her for actual percentage values + percfrac = perc / 100.0; + } + vector amps; + amps = d.s; + vector::iterator ptr; + for (ptr = amps.begin(); ptr != amps.end(); ++ptr) + *ptr = fabs(*ptr); + sort(amps.begin(), amps.end()); + size_t n = amps.size(); + size_t iperc = static_cast(percfrac * static_cast(n)); + return amps[iperc]; } -double PercAmplitude(const CoreSeismogram& d,const double perc) -{ - vector amps; - amps.reserve(d.npts()); - for(int i=0;i(perc*static_cast(n)); - /* Silently return 100% if iperc exceeds the range of amps*/ - if(iperc>=amps.size()) iperc = amps.size() - 1; - return amps[iperc]; +double PercAmplitude(const CoreSeismogram &d, const double perc) { + vector amps; + amps.reserve(d.npts()); + for (int i = 0; i < d.npts(); ++i) { + double thisamp = dnrm2(3, d.u.get_address(0, i), 1); + amps.push_back(thisamp); + } + sort(amps.begin(), amps.end()); + size_t n = amps.size(); + /* n-1 because C arrays start at 0 */ + size_t iperc = static_cast(perc * static_cast(n)); + /* Silently return 100% if iperc exceeds the range of amps*/ + if (iperc >= amps.size()) + iperc = amps.size() - 1; + return amps[iperc]; } /* This pair could be made a template, but they are so simple it is clearer to keep them here with the related functions */ -double MADAmplitude(const CoreTimeSeries& d) -{ - return PercAmplitude(d,0.5); -} -double MADAmplitude(const CoreSeismogram& d) -{ - return PercAmplitude(d,0.5); -} -} //End mspass namespace encapsulation +double MADAmplitude(const CoreTimeSeries &d) { return PercAmplitude(d, 0.5); } +double MADAmplitude(const CoreSeismogram &d) { return PercAmplitude(d, 0.5); } +} // namespace mspass::algorithms::amplitudes diff --git a/cxx/src/lib/algorithms/bundle.cc b/cxx/src/lib/algorithms/bundle.cc index 794e75f46..cca40ec34 100644 --- a/cxx/src/lib/algorithms/bundle.cc +++ b/cxx/src/lib/algorithms/bundle.cc @@ -1,103 +1,87 @@ -#include -#include -#include "mspass/utility/MsPASSError.h" -#include "mspass/seismic/keywords.h" -#include "mspass/seismic/TimeSeries.h" -#include "mspass/seismic/Seismogram.h" -#include "mspass/seismic/Ensemble.h" #include "mspass/algorithms/algorithms.h" -namespace mspass::algorithms -{ +#include "mspass/seismic/Ensemble.h" +#include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" +#include "mspass/seismic/keywords.h" +#include "mspass/utility/MsPASSError.h" +#include +#include +namespace mspass::algorithms { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; - /* Cautious comparison of two string metadata fields defined by key. Return -1 if xy. Undefined values require a definition. two undefined field compare equal but and undefined value always compare > a defined field. */ -int cautious_compare(const TimeSeries& x, const TimeSeries& y, const string key) -{ - if(x.is_defined(key)) - { - if(y.is_defined(key)) - { - string valx,valy; - valx=x.get(key); - valy=y.get(key); - if(valx>valy) +int cautious_compare(const TimeSeries &x, const TimeSeries &y, + const string key) { + if (x.is_defined(key)) { + if (y.is_defined(key)) { + string valx, valy; + valx = x.get(key); + valy = y.get(key); + if (valx > valy) return 1; - else if(valx==valy) + else if (valx == valy) return 0; else return -1; - } - else - { + } else { /* x_defined and y_undefined here - x_defined >y_undefined*/ return 1; - } - }else if(y.is_defined(key)) - { + } else if (y.is_defined(key)) { /* x_undefined and y_defined - x_undefined < y_defined */ return -1; - } - else - { + } else { /* Both undefined - weak order requires return false. */ return 0; } } -struct greater_seedorder -{ +struct greater_seedorder { /* Note this sort so undefined will be less than any defined value. Could be the reverse but an arbitrary choice */ - bool operator()(TimeSeries x, TimeSeries y) - { - int retnet,retsta,retloc,retchan; - retnet=cautious_compare(x,y,SEISMICMD_net); - switch(retnet) - { + bool operator()(TimeSeries x, TimeSeries y) { + int retnet, retsta, retloc, retchan; + retnet = cautious_compare(x, y, SEISMICMD_net); + switch (retnet) { + case -1: + return false; + case +1: + return true; + case 0: + default: + retsta = cautious_compare(x, y, SEISMICMD_sta); + switch (retsta) { case -1: return false; case +1: return true; case 0: default: - retsta=cautious_compare(x,y,SEISMICMD_sta); - switch(retsta) - { - case -1: - return false; + retloc = cautious_compare(x, y, SEISMICMD_loc); + switch (retloc) { + case -1: + return false; + case +1: + return true; + case 0: + default: + retchan = cautious_compare(x, y, SEISMICMD_chan); + switch (retchan) { case +1: return true; - case 0: default: - retloc=cautious_compare(x,y,SEISMICMD_loc); - switch(retloc) - { - case -1: - return false; - case +1: - return true; - case 0: - default: - retchan=cautious_compare(x,y,SEISMICMD_chan); - switch(retchan) - { - case +1: - return true; - default: - /* Weak order as the final condition is strictly > so - equal case only here returns false */ - return false; - }; - }; + /* Weak order as the final condition is strictly > so + equal case only here returns false */ + return false; + }; }; + }; }; }; }; @@ -105,19 +89,14 @@ struct greater_seedorder When bundling fails we need a way to id the body. We do that here by merging all the Metadata with the Metadata += operator. That will leave some relics of the order, but should allow the user to id the body */ -Seismogram dogtag(vector& bundle) -{ +Seismogram dogtag(vector &bundle) { Metadata md; size_t i(0); - for(auto d=bundle.begin();d!=bundle.end();++d,++i) - { - if(i==0) - { - md=dynamic_cast(*d); - } - else - { - md += dynamic_cast(*d); + for (auto d = bundle.begin(); d != bundle.end(); ++d, ++i) { + if (i == 0) { + md = dynamic_cast(*d); + } else { + md += dynamic_cast(*d); } } Seismogram result(0); @@ -131,20 +110,19 @@ Seismogram dogtag(vector& bundle) from reading TimeSeries from MongoDB. We use it below to handle ensemble groupings that are irregular but it has broader use provided the data are from miniseed and have chan defined. */ -Seismogram BundleSEEDGroup(const std::vector& d, - const size_t i0, const size_t iend) -{ +Seismogram BundleSEEDGroup(const std::vector &d, const size_t i0, + const size_t iend) { const string algname("BundleSEEDGroup"); - try{ + try { vector bundle; - vector hvec; + vector hvec; /* I had to force this alias for the input d from a const cast to allow the dynamic casting below to work for ProcessingHistory. Unclear why but I think it is a limitation of std::vector. This is a workaround*/ std::vector d_const; - d_const=const_cast&>(d); - Seismogram d3c; // this holds the result even if marked dead - string net,sta,chan,loc; + d_const = const_cast &>(d); + Seismogram d3c; // this holds the result even if marked dead + string net, sta, chan, loc; /*this holds the first 2 characters in the chan code that seed from data centers is always constant for a 3c set.*/ string sta2; @@ -152,29 +130,28 @@ Seismogram BundleSEEDGroup(const std::vector& d, Depends on set inserts overwrite so the set container will only have unique keys while the vector has the chan for each vector component */ vector chans_this_group; - set keys,stations,networks,loccodes,sta2set; + set keys, stations, networks, loccodes, sta2set; size_t nlive(0); - for(size_t i=i0;i<=iend;++i) - { - /* Skip any members marked dead but put a special entry in chans_this_group*/ - if(d[i].dead()) - { + for (size_t i = i0; i <= iend; ++i) { + /* Skip any members marked dead but put a special entry in + * chans_this_group*/ + if (d[i].dead()) { chans_this_group.push_back("DEADCHANNEL"); continue; } - /* net and loc may not always be defined - handle them carefully and + /* net and loc may not always be defined - handle them carefully and use an internal string to define them as undefined */ - if(d[i].is_defined(SEISMICMD_net)) - net=d[i].get_string(SEISMICMD_net); + if (d[i].is_defined(SEISMICMD_net)) + net = d[i].get_string(SEISMICMD_net); else net = "Undefined"; - if(d[i].is_defined(SEISMICMD_loc)) - loc=d[i].get_string(SEISMICMD_loc); + if (d[i].is_defined(SEISMICMD_loc)) + loc = d[i].get_string(SEISMICMD_loc); else loc = "Undefined"; - sta=d[i].get_string(SEISMICMD_sta); - chan=d[i].get_string(SEISMICMD_chan); - sta2.assign(chan,0,2); + sta = d[i].get_string(SEISMICMD_sta); + chan = d[i].get_string(SEISMICMD_chan); + sta2.assign(chan, 0, 2); chans_this_group.push_back(chan); networks.insert(net); stations.insert(sta); @@ -188,381 +165,338 @@ Seismogram BundleSEEDGroup(const std::vector& d, algorithm are violated. The "immediately" is not so obvious because we have to do a lot of housecleaning - there is a return at the end of this code block for the true condition*/ - if( (stations.size()!=1) || (networks.size()!=1) - || (loccodes.size()!=1) || (sta2set.size()!=1) ) - { - for(size_t i=i0;i<=iend;++i) - { - bundle.push_back(dynamic_cast(d[i])); - if( ! (d[i].is_empty()) ) - { - hvec.push_back(dynamic_cast(&d_const[i])); + if ((stations.size() != 1) || (networks.size() != 1) || + (loccodes.size() != 1) || (sta2set.size() != 1)) { + for (size_t i = i0; i <= iend; ++i) { + bundle.push_back(dynamic_cast(d[i])); + if (!(d[i].is_empty())) { + hvec.push_back(dynamic_cast(&d_const[i])); } } - d3c=dogtag(bundle); - if(hvec.size()>0) d3c.add_many_inputs(hvec); + d3c = dogtag(bundle); + if (hvec.size() > 0) + d3c.add_many_inputs(hvec); d3c.kill(); stringstream ss; - ss << "Irregular grouping: inconsistent seed name codes in group"<1) - { + ss << "Irregular grouping: inconsistent seed name codes in group" + << endl; + if (stations.size() > 1) { ss << "List of inconsistent station names: "; - for(auto sptr=stations.begin();sptr!=stations.end();++sptr) - ss << *sptr << " "; + for (auto sptr = stations.begin(); sptr != stations.end(); ++sptr) + ss << *sptr << " "; ss << endl; } - if(networks.size()>1) - { + if (networks.size() > 1) { ss << "List of inconsistent network names: "; - for(auto sptr=networks.begin();sptr!=networks.end();++sptr) - ss << *sptr << " "; + for (auto sptr = networks.begin(); sptr != networks.end(); ++sptr) + ss << *sptr << " "; ss << endl; } - if(loccodes.size()>1) - { + if (loccodes.size() > 1) { ss << "List of inconsistent location names: "; - for(auto sptr=loccodes.begin();sptr!=loccodes.end();++sptr) - { + for (auto sptr = loccodes.begin(); sptr != loccodes.end(); ++sptr) { /* have to handle the common case of a loc code defined by an empty string - not null empty */ - if(sptr->length()==0) - //ss << "BLANK_LOC_CODE"<<" "; + if (sptr->length() == 0) + // ss << "BLANK_LOC_CODE"<<" "; ss << "\" \" "; else - ss << *sptr <<" "; + ss << *sptr << " "; } ss << endl; } - if(sta2set.size()>1) - { - ss << "List of inconsistent channel codes (first 2 characters should be unique and they are not): "; - for(auto sptr=sta2set.begin();sptr!=sta2set.end();++sptr) - ss << *sptr << " "; + if (sta2set.size() > 1) { + ss << "List of inconsistent channel codes (first 2 characters should " + "be unique and they are not): "; + for (auto sptr = sta2set.begin(); sptr != sta2set.end(); ++sptr) + ss << *sptr << " "; ss << endl; } - d3c.elog.log_error("BundleSEEDGroup",ss.str(),ErrorSeverity::Invalid); + d3c.elog.log_error("BundleSEEDGroup", ss.str(), ErrorSeverity::Invalid); return d3c; } - size_t nkeys=keys.size(); + size_t nkeys = keys.size(); - if(nlive<=3) - { + if (nlive <= 3) { /* We can handle deficient groups here by this algorithm. After conditionals we kill output when size is less than 3.*/ - for(size_t i=i0;i<=iend;++i) - { - bundle.push_back(dynamic_cast(d[i])); - if( ! (d[i].is_empty()) ) - { - hvec.push_back(dynamic_cast(&d_const[i])); + for (size_t i = i0; i <= iend; ++i) { + bundle.push_back(dynamic_cast(d[i])); + if (!(d[i].is_empty())) { + hvec.push_back(dynamic_cast(&d_const[i])); } } - } - else - { + } else { /* We use this multimap to sort out duplicates */ - multimap xref; + multimap xref; /* We use this for handling find returns from xref */ - multimap::iterator xptr; - size_t i,ii; - for(i=i0,ii=0;i<=iend;++i,++ii) - { - if(d[i].live()) - { - chan=chans_this_group[ii]; + multimap::iterator xptr; + size_t i, ii; + for (i = i0, ii = 0; i <= iend; ++i, ++ii) { + if (d[i].live()) { + chan = chans_this_group[ii]; keys.insert(chan); - xref.insert(pair(chan,i)); + xref.insert(pair(chan, i)); } } /* Only when number of keys is 3 can we hope to form a valid bundle. This first block does that resolving duplicates by the channel with the closest match to the start time defined by minimum time of the group*/ - if(nkeys==3) - { - for(auto kptr=keys.begin();kptr!=keys.end();++kptr) - { + if (nkeys == 3) { + for (auto kptr = keys.begin(); kptr != keys.end(); ++kptr) { /* Note we don't have to test for no match because it isn't possible*/ - if(xref.count(*kptr)==1) - { - xptr=xref.find(*kptr); - bundle.push_back(dynamic_cast - (d[xptr->second])); - if( ! (d[xptr->second].is_empty()) ) - { - hvec.push_back(dynamic_cast - (&(d_const[xptr->second]))); + if (xref.count(*kptr) == 1) { + xptr = xref.find(*kptr); + bundle.push_back( + dynamic_cast(d[xptr->second])); + if (!(d[xptr->second].is_empty())) { + hvec.push_back( + dynamic_cast(&(d_const[xptr->second]))); } - } - else - { + } else { /* We use the algorithm described in the include file doxygen docs. Briefly find the minimum time of this entire group and use that as an anchor. component with t0 closest to that time is selected. So first get min t0*/ - double t0min=d[i0].t0(); - for(i=i0+1;i<=iend;++i) - { - if(d[i].t0()::iterator,multimap::iterator> ret; - ret=xref.equal_range(*kptr); - size_t j(0),jjmin(0),jj; - double dt,dtmin; - for(xptr=ret.first;xptr!=ret.second;++xptr,++j) - { - jj=xptr->second; - if(j==0) - { - jjmin=jj; - dtmin=fabs(d[jj].t0()-t0min); - } - else - { - dt=fabs(d[jj].t0()-t0min); - if(dt::iterator, + multimap::iterator> + ret; + ret = xref.equal_range(*kptr); + size_t j(0), jjmin(0), jj; + double dt, dtmin; + for (xptr = ret.first; xptr != ret.second; ++xptr, ++j) { + jj = xptr->second; + if (j == 0) { + jjmin = jj; + dtmin = fabs(d[jj].t0() - t0min); + } else { + dt = fabs(d[jj].t0() - t0min); + if (dt < dtmin) { + jjmin = jj; + dtmin = dt; } } } - bundle.push_back(dynamic_cast(d[jjmin])); - if( ! (d[jjmin].is_empty()) ) - { - hvec.push_back(dynamic_cast(&d_const[jjmin])); + bundle.push_back(dynamic_cast(d[jjmin])); + if (!(d[jjmin].is_empty())) { + hvec.push_back( + dynamic_cast(&d_const[jjmin])); } } } - } - else - { + } else { /* We handle deficient and excess keys identically in terms of the loop below. We split them up for different error messages when we try to create a seismogram below */ - for(size_t i=i0;i<=iend;++i) - { - bundle.push_back(dynamic_cast(d[i])); - if( ! (d[i].is_empty()) ) - { - hvec.push_back(dynamic_cast(&d_const[i])); + for (size_t i = i0; i <= iend; ++i) { + bundle.push_back(dynamic_cast(d[i])); + if (!(d[i].is_empty())) { + hvec.push_back(dynamic_cast(&d_const[i])); } } } } - if( (nkeys==3) && (nlive>=3) ) - { - try{ - d3c=Seismogram(CoreSeismogram(bundle),algname); - if(hvec.size()==3) d3c.add_many_inputs(hvec); - } - catch(MsPASSError& err) - { - d3c=dogtag(bundle); - if(hvec.size()>0) d3c.add_many_inputs(hvec); + if ((nkeys == 3) && (nlive >= 3)) { + try { + d3c = Seismogram(CoreSeismogram(bundle), algname); + if (hvec.size() == 3) + d3c.add_many_inputs(hvec); + } catch (MsPASSError &err) { + d3c = dogtag(bundle); + if (hvec.size() > 0) + d3c.add_many_inputs(hvec); d3c.kill(); d3c.elog.log_error(err); }; - } - else - { - d3c=dogtag(bundle); - if(hvec.size()>0) d3c.add_many_inputs(hvec); + } else { + d3c = dogtag(bundle); + if (hvec.size() > 0) + d3c.add_many_inputs(hvec); d3c.kill(); stringstream ss; - if(nkeys<3) - { - ss<<"Insufficient data to generate Seismogram object."< bundle_seed_data(LoggingEnsemble& d) -{ +LoggingEnsemble bundle_seed_data(LoggingEnsemble &d) { string algname("bundle_seed_data"); - try{ - if(d.dead()) - { - LoggingEnsemble d3c(dynamic_cast(d),0); + try { + if (d.dead()) { + LoggingEnsemble d3c(dynamic_cast(d), 0); d3c.elog += d.elog; stringstream ss; - ss << "Received an ensemble marked dead; returning an empty ensemble cloning only ensemble Metadata and elog containers"< ens3c(dynamic_cast(d),d.member.size()/3); + LoggingEnsemble ens3c(dynamic_cast(d), + d.member.size() / 3); vector::iterator dptr; - string laststa,lastloc,lastchan,lastnet; - string net(""),sta,chan,loc(""); + string laststa, lastloc, lastchan, lastnet; + string net(""), sta, chan, loc(""); bool has_dead_channel(false); - size_t i0,iend; + size_t i0, iend; size_t i; - for(i=0,dptr=d.member.begin();dptr!=d.member.end();++i,++dptr) - { - if(dptr->dead()) - { + for (i = 0, dptr = d.member.begin(); dptr != d.member.end(); ++i, ++dptr) { + if (dptr->dead()) { /* If net, sta, and loc are defined we try to blunder on so we can retain elog entries in data received as dead. Necessary or the user won't be able to track the reason something was dropped easily*/ - if( dptr->is_defined(SEISMICMD_net) && dptr->is_defined(SEISMICMD_sta) - && dptr->is_defined(SEISMICMD_loc) ) - { - has_dead_channel=true; - } - else - { + if (dptr->is_defined(SEISMICMD_net) && + dptr->is_defined(SEISMICMD_sta) && + dptr->is_defined(SEISMICMD_loc)) { + has_dead_channel = true; + } else { /* In this situation we have to just drop the bad datum and - blunder on. The best we can do then is log the error to the ensemble - elog container. The history will be incomplete but at least we + blunder on. The best we can do then is log the error to the ensemble + elog container. The history will be incomplete but at least we leave a record of the problem in the ensemble.*/ stringstream ss; - ss << "Member "<is_defined(SEISMICMD_net)) - { - lastnet=dptr->get(SEISMICMD_net); - } - else - { - lastnet="Undefined"; + if (dptr->is_defined(SEISMICMD_net)) { + lastnet = dptr->get(SEISMICMD_net); + } else { + lastnet = "Undefined"; } - if(dptr->is_defined(SEISMICMD_loc)) - { - lastloc=dptr->get(SEISMICMD_loc); + if (dptr->is_defined(SEISMICMD_loc)) { + lastloc = dptr->get(SEISMICMD_loc); + } else { + lastloc = "Undefined"; } - else - { - lastloc="Undefined"; + laststa = dptr->get(SEISMICMD_sta); + lastchan = dptr->get(SEISMICMD_chan); + i0 = 0; + } else { + if (dptr->is_defined(SEISMICMD_net)) { + net = dptr->get(SEISMICMD_net); + } else { + net = "Undefined"; } - laststa=dptr->get(SEISMICMD_sta); - lastchan=dptr->get(SEISMICMD_chan); - i0=0; - } - else - { - if(dptr->is_defined(SEISMICMD_net)) - { - net=dptr->get(SEISMICMD_net); - } - else - { - net="Undefined"; + if (dptr->is_defined(SEISMICMD_loc)) { + loc = dptr->get(SEISMICMD_loc); + } else { + loc = "Undefined"; } - if(dptr->is_defined(SEISMICMD_loc)) - { - loc=dptr->get(SEISMICMD_loc); - } - else - { - loc="Undefined"; - } - sta=dptr->get(SEISMICMD_sta); - chan=dptr->get(SEISMICMD_chan); - if( (lastnet!=net || laststa!=sta || lastloc!=loc) || - (dptr==(d.member.end() - 1)) ) - { + sta = dptr->get(SEISMICMD_sta); + chan = dptr->get(SEISMICMD_chan); + if ((lastnet != net || laststa != sta || lastloc != loc) || + (dptr == (d.member.end() - 1))) { /* The end condition has to be handled specially because when we reach the end we have attempt a bundle. Inside the ensemble the test is against the previous values of the seed keys*/ - if(dptr==(d.member.end()-1)) - iend=i; + if (dptr == (d.member.end() - 1)) + iend = i; else - iend=i-1; + iend = i - 1; /* count the number of live channels when the has_dead_channel is set true. We use that as a logic test to know if we should try to recover something */ size_t nlive; - if(has_dead_channel) - { - nlive=0; - for(auto ii=i0;ii<=iend;++ii) - if(d.member[ii].live())++nlive; - } - else - { - nlive=iend-i0+1; + if (has_dead_channel) { + nlive = 0; + for (auto ii = i0; ii <= iend; ++ii) + if (d.member[ii].live()) + ++nlive; + } else { + nlive = iend - i0 + 1; } - //if((iend-i0)>=2) //Use iend to handle end condition instead of i - if(nlive>=3) - { + // if((iend-i0)>=2) //Use iend to handle end condition instead of i + if (nlive >= 3) { /* We can only have 3 live channels and iend-i0 be 2 if there are no channels marked dead */ - if((iend-i0) == 2) - { + if ((iend - i0) == 2) { vector work; - vector hvec; + vector hvec; work.reserve(3); - for(size_t k=0;k<3;++k) - { - work.push_back(dynamic_cast(d.member[i0+k])); - if( ! (d.member[i0+k].is_empty()) ) - { - hvec.push_back(dynamic_cast(&d.member[i0+k])); + for (size_t k = 0; k < 3; ++k) { + work.push_back( + dynamic_cast(d.member[i0 + k])); + if (!(d.member[i0 + k].is_empty())) { + hvec.push_back( + dynamic_cast(&d.member[i0 + k])); } } Seismogram d3c; - try{ - d3c=Seismogram(CoreSeismogram(work),algname); - if(hvec.size()==3) d3c.add_many_inputs(hvec); - }catch(MsPASSError& err) - { - d3c=dogtag(work); - if(hvec.size()>0) d3c.add_many_inputs(hvec); + try { + d3c = Seismogram(CoreSeismogram(work), algname); + if (hvec.size() == 3) + d3c.add_many_inputs(hvec); + } catch (MsPASSError &err) { + d3c = dogtag(work); + if (hvec.size() > 0) + d3c.add_many_inputs(hvec); d3c.kill(); d3c.elog.log_error(err); } clear_channel_metadata(d3c); ens3c.member.push_back(d3c); - } - else - { + } else { /* We put this in a function because there is a fair amount of complexity in sorting out channel groups better kept together. This main function is already complicated enough. The @@ -571,61 +505,64 @@ LoggingEnsemble bundle_seed_data(LoggingEnsemble& d) Among other things it has to handle channels marked dead */ - Seismogram dgrp(BundleSEEDGroup(d.member,i0,iend)); + Seismogram dgrp(BundleSEEDGroup(d.member, i0, iend)); clear_channel_metadata(dgrp); ens3c.member.push_back(dgrp); } - } - else - { + } else { /* we land here for deficient groups. We create a body bag with the dogtag function and handle the components from this case exactly like that when the Seismogram constructor above throws an exception */ vector work; - vector hvec; - for(size_t k=i0;k<=iend;++k) - { - work.push_back(dynamic_cast(d.member[k])); - if( ! (d.member[k].is_empty()) ) - { - hvec.push_back(dynamic_cast(&d.member[k])); + vector hvec; + for (size_t k = i0; k <= iend; ++k) { + work.push_back(dynamic_cast(d.member[k])); + if (!(d.member[k].is_empty())) { + hvec.push_back(dynamic_cast(&d.member[k])); } } Seismogram d3c; - d3c=dogtag(work); - if(hvec.size()>0) d3c.add_many_inputs(hvec); + d3c = dogtag(work); + if (hvec.size() > 0) + d3c.add_many_inputs(hvec); d3c.kill(); stringstream ss; - ss<<"Insufficient data to generate Seismogram object."<0) ens3c.set_live(); + if (ens3c.member.size() > 0) + ens3c.set_live(); return ens3c; - }catch(...){throw;}; + } catch (...) { + throw; + }; } /* This one liner is a useful as a processing function and is thus exposed to python. */ -void seed_ensemble_sort(LoggingEnsemble& d) -{ - try{ - std::sort(d.member.begin(),d.member.end(),greater_seedorder()); - }catch(...){throw;}; +void seed_ensemble_sort(LoggingEnsemble &d) { + try { + std::sort(d.member.begin(), d.member.end(), greater_seedorder()); + } catch (...) { + throw; + }; } -} // End namespace +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/deconvolution/CNR3CDecon.cc b/cxx/src/lib/algorithms/deconvolution/CNR3CDecon.cc index a4802d80f..273d6e4e3 100644 --- a/cxx/src/lib/algorithms/deconvolution/CNR3CDecon.cc +++ b/cxx/src/lib/algorithms/deconvolution/CNR3CDecon.cc @@ -1,10 +1,10 @@ -#include //needed for memcpy -#include -#include "mspass/utility/MsPASSError.h" #include "mspass/algorithms/deconvolution/CNR3CDecon.h" -#include "mspass/seismic/TimeSeries.h" -#include "mspass/seismic/Seismogram.h" #include "mspass/algorithms/algorithms.h" +#include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" +#include "mspass/utility/MsPASSError.h" +#include +#include //needed for memcpy /* This enum is file scope to intentionally exclude it from python wrappers. It is used internally to define the algorithm the processor is to run. @@ -15,353 +15,325 @@ the parameter setup as opposed to having to select the right symbolic name to construct. Anyway, this enum defines algorithms that can be chosen for processing. */ -enum class CNR3C_algorithms{ - generalized_water_level, - colored_noise_damping -}; -namespace mspass::algorithms::deconvolution -{ +enum class CNR3C_algorithms { generalized_water_level, colored_noise_damping }; +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; using namespace mspass::algorithms; using namespace mspass::algorithms::amplitudes; -CNR3CDecon::CNR3CDecon() : FFTDeconOperator(), - signalengine(),waveletengine(),dnoise_engine(),wnoise_engine(), - shapingwavelet(),psnoise(),psnoise_data(),decondata(),wavelet() -{ - algorithm=CNR3C_algorithms::undefined; - operator_dt=1.0; - winlength=0; - processing_window=TimeWindow(0.0,1.0); - noise_window=TimeWindow(0.0,1.0); - damp=0.0; - noise_floor=0.0001; - snr_regularization_floor=1.5; - taper_data=false; - fhs=2.0; // appropriate for teleseismic P wave data - for(int k=0;k<3;++k) - { - signal_bandwidth_fraction[k]=0.0; - peak_snr[k]=0.0; +CNR3CDecon::CNR3CDecon() + : FFTDeconOperator(), signalengine(), waveletengine(), dnoise_engine(), + wnoise_engine(), shapingwavelet(), psnoise(), psnoise_data(), decondata(), + wavelet() { + algorithm = CNR3C_algorithms::undefined; + operator_dt = 1.0; + winlength = 0; + processing_window = TimeWindow(0.0, 1.0); + noise_window = TimeWindow(0.0, 1.0); + damp = 0.0; + noise_floor = 0.0001; + snr_regularization_floor = 1.5; + taper_data = false; + fhs = 2.0; // appropriate for teleseismic P wave data + for (int k = 0; k < 3; ++k) { + signal_bandwidth_fraction[k] = 0.0; + peak_snr[k] = 0.0; } } -CNR3CDecon::CNR3CDecon(const AntelopePf& pf) - : FFTDeconOperator(pf),signalengine(),waveletengine(), - dnoise_engine(),wnoise_engine(),shapingwavelet(pf),psnoise(), - psnoise_data(),decondata(),wavelet() -{ -//DEBUG -//cout << "Entering pf constructor"<read_parameters(pf); } /* Note this method assumes BasicMetadata is actually an AntelopePf.*/ -void CNR3CDecon::change_parameters(const BasicMetadata& basemd) -{ - try{ - AntelopePf pf=dynamic_cast(basemd); - this->read_parameters(pf); - }catch(...){throw;}; +void CNR3CDecon::change_parameters(const BasicMetadata &basemd) { + try { + AntelopePf pf = dynamic_cast(basemd); + this->read_parameters(pf); + } catch (...) { + throw; + }; } -void CNR3CDecon::read_parameters(const AntelopePf& pf) -{ - try{ +void CNR3CDecon::read_parameters(const AntelopePf &pf) { + try { string stmp; - stmp=pf.get_string("algorithm"); - if(stmp=="generalized_water_level") - { - algorithm=CNR3C_algorithms::generalized_water_level; - } - else if(stmp=="colored_noise_damping") - { - algorithm=CNR3C_algorithms::colored_noise_damping; - } - else - { - throw MsPASSError("CNR3CDecon::read_parameters: invalid value for parameter algorithm=" - + stmp,ErrorSeverity::Invalid); + stmp = pf.get_string("algorithm"); + if (stmp == "generalized_water_level") { + algorithm = CNR3C_algorithms::generalized_water_level; + } else if (stmp == "colored_noise_damping") { + algorithm = CNR3C_algorithms::colored_noise_damping; + } else { + throw MsPASSError("CNR3CDecon::read_parameters: invalid value for " + "parameter algorithm=" + + stmp, + ErrorSeverity::Invalid); } - this->damp=pf.get_double("damping_factor"); + this->damp = pf.get_double("damping_factor"); /* Note this paramter is used for both the damping method and the generalized_water_level */ - this->noise_floor=pf.get_double("noise_floor"); - this->snr_regularization_floor=pf.get_double("snr_regularization_floor"); - this->snr_bandwidth=pf.get_double("snr_for_bandwidth_estimator"); - this->operator_dt=pf.get_double("target_sample_interval"); + this->noise_floor = pf.get_double("noise_floor"); + this->snr_regularization_floor = pf.get_double("snr_regularization_floor"); + this->snr_bandwidth = pf.get_double("snr_for_bandwidth_estimator"); + this->operator_dt = pf.get_double("target_sample_interval"); this->fhs = pf.get_double("high_frequency_search_start"); - double ts,te; - ts=pf.get_double("deconvolution_data_window_start"); - te=pf.get_double("deconvolution_data_window_end"); - this->processing_window=TimeWindow(ts,te); - this->winlength=round((te-ts)/operator_dt)+1; + double ts, te; + ts = pf.get_double("deconvolution_data_window_start"); + te = pf.get_double("deconvolution_data_window_end"); + this->processing_window = TimeWindow(ts, te); + this->winlength = round((te - ts) / operator_dt) + 1; /* In this algorithm we are very careful to avoid circular convolution artifacts that I (glp) suspect may be a problem in some frequency domain implementations of rf deconvolution. Here we set the length of the fft (nfft) to a minimum of 3 times the window size. That allows 1 window of padding around both ends of the waveform being deconvolved. Circular shift is used to put the result back in a rational time base. */ - int minwinsize=3*(this->winlength); + int minwinsize = 3 * (this->winlength); /* This complicated set of tests to set nfft is needed to mesh with - * ShapingWavelet constructor and FFTDeconOperator api constraints created by - * use in other classes in this directory that also use these */ - int nfftneeded=nextPowerOf2(minwinsize); - int nfftpf=pf.get("operator_nfft"); - if(nfftneeded!=nfftpf) - { + * ShapingWavelet constructor and FFTDeconOperator api constraints created + * by use in other classes in this directory that also use these */ + int nfftneeded = nextPowerOf2(minwinsize); + int nfftpf = pf.get("operator_nfft"); + if (nfftneeded != nfftpf) { FFTDeconOperator::change_size(nfftneeded); AntelopePf pfcopy(pf); - pfcopy.put("operator_nfft",nfftneeded); - this->shapingwavelet=ShapingWavelet(pfcopy); + pfcopy.put("operator_nfft", nfftneeded); + this->shapingwavelet = ShapingWavelet(pfcopy); } /* ShapingWavelet has more options than can be accepted in this algorithm so this test is needed */ - string swname=this->shapingwavelet.type(); - if( !( (swname == "ricker") || (swname == "butterworth") ) ) - { - throw MsPASSError(string("CNR3CDecon(AntelopePf constructor): ") - + "Cannot use shaping wavelet type="+swname - + "\nMust be either ricker or butterworth for this algorithm", + string swname = this->shapingwavelet.type(); + if (!((swname == "ricker") || (swname == "butterworth"))) { + throw MsPASSError( + string("CNR3CDecon(AntelopePf constructor): ") + + "Cannot use shaping wavelet type=" + swname + + "\nMust be either ricker or butterworth for this algorithm", ErrorSeverity::Invalid); } FFTDeconOperator::change_size(nextPowerOf2(minwinsize)); - ts=pf.get_double("noise_window_start"); - te=pf.get_double("noise_window_end"); - this->noise_window=TimeWindow(ts,te); - int noise_winlength=round((te-ts)/operator_dt)+1; - double tbp=pf.get_double("time_bandwidth_product"); - long ntapers=pf.get_long("number_tapers"); - this->dnoise_engine=MTPowerSpectrumEngine(noise_winlength,tbp,ntapers); + ts = pf.get_double("noise_window_start"); + te = pf.get_double("noise_window_end"); + this->noise_window = TimeWindow(ts, te); + int noise_winlength = round((te - ts) / operator_dt) + 1; + double tbp = pf.get_double("time_bandwidth_product"); + long ntapers = pf.get_long("number_tapers"); + this->dnoise_engine = MTPowerSpectrumEngine(noise_winlength, tbp, ntapers); /* Default wavelet noise window to data window length - adjusted dynamically if changed*/ - this->wnoise_engine=MTPowerSpectrumEngine(noise_winlength,tbp,ntapers); - /* Set initial signal and wavelet engine spectrum estimators to length defined - by data window above */ - this->signalengine=MTPowerSpectrumEngine(this->winlength,tbp,ntapers); - this->waveletengine=MTPowerSpectrumEngine(this->winlength,tbp,ntapers); + this->wnoise_engine = MTPowerSpectrumEngine(noise_winlength, tbp, ntapers); + /* Set initial signal and wavelet engine spectrum estimators to length + defined by data window above */ + this->signalengine = MTPowerSpectrumEngine(this->winlength, tbp, ntapers); + this->waveletengine = MTPowerSpectrumEngine(this->winlength, tbp, ntapers); string sval; - sval=pf.get_string("taper_type"); + sval = pf.get_string("taper_type"); /* New parameter added for dynamic bandwidth adjustment feature implemented december 2020 */ - decon_bandwidth_cutoff=pf.get_double("decon_bandwidth_cutoff"); - if(sval=="linear") - { - double f0,f1,t1,t0; - AntelopePf pfb=pf.get_branch("LinearTaper"); - AntelopePf pfbranch=pfb.get_branch("wavelet_taper"); - f0=pfbranch.get_double("front0"); - f1=pfbranch.get_double("front1"); - t1=pfbranch.get_double("tail1"); - t0=pfbranch.get_double("tail0"); - wavelet_taper=shared_ptr(new LinearTaper(f0,f1,t1,t0)); - pfbranch=pfb.get_branch("data_taper"); - f0=pfbranch.get_double("front0"); - f1=pfbranch.get_double("front1"); - t1=pfbranch.get_double("tail1"); - t0=pfbranch.get_double("tail0"); - data_taper=shared_ptr(new LinearTaper(f0,f1,t1,t0)); - taper_data=true; - } - else if(sval=="cosine") - { - double f0,f1,t1,t0; - AntelopePf pfb=pf.get_branch("CosineTaper"); - AntelopePf pfbranch=pfb.get_branch("wavelet_taper"); - f0=pfbranch.get_double("front0"); - f1=pfbranch.get_double("front1"); - t1=pfbranch.get_double("tail1"); - t0=pfbranch.get_double("tail0"); - wavelet_taper=shared_ptr(new CosineTaper(f0,f1,t1,t0)); - pfbranch=pfb.get_branch("data_taper"); - f0=pfbranch.get_double("front0"); - f1=pfbranch.get_double("front1"); - t1=pfbranch.get_double("tail1"); - t0=pfbranch.get_double("tail0"); - data_taper=shared_ptr(new CosineTaper(f0,f1,t1,t0)); - taper_data=true; - } - else if(sval=="vector") - { - AntelopePf pfbranch=pf.get_branch("VectorTaper"); + decon_bandwidth_cutoff = pf.get_double("decon_bandwidth_cutoff"); + if (sval == "linear") { + double f0, f1, t1, t0; + AntelopePf pfb = pf.get_branch("LinearTaper"); + AntelopePf pfbranch = pfb.get_branch("wavelet_taper"); + f0 = pfbranch.get_double("front0"); + f1 = pfbranch.get_double("front1"); + t1 = pfbranch.get_double("tail1"); + t0 = pfbranch.get_double("tail0"); + wavelet_taper = shared_ptr(new LinearTaper(f0, f1, t1, t0)); + pfbranch = pfb.get_branch("data_taper"); + f0 = pfbranch.get_double("front0"); + f1 = pfbranch.get_double("front1"); + t1 = pfbranch.get_double("tail1"); + t0 = pfbranch.get_double("tail0"); + data_taper = shared_ptr(new LinearTaper(f0, f1, t1, t0)); + taper_data = true; + } else if (sval == "cosine") { + double f0, f1, t1, t0; + AntelopePf pfb = pf.get_branch("CosineTaper"); + AntelopePf pfbranch = pfb.get_branch("wavelet_taper"); + f0 = pfbranch.get_double("front0"); + f1 = pfbranch.get_double("front1"); + t1 = pfbranch.get_double("tail1"); + t0 = pfbranch.get_double("tail0"); + wavelet_taper = shared_ptr(new CosineTaper(f0, f1, t1, t0)); + pfbranch = pfb.get_branch("data_taper"); + f0 = pfbranch.get_double("front0"); + f1 = pfbranch.get_double("front1"); + t1 = pfbranch.get_double("tail1"); + t0 = pfbranch.get_double("tail0"); + data_taper = shared_ptr(new CosineTaper(f0, f1, t1, t0)); + taper_data = true; + } else if (sval == "vector") { + AntelopePf pfbranch = pf.get_branch("VectorTaper"); vector tdataread; list tdl; - tdl=pfbranch.get_tbl("wavelet_taper_vector"); + tdl = pfbranch.get_tbl("wavelet_taper_vector"); tdataread.reserve(tdl.size()); list::iterator tptr; - for(tptr=tdl.begin();tptr!=tdl.end();++tptr) - { + for (tptr = tdl.begin(); tptr != tdl.end(); ++tptr) { double val; - sscanf(tptr->c_str(),"%lf",&val); + sscanf(tptr->c_str(), "%lf", &val); tdataread.push_back(val); } - wavelet_taper=shared_ptr(new VectorTaper(tdataread)); + wavelet_taper = shared_ptr(new VectorTaper(tdataread)); tdataread.clear(); tdl.clear(); - tdl=pfbranch.get_tbl("data_taper_vector"); + tdl = pfbranch.get_tbl("data_taper_vector"); tdataread.reserve(tdl.size()); - for(tptr=tdl.begin();tptr!=tdl.end();++tptr) - { + for (tptr = tdl.begin(); tptr != tdl.end(); ++tptr) { double val; - sscanf(tptr->c_str(),"%lf",&val); + sscanf(tptr->c_str(), "%lf", &val); tdataread.push_back(val); } - data_taper=shared_ptr(new VectorTaper(tdataread)); - taper_data=true; - } - else - { - //wavelet_taper=NULL; - //data_taper=NULL; - taper_data=false; + data_taper = shared_ptr(new VectorTaper(tdataread)); + taper_data = true; + } else { + // wavelet_taper=NULL; + // data_taper=NULL; + taper_data = false; } - }catch(...){throw;}; + } catch (...) { + throw; + }; } -CNR3CDecon::CNR3CDecon(const CNR3CDecon& parent) : - processing_window(parent.processing_window), - noise_window(parent.noise_window), - signalengine(parent.signalengine), - waveletengine(parent.waveletengine), - dnoise_engine(parent.dnoise_engine), - wnoise_engine(parent.wnoise_engine), - shapingwavelet(parent.shapingwavelet), - psnoise(parent.psnoise), - psnoise_data(parent.psnoise_data), - pssignal(parent.pssignal), - pswavelet(parent.pswavelet), - decondata(parent.decondata), - wavelet(parent.wavelet), - wavelet_taper(parent.wavelet_taper), - data_taper(parent.data_taper), - ao_fft(parent.ao_fft), - wavelet_bwd(parent.wavelet_bwd), - signal_bwd(parent.signal_bwd), - wavelet_snr(parent.wavelet_snr) +CNR3CDecon::CNR3CDecon(const CNR3CDecon &parent) + : processing_window(parent.processing_window), + noise_window(parent.noise_window), signalengine(parent.signalengine), + waveletengine(parent.waveletengine), dnoise_engine(parent.dnoise_engine), + wnoise_engine(parent.wnoise_engine), + shapingwavelet(parent.shapingwavelet), psnoise(parent.psnoise), + psnoise_data(parent.psnoise_data), pssignal(parent.pssignal), + pswavelet(parent.pswavelet), decondata(parent.decondata), + wavelet(parent.wavelet), wavelet_taper(parent.wavelet_taper), + data_taper(parent.data_taper), ao_fft(parent.ao_fft), + wavelet_bwd(parent.wavelet_bwd), signal_bwd(parent.signal_bwd), + wavelet_snr(parent.wavelet_snr) { - algorithm=parent.algorithm; - taper_data=parent.taper_data; - operator_dt=parent.operator_dt; - winlength=parent.winlength; - damp=parent.damp; - noise_floor=parent.noise_floor; - snr_regularization_floor=parent.snr_regularization_floor; - snr_bandwidth=parent.snr_bandwidth; - band_snr_floor=parent.band_snr_floor; - regularization_bandwidth_fraction=parent.regularization_bandwidth_fraction; - decon_bandwidth_cutoff=parent.decon_bandwidth_cutoff; - fhs=parent.fhs; - for(int k=0;k<3;++k) - { - signal_bandwidth_fraction[k]=parent.signal_bandwidth_fraction[k]; - peak_snr[k]=parent.peak_snr[k]; + algorithm = parent.algorithm; + taper_data = parent.taper_data; + operator_dt = parent.operator_dt; + winlength = parent.winlength; + damp = parent.damp; + noise_floor = parent.noise_floor; + snr_regularization_floor = parent.snr_regularization_floor; + snr_bandwidth = parent.snr_bandwidth; + band_snr_floor = parent.band_snr_floor; + regularization_bandwidth_fraction = parent.regularization_bandwidth_fraction; + decon_bandwidth_cutoff = parent.decon_bandwidth_cutoff; + fhs = parent.fhs; + for (int k = 0; k < 3; ++k) { + signal_bandwidth_fraction[k] = parent.signal_bandwidth_fraction[k]; + peak_snr[k] = parent.peak_snr[k]; } } -CNR3CDecon& CNR3CDecon::operator=(const CNR3CDecon& parent) -{ - if(this!=(&parent)) - { - algorithm=parent.algorithm; - processing_window=parent.processing_window; - noise_window=parent.noise_window; - signalengine=parent.signalengine; - waveletengine=parent.waveletengine; - dnoise_engine=parent.dnoise_engine; - wnoise_engine=parent.wnoise_engine; - psnoise=parent.psnoise; - psnoise_data=parent.psnoise_data; - pssignal=parent.pssignal; - pswavelet=parent.pswavelet; - decondata=parent.decondata; - wavelet=parent.wavelet; - shapingwavelet=parent.shapingwavelet; - ao_fft=parent.ao_fft; - wavelet_snr=parent.wavelet_snr; - taper_data=parent.taper_data; - operator_dt=parent.operator_dt; - winlength=parent.winlength; - damp=parent.damp; - noise_floor=parent.noise_floor; - snr_regularization_floor=parent.snr_regularization_floor; - snr_bandwidth=parent.snr_bandwidth; - band_snr_floor=parent.band_snr_floor; - regularization_bandwidth_fraction=parent.regularization_bandwidth_fraction; - decon_bandwidth_cutoff=parent.decon_bandwidth_cutoff; - fhs=parent.fhs; - wavelet_bwd=parent.wavelet_bwd; - signal_bwd=parent.signal_bwd; - for(int k=0;k<3;++k) - { - signal_bandwidth_fraction[k]=parent.signal_bandwidth_fraction[k]; - peak_snr[k]=parent.peak_snr[k]; +CNR3CDecon &CNR3CDecon::operator=(const CNR3CDecon &parent) { + if (this != (&parent)) { + algorithm = parent.algorithm; + processing_window = parent.processing_window; + noise_window = parent.noise_window; + signalengine = parent.signalengine; + waveletengine = parent.waveletengine; + dnoise_engine = parent.dnoise_engine; + wnoise_engine = parent.wnoise_engine; + psnoise = parent.psnoise; + psnoise_data = parent.psnoise_data; + pssignal = parent.pssignal; + pswavelet = parent.pswavelet; + decondata = parent.decondata; + wavelet = parent.wavelet; + shapingwavelet = parent.shapingwavelet; + ao_fft = parent.ao_fft; + wavelet_snr = parent.wavelet_snr; + taper_data = parent.taper_data; + operator_dt = parent.operator_dt; + winlength = parent.winlength; + damp = parent.damp; + noise_floor = parent.noise_floor; + snr_regularization_floor = parent.snr_regularization_floor; + snr_bandwidth = parent.snr_bandwidth; + band_snr_floor = parent.band_snr_floor; + regularization_bandwidth_fraction = + parent.regularization_bandwidth_fraction; + decon_bandwidth_cutoff = parent.decon_bandwidth_cutoff; + fhs = parent.fhs; + wavelet_bwd = parent.wavelet_bwd; + signal_bwd = parent.signal_bwd; + for (int k = 0; k < 3; ++k) { + signal_bandwidth_fraction[k] = parent.signal_bandwidth_fraction[k]; + peak_snr[k] = parent.peak_snr[k]; } - wavelet_taper=parent.wavelet_taper; - data_taper=parent.data_taper; - + wavelet_taper = parent.wavelet_taper; + data_taper = parent.data_taper; } return *this; } -CNR3CDecon::~CNR3CDecon() -{ - //if(wavelet_taper!=NULL) delete wavelet_taper; - //if(data_taper!=NULL) delete data_taper; +CNR3CDecon::~CNR3CDecon() { + // if(wavelet_taper!=NULL) delete wavelet_taper; + // if(data_taper!=NULL) delete data_taper; } /* Small helper to test for common possible input data issues. If return is nonzero errors were encountered. Can be retrieved from elog of d */ -int CNR3CDecon::TestSeismogramInput(Seismogram& d,const int wcomp,const bool loadnoise) -{ +int CNR3CDecon::TestSeismogramInput(Seismogram &d, const int wcomp, + const bool loadnoise) { /* Fractional error allowed in sample interval */ const double DTSKEW(0.0001); const string base_error("TestSeismogramInput: "); int error_count(0); - if(d.time_is_UTC()) - { + if (d.time_is_UTC()) { stringstream ss; - ss<2) && (wcomp!=-9999)) - { + if ((wcomp < 0 || wcomp > 2) && (wcomp != -9999)) { stringstream ss; - ss<DTSKEW) - { + if ((abs(d.dt() - operator_dt) / operator_dt) > DTSKEW) { stringstream ss; - ss<processing_window.startprocessing_window.end>d.endtime()) - { + if (this->processing_window.start < d.t0() || + this->processing_window.end > d.endtime()) { stringstream ss; - ss<processing_window.start - << " to " <processing_window.start<<" is not inside data range"<processing_window.start + << " to " << this->processing_window.start << " is not inside data range" + << endl; + d.elog.log_error("CNR3CDecon", ss.str(), ErrorSeverity::Invalid); ++error_count; } - if(loadnoise) - { - if(this->noise_window.startnoise_window.end>d.endtime()) - { + if (loadnoise) { + if (this->noise_window.start < d.t0() || + this->noise_window.end > d.endtime()) { stringstream ss; - ss<noise_window.start<noise_window.start<<" is not inside data range"<noise_window.start << d.t0() + << " to " << this->noise_window.start << " is not inside data range" + << endl; - d.elog.log_error("CNR3CDecon",ss.str(),ErrorSeverity::Invalid); + d.elog.log_error("CNR3CDecon", ss.str(), ErrorSeverity::Invalid); ++error_count; } } @@ -371,52 +343,55 @@ int CNR3CDecon::TestSeismogramInput(Seismogram& d,const int wcomp,const bool loa /* this method is really just a wrapper for the Seismogram, boolean overloaded function. It is appropriate only for conventional rf estimates where the vertical/longitudinal defines the wavelet. */ -void CNR3CDecon::loaddata(Seismogram& d, const int wcomp,const bool loadnoise) -{ -//DEBUG -//cout << "entering loaddata"<loaddata(d,loadnoise); + this->loaddata(d, loadnoise); /* We need to pull wcomp now because we alter the decondata matrix with * padding next. We don't want that for the wavelet at this stage as * the loadwavelet method handles the padding stuff and we call it after * windowing*/ - CoreTimeSeries wtmp(ExtractComponent(decondata,wcomp)); - wtmp=WindowData(wtmp,this->processing_window); - TimeSeries wtmp2(wtmp,"Invalid"); + CoreTimeSeries wtmp(ExtractComponent(decondata, wcomp)); + wtmp = WindowData(wtmp, this->processing_window); + TimeSeries wtmp2(wtmp, "Invalid"); this->loadwavelet(wtmp2); - }catch(...){throw;}; + } catch (...) { + throw; + }; } -void CNR3CDecon::loaddata(Seismogram& d,const bool nload) -{ - if(d.dead()) throw MsPASSError("CNR3CDecon::loaddata method received data marked dead", - ErrorSeverity::Invalid); - try{ +void CNR3CDecon::loaddata(Seismogram &d, const bool nload) { + if (d.dead()) + throw MsPASSError("CNR3CDecon::loaddata method received data marked dead", + ErrorSeverity::Invalid); + try { int errcount; /* The -9999 is a magic number used to signal the test is coming from this variant*/ - errcount=TestSeismogramInput(d,-9999,nload); - if(errcount>0) - { + errcount = TestSeismogramInput(d, -9999, nload); + if (errcount > 0) { stringstream ss; - ss<<"CNR3CDecon::loaddata: "<processing_window)); - this->decondata=Seismogram(dtmp,string("invalid")); - if(FFTDeconOperator::nfft<(2*this->winlength)) - { - cerr << "CNR3CDecon: coding error in loaddata method"<winlength<processing_window)); + this->decondata = Seismogram(dtmp, string("invalid")); + if (FFTDeconOperator::nfft < (2 * this->winlength)) { + cerr << "CNR3CDecon: coding error in loaddata method" << endl + << "fft buffer size=" << FFTDeconOperator::nfft << endl + << "winlength is only " << this->winlength << endl + << "fft length is required to be at least twice winlength" << endl + << "Debug exit to avoid seg fault" << endl; exit(-1); } /* In the old api this was necessary - no longer so because set_npts does @@ -425,15 +400,14 @@ void CNR3CDecon::loaddata(Seismogram& d,const bool nload) decondata.u.zero(); */ decondata.set_npts(FFTDeconOperator::nfft); - double newt0= (-operator_dt*static_cast(winlength)); + double newt0 = (-operator_dt * static_cast(winlength)); decondata.set_t0(newt0); - int k,i,ii; - for(i=0;i=0) && (ii= 0) && (ii < decondata.npts())) { + for (k = 0; k < 3; ++k) + decondata.u(k, ii) = dtmp.u(k, i); } } @@ -454,62 +428,67 @@ void CNR3CDecon::loaddata(Seismogram& d,const bool nload) to compute the bandwidth of the shaping wavelet in process. This is the ONLY place right now this is computed. Caution in order if there are changes in implementation later. */ - this->pssignal=this->ThreeCPower(dtmp); - if(nload) - { - Seismogram ntmp(WindowData(d,this->noise_window),string("Invalid")); + this->pssignal = this->ThreeCPower(dtmp); + if (nload) { + Seismogram ntmp(WindowData(d, this->noise_window), string("Invalid")); this->loadnoise_data(ntmp); } - signal_bwd=EstimateBandwidth(FFTDeconOperator::df(this->operator_dt), - pssignal,psnoise_data,snr_bandwidth, - signalengine.time_bandwidth_product(),this->fhs); - }catch(...){throw;}; + signal_bwd = EstimateBandwidth( + FFTDeconOperator::df(this->operator_dt), pssignal, psnoise_data, + snr_bandwidth, signalengine.time_bandwidth_product(), this->fhs); + } catch (...) { + throw; + }; } /* Note we intentionally do not trap nfft size mismatch in this function because - * we assume loadwavelet would be called within loaddata or after calls to loaddata + * we assume loadwavelet would be called within loaddata or after calls to + * loaddata * */ -void CNR3CDecon::loadwavelet(const TimeSeries& w) -{ - if(w.dead()) throw MsPASSError("CNR3CDecon::loadwavelet method received data marked dead", - ErrorSeverity::Invalid); - if(w.npts()<=0) throw MsPASSError("CNR3CDecon::loadwavelet method received an empty TimeSeries (number samples <=0)", - ErrorSeverity::Invalid); - try{ +void CNR3CDecon::loadwavelet(const TimeSeries &w) { + if (w.dead()) + throw MsPASSError( + "CNR3CDecon::loadwavelet method received data marked dead", + ErrorSeverity::Invalid); + if (w.npts() <= 0) + throw MsPASSError("CNR3CDecon::loadwavelet method received an empty " + "TimeSeries (number samples <=0)", + ErrorSeverity::Invalid); + try { /* Automatically and silently adjust the window size if input wavelet length changes. We always compute the wavelet spectrum from the full signal loaded. */ - if(this->waveletengine.taper_length()!=(w.npts())) - { - this->waveletengine=MTPowerSpectrumEngine(w.npts(), - this->waveletengine.time_bandwidth_product(), + if (this->waveletengine.taper_length() != (w.npts())) { + this->waveletengine = MTPowerSpectrumEngine( + w.npts(), this->waveletengine.time_bandwidth_product(), this->waveletengine.number_tapers()); } - this->pswavelet=this->waveletengine.apply(w); + this->pswavelet = this->waveletengine.apply(w); /* for now use the same snr floor as regularization - may need to be an independent parameter */ - this->wavelet_bwd=EstimateBandwidth(FFTDeconOperator::df(this->operator_dt),pswavelet, - psnoise,snr_bandwidth,waveletengine.time_bandwidth_product(),this->fhs); + this->wavelet_bwd = EstimateBandwidth( + FFTDeconOperator::df(this->operator_dt), pswavelet, psnoise, + snr_bandwidth, waveletengine.time_bandwidth_product(), this->fhs); /* Now load the wavelet into the fft buffer area - there are some tricky things to do here to align the data correctly using the relative time reference t0 stored with the TimeSeries data */ - int k,kk; - int ns_to_copy,ntest; - this->wavelet=w; - ntest=w.npts()-w.sample_number(0.0); - if(ntest>(this->winlength)) - { - ns_to_copy=this->winlength; + int k, kk; + int ns_to_copy, ntest; + this->wavelet = w; + ntest = w.npts() - w.sample_number(0.0); + if (ntest > (this->winlength)) { + ns_to_copy = this->winlength; stringstream ss; - ss<<"loadwavelet method: size mismatch. Wavelet received has "<0.0"<winlength<0.0" << endl + << "This is larger than the processing window length of " + << this->winlength << endl + << "Wavelet must be contained in the processing window size" << endl + << "Truncated on the right to processing window length - results may " + "be invalid" + << endl; + wavelet.elog.log_error("CNR3CDecon", ss.str(), ErrorSeverity::Complaint); + } else + ns_to_copy = w.npts(); /* These were in the old api but are no longer needed because set_npts does this operation this->wavelet.s.clear(); @@ -517,210 +496,222 @@ void CNR3CDecon::loadwavelet(const TimeSeries& w) for(k=0;kwavelet.s.push_back(0.0); */ this->wavelet.set_npts(FFTDeconOperator::nfft); - this->wavelet.set_t0(-(this->winlength)*(this->operator_dt)); -//DEBUG -//cout << "t0 of wavelet stored now wavelet data objec="<wavelet.set_t0(-(this->winlength) * (this->operator_dt)); + // DEBUG + // cout << "t0 of wavelet stored now wavelet data + // objec="<=0.0)++ntest; - if(ntest>ns_to_copy) break; - kk=this->wavelet.sample_number(t); - if( (kk>=0) && (kkwavelet.npts()) ) - this->wavelet.s[kk]=w.s[k]; + for (k = 0, ntest = 0; k < w.npts(); ++k) { + double t = w.time(k); + if (t >= 0.0) + ++ntest; + if (ntest > ns_to_copy) + break; + kk = this->wavelet.sample_number(t); + if ((kk >= 0) && (kk < this->wavelet.npts())) + this->wavelet.s[kk] = w.s[k]; } - /* This was the old code fixed Dec 2020 - remove when revision is know to work + /* This was the old code fixed Dec 2020 - remove when revision is know to + work for(k=0,kk=this->winlength;kwavelet.s[kk]=w.s[k]; */ - //this->wavelet.t0 -= operator_dt*static_cast(winlength); - //this->wavelet.ns=FFTDeconOperator::nfft; - //double newt0; - //newt0=this->wavelet.t0() - operator_dt*static_cast(winlength); - //this->wavelet.set_t0(newt0); - + // this->wavelet.t0 -= operator_dt*static_cast(winlength); + // this->wavelet.ns=FFTDeconOperator::nfft; + // double newt0; + // newt0=this->wavelet.t0() - operator_dt*static_cast(winlength); + // this->wavelet.set_t0(newt0); - //debug + // debug /*cout << "Wavelet t, data"<psnoise_data=this->ThreeCPower(n); - }catch(...){throw;}; +void CNR3CDecon::loadnoise_data(const Seismogram &n) { + if (n.dead()) + throw MsPASSError( + "CNR3CDecon::loadnoise_data method received data marked dead", + ErrorSeverity::Invalid); + try { + this->psnoise_data = this->ThreeCPower(n); + } catch (...) { + throw; + }; } -void CNR3CDecon::loadnoise_data(const PowerSpectrum& d) -{ - try{ - psnoise_data=d; - }catch(...){throw;}; +void CNR3CDecon::loadnoise_data(const PowerSpectrum &d) { + try { + psnoise_data = d; + } catch (...) { + throw; + }; } -void CNR3CDecon::loadnoise_wavelet(const TimeSeries& n) -{ -//DEBUG -//cout << "Entering loadnoise_wavelet"<wnoise_engine.apply(n); - }catch(...){throw;}; + psnoise = this->wnoise_engine.apply(n); + } catch (...) { + throw; + }; } -void CNR3CDecon::loadnoise_wavelet(const PowerSpectrum& d) -{ - try{ - psnoise=d; - }catch(...){throw;}; +void CNR3CDecon::loadnoise_wavelet(const PowerSpectrum &d) { + try { + psnoise = d; + } catch (...) { + throw; + }; } /* Note this is intentionally not a reference to assure this is a copy */ -void CNR3CDecon::compute_gwl_inverse() -{ - try{ - if(taper_data) wavelet_taper->apply(this->wavelet); - if(this->wavelet.npts() != FFTDeconOperator::nfft) - { - throw MsPASSError("CNR3CDecon::compute_gwl_inverse(): wavelet size and fft size t0 not match - this should not happen and indicates a bug that needs to be fixed", - ErrorSeverity::Fatal); +void CNR3CDecon::compute_gwl_inverse() { + try { + if (taper_data) + wavelet_taper->apply(this->wavelet); + if (this->wavelet.npts() != FFTDeconOperator::nfft) { + throw MsPASSError("CNR3CDecon::compute_gwl_inverse(): wavelet size and " + "fft size t0 not match - this should not happen and " + "indicates a bug that needs to be fixed", + ErrorSeverity::Fatal); } - ComplexArray cwvec(this->wavelet.npts(),&(this->wavelet.s[0])); - gsl_fft_complex_forward(cwvec.ptr(),1,FFTDeconOperator::nfft, - wavetable,workspace); + ComplexArray cwvec(this->wavelet.npts(), &(this->wavelet.s[0])); + gsl_fft_complex_forward(cwvec.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); /* This computes the (regularized) denominator for the decon operator*/ - double df,fNy; - df=1.0/(operator_dt*static_cast(FFTDeconOperator::nfft)); - fNy=df*static_cast(FFTDeconOperator::nfft/2); + double df, fNy; + df = 1.0 / (operator_dt * static_cast(FFTDeconOperator::nfft)); + fNy = df * static_cast(FFTDeconOperator::nfft / 2); /* We need largest noise amplitude to establish a relative noise floor. We use this std::algorithm to find it in the spectrum vector */ vector::iterator maxnoise; - maxnoise=max_element(psnoise.spectrum.begin(),psnoise.spectrum.end()); - //spectrum is power, we need amplitude so sqrt here - double scaled_noise_floor=noise_floor*sqrt(*maxnoise); + maxnoise = max_element(psnoise.spectrum.begin(), psnoise.spectrum.end()); + // spectrum is power, we need amplitude so sqrt here + double scaled_noise_floor = noise_floor * sqrt(*maxnoise); wavelet_snr.clear(); int nreg(0); - for(int j=0;j(j); - if(f>fNy) f=2.0*fNy-f; // Fold frequency axis - double namp=sqrt(psnoise.power(f)); + f = df * static_cast(j); + if (f > fNy) + f = 2.0 * fNy - f; // Fold frequency axis + double namp = sqrt(psnoise.power(f)); /* Avoid divide by zero that could randomly happen with simulation data*/ double snr; - if((namp/amp)(nreg) - / static_cast(FFTDeconOperator::nfft); - double *d0=new double[FFTDeconOperator::nfft]; - for(int k=0;k(nreg) / static_cast(FFTDeconOperator::nfft); + double *d0 = new double[FFTDeconOperator::nfft]; + for (int k = 0; k < FFTDeconOperator::nfft; ++k) + d0[k] = 0.0; + d0[0] = 1.0; + ComplexArray delta0(FFTDeconOperator::nfft, d0); + delete[] d0; + gsl_fft_complex_forward(delta0.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); + winv = delta0 / cwvec; + } catch (...) { + throw; + }; } /* Note this is intentionally not a reference to assure this is a copy */ -void CNR3CDecon::compute_gdamp_inverse() -{ - try{ - if(taper_data) wavelet_taper->apply(this->wavelet); +void CNR3CDecon::compute_gdamp_inverse() { + try { + if (taper_data) + wavelet_taper->apply(this->wavelet); /* Assume if we got here wavelet.npts() == nfft*/ - ComplexArray b_fft(this->wavelet.npts(),&(this->wavelet.s[0])); - gsl_fft_complex_forward(b_fft.ptr(),1,FFTDeconOperator::nfft, - wavetable,workspace); + ComplexArray b_fft(this->wavelet.npts(), &(this->wavelet.s[0])); + gsl_fft_complex_forward(b_fft.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); ComplexArray conj_b_fft(b_fft); conj_b_fft.conj(); - ComplexArray denom(conj_b_fft*b_fft); + ComplexArray denom(conj_b_fft * b_fft); /* Compute scaling constants for noise based on noise_floor and the noise spectrum */ - double df,fNy; - df=1.0/(operator_dt*static_cast(FFTDeconOperator::nfft)); - fNy=df*static_cast(FFTDeconOperator::nfft/2); + double df, fNy; + df = 1.0 / (operator_dt * static_cast(FFTDeconOperator::nfft)); + fNy = df * static_cast(FFTDeconOperator::nfft / 2); /* We need largest noise amplitude to establish a relative noise floor. We use this std::algorithm to find it in the spectrum vector */ vector::iterator maxnoise; - maxnoise=max_element(psnoise.spectrum.begin(),psnoise.spectrum.end()); - //Spectrum is power but need amplitude in this context so sqrt here - double scaled_noise_floor=noise_floor*sqrt(*maxnoise); - //debug - //cout << "Damping values used with f"<(k); - if(f>fNy) f=2.0*fNy-f; // Fold frequency axis - double namp=sqrt(psnoise.power(f)); + f = df * static_cast(k); + if (f > fNy) + f = 2.0 * fNy - f; // Fold frequency axis + double namp = sqrt(psnoise.power(f)); double theta; - if(namp>scaled_noise_floor) - { - theta=damp*namp; - } - else - { - theta=damp*scaled_noise_floor; + if (namp > scaled_noise_floor) { + theta = damp * namp; + } else { + theta = damp * scaled_noise_floor; } /* This uses a normal equation form so theta must be squared to be a form of the standard damped least squares inverse */ - theta=theta*theta; + theta = theta * theta; /* ptr points to the real part - an oddity of this interface */ - //Debug test - make pure damping + // Debug test - make pure damping *ptr += theta; - //Debug - //cout << f<<" "< b2.high_edge_f) - { + if (b1.high_edge_f > b2.high_edge_f) { overlap.high_edge_f = b2.high_edge_f; overlap.high_edge_snr = b2.high_edge_snr; - } - else - { + } else { overlap.high_edge_f = b1.high_edge_f; overlap.high_edge_snr = b1.high_edge_snr; } - if(b2.f_range<=b1.f_range) - overlap.f_range=b2.f_range; + if (b2.f_range <= b1.f_range) + overlap.f_range = b2.f_range; else - overlap.f_range=b1.f_range; + overlap.f_range = b1.f_range; return overlap; } /* another helper used below - posts bandwidth data to Metadata. Put in a function to make sure it all is in one place */ -void post_bandwidth_data(Seismogram& d,const BandwidthData& bwd) -{ - d.put("CNR3CDecon_low_corner",bwd.low_edge_f); - d.put("CNR3CDecon_high_corner",bwd.high_edge_f); - d.put("CNR3CDecon_bandwidth",bwd.bandwidth()); - d.put("CNR3CDecon_low_f_snr",bwd.low_edge_snr); - d.put("CNR3CDecon_high_f_snr",bwd.high_edge_snr); +void post_bandwidth_data(Seismogram &d, const BandwidthData &bwd) { + d.put("CNR3CDecon_low_corner", bwd.low_edge_f); + d.put("CNR3CDecon_high_corner", bwd.high_edge_f); + d.put("CNR3CDecon_bandwidth", bwd.bandwidth()); + d.put("CNR3CDecon_low_f_snr", bwd.low_edge_snr); + d.put("CNR3CDecon_high_f_snr", bwd.high_edge_snr); } /* DEBUG Temporary for debug - remove or comment out for release */ -void print_bwdata(const BandwidthData& bwd) -{ - cout <<"low edge frequency="<decon_bandwidth_cutoff)) - { + if (bo.bandwidth() < (this->decon_bandwidth_cutoff)) { /* this is a bit inefficient to create this work space and immediately destroy it but will do it this way until this proves to be a performance problem. */ Seismogram no_can_do(decondata); - no_can_do.u=mspass::utility::dmatrix(1,1); + no_can_do.u = mspass::utility::dmatrix(1, 1); /* Be sure this is marked dead */ no_can_do.kill(); stringstream ss; - ss << "Killed because estimated bandwidth="<decon_bandwidth_cutoff<decon_bandwidth_cutoff + << endl; + no_can_do.elog.log_error("CNR3CDecon", ss.str(), ErrorSeverity::Invalid); return no_can_do; } this->update_shaping_wavelet(bo); Seismogram rfest(decondata); - post_bandwidth_data(rfest,bo); + post_bandwidth_data(rfest, bo); /* This is used to apply a shift to the fft outputs to put signals at relative time 0 */ int t0_shift; - t0_shift= round((-rfest.t0())/rfest.dt()); - //DEBUG - //cout << "time shift applied to output signal="<(FFTDeconOperator::nfft)); - for(k=0;k<3;++k) - { + df = 1.0 / (operator_dt * static_cast(FFTDeconOperator::nfft)); + for (k = 0; k < 3; ++k) { TimeSeries work; - work=TimeSeries(ExtractComponent(decondata,k),"Invalid"); + work = TimeSeries(ExtractComponent(decondata, k), "Invalid"); /* Debug - temporarily remove taper if(taper_data) data_taper->apply(work); */ wvec.clear(); - int ntocopy=FFTDeconOperator::nfft; - if(ntocopy>work.npts()) ntocopy=work.npts(); - for(j=0;j work.npts()) + ntocopy = work.npts(); + for (j = 0; j < ntocopy; ++j) + wvec.push_back(work.s[j]); + for (j = ntocopy; j < FFTDeconOperator::nfft; ++j) + wvec.push_back(0.0); - ComplexArray numerator(FFTDeconOperator::nfft,&(wvec[0])); - //Debug - //cout << "numerator data after taper for component="<(j); - Complex64 z=numerator[j]; - double sigamp=abs(z); - double namp=sqrt(psnoise.power(f)); - double snr=sigamp/namp; - //Debug - //cout <(j); + Complex64 z = numerator[j]; + double sigamp = abs(z); + double namp = sqrt(psnoise.power(f)); + double snr = sigamp / namp; + // Debug + // cout <snrmax) snrmax=snr; - if(snr>band_snr_floor) ++nhighsnr; + if (snr > snrmax) + snrmax = snr; + if (snr > band_snr_floor) + ++nhighsnr; } - signal_bandwidth_fraction[k]=static_cast(nhighsnr) - / static_cast(FFTDeconOperator::nfft/2); - peak_snr[k]=snrmax; - ComplexArray rftmp=numerator*winv; - rftmp=(*shapingwavelet.wavelet())*rftmp; - gsl_fft_complex_inverse(rftmp.ptr(), 1, FFTDeconOperator::nfft, - wavetable, workspace); + signal_bandwidth_fraction[k] = + static_cast(nhighsnr) / + static_cast(FFTDeconOperator::nfft / 2); + peak_snr[k] = snrmax; + ComplexArray rftmp = numerator * winv; + rftmp = (*shapingwavelet.wavelet()) * rftmp; + gsl_fft_complex_inverse(rftmp.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); wvec.clear(); - for(j=0;jshapingwavelet.impulse_response(); + return TimeSeries(ideal_tmp, "Invalid"); + } catch (...) { + throw; + }; } -TimeSeries CNR3CDecon::actual_output() -{ +TimeSeries CNR3CDecon::actual_output() { try { - ComplexArray W(FFTDeconOperator::nfft,&(wavelet.s[0])); - gsl_fft_complex_forward(W.ptr(),1,FFTDeconOperator::nfft,wavetable,workspace); - ComplexArray ao_fft; - ao_fft=winv*W; - /* We always apply the shaping wavelet - this perhaps should be optional - but probably better done with a none option for the shaping wavelet */ - ao_fft=(*shapingwavelet.wavelet())*ao_fft; - gsl_fft_complex_inverse(ao_fft.ptr(),1,FFTDeconOperator::nfft,wavetable,workspace); - vector ao; - ao.reserve(FFTDeconOperator::nfft); - for(int k=0; koperator_dt); - result.set_tref(TimeReferenceType::Relative); - /* set_npts always initializes the s buffer so it is more efficient to - copy ao elements rather than what was here before: - result.s=ao; - */ - for(int k=0;k ao; + ao.reserve(FFTDeconOperator::nfft); + for (int k = 0; k < ao_fft.size(); ++k) + ao.push_back(ao_fft[k].real()); + /* We always shift this wavelet to the center of the data vector. + We handle the time through the CoreTimeSeries object. */ + int i0 = FFTDeconOperator::nfft / 2; + ao = circular_shift(ao, i0); + TimeSeries result( + wavelet); // Use this to clone metadata and elog from wavelet + result.set_npts(FFTDeconOperator::nfft); + /* Force these even though they are likely already defined as + in the parent wavelet TimeSeries. */ + result.set_live(); + // result.s=ao; + result.set_t0(operator_dt * (-(double)i0)); + result.set_dt(this->operator_dt); + result.set_tref(TimeReferenceType::Relative); + /* set_npts always initializes the s buffer so it is more efficient to + copy ao elements rather than what was here before: + result.s=ao; + */ + for (int k = 0; k < FFTDeconOperator::nfft; ++k) + result.s[k] = ao[k]; + return result; + } catch (...) { + throw; }; } -TimeSeries CNR3CDecon::inverse_wavelet(double tshift0) -{ +TimeSeries CNR3CDecon::inverse_wavelet(double tshift0) { try { /* Using the time shift of wavelet.t0() may be a bad idea here. Will need to sort that out in debugging behaviour*/ double timeshift(tshift0); - timeshift+=wavelet.t0(); - //cout << "wavelet t0="<operator_dt,FFTDeconOperator::nfft); - }else if(wtype=="ricker") - { - double favg=(bwd.high_edge_f-bwd.low_edge_f)/2.0; - shapingwavelet=ShapingWavelet(favg,operator_dt,FFTDeconOperator::nfft); - }else - { + shapingwavelet = ShapingWavelet(2, bwd.low_edge_f, 2, bwd.high_edge_f, + this->operator_dt, FFTDeconOperator::nfft); + } else if (wtype == "ricker") { + double favg = (bwd.high_edge_f - bwd.low_edge_f) / 2.0; + shapingwavelet = ShapingWavelet(favg, operator_dt, FFTDeconOperator::nfft); + } else { /* this really shouldn't happen but trap it anyway for completeness. Because it shouldn't happen we set the severity fatal*/ - throw MsPASSError(string("CNR3CDecon::update_shaping_wavelet: ") - + "shaping wavelet has unsupported type defined="+wtype, - ErrorSeverity::Fatal); + throw MsPASSError( + string("CNR3CDecon::update_shaping_wavelet: ") + + "shaping wavelet has unsupported type defined=" + wtype, + ErrorSeverity::Fatal); } } /* Note this private function should only be called for noise data */ -PowerSpectrum CNR3CDecon::ThreeCPower(const Seismogram& d) -{ - try{ +PowerSpectrum CNR3CDecon::ThreeCPower(const Seismogram &d) { + try { PowerSpectrum avg3c; TimeSeries tswork; - if(d.npts()!=dnoise_engine.taper_length()) - { - dnoise_engine=MTPowerSpectrumEngine(d.npts(), - dnoise_engine.time_bandwidth_product(),dnoise_engine.number_tapers()); + if (d.npts() != dnoise_engine.taper_length()) { + dnoise_engine = MTPowerSpectrumEngine( + d.npts(), dnoise_engine.time_bandwidth_product(), + dnoise_engine.number_tapers()); } - for(int k=0;k<3;++k) - { - tswork=TimeSeries(ExtractComponent(d,k),"Invalid"); - if(k==0) + for (int k = 0; k < 3; ++k) { + tswork = TimeSeries(ExtractComponent(d, k), "Invalid"); + if (k == 0) avg3c = this->dnoise_engine.apply(tswork); else avg3c += this->dnoise_engine.apply(tswork); } /* We define total power as the average on all three components */ - double scl=1.0/3.0; - for(int i=0;i -#include -#include "mspass/utility/MsPASSError.h" +#include "mspass/algorithms/deconvolution/CNRDeconEngine.h" #include "mspass/algorithms/algorithms.h" #include "mspass/algorithms/amplitudes.h" -#include "mspass/algorithms/deconvolution/CNRDeconEngine.h" - +#include "mspass/utility/MsPASSError.h" +#include +#include -namespace mspass::algorithms::deconvolution -{ +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::utility; using namespace mspass::seismic; using namespace mspass::algorithms::deconvolution; using mspass::algorithms::amplitudes::normalize; -CNRDeconEngine::CNRDeconEngine() : FFTDeconOperator() -{ +CNRDeconEngine::CNRDeconEngine() : FFTDeconOperator() { /* This constructor does not initialize everything. It initializes only the simple types and the values are not necessarily reasonable. */ algorithm = CNR3C_algorithms::colored_noise_damping; - damp=1.0; + damp = 1.0; noise_floor = 1.5; band_snr_floor = 1.5; - shaping_wavelet_number_poles=3; + shaping_wavelet_number_poles = 3; snr_regularization_floor = 2.0; /* These are computed private variables - we initialize them all to 0*/ regularization_bandwidth_fraction = 0.0; - for(auto i=0;i<3;++i) - { + for (auto i = 0; i < 3; ++i) { peak_snr[i] = 0.0; signal_bandwidth_fraction[i] = 0.0; } } -CNRDeconEngine::CNRDeconEngine(const AntelopePf& pf) - : FFTDeconOperator(dynamic_cast(pf)) -{ - try{ +CNRDeconEngine::CNRDeconEngine(const AntelopePf &pf) + : FFTDeconOperator(dynamic_cast(pf)) { + try { string stmp; - stmp=pf.get_string("algorithm"); - if(stmp=="generalized_water_level") - { - this->algorithm=CNR3C_algorithms::generalized_water_level; + stmp = pf.get_string("algorithm"); + if (stmp == "generalized_water_level") { + this->algorithm = CNR3C_algorithms::generalized_water_level; + } else if (stmp == "colored_noise_damping") { + this->algorithm = CNR3C_algorithms::colored_noise_damping; + } else { + throw MsPASSError("CNRDeconEngine(constructor): invalid value for " + "parameter algorithm=" + + stmp, + ErrorSeverity::Invalid); } - else if(stmp=="colored_noise_damping") - { - this->algorithm=CNR3C_algorithms::colored_noise_damping; - } - else - { - throw MsPASSError("CNRDeconEngine(constructor): invalid value for parameter algorithm=" - + stmp,ErrorSeverity::Invalid); - } - this->damp=pf.get_double("damping_factor"); + this->damp = pf.get_double("damping_factor"); /* Note this paramter is used for both the damping method and the generalized_water_level */ - this->noise_floor=pf.get_double("noise_floor"); - this->snr_regularization_floor=pf.get_double("snr_regularization_floor"); + this->noise_floor = pf.get_double("noise_floor"); + this->snr_regularization_floor = pf.get_double("snr_regularization_floor"); this->band_snr_floor = pf.get_double("snr_data_bandwidth_floor"); - this->operator_dt=pf.get_double("target_sample_interval"); + this->operator_dt = pf.get_double("target_sample_interval"); /* These parameters are not cached to the object directly but are used to initialize the multitaper engines. A window is used instead of number of samples as it is less error prone to a user than requiring them to compute the number from the sample interval. */ - double ts,te; - ts=pf.get_double("deconvolution_data_window_start"); - te=pf.get_double("deconvolution_data_window_end"); - this->winlength=round((te-ts)/this->operator_dt)+1; + double ts, te; + ts = pf.get_double("deconvolution_data_window_start"); + te = pf.get_double("deconvolution_data_window_end"); + this->winlength = round((te - ts) / this->operator_dt) + 1; /* In this algorithm we are very careful to avoid circular convolution artifacts that I (glp) suspect may be a problem in some frequency domain implementations of rf deconvolution. Here we set the length of the fft (nfft) to a minimum of 3 times the window size. That allows 1 window of padding around both ends of the waveform being deconvolved. Circular shift is used to put the result back in a rational time base. */ - int minwinsize=3*(this->winlength); + int minwinsize = 3 * (this->winlength); /* This complicated set of tests to set nfft is needed to mesh with - * ShapingWavelet constructor and FFTDeconOperator api constraints created by - * use in other classes in this directory that also use these */ - int nfftneeded=nextPowerOf2(minwinsize); + * ShapingWavelet constructor and FFTDeconOperator api constraints created + * by use in other classes in this directory that also use these */ + int nfftneeded = nextPowerOf2(minwinsize); /* This compication is needed because FFTDeconOperator(pf) is called prior to this function and it requires operator_nfft. We need to be sure it size is consistent with window size that we just computed */ - if(nfftneeded!=this->get_size()) - { + if (nfftneeded != this->get_size()) { FFTDeconOperator::change_size(nfftneeded); Metadata pfcopy(pf); - pfcopy.put("operator_nfft",nfftneeded); - this->shapingwavelet=ShapingWavelet(pfcopy); - } - else - { - this->shapingwavelet=ShapingWavelet(pf); + pfcopy.put("operator_nfft", nfftneeded); + this->shapingwavelet = ShapingWavelet(pfcopy); + } else { + this->shapingwavelet = ShapingWavelet(pf); } /* ShapingWavelet has more options than can be accepted in this algorithm so this test is needed */ - string swname=this->shapingwavelet.type(); - if( !( (swname == "ricker") || (swname == "butterworth") ) ) - { - throw MsPASSError(string("CNRDeconEngine(AntelopePf constructor): ") - + "Cannot use shaping wavelet type="+swname - + "\nMust be either ricker or butterworth for this algorithm", + string swname = this->shapingwavelet.type(); + if (!((swname == "ricker") || (swname == "butterworth"))) { + throw MsPASSError( + string("CNRDeconEngine(AntelopePf constructor): ") + + "Cannot use shaping wavelet type=" + swname + + "\nMust be either ricker or butterworth for this algorithm", ErrorSeverity::Fatal); } - if(swname == "butterworth") - { + if (swname == "butterworth") { /* These MUST be consistent with FFTDeconOperator pf constructor names. */ - int npoles_lo,npoles_hi; - npoles_lo=pf.get_int("npoles_lo"); - npoles_hi=pf.get_int("npoles_hi"); - if(npoles_hi != npoles_lo) - { + int npoles_lo, npoles_hi; + npoles_lo = pf.get_int("npoles_lo"); + npoles_hi = pf.get_int("npoles_hi"); + if (npoles_hi != npoles_lo) { stringstream ss; ss << "CNRDeconEngine(Metadata constructor): " - << "Butterworth filter high and low number of poles must be equal for this operator" + << "Butterworth filter high and low number of poles must be equal " + "for this operator" << endl - << "Found npoles_lo="<shaping_wavelet_number_poles = npoles_lo; } /* As with signal we use this for initializing the noise engine rather than the number of points, which is all the engine cares about. */ - ts=pf.get_double("noise_window_start"); - te=pf.get_double("noise_window_end"); - int noise_winlength=round((te-ts)/operator_dt)+1; - double tbp=pf.get_double("time_bandwidth_product"); - long ntapers=pf.get_long("number_tapers"); - this->noise_engine=MTPowerSpectrumEngine(noise_winlength,tbp,ntapers,noise_winlength,this->operator_dt); - this->signal_engine=MTPowerSpectrumEngine(this->winlength,tbp,ntapers,this->winlength,this->operator_dt); - }catch(...){throw;}; + ts = pf.get_double("noise_window_start"); + te = pf.get_double("noise_window_end"); + int noise_winlength = round((te - ts) / operator_dt) + 1; + double tbp = pf.get_double("time_bandwidth_product"); + long ntapers = pf.get_long("number_tapers"); + this->noise_engine = MTPowerSpectrumEngine( + noise_winlength, tbp, ntapers, noise_winlength, this->operator_dt); + this->signal_engine = MTPowerSpectrumEngine( + this->winlength, tbp, ntapers, this->winlength, this->operator_dt); + } catch (...) { + throw; + }; } /* Standard copy constructor */ -CNRDeconEngine::CNRDeconEngine(const CNRDeconEngine& parent) - : FFTDeconOperator(parent), - shapingwavelet(parent.shapingwavelet), - signal_engine(parent.signal_engine), - noise_engine(parent.noise_engine), - winv(parent.winv) -{ +CNRDeconEngine::CNRDeconEngine(const CNRDeconEngine &parent) + : FFTDeconOperator(parent), shapingwavelet(parent.shapingwavelet), + signal_engine(parent.signal_engine), noise_engine(parent.noise_engine), + winv(parent.winv) { this->algorithm = parent.algorithm; this->damp = parent.damp; this->noise_floor = parent.noise_floor; @@ -146,22 +137,20 @@ CNRDeconEngine::CNRDeconEngine(const CNRDeconEngine& parent) this->winlength = parent.winlength; this->shaping_wavelet_number_poles = parent.shaping_wavelet_number_poles; this->snr_regularization_floor = parent.snr_regularization_floor; - this->regularization_bandwidth_fraction = parent.regularization_bandwidth_fraction; - for(int i=0;i<3;++i) - { + this->regularization_bandwidth_fraction = + parent.regularization_bandwidth_fraction; + for (int i = 0; i < 3; ++i) { this->peak_snr[i] = parent.peak_snr[i]; this->signal_bandwidth_fraction[i] = parent.signal_bandwidth_fraction[i]; } winv_t0_lag = parent.winv_t0_lag; } -CNRDeconEngine& CNRDeconEngine::operator=(const CNRDeconEngine& parent) -{ - if(&parent != this) - { - this->shapingwavelet=parent.shapingwavelet; - this->signal_engine=parent.signal_engine; - this->noise_engine=parent.noise_engine; - this->winv=parent.winv; +CNRDeconEngine &CNRDeconEngine::operator=(const CNRDeconEngine &parent) { + if (&parent != this) { + this->shapingwavelet = parent.shapingwavelet; + this->signal_engine = parent.signal_engine; + this->noise_engine = parent.noise_engine; + this->winv = parent.winv; this->winv_t0_lag = parent.winv_t0_lag; this->algorithm = parent.algorithm; this->damp = parent.damp; @@ -170,124 +159,123 @@ CNRDeconEngine& CNRDeconEngine::operator=(const CNRDeconEngine& parent) this->operator_dt = parent.operator_dt; this->shaping_wavelet_number_poles = parent.shaping_wavelet_number_poles; this->snr_regularization_floor = parent.snr_regularization_floor; - this->regularization_bandwidth_fraction = parent.regularization_bandwidth_fraction; - for(int i=0;i<3;++i) - { + this->regularization_bandwidth_fraction = + parent.regularization_bandwidth_fraction; + for (int i = 0; i < 3; ++i) { this->peak_snr[i] = parent.peak_snr[i]; this->signal_bandwidth_fraction[i] = parent.signal_bandwidth_fraction[i]; } } return *this; } -void CNRDeconEngine::initialize_inverse_operator(const TimeSeries& wavelet, - const TimeSeries& noise_data) -{ +void CNRDeconEngine::initialize_inverse_operator(const TimeSeries &wavelet, + const TimeSeries &noise_data) { const string alg("CNRDeconEngine::initialize_inverse_operator"); - if(wavelet.dead() || noise_data.dead()) - { + if (wavelet.dead() || noise_data.dead()) { string message; message = alg + string(": Received TimeSeries inputs marked dead\n"); - if(wavelet.dead()) message += "wavelet signal input was marked dead\n"; - if(noise_data.dead()) message += "noise data segment was marked dead\n"; - throw MsPASSError(message,ErrorSeverity::Invalid); + if (wavelet.dead()) + message += "wavelet signal input was marked dead\n"; + if (noise_data.dead()) + message += "noise data segment was marked dead\n"; + throw MsPASSError(message, ErrorSeverity::Invalid); } - try{ + try { /* Assume wavelet and noise_data have the correct sample rate. - * python wrapper should guarantee that */ + * python wrapper should guarantee that */ PowerSpectrum psnoise(this->compute_noise_spectrum(noise_data)); - if(psnoise.dead()) - { + if (psnoise.dead()) { string message; - message = alg + string("compute_noise_spectrum method failed - cannot compute inverse opeator"); - throw MsPASSError(message,ErrorSeverity::Invalid); + message = alg + string("compute_noise_spectrum method failed - cannot " + "compute inverse opeator"); + throw MsPASSError(message, ErrorSeverity::Invalid); } - this->initialize_inverse_operator(wavelet,psnoise); - }catch(...){throw;}; + this->initialize_inverse_operator(wavelet, psnoise); + } catch (...) { + throw; + }; } -void CNRDeconEngine::initialize_inverse_operator(const TimeSeries& wavelet, - const PowerSpectrum& noise_spectrum) -{ +void CNRDeconEngine::initialize_inverse_operator( + const TimeSeries &wavelet, const PowerSpectrum &noise_spectrum) { string alg("CNRDeconEngine::initialize_inverse_operator"); - if(wavelet.dead()) - { + if (wavelet.dead()) { string message; message = alg + string("Received wavelet signal marked dead"); - throw MsPASSError(message,ErrorSeverity::Invalid); + throw MsPASSError(message, ErrorSeverity::Invalid); } - if(noise_spectrum.dead()) - { + if (noise_spectrum.dead()) { string message; message = alg + string("Received a PowerSpectrum object marked dead"); - throw MsPASSError(message,ErrorSeverity::Invalid); + throw MsPASSError(message, ErrorSeverity::Invalid); } - try{ - this->compute_winv(wavelet,noise_spectrum); - }catch(...){throw;}; + try { + this->compute_winv(wavelet, noise_spectrum); + } catch (...) { + throw; + }; } -PowerSpectrum CNRDeconEngine::compute_noise_spectrum(const TimeSeries& n) -{ - if(n.dead()) - { +PowerSpectrum CNRDeconEngine::compute_noise_spectrum(const TimeSeries &n) { + if (n.dead()) { PowerSpectrum badout; badout.elog.log_error("CNRDeconEngine:compute_noise", - "Received noise data segment marked dead", - ErrorSeverity::Invalid); + "Received noise data segment marked dead", + ErrorSeverity::Invalid); return badout; } - try{ - if(n.npts()!=noise_engine.taper_length()) - { + try { + if (n.npts() != noise_engine.taper_length()) { /* use this varaint of the construtor too allow the fft size to * be automatically changed if necessary. */ - this->noise_engine=MTPowerSpectrumEngine(n.npts(), - noise_engine.time_bandwidth_product(),noise_engine.number_tapers()); + this->noise_engine = + MTPowerSpectrumEngine(n.npts(), noise_engine.time_bandwidth_product(), + noise_engine.number_tapers()); /* with auto fft we also need this to set the df and dt correctly */ this->noise_engine.set_df(this->operator_dt); } return this->noise_engine.apply(n); - }catch(...){throw;}; + } catch (...) { + throw; + }; } -PowerSpectrum CNRDeconEngine::compute_noise_spectrum(const Seismogram& n) -{ - if(n.dead()) - { +PowerSpectrum CNRDeconEngine::compute_noise_spectrum(const Seismogram &n) { + if (n.dead()) { PowerSpectrum badout; badout.elog.log_error("CNRDeconEngine:compute_noise", - "Received noise data segment marked dead", - ErrorSeverity::Invalid); + "Received noise data segment marked dead", + ErrorSeverity::Invalid); return badout; } - try{ + try { PowerSpectrum avg3c; TimeSeries tswork; - if(n.npts()!=noise_engine.taper_length()) - { + if (n.npts() != noise_engine.taper_length()) { /* this may change fft size and will set dt wrong so we need the * call to set_df afterward to correct that. */ - noise_engine=MTPowerSpectrumEngine(n.npts(), - noise_engine.time_bandwidth_product(),noise_engine.number_tapers()); + noise_engine = + MTPowerSpectrumEngine(n.npts(), noise_engine.time_bandwidth_product(), + noise_engine.number_tapers()); noise_engine.set_df(this->operator_dt); } - for(int k=0;k<3;++k) - { - tswork=TimeSeries(ExtractComponent(n,k),"Invalid"); + for (int k = 0; k < 3; ++k) { + tswork = TimeSeries(ExtractComponent(n, k), "Invalid"); PowerSpectrum psnoise = this->noise_engine.apply(tswork); - if(psnoise.dead()) - { + if (psnoise.dead()) { return psnoise; } - if(k==0) + if (k == 0) avg3c = psnoise; else avg3c += psnoise; } /* We define total power as the average on all three components */ - double scl=1.0/3.0; - for(int i=0;iwinv_t0_lag = w.sample_number(0.0); - switch(algorithm) - { - case CNR3C_algorithms::generalized_water_level: - compute_gwl_inverse(w,psnoise); - break; - case CNR3C_algorithms::colored_noise_damping: - default: - compute_gdamp_inverse(w,psnoise); + switch (algorithm) { + case CNR3C_algorithms::generalized_water_level: + compute_gwl_inverse(w, psnoise); + break; + case CNR3C_algorithms::colored_noise_damping: + default: + compute_gdamp_inverse(w, psnoise); }; - }catch(...){throw;}; + } catch (...) { + throw; + }; } -void CNRDeconEngine::compute_gwl_inverse(const TimeSeries& wavelet, const PowerSpectrum& psnoise) -{ - try{ - if(wavelet.npts() != FFTDeconOperator::nfft) - { - throw MsPASSError("CNRDeconEngine::compute_gwl_inverse(): wavelet size and fft size t0 not match - this should not happen and indicates a bug that needs to be fixed", - ErrorSeverity::Fatal); +void CNRDeconEngine::compute_gwl_inverse(const TimeSeries &wavelet, + const PowerSpectrum &psnoise) { + try { + if (wavelet.npts() != FFTDeconOperator::nfft) { + throw MsPASSError("CNRDeconEngine::compute_gwl_inverse(): wavelet size " + "and fft size t0 not match - this should not happen " + "and indicates a bug that needs to be fixed", + ErrorSeverity::Fatal); } - ComplexArray cwvec(wavelet.npts(),wavelet.s[0]); - gsl_fft_complex_forward(cwvec.ptr(),1,FFTDeconOperator::nfft, - wavetable,workspace); + ComplexArray cwvec(wavelet.npts(), wavelet.s[0]); + gsl_fft_complex_forward(cwvec.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); /* This computes the (regularized) denominator for the decon operator*/ - double df,fNy; - df=1.0/(operator_dt*static_cast(FFTDeconOperator::nfft)); - fNy=df*static_cast(FFTDeconOperator::nfft/2); + double df, fNy; + df = 1.0 / (operator_dt * static_cast(FFTDeconOperator::nfft)); + fNy = df * static_cast(FFTDeconOperator::nfft / 2); /* We need largest noise amplitude to establish a relative noise floor. We use this std::algorithm to find it in the spectrum vector */ vector::iterator maxnoise; /* Copy needed because max_element alters content of work vector */ vector work(psnoise.spectrum); - maxnoise=std::max_element(work.begin(),work.end()); - //spectrum is power, we need amplitude so sqrt here - double scaled_noise_floor=noise_floor*sqrt(*maxnoise); + maxnoise = std::max_element(work.begin(), work.end()); + // spectrum is power, we need amplitude so sqrt here + double scaled_noise_floor = noise_floor * sqrt(*maxnoise); vector wavelet_snr; wavelet_snr.clear(); int nreg(0); - for(int j=0;j(j); - if(f>fNy) f=2.0*fNy-f; // Fold frequency axis - double namp=sqrt(psnoise.power(f)); + f = df * static_cast(j); + if (f > fNy) + f = 2.0 * fNy - f; // Fold frequency axis + double namp = sqrt(psnoise.power(f)); /* Avoid divide by zero that could randomly happen with simulation data*/ double snr; - if((namp/amp)regularization_bandwidth_fraction=static_cast(nreg) - / static_cast(FFTDeconOperator::nfft); - double *d0=new double[FFTDeconOperator::nfft]; - for(int k=0;kregularization_bandwidth_fraction = + static_cast(nreg) / static_cast(FFTDeconOperator::nfft); + double *d0 = new double[FFTDeconOperator::nfft]; + for (int k = 0; k < FFTDeconOperator::nfft; ++k) + d0[k] = 0.0; + d0[0] = 1.0; + ComplexArray delta0(FFTDeconOperator::nfft, d0); + delete[] d0; + gsl_fft_complex_forward(delta0.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); + winv = delta0 / cwvec; + } catch (...) { + throw; + }; } /* Note this is intentionally not a reference to assure this is a copy */ -void CNRDeconEngine::compute_gdamp_inverse(const TimeSeries& wavelet, const PowerSpectrum& psnoise) -{ - try{ +void CNRDeconEngine::compute_gdamp_inverse(const TimeSeries &wavelet, + const PowerSpectrum &psnoise) { + try { /* Assume if we got here wavelet.npts() == nfft*/ ComplexArray b_fft; - if(wavelet.npts()==FFTDeconOperator::nfft) - { - b_fft = ComplexArray(wavelet.npts(),wavelet.s[0]); - } - else if(wavelet.npts() < FFTDeconOperator::nfft) - { + if (wavelet.npts() == FFTDeconOperator::nfft) { + b_fft = ComplexArray(wavelet.npts(), wavelet.s[0]); + } else if (wavelet.npts() < FFTDeconOperator::nfft) { /* In this case we zero pad*/ std::vector btmp; btmp.reserve(FFTDeconOperator::nfft); - for(auto i=0;ichange_size(wavelet.npts()); } - gsl_fft_complex_forward(b_fft.ptr(),1,FFTDeconOperator::nfft, - wavetable,workspace); + gsl_fft_complex_forward(b_fft.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); ComplexArray conj_b_fft(b_fft); conj_b_fft.conj(); - ComplexArray denom(conj_b_fft*b_fft); + ComplexArray denom(conj_b_fft * b_fft); /* Compute scaling constants for noise based on noise_floor and the noise spectrum */ - double df,fNy; - df=1.0/(operator_dt*static_cast(FFTDeconOperator::nfft)); - fNy=df*static_cast(FFTDeconOperator::nfft/2); + double df, fNy; + df = 1.0 / (operator_dt * static_cast(FFTDeconOperator::nfft)); + fNy = df * static_cast(FFTDeconOperator::nfft / 2); /* We need largest noise amplitude to establish a relative noise floor. We use this std::algorithm to find it in the spectrum vector */ vector::iterator maxnoise; /* Copy needed because max_element alters content of work vector */ vector work(psnoise.spectrum); - maxnoise=std::max_element(work.begin(),work.end()); - //Spectrum is power but need amplitude in this context so sqrt here - double scaled_noise_floor=noise_floor*sqrt(*maxnoise);\ + maxnoise = std::max_element(work.begin(), work.end()); + // Spectrum is power but need amplitude in this context so sqrt here + double scaled_noise_floor = noise_floor * sqrt(*maxnoise); - for(int k=0;k(k); - if(f>fNy) f=2.0*fNy-f; // Fold frequency axis - double namp=sqrt(psnoise.power(f)); + f = df * static_cast(k); + if (f > fNy) + f = 2.0 * fNy - f; // Fold frequency axis + double namp = sqrt(psnoise.power(f)); double theta; - if(namp>scaled_noise_floor) - { - theta=damp*namp; - } - else - { - theta=damp*scaled_noise_floor; + if (namp > scaled_noise_floor) { + theta = damp * namp; + } else { + theta = damp * scaled_noise_floor; } /* This uses a normal equation form so theta must be squared to be a form of the standard damped least squares inverse */ - theta=theta*theta; + theta = theta * theta; /* ptr points to the real part - an oddity of this interface */ *ptr += theta; } - winv=conj_b_fft/denom; - }catch(...){throw;}; + winv = conj_b_fft / denom; + } catch (...) { + throw; + }; } /* Computes deconvolution of data in d using inverse operator that was assumed to be previously loaded via the initialize_wavelet method. Note a potential @@ -500,147 +483,150 @@ don't apply them to either the numerator or denominator. We do need to post filter with the shaping wavelet, which is done here, or the output will almost always be junk. */ -Seismogram CNRDeconEngine::process(const Seismogram& d, const PowerSpectrum& psnoise, - const double fl, const double fh) -{ +Seismogram CNRDeconEngine::process(const Seismogram &d, + const PowerSpectrum &psnoise, + const double fl, const double fh) { const string alg("CNRDeconEngine::process"); - if(d.dead() || psnoise.dead()) - { + if (d.dead() || psnoise.dead()) { Seismogram dout(d); dout.set_npts(0); - if(d.dead()) - { - dout.elog.log_error(alg, - "received Seismogram input segment marked dead - cannot process", - ErrorSeverity::Invalid); + if (d.dead()) { + dout.elog.log_error( + alg, "received Seismogram input segment marked dead - cannot process", + ErrorSeverity::Invalid); } - if(psnoise.dead()) - { - dout.elog.log_error(alg, - "received PowerSpectrum object marked dead - cannot process", - ErrorSeverity::Invalid); + if (psnoise.dead()) { + dout.elog.log_error( + alg, "received PowerSpectrum object marked dead - cannot process", + ErrorSeverity::Invalid); } dout.kill(); return dout; } - try{ + try { string base_error("CNRDeconEngine::process: "); - this->update_shaping_wavelet(fl,fh); + this->update_shaping_wavelet(fl, fh); Seismogram rfest(d); /* This is used to apply a shift to the fft outputs to put signals at relative time 0 */ int t0_shift; - t0_shift= round((-rfest.t0())/rfest.dt()); + t0_shift = round((-rfest.t0()) / rfest.dt()); vector wvec; wvec.reserve(FFTDeconOperator::nfft); - if(rfest.npts()!=FFTDeconOperator::nfft) rfest.u=dmatrix(3,FFTDeconOperator::nfft); + if (rfest.npts() != FFTDeconOperator::nfft) + rfest.u = dmatrix(3, FFTDeconOperator::nfft); int nhighsnr; double df; - df=1.0/(operator_dt*static_cast(FFTDeconOperator::nfft)); - for(int k=0;k<3;++k) - { + df = 1.0 / (operator_dt * static_cast(FFTDeconOperator::nfft)); + for (int k = 0; k < 3; ++k) { TimeSeries work; - work=TimeSeries(ExtractComponent(d,k),"Invalid"); + work = TimeSeries(ExtractComponent(d, k), "Invalid"); wvec.clear(); - int ntocopy=FFTDeconOperator::nfft; - if(ntocopy>work.npts()) ntocopy=work.npts(); - for(int j=0;j work.npts()) + ntocopy = work.npts(); + for (int j = 0; j < ntocopy; ++j) + wvec.push_back(work.s[j]); + for (int j = ntocopy; j < FFTDeconOperator::nfft; ++j) + wvec.push_back(0.0); - ComplexArray numerator(FFTDeconOperator::nfft,&(wvec[0])); - gsl_fft_complex_forward(numerator.ptr(),1,FFTDeconOperator::nfft, - wavetable,workspace); + ComplexArray numerator(FFTDeconOperator::nfft, &(wvec[0])); + gsl_fft_complex_forward(numerator.ptr(), 1, FFTDeconOperator::nfft, + wavetable, workspace); /* This loop computes QCMetrics of bandwidth fraction that is above a defined snr floor - not necessarily the same as the regularization floor used in computing the inverse */ double snrmax; - snrmax=1.0; - nhighsnr=0; - for(int j=0;j(j); - Complex64 z=numerator[j]; - double sigamp=abs(z); - double namp=sqrt(psnoise.power(f)); - double snr=sigamp/namp; + f = df * static_cast(j); + Complex64 z = numerator[j]; + double sigamp = abs(z); + double namp = sqrt(psnoise.power(f)); + double snr = sigamp / namp; - if(snr > snrmax) snrmax=snr; - if(snr > this->band_snr_floor) ++nhighsnr; + if (snr > snrmax) + snrmax = snr; + if (snr > this->band_snr_floor) + ++nhighsnr; } - signal_bandwidth_fraction[k]=static_cast(nhighsnr) - / static_cast(FFTDeconOperator::nfft/2); - peak_snr[k]=snrmax; - ComplexArray rftmp=numerator*winv; - rftmp=(*this->shapingwavelet.wavelet())*rftmp; - gsl_fft_complex_inverse(rftmp.ptr(), 1, FFTDeconOperator::nfft, - wavetable, workspace); + signal_bandwidth_fraction[k] = + static_cast(nhighsnr) / + static_cast(FFTDeconOperator::nfft / 2); + peak_snr[k] = snrmax; + ComplexArray rftmp = numerator * winv; + rftmp = (*this->shapingwavelet.wavelet()) * rftmp; + gsl_fft_complex_inverse(rftmp.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); wvec.clear(); - for(int j=0;jshaping_wavelet_number_poles,fl, - this->shaping_wavelet_number_poles,fh, - this->operator_dt,FFTDeconOperator::nfft); - }else if(wtype=="ricker") - { - double favg=(fh-fl)/2.0; - shapingwavelet=ShapingWavelet(favg,operator_dt,FFTDeconOperator::nfft); - }else - { + shapingwavelet = ShapingWavelet(this->shaping_wavelet_number_poles, fl, + this->shaping_wavelet_number_poles, fh, + this->operator_dt, FFTDeconOperator::nfft); + } else if (wtype == "ricker") { + double favg = (fh - fl) / 2.0; + shapingwavelet = ShapingWavelet(favg, operator_dt, FFTDeconOperator::nfft); + } else { /* this really shouldn't happen but trap it anyway for completeness. Because it shouldn't happen we set the severity fatal*/ - throw MsPASSError(string("CNRDeconEngine::update_shaping_wavelet: ") - + "shaping wavelet has unsupported type defined="+wtype, - ErrorSeverity::Fatal); + throw MsPASSError( + string("CNRDeconEngine::update_shaping_wavelet: ") + + "shaping wavelet has unsupported type defined=" + wtype, + ErrorSeverity::Fatal); } } -TimeSeries CNRDeconEngine::ideal_output() -{ - try{ - CoreTimeSeries ideal_tmp=this->shapingwavelet.impulse_response(); - return TimeSeries(ideal_tmp,"Invalid"); - }catch(...){throw;}; +TimeSeries CNRDeconEngine::ideal_output() { + try { + CoreTimeSeries ideal_tmp = this->shapingwavelet.impulse_response(); + return TimeSeries(ideal_tmp, "Invalid"); + } catch (...) { + throw; + }; } -TimeSeries CNRDeconEngine::actual_output(const TimeSeries& wavelet) -{ - if(wavelet.dead()) - { +TimeSeries CNRDeconEngine::actual_output(const TimeSeries &wavelet) { + if (wavelet.dead()) { TimeSeries badout(wavelet); badout.kill(); badout.set_npts(0); - badout.elog.log_error("CRFDeconEngine::actual_output", - "received wavelet data via arg0 marked dead - cannot procede", - ErrorSeverity::Invalid); + badout.elog.log_error( + "CRFDeconEngine::actual_output", + "received wavelet data via arg0 marked dead - cannot procede", + ErrorSeverity::Invalid); return badout; } - TimeSeries result(wavelet); // Use this to clone metadata and elog from wavelet + TimeSeries result( + wavelet); // Use this to clone metadata and elog from wavelet result.set_npts(FFTDeconOperator::nfft); /* Force these even though they are likely already defined as in the parent wavelet TimeSeries. */ result.set_live(); /* We always shift this wavelet to the center of the data vector. We handle the time through the CoreTimeSeries object. */ - int i0=FFTDeconOperator::nfft/2; - result.set_t0(operator_dt*(-(double)i0)); + int i0 = FFTDeconOperator::nfft / 2; + result.set_t0(operator_dt * (-(double)i0)); result.set_dt(this->operator_dt); result.set_tref(TimeReferenceType::Relative); /* We need to require that wavelet time range is consistent with @@ -653,113 +639,112 @@ TimeSeries CNRDeconEngine::actual_output(const TimeSeries& wavelet) saved when winv was created. */ w_t0_lag -= this->winv_t0_lag; /* note we handle two extremes differently*/ - if(w_t0_lag>=FFTDeconOperator::nfft) - { + if (w_t0_lag >= FFTDeconOperator::nfft) { stringstream ss; - ss << "actual_output method received wavelet with t0=" - << wavelet.t0() << " that resolves to offset of "<0" - <0" + << endl; + result.elog.log_error("CNRDeconEngine::actual_output", ss.str(), + ErrorSeverity::Invalid); result.kill(); result.set_npts(0); return result; - } - else if(w_t0_lag>(FFTDeconOperator::nfft)/2) - { + } else if (w_t0_lag > (FFTDeconOperator::nfft) / 2) { stringstream ss; ss << "Warning: actual output method received wavelet with t0=" - << wavelet.t0() << " that resolves to offset of "< work; - if(wavelet.npts() == FFTDeconOperator::nfft) - { - work = wavelet.s; - } + std::vector work; + if (wavelet.npts() == FFTDeconOperator::nfft) { + work = wavelet.s; + } else { + work.reserve(FFTDeconOperator::nfft); + int i, nend; + for (i = 0; i < FFTDeconOperator::nfft; ++i) + work.push_back(0.0); + if (wavelet.npts() > FFTDeconOperator::nfft) + nend = FFTDeconOperator::nfft; else - { - work.reserve(FFTDeconOperator::nfft); - int i,nend; - for(i=0;iFFTDeconOperator::nfft) - nend = FFTDeconOperator::nfft; - else - nend = wavelet.npts(); - for(i=0;iwinv*W; - ComplexArray *stmp = this->shapingwavelet.wavelet(); - /* We always apply the shaping wavelet - this perhaps should be optional - but probably better done with a none option for the shaping wavelet */ - ao_fft=(*stmp)*ao_fft; - gsl_fft_complex_inverse(ao_fft.ptr(),1,FFTDeconOperator::nfft,wavetable,workspace); - vector ao; - ao.reserve(FFTDeconOperator::nfft); - for(int k=0; k(ao); - /* set_npts always initializes the s buffer so it is more efficient to - copy ao elements rather than what was here before: - result.s=ao; - */ - for(int k=0;kwinv * W; + ComplexArray *stmp = this->shapingwavelet.wavelet(); + /* We always apply the shaping wavelet - this perhaps should be optional + but probably better done with a none option for the shaping wavelet */ + ao_fft = (*stmp) * ao_fft; + gsl_fft_complex_inverse(ao_fft.ptr(), 1, FFTDeconOperator::nfft, wavetable, + workspace); + vector ao; + ao.reserve(FFTDeconOperator::nfft); + for (int k = 0; k < ao_fft.size(); ++k) + ao.push_back(ao_fft[k].real()); + ao = circular_shift(ao, i0); + ao = normalize(ao); + /* set_npts always initializes the s buffer so it is more efficient to + copy ao elements rather than what was here before: + result.s=ao; + */ + for (int k = 0; k < FFTDeconOperator::nfft; ++k) + result.s[k] = ao[k]; + return result; + } catch (...) { + throw; }; } -TimeSeries CNRDeconEngine::inverse_wavelet(const TimeSeries& wavelet, const double tshift0) -{ +TimeSeries CNRDeconEngine::inverse_wavelet(const TimeSeries &wavelet, + const double tshift0) { try { /* Using the time shift of wavelet.t0() may be a bad idea here. Will need to sort that out in debugging behaviour*/ double timeshift(tshift0); - timeshift+=wavelet.t0(); - timeshift -= operator_dt*((double)this->winlength); - CoreTimeSeries invcore(this->FFTDeconOperator::FourierInverse(this->winv, - *this->shapingwavelet.wavelet(),operator_dt,timeshift)); - TimeSeries result(invcore,"Invalid"); + timeshift += wavelet.t0(); + timeshift -= operator_dt * ((double)this->winlength); + CoreTimeSeries invcore(this->FFTDeconOperator::FourierInverse( + this->winv, *this->shapingwavelet.wavelet(), operator_dt, timeshift)); + TimeSeries result(invcore, "Invalid"); /* Copy the error log from wavelet and post some information parameters to metadata */ - result.elog=wavelet.elog; - result.put("waveform_type","deconvolution_inverse_wavelet"); - result.put("decon_type","CNRDeconEngine"); + result.elog = wavelet.elog; + result.put("waveform_type", "deconvolution_inverse_wavelet"); + result.put("decon_type", "CNRDeconEngine"); return result; - } catch(...) { - throw; + } catch (...) { + throw; }; } -Metadata CNRDeconEngine::QCMetrics() -{ +Metadata CNRDeconEngine::QCMetrics() { Metadata result; - result.put("waveletbf",this->regularization_bandwidth_fraction); - result.put("maxsnr0",peak_snr[0]); - result.put("maxsnr1",peak_snr[1]); - result.put("maxsnr2",peak_snr[2]); - result.put("signalbf0",signal_bandwidth_fraction[0]); - result.put("signalbf1",signal_bandwidth_fraction[1]); - result.put("signalbf2",signal_bandwidth_fraction[2]); + result.put("waveletbf", this->regularization_bandwidth_fraction); + result.put("maxsnr0", peak_snr[0]); + result.put("maxsnr1", peak_snr[1]); + result.put("maxsnr2", peak_snr[2]); + result.put("signalbf0", signal_bandwidth_fraction[0]); + result.put("signalbf1", signal_bandwidth_fraction[1]); + result.put("signalbf2", signal_bandwidth_fraction[2]); return result; } -} // end namespace enscapsulation +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/ComplexArray.cc b/cxx/src/lib/algorithms/deconvolution/ComplexArray.cc index f0c337f1d..2e7e27c77 100644 --- a/cxx/src/lib/algorithms/deconvolution/ComplexArray.cc +++ b/cxx/src/lib/algorithms/deconvolution/ComplexArray.cc @@ -1,313 +1,276 @@ -#include -#include "mspass/utility/MsPASSError.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" -namespace mspass::algorithms::deconvolution -{ +#include "mspass/utility/MsPASSError.h" +#include +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::utility; -ComplexArray::ComplexArray() -{ - nsamp=0; - /* Note declaration of shared_ptr initializes it with a NULL - pointer equivalent - no initialization is needed */ +ComplexArray::ComplexArray() { + nsamp = 0; + /* Note declaration of shared_ptr initializes it with a NULL + pointer equivalent - no initialization is needed */ } -ComplexArray::ComplexArray(vector &d) -{ - nsamp=d.size(); - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i &d) { + nsamp = d.size(); + data = std::shared_ptr(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = d[i].real(); + data[i].imag = d[i].imag(); + } } -ComplexArray::ComplexArray(vector &d) -{ - nsamp=d.size(); - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i &d) { + nsamp = d.size(); + data = std::shared_ptr(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = d[i].real(); + data[i].imag = d[i].imag(); + } } -ComplexArray::ComplexArray(int n, FortranComplex32 *d) -{ - nsamp=n; - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = d[i].real; + data[i].imag = d[i].imag; + } } -ComplexArray::ComplexArray(int n, FortranComplex64 *d) -{ - nsamp=n; - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = d[i].real; + data[i].imag = d[i].imag; + } } -ComplexArray::ComplexArray(int n, float *d) -{ - nsamp=n; - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = d[i]; + data[i].imag = 0.0; + } } -ComplexArray::ComplexArray(int n, double *d) -{ - nsamp=n; - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = d[i]; + data[i].imag = 0.0; + } } -ComplexArray::ComplexArray(int n) -{ - nsamp=n; - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = 0.0; + data[i].imag = 0.0; + } } -ComplexArray::ComplexArray(vector mag,vector phase) -{ - nsamp=mag.size(); - if(nsamp==phase.size()) - { - data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i mag,vector phase): Length of magnitude vector and phase vector do not match", - ErrorSeverity::Invalid); - } -} -ComplexArray::ComplexArray(const ComplexArray &parent) -{ - nsamp=parent.nsamp; +ComplexArray::ComplexArray(vector mag, vector phase) { + nsamp = mag.size(); + if (nsamp == phase.size()) { data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; i mag,vector phase): " + "Length of magnitude vector and phase vector do not match", + ErrorSeverity::Invalid); + } +} +ComplexArray::ComplexArray(const ComplexArray &parent) { + nsamp = parent.nsamp; + data = std::shared_ptr(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + data[i].real = parent.data[i].real; + data[i].imag = parent.data[i].imag; + } } -ComplexArray& ComplexArray::operator=(const ComplexArray &parent) -{ - if(&parent != this) - { - this->nsamp=parent.nsamp; - this->data = std::shared_ptr(new FortranComplex64[nsamp]); - for(std::size_t i=0; idata[i].real=parent.data[i].real; - this->data[i].imag=parent.data[i].imag; - } +ComplexArray &ComplexArray::operator=(const ComplexArray &parent) { + if (&parent != this) { + this->nsamp = parent.nsamp; + this->data = + std::shared_ptr(new FortranComplex64[nsamp]); + for (std::size_t i = 0; i < nsamp; i++) { + this->data[i].real = parent.data[i].real; + this->data[i].imag = parent.data[i].imag; } - return *this; + } + return *this; } -ComplexArray::~ComplexArray() -{ - /*Original implementation used a raw pointer for data array. Changed - Dec. 2024 to shared_ptr so this destructor now does nothing.*/ - //delete[] data; +ComplexArray::~ComplexArray() { + /*Original implementation used a raw pointer for data array. Changed + Dec. 2024 to shared_ptr so this destructor now does nothing.*/ + // delete[] data; } -double *ComplexArray::ptr() -{ - return reinterpret_cast(&data[0].real); +double *ComplexArray::ptr() { + return reinterpret_cast(&data[0].real); } -double *ComplexArray::ptr(int sample) -{ - return reinterpret_cast(&data[sample].real); +double *ComplexArray::ptr(int sample) { + return reinterpret_cast(&data[sample].real); } -Complex64 ComplexArray::operator[](int sample) -{ - return *reinterpret_cast(&data[sample].real); +Complex64 ComplexArray::operator[](int sample) { + return *reinterpret_cast(&data[sample].real); } -ComplexArray& ComplexArray::operator +=(const ComplexArray& other) -{ - if(nsamp != other.nsamp) - { - stringstream sserr; - sserr << "ComplexArray::operator+=: Inconsistent array sizes"< ComplexArray::abs() const -{ - vector result; - result.reserve(nsamp); - for(std::size_t i=0; i ComplexArray::abs() const { + vector result; + result.reserve(nsamp); + for (std::size_t i = 0; i < nsamp; i++) + result.push_back(sqrt((double)data[i].real * data[i].real + + data[i].imag * data[i].imag)); + return result; } -double ComplexArray::rms() const -{ - double result=0; - for(std::size_t i=0; i ComplexArray::phase() const -{ - vector result; - result.reserve(nsamp); - for(std::size_t i=0; i ComplexArray::phase() const { + vector result; + result.reserve(nsamp); + for (std::size_t i = 0; i < nsamp; i++) + result.push_back(atan2((double)data[i].imag, (double)data[i].real)); + return result; } -} //End namespace +int ComplexArray::size() const { return nsamp; } +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/ComputeTaperLength.cc b/cxx/src/lib/algorithms/deconvolution/ComputeTaperLength.cc index 668f7d433..24203b879 100644 --- a/cxx/src/lib/algorithms/deconvolution/ComputeTaperLength.cc +++ b/cxx/src/lib/algorithms/deconvolution/ComputeTaperLength.cc @@ -1,20 +1,18 @@ -#include #include "mspass/utility/Metadata.h" -namespace mspass::algorithms::deconvolution -{ +#include +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::utility; -int ComputeTaperLength(const Metadata& md) -{ - try { - double ts,te,dt; - ts=md.get("deconvolution_data_window_start"); - te=md.get("deconvolution_data_window_end"); - dt=md.get("target_sample_interval"); - return round((te-ts)/dt) + 1; - } catch(...) { - throw; - }; -} +int ComputeTaperLength(const Metadata &md) { + try { + double ts, te, dt; + ts = md.get("deconvolution_data_window_start"); + te = md.get("deconvolution_data_window_end"); + dt = md.get("target_sample_interval"); + return round((te - ts) / dt) + 1; + } catch (...) { + throw; + }; } +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/FFTDeconOperator.cc b/cxx/src/lib/algorithms/deconvolution/FFTDeconOperator.cc index 33576a07b..6b9dfebb6 100644 --- a/cxx/src/lib/algorithms/deconvolution/FFTDeconOperator.cc +++ b/cxx/src/lib/algorithms/deconvolution/FFTDeconOperator.cc @@ -1,111 +1,107 @@ -#include #include "mspass/algorithms/deconvolution/FFTDeconOperator.h" -#include "mspass/utility/MsPASSError.h" #include "mspass/seismic/CoreTimeSeries.h" -namespace mspass::algorithms::deconvolution -{ +#include "mspass/utility/MsPASSError.h" +#include +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; -FFTDeconOperator::FFTDeconOperator() -{ - nfft=0; - sample_shift=0; - wavetable=NULL; - workspace=NULL; +FFTDeconOperator::FFTDeconOperator() { + nfft = 0; + sample_shift = 0; + wavetable = NULL; + workspace = NULL; } -FFTDeconOperator::FFTDeconOperator(const Metadata& md) -{ +FFTDeconOperator::FFTDeconOperator(const Metadata &md) { try { const string base_error("FFTDeconOperator Metadata constructor: "); - int nfftpf=md.get_int("operator_nfft"); + int nfftpf = md.get_int("operator_nfft"); /* We force a power of 2 algorithm for efficiency and always round up*/ - this->nfft=nextPowerOf2(nfftpf); - /* We compute the sample shift from the window start time and dt. This assures - * the output will be phase shifted so zero lag is at the zero position of the - * array. Necessary because operators using this object internally only return - * a raw vector of samples not a child of BasicTimeSeries. */ - double dt_to_use=md.get_double("target_sample_interval"); - double dwinstart=md.get_double("deconvolution_data_window_start"); - int i0=round(dwinstart/dt_to_use); - /* sample shift is positive for a negative i0 */ - this->sample_shift = (-i0); - if(this->sample_shift<0) - throw MsPASSError(base_error - + "illegal sample_shift parameter - must be ge 0", - ErrorSeverity::Invalid); - if((this->sample_shift)>nfft) - throw MsPASSError(base_error - + "Computed shift parameter exceeds length of fft\n" - + "Deconvolution data window parameters are probably nonsense", - ErrorSeverity::Invalid); - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); - } catch(...) { - throw; - }; + this->nfft = nextPowerOf2(nfftpf); + /* We compute the sample shift from the window start time and dt. This + * assures the output will be phase shifted so zero lag is at the zero + * position of the array. Necessary because operators using this object + * internally only return a raw vector of samples not a child of + * BasicTimeSeries. */ + double dt_to_use = md.get_double("target_sample_interval"); + double dwinstart = md.get_double("deconvolution_data_window_start"); + int i0 = round(dwinstart / dt_to_use); + /* sample shift is positive for a negative i0 */ + this->sample_shift = (-i0); + if (this->sample_shift < 0) + throw MsPASSError(base_error + + "illegal sample_shift parameter - must be ge 0", + ErrorSeverity::Invalid); + if ((this->sample_shift) > nfft) + throw MsPASSError( + base_error + "Computed shift parameter exceeds length of fft\n" + + "Deconvolution data window parameters are probably nonsense", + ErrorSeverity::Invalid); + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + } catch (...) { + throw; + }; } -FFTDeconOperator::FFTDeconOperator(const FFTDeconOperator& parent) -{ - nfft=parent.nfft; - sample_shift=parent.sample_shift; - /* copies need their own work space */ - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); +FFTDeconOperator::FFTDeconOperator(const FFTDeconOperator &parent) { + nfft = parent.nfft; + sample_shift = parent.sample_shift; + /* copies need their own work space */ + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); } -FFTDeconOperator::~FFTDeconOperator() -{ - if(wavetable!=NULL) gsl_fft_complex_wavetable_free (wavetable); - if(workspace!=NULL) gsl_fft_complex_workspace_free (workspace); +FFTDeconOperator::~FFTDeconOperator() { + if (wavetable != NULL) + gsl_fft_complex_wavetable_free(wavetable); + if (workspace != NULL) + gsl_fft_complex_workspace_free(workspace); } -FFTDeconOperator& FFTDeconOperator::operator=(const FFTDeconOperator& parent) -{ - if(this != &parent) - { - nfft=parent.nfft; - sample_shift=parent.sample_shift; - /* copies need their own work space */ - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); - } - return *this; +FFTDeconOperator &FFTDeconOperator::operator=(const FFTDeconOperator &parent) { + if (this != &parent) { + nfft = parent.nfft; + sample_shift = parent.sample_shift; + /* copies need their own work space */ + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + } + return *this; } -void FFTDeconOperator::changeparameter(const Metadata& md) -{ - try { - size_t nfft_test=md.get_int("operator_nfft"); - if(nfft_test != nfft) - { - nfft=nfft_test; - if(wavetable!=NULL) gsl_fft_complex_wavetable_free (wavetable); - if(workspace!=NULL) gsl_fft_complex_workspace_free (workspace); - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); - } - sample_shift=md.get_int("sample_shift"); - if(sample_shift<0) - throw MsPASSError(string("FFTDeconOperator::changeparameter: ") - + "illegal sample_shift parameter - must be ge 0", - ErrorSeverity::Invalid); - } catch(...) { - throw; - }; +void FFTDeconOperator::changeparameter(const Metadata &md) { + try { + size_t nfft_test = md.get_int("operator_nfft"); + if (nfft_test != nfft) { + nfft = nfft_test; + if (wavetable != NULL) + gsl_fft_complex_wavetable_free(wavetable); + if (workspace != NULL) + gsl_fft_complex_workspace_free(workspace); + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + } + sample_shift = md.get_int("sample_shift"); + if (sample_shift < 0) + throw MsPASSError(string("FFTDeconOperator::changeparameter: ") + + "illegal sample_shift parameter - must be ge 0", + ErrorSeverity::Invalid); + } catch (...) { + throw; + }; } -void FFTDeconOperator::change_size(const int n) -{ - try { - if(nfft!=0) - { - if(wavetable!=NULL) gsl_fft_complex_wavetable_free (wavetable); - if(workspace!=NULL) gsl_fft_complex_workspace_free (workspace); - } - nfft=n; - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); - } catch(...) { - throw; - }; +void FFTDeconOperator::change_size(const int n) { + try { + if (nfft != 0) { + if (wavetable != NULL) + gsl_fft_complex_wavetable_free(wavetable); + if (workspace != NULL) + gsl_fft_complex_workspace_free(workspace); + } + nfft = n; + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + } catch (...) { + throw; + }; } /* Helper method to avoid repetitious code in Fourier methods. Computes the inverse FIR filter for the inverse_wavelet methods of fourier decon operators. @@ -116,10 +112,11 @@ void FFTDeconOperator::change_size(const int n) t0parent is as described in ScalarDecon::inverse_wavelet defintions. */ -CoreTimeSeries FFTDeconOperator::FourierInverse(const ComplexArray& winv, const ComplexArray& sw, - const double dt, const double t0parent) -{ - try{ +CoreTimeSeries FFTDeconOperator::FourierInverse(const ComplexArray &winv, + const ComplexArray &sw, + const double dt, + const double t0parent) { + try { const string base_error("FFTDeconOperator::FourierInverse: "); /* Compute wrap point for circular shift operator - note negative because a positive t0 is requires wrapping at a point i0 to the left @@ -128,17 +125,21 @@ CoreTimeSeries FFTDeconOperator::FourierInverse(const ComplexArray& winv, const * showed they created valid results only if the filter used a * circular shift of nfft/2. This sets that as a requirement here. */ int ntest; - ntest=winv.size(); - if(ntest != nfft) throw MsPASSError(base_error - + "wavelet inverse fourier array size mismatch with operator", - ErrorSeverity::Invalid); - if(sw.size() != nfft) throw MsPASSError(base_error - + "shaping wavelet fourier array size mismatch with operator", - ErrorSeverity::Invalid); + ntest = winv.size(); + if (ntest != nfft) + throw MsPASSError( + base_error + + "wavelet inverse fourier array size mismatch with operator", + ErrorSeverity::Invalid); + if (sw.size() != nfft) + throw MsPASSError( + base_error + + "shaping wavelet fourier array size mismatch with operator", + ErrorSeverity::Invalid); ComplexArray winv_work(winv); /* This applies the shaping wavelet*/ winv_work *= sw; - gsl_fft_complex_inverse(winv_work.ptr(),1,nfft,wavetable,workspace); + gsl_fft_complex_inverse(winv_work.ptr(), 1, nfft, wavetable, workspace); CoreTimeSeries result; result.set_t0(t0parent); result.set_dt(dt); @@ -147,38 +148,40 @@ CoreTimeSeries FFTDeconOperator::FourierInverse(const ComplexArray& winv, const the values not use push back below */ result.set_npts(nfft); result.set_tref(TimeReferenceType::Relative); - for(int k=0; k(((w.end-w.start)/dt))+1; - nfft=nextPowerOf2(nsamples); - return nfft; +int ComputeFFTLength(const TimeWindow w, const double dt) { + int nsamples, nfft; + nsamples = static_cast(((w.end - w.start) / dt)) + 1; + nfft = nextPowerOf2(nsamples); + return nfft; } /* Newbies note this works because of the fundamental concept of overloading in C++ */ -int ComputeFFTLength(const Metadata& md) -{ - try { - double ts,te,dt; - ts=md.get("deconvolution_data_window_start"); - te=md.get("deconvolution_data_window_end"); - TimeWindow w(ts,te); - dt=md.get("target_sample_interval"); - int nfft; - nfft=ComputeFFTLength(w,dt); - if(nfft<2) - throw MsPASSError(string("FFTDeconOperator ComputeFFTLength procedure: ") - + "Computed fft length is less than 2 - check window parameters", - ErrorSeverity::Invalid); - return nfft; - } catch(MetadataGetError& mde) { - throw mde; - }; +int ComputeFFTLength(const Metadata &md) { + try { + double ts, te, dt; + ts = md.get("deconvolution_data_window_start"); + te = md.get("deconvolution_data_window_end"); + TimeWindow w(ts, te); + dt = md.get("target_sample_interval"); + int nfft; + nfft = ComputeFFTLength(w, dt); + if (nfft < 2) + throw MsPASSError( + string("FFTDeconOperator ComputeFFTLength procedure: ") + + "Computed fft length is less than 2 - check window parameters", + ErrorSeverity::Invalid); + return nfft; + } catch (MetadataGetError &mde) { + throw mde; + }; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/GeneralIterDecon.cc b/cxx/src/lib/algorithms/deconvolution/GeneralIterDecon.cc index aa0413c4c..cd22a56fb 100644 --- a/cxx/src/lib/algorithms/deconvolution/GeneralIterDecon.cc +++ b/cxx/src/lib/algorithms/deconvolution/GeneralIterDecon.cc @@ -1,364 +1,363 @@ -#include -#include -#include +#include "mspass/algorithms/deconvolution/GeneralIterDecon.h" #include "gsl/gsl_cblas.h" -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/AntelopePf.h" -#include "mspass/seismic/CoreTimeSeries.h" -#include "mspass/seismic/CoreSeismogram.h" #include "mspass/algorithms/algorithms.h" -#include "mspass/algorithms/deconvolution/WaterLevelDecon.h" #include "mspass/algorithms/deconvolution/LeastSquareDecon.h" #include "mspass/algorithms/deconvolution/MultiTaperXcorDecon.h" -#include "mspass/algorithms/deconvolution/GeneralIterDecon.h" -namespace mspass::algorithms::deconvolution -{ +#include "mspass/algorithms/deconvolution/WaterLevelDecon.h" +#include "mspass/seismic/CoreSeismogram.h" +#include "mspass/seismic/CoreTimeSeries.h" +#include "mspass/utility/AntelopePf.h" +#include "mspass/utility/MsPASSError.h" +#include +#include +#include +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; using namespace mspass::algorithms; -IterDeconType parse_for_itertype(const AntelopePf& md) -{ - string sval=md.get_string("deconvolution_type"); - if(sval=="water_level") - return WATER_LEVEL; - else if(sval=="least_square") - return LEAST_SQ; - else if(sval=="multi_taper") - return MULTI_TAPER; - else - throw MsPASSError("GeneralIterDecon: unknown or illegal value of deconvolution_type parameter=" - + sval,ErrorSeverity::Invalid); +IterDeconType parse_for_itertype(const AntelopePf &md) { + string sval = md.get_string("deconvolution_type"); + if (sval == "water_level") + return WATER_LEVEL; + else if (sval == "least_square") + return LEAST_SQ; + else if (sval == "multi_taper") + return MULTI_TAPER; + else + throw MsPASSError("GeneralIterDecon: unknown or illegal value of " + "deconvolution_type parameter=" + + sval, + ErrorSeverity::Invalid); } -double Linf(dmatrix& d) -{ - int nc,nr; - nr=d.rows(); - nc=d.columns(); - double *dmax; - /* This C++ generic algorithm works on raw pointers in a vector so - because a dmatrix stores the data as a contiguous memory block we - can use it in one call.*/ - dmax=max_element(d.get_address(0,0),d.get_address(nr-1,nc-1)); - return *dmax; +double Linf(dmatrix &d) { + int nc, nr; + nr = d.rows(); + nc = d.columns(); + double *dmax; + /* This C++ generic algorithm works on raw pointers in a vector so + because a dmatrix stores the data as a contiguous memory block we + can use it in one call.*/ + dmax = max_element(d.get_address(0, 0), d.get_address(nr - 1, nc - 1)); + return *dmax; } /* Similar function for L2 norm to Linf but here we use dnrm2. */ -double L2(dmatrix& d) -{ - int nd; - nd=d.rows()*d.columns(); - double dl2; - dl2=cblas_dnrm2(nd,d.get_address(0,0),1); - return dl2; +double L2(dmatrix &d) { + int nd; + nd = d.rows() * d.columns(); + double dl2; + dl2 = cblas_dnrm2(nd, d.get_address(0, 0), 1); + return dl2; } -GeneralIterDecon::GeneralIterDecon(AntelopePf &mdtoplevel) : ScalarDecon() -{ - const string base_error("GeneralIterDecon AntelopePf contructor: "); - stringstream ss; // used for constructing error messages - /* The pf used for initializing this object has Antelope Arr section - for each algorithm. Since the generalized iterative method is a - two-stage algorithm we have a section for the iterative algorithm - and a (variable) sectin for the preprocessor algorithm. We use - the AntelopePf to parse this instead of raw antelope pfget - C calls. */ - try { - AntelopePf md=mdtoplevel.get_branch("deconvolution_operator_type"); - AntelopePf mdgiter=md.get_branch("generalized_iterative_deconvolution"); - IterDeconType dct = parse_for_itertype(mdgiter); - this->decon_type=dct; - double ts,te; - ts=mdgiter.get("full_data_window_start"); - te=mdgiter.get("full_data_window_end"); - dwin=TimeWindow(ts,te); - ts=mdgiter.get("deconvolution_data_window_start"); - te=mdgiter.get("deconvolution_data_window_end"); - fftwin=TimeWindow(ts,te); - ts=mdgiter.get("noise_window_start"); - te=mdgiter.get("noise_window_end"); - nwin=TimeWindow(ts,te); - /* We need to make sure the noise and decon windows are inside the full_data_window*/ - if(fftwin.startdwin.end) - { - stringstream ss; - ss << base_error << "decon window error"<("noise_component"); - double target_dt=mdgiter.get("target_sample_interval"); - int maxns=static_cast((fftwin.end-fftwin.start)/target_dt); - ++maxns; // Add one - points not intervals - nfft=nextPowerOf2(maxns); - /* This should override this even if it was previously set */ - mdgiter.put("operator_nfft",nfft); - this->ScalarDecon::changeparameter(mdgiter); - shapingwavelet=ShapingWavelet(mdgiter,nfft); - /* Note minus sign here */ - time_shift = -(dwin.start)/target_dt; - AntelopePf mdleaf; - /* We make sure the window parameters in each algorithm mactch what - is set for this algorithm. Abort if they are not consistent. The - test code is a bit repetitious but a necessary evil to allow the message - to be clearer. */ - int n1,n2; //temporaries used below - needed because declrations illegal inside case - switch(decon_type) - { - case WATER_LEVEL: - mdleaf=md.get_branch("water_level"); - ts=mdleaf.get("deconvolution_data_window_start"); - te=mdleaf.get("deconvolution_data_window_end"); - if((ts!=fftwin.start) || (te!=fftwin.end)) - { - ss << base_error - <<"water level method specification of processing window is not" - " consistent with gid parameters"<("deconvolution_data_window_start"); - te=mdleaf.get("deconvolution_data_window_end"); - if((ts!=fftwin.start) || (te!=fftwin.end)) - { - ss << base_error - <<"least square method specification of processing window is not" - " consistent with gid parameters"<("deconvolution_data_window_start"); - te=mdleaf.get("deconvolution_data_window_end"); - if((ts!=fftwin.start) || (te!=fftwin.end)) - { - ss << base_error - <<"mulittaper method specification of processing window is not" - " consistent with gid parameters"<construct_weight_penalty_function(mdgiter); - /* Set convergencd parameters from md keys */ - iter_max=mdgiter.get("maximum_iterations"); - lw_linf_floor=mdgiter.get("lag_weight_Linf_floor"); - lw_l2_floor=mdgiter.get("lag_weight_rms_floor"); - resid_linf_prob=mdgiter.get("residual_noise_rms_probability_floor"); - resid_l2_tol=mdgiter.get("residual_fractional_improvement_floor"); - } catch(...) { - throw; +GeneralIterDecon::GeneralIterDecon(AntelopePf &mdtoplevel) : ScalarDecon() { + const string base_error("GeneralIterDecon AntelopePf contructor: "); + stringstream ss; // used for constructing error messages + /* The pf used for initializing this object has Antelope Arr section + for each algorithm. Since the generalized iterative method is a + two-stage algorithm we have a section for the iterative algorithm + and a (variable) sectin for the preprocessor algorithm. We use + the AntelopePf to parse this instead of raw antelope pfget + C calls. */ + try { + AntelopePf md = mdtoplevel.get_branch("deconvolution_operator_type"); + AntelopePf mdgiter = md.get_branch("generalized_iterative_deconvolution"); + IterDeconType dct = parse_for_itertype(mdgiter); + this->decon_type = dct; + double ts, te; + ts = mdgiter.get("full_data_window_start"); + te = mdgiter.get("full_data_window_end"); + dwin = TimeWindow(ts, te); + ts = mdgiter.get("deconvolution_data_window_start"); + te = mdgiter.get("deconvolution_data_window_end"); + fftwin = TimeWindow(ts, te); + ts = mdgiter.get("noise_window_start"); + te = mdgiter.get("noise_window_end"); + nwin = TimeWindow(ts, te); + /* We need to make sure the noise and decon windows are inside the + * full_data_window*/ + if (fftwin.start < dwin.start || fftwin.end > dwin.end) { + stringstream ss; + ss << base_error << "decon window error" << endl + << "Wavelet inversion window is not inside analysis window" << endl + << "full_data_window (analysis) range=" << dwin.start << " to " + << dwin.end << endl + << "decon_window (wavelet inversion) range=" << fftwin.start << " to " + << fftwin.end << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + noise_component = mdgiter.get("noise_component"); + double target_dt = mdgiter.get("target_sample_interval"); + int maxns = static_cast((fftwin.end - fftwin.start) / target_dt); + ++maxns; // Add one - points not intervals + nfft = nextPowerOf2(maxns); + /* This should override this even if it was previously set */ + mdgiter.put("operator_nfft", nfft); + this->ScalarDecon::changeparameter(mdgiter); + shapingwavelet = ShapingWavelet(mdgiter, nfft); + /* Note minus sign here */ + time_shift = -(dwin.start) / target_dt; + AntelopePf mdleaf; + /* We make sure the window parameters in each algorithm mactch what + is set for this algorithm. Abort if they are not consistent. The + test code is a bit repetitious but a necessary evil to allow the message + to be clearer. */ + int n1, n2; // temporaries used below - needed because declrations illegal + // inside case + switch (decon_type) { + case WATER_LEVEL: + mdleaf = md.get_branch("water_level"); + ts = mdleaf.get("deconvolution_data_window_start"); + te = mdleaf.get("deconvolution_data_window_end"); + if ((ts != fftwin.start) || (te != fftwin.end)) { + ss << base_error + << "water level method specification of processing window is not" + " consistent with gid parameters" + << endl + << "water level parameters: deconvolution_data_window_start=" << ts + << ", decon_window_end=" << te << endl + << "GID parameters: decon_window_start=" << fftwin.start + << ", decon_window_end=" << fftwin.end << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + preprocessor = new WaterLevelDecon(mdleaf); + break; + case LEAST_SQ: + mdleaf = md.get_branch("least_square"); + ts = mdleaf.get("deconvolution_data_window_start"); + te = mdleaf.get("deconvolution_data_window_end"); + if ((ts != fftwin.start) || (te != fftwin.end)) { + ss << base_error + << "least square method specification of processing window is not" + " consistent with gid parameters" + << endl + << "least square parameters: deconvolution_data_window_start=" << ts + << ", decon_window_end=" << te << endl + << "GID parameters: decon_window_start=" << fftwin.start + << ", decon_window_end=" << fftwin.end << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + preprocessor = new LeastSquareDecon(mdleaf); + break; + case MULTI_TAPER: + default: + mdleaf = md.get_branch("multi_taper"); + ts = mdleaf.get("deconvolution_data_window_start"); + te = mdleaf.get("deconvolution_data_window_end"); + if ((ts != fftwin.start) || (te != fftwin.end)) { + ss << base_error + << "mulittaper method specification of processing window is not" + " consistent with gid parameters" + << endl + << "multitaper parameters: deconvolution_data_window_start=" << ts + << ", decon_window_end=" << te << endl + << "GID parameters: decon_window_start=" << fftwin.start + << ", decon_window_end=" << fftwin.end << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + /* Here we also have to test the noise parameters, but the gid + window can be different fromt that passed to the mulittaper method. + Hence we test only that the mulittaper noise window is within the bounds + of the gid noise window */ + n1 = static_cast((fftwin.end - fftwin.start) / target_dt) + 1; + n2 = static_cast((nwin.end - nwin.start) / target_dt) + 1; + if (n1 > n2) { + ss << base_error << "inconsistent noise window specification" << endl + << "multitaper parameters specify taper length=" << n1 << " samples" + << endl + << "GID noise window parameters define noise_window_start=" + << nwin.start << " and noise_window_end=" << nwin.end << endl + << "The GID window has a length of " << n2 << " samples" << endl + << "GID implementation insists multitaper noise window be smaller " + "or equal to GID noise window" + << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + preprocessor = new MultiTaperXcorDecon(mdleaf); }; + /* Because this may evolve we make this a private method to + make changes easier to implement. */ + this->construct_weight_penalty_function(mdgiter); + /* Set convergencd parameters from md keys */ + iter_max = mdgiter.get("maximum_iterations"); + lw_linf_floor = mdgiter.get("lag_weight_Linf_floor"); + lw_l2_floor = mdgiter.get("lag_weight_rms_floor"); + resid_linf_prob = + mdgiter.get("residual_noise_rms_probability_floor"); + resid_l2_tol = mdgiter.get("residual_fractional_improvement_floor"); + } catch (...) { + throw; + }; } -GeneralIterDecon::~GeneralIterDecon() -{ - delete preprocessor; -} +GeneralIterDecon::~GeneralIterDecon() { delete preprocessor; } vector wtf; int nwtf; -void GeneralIterDecon::construct_weight_penalty_function(const Metadata& md) -{ - try { - const string base_error("GeneralIterDecon::construct_weight_penalty_function: "); - int i; - /* All options use this scale factor */ - double wtf_scale=md.get("lag_weight_penalty_scale_factor"); - if( (wtf_scale<=0) || (wtf_scale>1.0) ) - throw MsPASSError(base_error - + "Illegal value for parameter lag_weight_penalty_scale_factor\n" - + "Must be a number in the interval (0,1]",ErrorSeverity::Invalid); - /* This keyword defines the options for defining the penalty function */ - string wtf_type=md.get_string("lag_weight_penalty_function"); - /* Most options use this parameter so we set it outside the - conditional. With usual use of pffiles this should not be a big issue. - */ - nwtf=md.get("lag_weight_function_width"); - /* nwtf must be forced to be an odd number to force the function to - be symmetric. */ - if((nwtf%2)==0) ++nwtf; - if(wtf_type=="boxcar") - { - for(i=0; ipreprocessor->actual_output(); - /* assume the wavelet is symmetric and get the half max sample position - from positive side only */ - int i0=ir.sample_number(0.0); - double peakirval=ir.s[i0]; - double halfmax=peakirval/2.0; - for(i=i0; i("lag_weight_penalty_scale_factor"); + if ((wtf_scale <= 0) || (wtf_scale > 1.0)) + throw MsPASSError( + base_error + + "Illegal value for parameter lag_weight_penalty_scale_factor\n" + + "Must be a number in the interval (0,1]", + ErrorSeverity::Invalid); + /* This keyword defines the options for defining the penalty function */ + string wtf_type = md.get_string("lag_weight_penalty_function"); + /* Most options use this parameter so we set it outside the + conditional. With usual use of pffiles this should not be a big issue. + */ + nwtf = md.get("lag_weight_function_width"); + /* nwtf must be forced to be an odd number to force the function to + be symmetric. */ + if ((nwtf % 2) == 0) + ++nwtf; + if (wtf_type == "boxcar") { + for (i = 0; i < nwtf; ++i) + wtf.push_back(wtf_scale); + } else if (wtf_type == "cosine_taper") { + /* This creates one cycle of cosine function with wavelength(period) + of nwtf, offset by 0.5 and scaled in amplitude. That means it tapers to + 1 at edges and has a minimum value of 1-wtf_scale. */ + double period = (double)(nwtf + 1); // set period so points one left and + // right (1) can be dropped + for (i = 0; i < nwtf; ++i) { + double f; + f = 0.5 * (-cos(2.0 * M_PI * ((double)(i + 1)) / period)); + f += 0.5; + f = 1.0 - wtf_scale * f; // This makes minimum f=1-wtf_scale + /* Avoid negatives */ + if (f < 0) + f = 0.0; + wtf.push_back(f); + } + } else if (wtf_type == "shaping_wavelet") { + /* In this method we use the points in the wavelet at 1/2 max. + We extract it from the shaping wavelet - the TimeSeries is baggage + but not an efficiency issue since this is called only once */ + CoreTimeSeries ir = this->preprocessor->actual_output(); + /* assume the wavelet is symmetric and get the half max sample position + from positive side only */ + int i0 = ir.sample_number(0.0); + double peakirval = ir.s[i0]; + double halfmax = peakirval / 2.0; + for (i = i0; i < ir.npts(); ++i) { + if (ir.s[i] < halfmax) + break; + } + int halfwidth = i; + int peakwidth = 2 * i + 1; // guaranteed odd numbers + for (i = i0 - halfwidth; i < (i0 + peakwidth); ++i) { + double f; + f = ir.s[i]; + f /= peakirval; // make sure scaled so peaks is 1.0 + f *= wtf_scale; + f = 1.0 - f; + wtf.push_back(f); + } + } else { + throw MsPASSError( + base_error + + "illegal value for parameter lag_weight_penalty_function=" + + wtf_type, + ErrorSeverity::Invalid); + } + } catch (...) { + throw; + }; } /* Some helpers for new implementation.*/ /* This procedure returns a vector of 3c amplitudes from a dmatrix extracted from a Seismogram. */ -vector amp3c(dmatrix& d) -{ - vector result; - int ncol=d.columns(); - int i,k; - for(i=0; i amp3c(dmatrix &d) { + vector result; + int ncol = d.columns(); + int i, k; + for (i = 0; i < ncol; ++i) { + double mag; + for (k = 0, mag = 0.0; k < 3; ++k) { + mag += d(k, i) * d(k, i); } - return result; + result.push_back(sqrt(mag)); + } + return result; } -int GeneralIterDecon::load(const CoreSeismogram& draw, TimeWindow dwin_in) -{ - try { - dwin=dwin_in; - /* First we load the requested window. Note we MUST always make this window - a bit larger than the range of desired lags as the iterative algorithm will - not allow lags at the edges (defined by a construction parameter wavelet_pad) - */ - d_all=WindowData(draw,dwin); - ndwin=d_all.npts(); - return 0; - } catch(...) { - throw; - }; +int GeneralIterDecon::load(const CoreSeismogram &draw, TimeWindow dwin_in) { + try { + dwin = dwin_in; + /* First we load the requested window. Note we MUST always make this window + a bit larger than the range of desired lags as the iterative algorithm will + not allow lags at the edges (defined by a construction parameter + wavelet_pad) + */ + d_all = WindowData(draw, dwin); + ndwin = d_all.npts(); + return 0; + } catch (...) { + throw; + }; } -int GeneralIterDecon::loadnoise(const CoreSeismogram& draw, TimeWindow nwin_in) -{ - try { - nwin=nwin_in; - n=WindowData(draw,nwin); - nnwin=n.npts(); - double ret=this->compute_resid_linf_floor(); - if(ret>0) - return 0; - else - return 1; - } catch(...) { - throw; - }; +int GeneralIterDecon::loadnoise(const CoreSeismogram &draw, + TimeWindow nwin_in) { + try { + nwin = nwin_in; + n = WindowData(draw, nwin); + nnwin = n.npts(); + double ret = this->compute_resid_linf_floor(); + if (ret > 0) + return 0; + else + return 1; + } catch (...) { + throw; + }; } -int GeneralIterDecon::load(const CoreSeismogram& draw, - TimeWindow dwin, TimeWindow nwin) -{ - try { - int iretn, iret; - iretn=this->loadnoise(draw,nwin); - iret=this->load(draw,dwin); - return (iretn+iret); - } catch(...) { - throw; - }; +int GeneralIterDecon::load(const CoreSeismogram &draw, TimeWindow dwin, + TimeWindow nwin) { + try { + int iretn, iret; + iretn = this->loadnoise(draw, nwin); + iret = this->load(draw, dwin); + return (iretn + iret); + } catch (...) { + throw; + }; } /* These are the set of private methods called from the process method */ -void GeneralIterDecon::update_residual_matrix(ThreeCSpike spk) -{ - try { - const string base_error("GeneralIterDecon::update_residual_matrix: "); - int ncol=this->r.u.columns(); - int col0=spk.col-actual_o_0;; - /* Avoid seg faults and test range here. This is an exception condition - * because if the lag weights are created correctly the edge conditions - * here should not be possible */ - if(col0<0) throw MsPASSError(base_error - + "Coding problem - computed lag is negative. lag_weights array is probably incorrect" - ,ErrorSeverity::Fatal); - if((col0+actual_o_fir.size())>=ncol) throw MsPASSError(base_error - + "Coding problem - computed lag is too large and would overflow residual matrix and seg fault.\n" +void GeneralIterDecon::update_residual_matrix(ThreeCSpike spk) { + try { + const string base_error("GeneralIterDecon::update_residual_matrix: "); + int ncol = this->r.u.columns(); + int col0 = spk.col - actual_o_0; + ; + /* Avoid seg faults and test range here. This is an exception condition + * because if the lag weights are created correctly the edge conditions + * here should not be possible */ + if (col0 < 0) + throw MsPASSError(base_error + + "Coding problem - computed lag is negative. " + "lag_weights array is probably incorrect", + ErrorSeverity::Fatal); + if ((col0 + actual_o_fir.size()) >= ncol) + throw MsPASSError(base_error + + "Coding problem - computed lag is too large and " + "would overflow residual matrix and seg fault.\n" - +"lag_weights array is probably incorrect",ErrorSeverity::Fatal); - for(int k=0; k<3; ++k) - { - /*Use the gsl version of daxpy hre to avoid type collisions with perf.h. */ - cblas_daxpy(actual_o_fir.size(),-spk.u[k],&(actual_o_fir[0]),1,this->r.u.get_address(k,col0),3); - } - } catch(...) { - throw; - }; + + "lag_weights array is probably incorrect", + ErrorSeverity::Fatal); + for (int k = 0; k < 3; ++k) { + /*Use the gsl version of daxpy hre to avoid type collisions with perf.h. + */ + cblas_daxpy(actual_o_fir.size(), -spk.u[k], &(actual_o_fir[0]), 1, + this->r.u.get_address(k, col0), 3); + } + } catch (...) { + throw; + }; } /* This method adds (not multiply add) the weighting function created by the constructor centered at lag = col. Because a range can be hit multiple @@ -367,33 +366,31 @@ we we use an explicit loop instead ofa call to daxpy as in the residual update method. Note like update_residual_matrix we assume nwtf is correct and don't test for memory faults for efficiency */ -void GeneralIterDecon::update_lag_weights(int col) -{ - try { - int i,ii; - for(i=0,ii=col; i amps(amp3c(n.u)); - sort(amps.begin(),amps.end()); - int floor_position; - floor_position=static_cast(resid_linf_prob*((double)amps.size())); - resid_linf_floor=amps[floor_position]; - return resid_linf_floor; - } catch(...) { - throw; - }; +double GeneralIterDecon::compute_resid_linf_floor() { + try { + /*Note - this needs an enhancement. We should not include points + in a padd region accounting for the inverse filter padding. */ + vector amps(amp3c(n.u)); + sort(amps.begin(), amps.end()); + int floor_position; + floor_position = static_cast(resid_linf_prob * ((double)amps.size())); + resid_linf_floor = amps[floor_position]; + return resid_linf_floor; + } catch (...) { + throw; + }; } /*! \brief Trim impulse response function for efficiency. @@ -418,324 +415,328 @@ a few samples wide should create a pretty effective envelope estimate. \return a copy of d shortened on both ends. */ -CoreTimeSeries trim(const CoreTimeSeries& d,double floor=0.005) -{ - try { - vector work; - /* First till work with absolute values of d.s from t=0 to end */ - int i,ii,k,kk; - int i0=d.sample_number(0.0); - for(i=i0; i(smoother_width); // mean calculation - if(avg(preprocessor)->loadnoise(nts.s); - } - /* For this case of receiver function deconvolution we always get the - wavelet from component 2 - assumed here to be Z or L. */ - CoreTimeSeries srcwavelet(ExtractComponent(d_decon,2)); - for(int k=0; k<3; ++k) - { - CoreTimeSeries dcomp(ExtractComponent(d_decon,k)); - /* Need the qualifier or we get the wrong overloaded - * load method */ - preprocessor->ScalarDecon::load(srcwavelet.s,dcomp.s); - preprocessor->process(); - vector deconout(preprocessor->getresult()); - int copysize=deconout.size(); - if(copysize>d_decon.npts()) copysize=d_decon.npts(); - cblas_dcopy(copysize,&(deconout[0]),1,uwork.get_address(k,0),3); - } - d_decon.u=uwork; - /* The inverse wavelet and the actual output signals are determined in all - current algorithms from srcwavelet. Hence, what is now stored will work. - If this is extended make sure that condition is satisfied. +void GeneralIterDecon::process() { + const string base_error("GeneralIterDecon::process method: "); + try { + /* We first have to run the signal processing style deconvolution. + This is defined by the base pointer available through the symbol + preprocessor. All those altorithms require load methods to be called + to initate the computation. A complication is that the multitaper is + different and requires a noise signal to also be loaded through loadnoise. + That complicates this a bit below, but the flow of the algorithm should + still be clear. Outer loop is over the three components were we assemble + a full 3c record. Note this is the same algorithm use in trace_decon + for anything but this iterative algorithm. + */ + /* d_decon will hold the preprocessor output. We normally expect to + derive it by windowing of t_all. We assume WindowData will be + successful - constructor should guarantee that. */ + d_decon = WindowData(d_all, fftwin); + dmatrix uwork(d_decon.u); + uwork.zero(); + /* We assume loadnoise has been called previously to set put the + right data here. We need a scalar function to pass to the multtitaper + algorithm though. */ + if (decon_type == MULTI_TAPER) { + CoreTimeSeries nts(ExtractComponent(n, noise_component)); + dynamic_cast(preprocessor)->loadnoise(nts.s); + } + /* For this case of receiver function deconvolution we always get the + wavelet from component 2 - assumed here to be Z or L. */ + CoreTimeSeries srcwavelet(ExtractComponent(d_decon, 2)); + for (int k = 0; k < 3; ++k) { + CoreTimeSeries dcomp(ExtractComponent(d_decon, k)); + /* Need the qualifier or we get the wrong overloaded + * load method */ + preprocessor->ScalarDecon::load(srcwavelet.s, dcomp.s); + preprocessor->process(); + vector deconout(preprocessor->getresult()); + int copysize = deconout.size(); + if (copysize > d_decon.npts()) + copysize = d_decon.npts(); + cblas_dcopy(copysize, &(deconout[0]), 1, uwork.get_address(k, 0), 3); + } + d_decon.u = uwork; + /* The inverse wavelet and the actual output signals are determined in all + current algorithms from srcwavelet. Hence, what is now stored will work. + If this is extended make sure that condition is satisfied. - We extract the inverse filter and use a time domain convolution with - data in the longer time window. Note for efficiency may want to - convert this to a frequency domain convolution if it proves to be - a bottleneck */ - //double dt; - //dt=this->shapingwavelet.sample_interval(); - //TimeSeries winv=this->preprocessor->inverse_wavelet(tshift,d_decon.t0()); -//DEBUG -//cerr << "inverse wavelet tshift="<shapingwavelet.sample_interval(); + // TimeSeries winv=this->preprocessor->inverse_wavelet(tshift,d_decon.t0()); + // DEBUG + // cerr << "inverse wavelet tshift="<preprocessor->actual_output()); - //DEBUG - /* - cerr << "Actual output raw"<preprocessor->actual_output()); + // DEBUG + /* + cerr << "Actual output raw"<::iterator aoptr; - for(aoptr=actual_o_fir.begin();aoptr!=actual_o_fir.end();++aoptr) - (*aoptr) /= peak_scale; - /* This is the size of the inverse wavelet convolution transient - we use it to prevent iterations in transient region of the deconvolved - data */ - wavelet_pad=winv.s.size(); - if(2*wavelet_pad>ndwin) - { - stringstream ss; - ss << base_error << "Inadequate data window size" - <::iterator aoptr; + for (aoptr = actual_o_fir.begin(); aoptr != actual_o_fir.end(); ++aoptr) + (*aoptr) /= peak_scale; + /* This is the size of the inverse wavelet convolution transient + we use it to prevent iterations in transient region of the deconvolved + data */ + wavelet_pad = winv.s.size(); + if (2 * wavelet_pad > ndwin) { + stringstream ss; + ss << base_error << "Inadequate data window size" << endl + << "trimmed FIR filter size for actual output signal=" << wavelet_pad + << endl + << "Data window length=" << ndwin << endl + << "Window size must be larger than two times FIR filter size" << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + /* These two signals should be trimmed by winv.npts() on both ends to remove + sections that are pure edge transients. + REMOVE me when that is done*/ - r=sparse_convolve(winv,d_all); - /* Replace n by convolution with inverse wavelet to get the levels correct */ - n=sparse_convolve(winv,n); - TimeWindow trimwin; - trimwin.start=n.t0()+(n.dt())*((double)(winv.npts())); - trimwin.end=n.endtime()-(n.dt())*((double)(winv.npts())); - n=WindowData(n,trimwin); - //double nfloor; - //nfloor=compute_resid_linf_floor(); - //DEBUG - for debug always print this. Should be a verbose option - //cerr << "Computed noise floor="<update_lag_weights(imax); - /* Subtract the actual output from the data at lag imax. Note - We don't test validity of the lag in spk, but depend on dmatrix to throw - an exception of the range is invalid */ - this->update_residual_matrix(spk); - ++iter_count; - } while(this->has_not_converged()); - if(iter_count>=iter_max) - throw MsPASSError("GeneralIterDecon::process did not converge" - ,ErrorSeverity::Suspect); - } catch(...) { - throw; - }; + /* d_all now contains the deconvolved data. Now enter the + generalized iterative method recursion */ + int i, k; + lag_weights.clear(); + vector amps, wamps; // raw and weighted amplitudes + amps.reserve(r.npts()); + wamps.reserve(r.npts()); + /* We need these iterators repeatedly in the main loop below */ + vector::iterator amax; + for (i = 0; i < r.npts(); ++i) + lag_weights.push_back(1.0); + // DEBUG - temporarily disabled for testing + // for(i=0; iupdate_lag_weights(imax); + /* Subtract the actual output from the data at lag imax. Note + We don't test validity of the lag in spk, but depend on dmatrix to throw + an exception of the range is invalid */ + this->update_residual_matrix(spk); + ++iter_count; + } while (this->has_not_converged()); + if (iter_count >= iter_max) + throw MsPASSError("GeneralIterDecon::process did not converge", + ErrorSeverity::Suspect); + } catch (...) { + throw; + }; } -bool GeneralIterDecon::has_not_converged() -{ - try { - double lw_linf_now,lw_l2_now,resid_linf_now,resid_l2_now; - vector::iterator vptr; - vptr=max_element(lag_weights.begin(),lag_weights.end()); - lw_linf_now=(*vptr); - lw_l2_now=cblas_dnrm2(lag_weights.size(),&(lag_weights[0]),1); - resid_linf_now=Linf(r.u); - resid_l2_now=L2(r.u); - /* DEBUG - saving the convergence vector - after testing delete*/ - lw_linf_history.push_back(lw_linf_now); - lw_l2_history.push_back(lw_l2_now); - resid_linf_history.push_back(resid_linf_now); - resid_l2_history.push_back(resid_l2_now); - //DEBUG - cerr << "Iteration count="<::iterator sptr; - int k,resultcol; - for(sptr=spikes.begin(); sptr!=spikes.end(); ++sptr) - { - if(((sptr->col)<0)||((sptr->col)>=result.npts())) throw MsPASSError(base_error - + "Coding error - spike lag is outside output data range" - ,ErrorSeverity::Fatal); - resultcol=(sptr->col)-delta_col; - for(k=0; k<3; ++k) result.u(k,resultcol)=sptr->u[k]; - } - string wtype=this->shapingwavelet.type(); - if(wtype!="none") - { - CoreTimeSeries w(this->shapingwavelet.impulse_response()); - result=sparse_convolve(w,result); - } - return result; - } catch(...) { - throw; - }; +CoreSeismogram GeneralIterDecon::getresult() { + try { + string base_error("GeneralIterDecon::getresult: "); + CoreSeismogram result(d_all); + /* We will make the output the size of the processing window for the + iteration. May want to alter this to trim the large lag that would not + be allowed due to wavelet duration anyway, BUT for GID method the + wavelet should be compact enough that should be a small factor. Hence + for now I omit that complexity until proven to be an issue. */ + result = WindowData(result, dwin); + result.u.zero(); + /* The spike sequences uses the time reference of the data in the + private copy r. This is the computed offset in samples to correct + lags in the spikes list container to be at correct time in result */ + double dt0; + int delta_col; + dt0 = result.t0() - r.t0(); + delta_col = round(dt0 / r.dt()); + list::iterator sptr; + int k, resultcol; + for (sptr = spikes.begin(); sptr != spikes.end(); ++sptr) { + if (((sptr->col) < 0) || ((sptr->col) >= result.npts())) + throw MsPASSError( + base_error + + "Coding error - spike lag is outside output data range", + ErrorSeverity::Fatal); + resultcol = (sptr->col) - delta_col; + for (k = 0; k < 3; ++k) + result.u(k, resultcol) = sptr->u[k]; + } + string wtype = this->shapingwavelet.type(); + if (wtype != "none") { + CoreTimeSeries w(this->shapingwavelet.impulse_response()); + result = sparse_convolve(w, result); + } + return result; + } catch (...) { + throw; + }; } -Metadata GeneralIterDecon::QCMetrics() -{ - cerr << "QCMetrics method not yet implemented"<change_size(nfft_from_win); - } - damp=md.get_double("damping_factor"); - /* Note this depends on nfft inheritance from FFTDeconOperator. - * That is a bit error prone with changes*/ - shapingwavelet=ShapingWavelet(md,nfft); - return 0; - } catch(...) { - throw; - }; +int LeastSquareDecon::read_metadata(const Metadata &md) { + try { + const string base_error("SimpleLeastTaperDecon::read_metadata method: "); + int nfft_from_win = ComputeFFTLength(md); + // window based nfft always overrides that extracted directly from md */ + if (nfft_from_win != nfft) { + this->change_size(nfft_from_win); + } + damp = md.get_double("damping_factor"); + /* Note this depends on nfft inheritance from FFTDeconOperator. + * That is a bit error prone with changes*/ + shapingwavelet = ShapingWavelet(md, nfft); + return 0; + } catch (...) { + throw; + }; } /* This constructor is little more than a call to read_metadata for this operator */ -LeastSquareDecon::LeastSquareDecon(const Metadata &md) - : FFTDeconOperator(md) -{ - try { - this->read_metadata(md); - } catch(...) { - throw; - } +LeastSquareDecon::LeastSquareDecon(const Metadata &md) : FFTDeconOperator(md) { + try { + this->read_metadata(md); + } catch (...) { + throw; + } } /* This method is really an alias for read_metadata for this operator */ -void LeastSquareDecon::changeparameter(const Metadata &md) -{ - try { - this->read_metadata(md); - } catch(...) { - throw; - }; +void LeastSquareDecon::changeparameter(const Metadata &md) { + try { + this->read_metadata(md); + } catch (...) { + throw; + }; } -LeastSquareDecon::LeastSquareDecon(const Metadata &md, - const vector &w,const vector &d) -{ - try { - this->read_metadata(md); - } catch(...) { - throw; - }; - wavelet=w; - data=d; +LeastSquareDecon::LeastSquareDecon(const Metadata &md, const vector &w, + const vector &d) { + try { + this->read_metadata(md); + } catch (...) { + throw; + }; + wavelet = w; + data = d; } -void LeastSquareDecon::process() -{ +void LeastSquareDecon::process() { - const string base_error("LeastSquareDecon::process: "); - //apply fft to the input trace data - if(data.size()get_shaping_wavelet()); - rf_fft=(*sw.wavelet())*rf_fft; + // apply shaping wavelet but only to rf estimate - actual output and + // inverse_wavelet methods apply it to when needed there for efficiency + ShapingWavelet sw(this->get_shaping_wavelet()); + rf_fft = (*sw.wavelet()) * rf_fft; - //ifft gets result - gsl_fft_complex_inverse(rf_fft.ptr(), 1, nfft, wavetable, workspace); - if(sample_shift>0) - { - for(int k=sample_shift; k>0; k--) - result.push_back(rf_fft[nfft-k].real()); - for(int k=0; k 0) { + for (int k = sample_shift; k > 0; k--) + result.push_back(rf_fft[nfft - k].real()); + for (int k = 0; k < data.size() - sample_shift; k++) + result.push_back(rf_fft[k].real()); + } else if (sample_shift == 0) { + for (int k = 0; k < data.size(); k++) + result.push_back(rf_fft[k].real()); + } else { + throw MsPASSError(base_error + "Coding error - trying to use an illegal " + "negative time shift parameter", + ErrorSeverity::Fatal); + } } -CoreTimeSeries LeastSquareDecon::actual_output() -{ - try { - ComplexArray W(nfft,&(wavelet[0])); - gsl_fft_complex_forward(W.ptr(),1,nfft,wavetable,workspace); - ComplexArray ao_fft; - ao_fft=winv*W; - /* We always apply the shaping wavelet - this perhaps should be optional - but probably better done with a none option for the shaping wavelet */ - ao_fft=(*shapingwavelet.wavelet())*ao_fft; - gsl_fft_complex_inverse(ao_fft.ptr(),1,nfft,wavetable,workspace); - vector ao; - ao.reserve(nfft); - for(int k=0; kshapingwavelet.sample_interval(); +CoreTimeSeries LeastSquareDecon::actual_output() { + try { + ComplexArray W(nfft, &(wavelet[0])); + gsl_fft_complex_forward(W.ptr(), 1, nfft, wavetable, workspace); + ComplexArray ao_fft; + ao_fft = winv * W; + /* We always apply the shaping wavelet - this perhaps should be optional + but probably better done with a none option for the shaping wavelet */ + ao_fft = (*shapingwavelet.wavelet()) * ao_fft; + gsl_fft_complex_inverse(ao_fft.ptr(), 1, nfft, wavetable, workspace); + vector ao; + ao.reserve(nfft); + for (int k = 0; k < ao_fft.size(); ++k) + ao.push_back(ao_fft[k].real()); + /* We always shift this wavelet to the center of the data vector. + We handle the time through the CoreTimeSeries object. */ + int i0 = nfft / 2; + ao = circular_shift(ao, i0); + CoreTimeSeries result(nfft); + /* Getting dt from here is unquestionably a flaw in the api, but will + retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ + double dt = this->shapingwavelet.sample_interval(); - result.set_t0(-dt*((double)i0)); - result.set_dt(dt); - result.set_live(); - result.set_npts(nfft); - result.set_tref(TimeReferenceType::Relative); - for(int k=0;k(result.s); - return result; - } catch(...) { - throw; - }; + result.set_t0(-dt * ((double)i0)); + result.set_dt(dt); + result.set_live(); + result.set_npts(nfft); + result.set_tref(TimeReferenceType::Relative); + for (int k = 0; k < nfft; ++k) + result.s[k] = ao[k]; + result.s = normalize(result.s); + return result; + } catch (...) { + throw; + }; } -CoreTimeSeries LeastSquareDecon::inverse_wavelet(const double t0parent) -{ - try { - /* Getting dt from here is unquestionably a flaw in the api, but will - * retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ - double dt=this->shapingwavelet.sample_interval(); - return (this->FourierInverse(this->winv, - *shapingwavelet.wavelet(),dt,t0parent)); - } catch(...) { - throw; - }; +CoreTimeSeries LeastSquareDecon::inverse_wavelet(const double t0parent) { + try { + /* Getting dt from here is unquestionably a flaw in the api, but will + * retain for now. Perhaps should a copy of dt in the ScalarDecon + * object. */ + double dt = this->shapingwavelet.sample_interval(); + return (this->FourierInverse(this->winv, *shapingwavelet.wavelet(), dt, + t0parent)); + } catch (...) { + throw; + }; } -CoreTimeSeries LeastSquareDecon::inverse_wavelet() -{ - try{ +CoreTimeSeries LeastSquareDecon::inverse_wavelet() { + try { return this->inverse_wavelet(0.0); - }catch(...){throw;}; + } catch (...) { + throw; + }; } -Metadata LeastSquareDecon::QCMetrics() -{ +Metadata LeastSquareDecon::QCMetrics() { /* Return only an empty Metadata container. Done as it is easier to maintain the code letting python do this work. This also anticipates new metrics being added which would be @@ -193,4 +183,4 @@ Metadata LeastSquareDecon::QCMetrics() Metadata md; return md; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/MTPowerSpectrumEngine.cc b/cxx/src/lib/algorithms/deconvolution/MTPowerSpectrumEngine.cc index fe25fb286..f829a6a25 100644 --- a/cxx/src/lib/algorithms/deconvolution/MTPowerSpectrumEngine.cc +++ b/cxx/src/lib/algorithms/deconvolution/MTPowerSpectrumEngine.cc @@ -1,243 +1,238 @@ -#include -#include "mspass/utility/utility.h" #include "mspass/algorithms/deconvolution/MTPowerSpectrumEngine.h" -#include "mspass/algorithms/deconvolution/dpss.h" #include "mspass/algorithms/deconvolution/ComplexArray.h" +#include "mspass/algorithms/deconvolution/dpss.h" +#include "mspass/utility/utility.h" +#include /* This C function is defined in FFTDeconOperator.h but it has a lot of other baggage that could create mysterious problems so we just define it again here. Maintenanc issue if the api changes.*/ extern "C" { - unsigned int nextPowerOf2(unsigned int n); +unsigned int nextPowerOf2(unsigned int n); } -namespace mspass::algorithms::deconvolution -{ +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; -MTPowerSpectrumEngine::MTPowerSpectrumEngine() -{ - taperlen=0; - ntapers=0; - nfft=0; - tbp=0.0; - deltaf=1.0; - operator_dt=1.0; - wavetable=NULL; - workspace=NULL; +MTPowerSpectrumEngine::MTPowerSpectrumEngine() { + taperlen = 0; + ntapers = 0; + nfft = 0; + tbp = 0.0; + deltaf = 1.0; + operator_dt = 1.0; + wavetable = NULL; + workspace = NULL; } MTPowerSpectrumEngine::MTPowerSpectrumEngine(const int winsize, - const double tbpin, - const int ntpin, - const int nfftin, - const double dtin) -{ - taperlen=winsize; - tbp=tbpin; - ntapers=ntpin; - if(nfftinset_df(dtin); - int nseq=static_cast(2.0*tbp); - if(ntapers>nseq) - { - cerr << "MTPowerSpectrumEngine (WARNING): requested number of tapers="<((taperlen+1)/2); + for (i = 0; i < ntapers; ++i) { + int lh; // matches Prieto algorithm name - see multitaper module + if (taperlen % 2) + lh = static_cast((taperlen + 1) / 2); else - lh = static_cast(taperlen/2); - if(tapers(i,lh)<0.0) - { - for(j=0;j(taperlen / 2); + if (tapers(i, lh) < 0.0) { + for (j = 0; j < taperlen; ++j) + tapers(i, j) = -tapers(i, j); } } - wavetable=gsl_fft_complex_wavetable_alloc (nfft); - workspace=gsl_fft_complex_workspace_alloc (nfft); + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); } -MTPowerSpectrumEngine::MTPowerSpectrumEngine(const MTPowerSpectrumEngine& parent) : tapers(parent.tapers) -{ - taperlen=parent.taperlen; - ntapers=parent.ntapers; - nfft=parent.nfft; - tbp=parent.tbp; - operator_dt=parent.operator_dt; - deltaf=parent.deltaf; - wavetable=gsl_fft_complex_wavetable_alloc (nfft); - workspace=gsl_fft_complex_workspace_alloc (nfft); +MTPowerSpectrumEngine::MTPowerSpectrumEngine( + const MTPowerSpectrumEngine &parent) + : tapers(parent.tapers) { + taperlen = parent.taperlen; + ntapers = parent.ntapers; + nfft = parent.nfft; + tbp = parent.tbp; + operator_dt = parent.operator_dt; + deltaf = parent.deltaf; + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); } -MTPowerSpectrumEngine::~MTPowerSpectrumEngine() -{ - if(wavetable!=NULL) gsl_fft_complex_wavetable_free (wavetable); - if(workspace!=NULL) gsl_fft_complex_workspace_free (workspace); +MTPowerSpectrumEngine::~MTPowerSpectrumEngine() { + if (wavetable != NULL) + gsl_fft_complex_wavetable_free(wavetable); + if (workspace != NULL) + gsl_fft_complex_workspace_free(workspace); } -MTPowerSpectrumEngine& MTPowerSpectrumEngine::operator=(const MTPowerSpectrumEngine& parent) -{ - if(&parent!=this) - { - taperlen=parent.taperlen; - ntapers=parent.ntapers; - nfft=parent.nfft; - tbp=parent.tbp; - operator_dt=parent.operator_dt; - deltaf=parent.deltaf; - tapers=parent.tapers; - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); +MTPowerSpectrumEngine & +MTPowerSpectrumEngine::operator=(const MTPowerSpectrumEngine &parent) { + if (&parent != this) { + taperlen = parent.taperlen; + ntapers = parent.ntapers; + nfft = parent.nfft; + tbp = parent.tbp; + operator_dt = parent.operator_dt; + deltaf = parent.deltaf; + tapers = parent.tapers; + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); } return *this; } -PowerSpectrum MTPowerSpectrumEngine::apply(const TimeSeries& d) -{ - try{ +PowerSpectrum MTPowerSpectrumEngine::apply(const TimeSeries &d) { + try { int k; /* Used to test for operator sample interval against data sample interval. - We don't use a epsilon comparison as slippery clock data sometime shave sample - rates small percentage difference from nominal.*/ + We don't use a epsilon comparison as slippery clock data sometime shave + sample rates small percentage difference from nominal.*/ const double DT_FRACTION_TOLERANCE(0.001); const string algorithm("MTPowerSpectrumEngine"); /* We need to define this here to allow posting problems to elog.*/ PowerSpectrum result; - int dsize=d.npts(); + int dsize = d.npts(); vector work; work.reserve(this->nfft); - double dtfrac=fabs(d.dt()-this->operator_dt)/this->operator_dt; - if(dtfrac>DT_FRACTION_TOLERANCE) - { + double dtfrac = fabs(d.dt() - this->operator_dt) / this->operator_dt; + if (dtfrac > DT_FRACTION_TOLERANCE) { stringstream ss; - ss << "Date sample interval="<taperlen) - { + ss << "Received data window of length=" << d.npts() << " samples" << endl + << "Operator length=" << taperlen << endl + << "Results may be unreliable" << endl; + result.elog.log_error(algorithm, string(ss.str()), + ErrorSeverity::Suspect); + for (k = 0; k < taperlen; ++k) + work.push_back(0.0); + for (k = 0; k < dsize; ++k) + work[k] = d.s[k]; + } else { + if (dsize > taperlen) { stringstream ss; - ss<<"Received data window of length="< spec(this->apply(work)); - result=PowerSpectrum(dynamic_cast(d), - spec,deltaf,string("Multitaper"),0.0,d.dt(),d.npts()); + result = PowerSpectrum(dynamic_cast(d), spec, deltaf, + string("Multitaper"), 0.0, d.dt(), d.npts()); /* We post these to metadata for the generic PowerSpectrum object. */ - result.put("time_bandwidth_product",tbp); - result.put("number_tapers",ntapers); + result.put("time_bandwidth_product", tbp); + result.put("number_tapers", ntapers); return result; - }catch(...){throw;}; + } catch (...) { + throw; + }; } -vector MTPowerSpectrumEngine::apply(const vector& d) -{ +vector MTPowerSpectrumEngine::apply(const vector &d) { /* This function must be dogmatic about d size = taperlen*/ - if(d.size() != this->taperlen) - { + if (d.size() != this->taperlen) { stringstream ss; - ss<<"MTPowerSpectrumEngine::apply method: input data vector length of " - << d.size()< tdata; tdata.reserve(ntapers); vector work; work.reserve(nfft); - for(i=0; i result; result.reserve(this->nf()); - for(j=0;jnf();++j) result.push_back(0.0); - for(i=0;inf();++j) - { + for (j = 0; j < this->nf(); ++j) + result.push_back(0.0); + for (i = 0; i < ntapers; ++i) { + for (j = 0; j < this->nf(); ++j) { mspass::algorithms::deconvolution::Complex64 z; - double rp,ip; + double rp, ip; z = tdata[i][j]; rp = z.real(); ip = z.imag(); - result[j] += rp*rp + ip*ip; + result[j] += rp * rp + ip * ip; } } /* Scale using Parseval's theorem - this is adapted from Prieto's @@ -247,24 +242,24 @@ vector MTPowerSpectrumEngine::apply(const vector& d) divide by data vector length. Not sure his formula is right as seems to me it shouild be nfft not npts. */ - double specssq(0.0),scale; - for(auto p=result.begin();p!=result.end();++p) specssq += (*p); - scale = ssq/(specssq*this->df()); + double specssq(0.0), scale; + for (auto p = result.begin(); p != result.end(); ++p) + specssq += (*p); + scale = ssq / (specssq * this->df()); /* Scaling for fft implementation - Established from zero pad tests it has to be this factor */ scale /= static_cast(d.size()); - for(j=0;jnf();++j) result[j] *= scale; + for (j = 0; j < this->nf(); ++j) + result[j] *= scale; return result; } -vector MTPowerSpectrumEngine::frequencies() -{ +vector MTPowerSpectrumEngine::frequencies() { vector f; /* If taperlen is odd this still works according to gsl documentation.*/ - for(int i=0;inf();++i) - { + for (int i = 0; i < this->nf(); ++i) { /* Here we assume i=0 frequency is 0 */ - f.push_back(deltaf*((double)i)); + f.push_back(deltaf * ((double)i)); } return f; } -} //end namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/MultiTaperSpecDivDecon.cc b/cxx/src/lib/algorithms/deconvolution/MultiTaperSpecDivDecon.cc index 12a9e6f91..646c934f3 100644 --- a/cxx/src/lib/algorithms/deconvolution/MultiTaperSpecDivDecon.cc +++ b/cxx/src/lib/algorithms/deconvolution/MultiTaperSpecDivDecon.cc @@ -1,319 +1,299 @@ -#include -#include -#include +#include "mspass/algorithms/deconvolution/MultiTaperSpecDivDecon.h" #include "misc/blas.h" +#include "mspass/algorithms/amplitudes.h" +#include "mspass/algorithms/deconvolution/dpss.h" #include "mspass/utility/Metadata.h" #include "mspass/utility/MsPASSError.h" #include "mspass/utility/utility.h" -#include "mspass/algorithms/amplitudes.h" -#include "mspass/algorithms/deconvolution/MultiTaperSpecDivDecon.h" -#include "mspass/algorithms/deconvolution/dpss.h" +#include +#include +#include /* this include is local to this directory*/ #include "mspass/algorithms/deconvolution/common_multitaper.h" -namespace mspass::algorithms::deconvolution -{ +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; using mspass::algorithms::amplitudes::normalize; MultiTaperSpecDivDecon::MultiTaperSpecDivDecon(const Metadata &md) - : FFTDeconOperator(md), ScalarDecon(md) -{ + : FFTDeconOperator(md), ScalarDecon(md) { - try { - this->read_metadata(md,false); - } catch(...) { - throw; - }; - /* assume tapers matrix is created in read_metadata. We call reserve - on the three stl vector containers for efficiency. */ - ScalarDecon::data.reserve(nfft); - ScalarDecon::wavelet.reserve(nfft); - noise.reserve(nfft); + try { + this->read_metadata(md, false); + } catch (...) { + throw; + }; + /* assume tapers matrix is created in read_metadata. We call reserve + on the three stl vector containers for efficiency. */ + ScalarDecon::data.reserve(nfft); + ScalarDecon::wavelet.reserve(nfft); + noise.reserve(nfft); } -MultiTaperSpecDivDecon::MultiTaperSpecDivDecon(const MultiTaperSpecDivDecon &parent) - : FFTDeconOperator(parent), - ScalarDecon(parent), - tapers(parent.tapers), - noise(parent.noise), - ao_fft(parent.ao_fft), - rfestimates(parent.rfestimates), - winv_taper(parent.winv_taper) -{ - nw=parent.nw; - nseq=parent.nseq; - damp=parent.damp; - taperlen=parent.taperlen; +MultiTaperSpecDivDecon::MultiTaperSpecDivDecon( + const MultiTaperSpecDivDecon &parent) + : FFTDeconOperator(parent), ScalarDecon(parent), tapers(parent.tapers), + noise(parent.noise), ao_fft(parent.ao_fft), + rfestimates(parent.rfestimates), winv_taper(parent.winv_taper) { + nw = parent.nw; + nseq = parent.nseq; + damp = parent.damp; + taperlen = parent.taperlen; } -int MultiTaperSpecDivDecon::read_metadata(const Metadata &md,bool refresh) -{ - try { - const string base_error("MultiTaperSpecDivDecon::read_metadata method: "); - int i,j,ii; - /* We use these temporaries to test for changes when we are not - initializing */ - int nfft_old,nseq_old, tl_old; - double nw_old; - if(refresh) - { - nfft_old=nfft; - nseq_old=nseq; - tl_old=taperlen; - nw_old=nw; - } - taperlen=ComputeTaperLength(md); - int nfft_from_win=ComputeFFTLength(md); - //window based nfft always overrides that extracted directly from md */ - if(nfft_from_win!=nfft) - { - this->change_size(nfft_from_win); - } - damp=md.get_double("damping_factor"); - nw=md.get_double("time_bandwidth_product"); - /* Wang originally had this as nw*2-2 but Park and Levin say - the maximum is nw*2-1 which we use here. P&L papers all use mw=2.5 - with K(seql here) of 3 */ - //seql=md.get_int("lower_dpss"); - nseq=md.get_int("number_tapers"); - int nseqtest=static_cast(2.0*nw); - if(nseq>nseqtest || (nseq<1)) - { - cerr << base_error << "(WARNING) Illegal value for number_tapers parameter="< norms; - for(i=0,ii=0; ichange_size(nfft_from_win); + } + damp = md.get_double("damping_factor"); + nw = md.get_double("time_bandwidth_product"); + /* Wang originally had this as nw*2-2 but Park and Levin say + the maximum is nw*2-1 which we use here. P&L papers all use mw=2.5 + with K(seql here) of 3 */ + // seql=md.get_int("lower_dpss"); + nseq = md.get_int("number_tapers"); + int nseqtest = static_cast(2.0 * nw); + if (nseq > nseqtest || (nseq < 1)) { + cerr << base_error + << "(WARNING) Illegal value for number_tapers parameter=" << nseq + << endl + << "Resetting to maximum of 2*(time_bandwidth_product)=" << nseqtest + << endl; + nseq = nseqtest; + cerr << nseq << endl; + } + int seql = nseq - 1; + /* taperlen must be less than or equal nfft */ + /* old - this can not happen with algorithm change + if(taperlen>nfft) + throw MsPASSError(base_error + +"illegal taper_length parameter.\ntaper_length must + be less than or equal nfft computed from decon time window"); + */ + /* This is a bit ugly, but the finite set of parameters that change make + this the best approach I (glp) can see */ + bool parameters_changed(false); + if (refresh) { + if ((nfft != nfft_old) || (nseq != nseq_old) || (taperlen != tl_old) || + (nw != nw_old)) + parameters_changed = true; + } + /* Odd negative logic here. Idea is to always call this section + with a constructor, but bypass it when called in refresh mode + if we don't need to recompute the slepian functions */ + if ((!refresh) || parameters_changed) { + double *work(NULL); + work = new double[nseq * taperlen]; + /* This procedure allows selection of slepian tapers over a range + from seql to sequ. We alway swan the first nseq values so + set them as follows */ + seql = 0; + int sequ = nseq - 1; + dpss_calc(taperlen, nw, seql, sequ, work); + /* The tapers are stored in row order in work. We preserve that + here but use the dmatrix to store the values as transpose*/ + tapers = dmatrix(nseq, taperlen); + // vector norms; + for (i = 0, ii = 0; i < nseq; ++i) { + for (j = 0; j < taperlen; ++j) { + tapers(i, j) = work[ii]; + ++ii; } - return 0; - } catch(...) { - throw; - }; -} -int MultiTaperSpecDivDecon::loadnoise(const vector &n) -{ - /* For this implementation we insist n be the same length - * as d (assumed taperlen) to avoid constant recomputing slepians. */ - if(n.size() == taperlen) - noise=n; - else - { - int nn=n.size(); - int k; - noise.clear(); - for(k=0; ktaperlen) nn=taperlen; - for(k=0; k &n) { + /* For this implementation we insist n be the same length + * as d (assumed taperlen) to avoid constant recomputing slepians. */ + if (n.size() == taperlen) + noise = n; + else { + int nn = n.size(); + int k; + noise.clear(); + for (k = 0; k < nfft; ++k) + noise.push_back(0.0); + /* This zero padds noise on right when input series length + * is short. If ns is long we always take the leading portion */ + if (nn > taperlen) + nn = taperlen; + for (k = 0; k < nn; ++k) + noise[k] = n[k]; + } + return 0; } -int MultiTaperSpecDivDecon::load(const vector& w, const vector& d, - const vector& n) -{ - try { - int lnr=this->loadnoise(n); - int ldr; - ldr=this->ScalarDecon::load(w,d); - return(lnr+ldr); - } catch(...) { - throw; - }; +int MultiTaperSpecDivDecon::load(const vector &w, + const vector &d, + const vector &n) { + try { + int lnr = this->loadnoise(n); + int ldr; + ldr = this->ScalarDecon::load(w, d); + return (lnr + ldr); + } catch (...) { + throw; + }; } MultiTaperSpecDivDecon::MultiTaperSpecDivDecon(const Metadata &md, - const vector &n,const vector &w,const vector &d) -{ - try { - this->read_metadata(md,false); - } catch(...) - { - throw; - } - wavelet=w; - data=d; - noise=n; + const vector &n, + const vector &w, + const vector &d) { + try { + this->read_metadata(md, false); + } catch (...) { + throw; + } + wavelet = w; + data = d; + noise = n; } -vector MultiTaperSpecDivDecon::taper_data(const vector& signal) -{ - const string base_error("taper_data procedure: "); - /* We put in this sanity check */ - if(signal.size()>nfft) throw MsPASSError(base_error - + "Illegal input parameters. Vector of data received is larger than the fft buffer space allocated", - ErrorSeverity::Invalid); - /* The tapered data are stored in this vector of arrays */ - int i,j; - vector tdata; - int ntapers=tapers.rows(); - tdata.reserve(ntapers); - vector work; - work.reserve(nfft); - for(j=0; jtapers.columns(); ++j) - { - work[j]=tapers(i,j)*signal[j]; - } - /* Force zero pads always */ - ComplexArray cwork(nfft,work); - tdata.push_back(cwork); +vector +MultiTaperSpecDivDecon::taper_data(const vector &signal) { + const string base_error("taper_data procedure: "); + /* We put in this sanity check */ + if (signal.size() > nfft) + throw MsPASSError(base_error + + "Illegal input parameters. Vector of data received " + "is larger than the fft buffer space allocated", + ErrorSeverity::Invalid); + /* The tapered data are stored in this vector of arrays */ + int i, j; + vector tdata; + int ntapers = tapers.rows(); + tdata.reserve(ntapers); + vector work; + work.reserve(nfft); + for (j = 0; j < nfft; ++j) + work.push_back(0.0); + for (i = 0; i < ntapers; ++i) { + /* This will assure part of vector between end of + * data and nfft is zero padded */ + for (j = 0; j < this->tapers.columns(); ++j) { + work[j] = tapers(i, j) * signal[j]; } - return tdata; + /* Force zero pads always */ + ComplexArray cwork(nfft, work); + tdata.push_back(cwork); + } + return tdata; } -void MultiTaperSpecDivDecon::process() -{ +void MultiTaperSpecDivDecon::process() { const string base_error("MultiTaperSpecDivDecon::process(): "); - try{ + try { /* WARNING about this algorithm. At present there is nothing to stop a coding error of calling the algorithm with inconsistent signal and noise data vectors. */ - if(noise.size()<=0) - { - throw MsPASSError(base_error+"noise data is empty.",ErrorSeverity::Invalid); + if (noise.size() <= 0) { + throw MsPASSError(base_error + "noise data is empty.", + ErrorSeverity::Invalid); } /* The tapered data are stored in this vector of arrays */ - int i,j; + int i, j; vector tdata; - tdata=taper_data(data); + tdata = taper_data(data); /* Apply fft to each tapered data vector */ - for(i=0; i wdata; - wdata=taper_data(wavelet); - for(i=0; i ndata; - ndata=taper_data(noise); - for(i=0; i noise_spectrum(ndata[0].abs()); - for(i=1; i nwork(ndata[i].abs()); - for(j=0; j nwork(ndata[i].abs()); + for (j = 0; j < noise_spectrum.size(); ++j) { + noise_spectrum[j] += nwork[j]; + } } /* normalize and add damping */ vector::iterator nptr; - /* This makes the scaling indepndent of the choise for tiem bandwidth product*/ - double scale=damp/(static_cast(nseq)); - for(nptr=noise_spectrum.begin(); nptr!=noise_spectrum.end(); ++nptr) - { - (*nptr) *= scale; + /* This makes the scaling indepndent of the choise for tiem bandwidth + * product*/ + double scale = damp / (static_cast(nseq)); + for (nptr = noise_spectrum.begin(); nptr != noise_spectrum.end(); ++nptr) { + (*nptr) *= scale; } /* We compute a RF estimate for each taper independently usinga variant of - the water level method. The variant is that the level is frequency dependent - defined by sacled noise level. + the water level method. The variant is that the level is frequency + dependent defined by sacled noise level. */ /* We need this for amplitude scaling - depend on Parseval's theorem*/ - double wnrm=dnrm2(wavelet.size(),&(wavelet[0]),1); + double wnrm = dnrm2(wavelet.size(), &(wavelet[0]), 1); vector denominator; - for(i=0;i rfestimates; + // vector rfestimates; /* Must clear rfestimate and winv_taper containers or they accumulate */ rfestimates.clear(); - for(i=0;i wtmp; - for(i=0;i0) - result=circular_shift(result,-sample_shift); - }catch(...){throw;}; + if (sample_shift > 0) + result = circular_shift(result, -sample_shift); + } catch (...) { + throw; + }; } -CoreTimeSeries MultiTaperSpecDivDecon::actual_output() -{ - try { - int i,k; - vector ao; - ao.reserve(nfft); - for(k=0;k(ao); - CoreTimeSeries result(nfft); - /* Getting dt from here is unquestionably a flaw in the api, but will - retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ - double dt=this->shapingwavelet.sample_interval(); - /* t0 is time of sample zero - hence normally negative*/ - /* Old API - result.t0=dt*(-(double)i0); - result.dt=dt; - result.live=true; - result.tref=TimeReferenceType::Relative; - result.s=ao; - result.ns=nfft; - */ +CoreTimeSeries MultiTaperSpecDivDecon::actual_output() { + try { + int i, k; + vector ao; + ao.reserve(nfft); + for (k = 0; k < nfft; ++k) + ao.push_back(0.0); + for (i = 0; i < nseq; ++i) { + ComplexArray work(ao_fft[i]); + work = (*shapingwavelet.wavelet()) * work; + gsl_fft_complex_inverse(work.ptr(), 1, nfft, wavetable, workspace); + for (k = 0; k < nfft; ++k) + ao[k] += work[k].real(); + } + double nrmscl = 1.0 / ((double)nseq); + for (k = 0; k < nfft; ++k) + ao[k] *= nrmscl; + /* We always shift this wavelet to the center of the data vector. + We handle the time through the CoreTimeSeries object. */ + int i0 = nfft / 2; + ao = circular_shift(ao, i0); + ao = normalize(ao); + CoreTimeSeries result(nfft); + /* Getting dt from here is unquestionably a flaw in the api, but will + retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ + double dt = this->shapingwavelet.sample_interval(); + /* t0 is time of sample zero - hence normally negative*/ + /* Old API + result.t0=dt*(-(double)i0); + result.dt=dt; + result.live=true; + result.tref=TimeReferenceType::Relative; + result.s=ao; + result.ns=nfft; + */ - result.set_t0(dt*(-(double)i0)); - result.set_dt(dt); - result.set_live(); - result.set_npts(nfft); - result.set_tref(TimeReferenceType::Relative); - for(k=0;kshapingwavelet.sample_interval(); - /*algorithm assumes this data vector in result is initialized to nfft zeos */ - CoreTimeSeries result(this->nfft); - for(int i=0;iFFTDeconOperator::FourierInverse(this->winv_taper[i], - *shapingwavelet.wavelet(),dt,t0parent)); - if(i==0) - result=work; - else - result+=work; - } - double nrmscal=1.0/((double)nseq); - for(int k=0;kshapingwavelet.sample_interval(); + /*algorithm assumes this data vector in result is initialized to nfft zeos + */ + CoreTimeSeries result(this->nfft); + for (int i = 0; i < nseq; ++i) { + CoreTimeSeries work(this->FFTDeconOperator::FourierInverse( + this->winv_taper[i], *shapingwavelet.wavelet(), dt, t0parent)); + if (i == 0) + result = work; + else + result += work; + } + double nrmscal = 1.0 / ((double)nseq); + for (int k = 0; k < result.s.size(); ++k) + result.s[k] *= nrmscal; + return result; + } catch (...) { + throw; + }; } -CoreTimeSeries MultiTaperSpecDivDecon::inverse_wavelet() -{ - try{ +CoreTimeSeries MultiTaperSpecDivDecon::inverse_wavelet() { + try { return this->inverse_wavelet(0.0); - }catch(...){throw;}; + } catch (...) { + throw; + }; } -std::vector MultiTaperSpecDivDecon::all_inverse_wavelets - (const double t0parent) -{ - try{ - std::vector all; - all.reserve(nseq); - double dt=this->shapingwavelet.sample_interval(); - for(int i=0;iFFTDeconOperator::FourierInverse - (this->winv_taper[i],*shapingwavelet.wavelet(),dt,t0parent)); - all.push_back(work); - } - return all; - }catch(...){throw;}; +std::vector +MultiTaperSpecDivDecon::all_inverse_wavelets(const double t0parent) { + try { + std::vector all; + all.reserve(nseq); + double dt = this->shapingwavelet.sample_interval(); + for (int i = 0; i < nseq; ++i) { + CoreTimeSeries work(this->FFTDeconOperator::FourierInverse( + this->winv_taper[i], *shapingwavelet.wavelet(), dt, t0parent)); + all.push_back(work); + } + return all; + } catch (...) { + throw; + }; } -std::vector MultiTaperSpecDivDecon::all_rfestimates - (const double t0parent) -{ - try{ - std::vector all; - all.reserve(nseq); - double dt=this->shapingwavelet.sample_interval(); - for(int i=0;iFFTDeconOperator::FourierInverse - (this->rfestimates[i],*shapingwavelet.wavelet(),dt,t0parent)); - all.push_back(work); - } - return all; - }catch(...){throw;}; +std::vector +MultiTaperSpecDivDecon::all_rfestimates(const double t0parent) { + try { + std::vector all; + all.reserve(nseq); + double dt = this->shapingwavelet.sample_interval(); + for (int i = 0; i < nseq; ++i) { + /* Althought this method of FFTDeconOperator was originally + * written to return inverse wavelet, it can work in this + * contest too*/ + CoreTimeSeries work(this->FFTDeconOperator::FourierInverse( + this->rfestimates[i], *shapingwavelet.wavelet(), dt, t0parent)); + all.push_back(work); + } + return all; + } catch (...) { + throw; + }; } -std::vector MultiTaperSpecDivDecon::all_actual_outputs - (const double t0parent) -{ - try{ - std::vector all; - all.reserve(nseq); - double dt=this->shapingwavelet.sample_interval(); - for(int i=0;iFFTDeconOperator::FourierInverse - (this->ao_fft[i],*shapingwavelet.wavelet(),dt,t0parent)); - all.push_back(work); - } - return all; - }catch(...){throw;}; +std::vector +MultiTaperSpecDivDecon::all_actual_outputs(const double t0parent) { + try { + std::vector all; + all.reserve(nseq); + double dt = this->shapingwavelet.sample_interval(); + for (int i = 0; i < nseq; ++i) { + /* Althought this method of FFTDeconOperator was originally + * written to return inverse wavelet, it can work in this + * contest too*/ + CoreTimeSeries work(this->FFTDeconOperator::FourierInverse( + this->ao_fft[i], *shapingwavelet.wavelet(), dt, t0parent)); + all.push_back(work); + } + return all; + } catch (...) { + throw; + }; } -Metadata MultiTaperSpecDivDecon::QCMetrics() -{ +Metadata MultiTaperSpecDivDecon::QCMetrics() { /* Return only an empty Metadata container. Done as it is easier to maintain the code letting python do this work. This also anticipates new metrics being added which would be @@ -507,4 +490,4 @@ Metadata MultiTaperSpecDivDecon::QCMetrics() Metadata md; return md; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/MultiTaperXcorDecon.cc b/cxx/src/lib/algorithms/deconvolution/MultiTaperXcorDecon.cc index 3d14a83a3..e9f9b0da5 100644 --- a/cxx/src/lib/algorithms/deconvolution/MultiTaperXcorDecon.cc +++ b/cxx/src/lib/algorithms/deconvolution/MultiTaperXcorDecon.cc @@ -1,411 +1,390 @@ -#include -#include +#include "mspass/algorithms/deconvolution/MultiTaperXcorDecon.h" +#include "mspass/algorithms/amplitudes.h" +#include "mspass/algorithms/deconvolution/common_multitaper.h" +#include "mspass/algorithms/deconvolution/dpss.h" +#include "mspass/seismic/CoreTimeSeries.h" #include "mspass/utility/Metadata.h" #include "mspass/utility/MsPASSError.h" #include "mspass/utility/utility.h" -#include "mspass/seismic/CoreTimeSeries.h" -#include "mspass/algorithms/amplitudes.h" -#include "mspass/algorithms/deconvolution/MultiTaperXcorDecon.h" -#include "mspass/algorithms/deconvolution/dpss.h" -#include "mspass/algorithms/deconvolution/common_multitaper.h" -namespace mspass::algorithms::deconvolution -{ +#include +#include +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; using mspass::algorithms::amplitudes::normalize; MultiTaperXcorDecon::MultiTaperXcorDecon(const Metadata &md) - : FFTDeconOperator(md) -{ + : FFTDeconOperator(md) { - try { - this->read_metadata(md,false); - } catch(...) { - throw; - }; - /* assume tapers matrix is created in read_metadata. We call reserve - on the three stl vector containers for efficiency. */ - data.reserve(nfft); - wavelet.reserve(nfft); - noise.reserve(nfft); + try { + this->read_metadata(md, false); + } catch (...) { + throw; + }; + /* assume tapers matrix is created in read_metadata. We call reserve + on the three stl vector containers for efficiency. */ + data.reserve(nfft); + wavelet.reserve(nfft); + noise.reserve(nfft); } MultiTaperXcorDecon::MultiTaperXcorDecon(const MultiTaperXcorDecon &parent) - : FFTDeconOperator(parent), - ScalarDecon(parent), - tapers(parent.tapers), - noise(parent.noise), - ao_fft(parent.ao_fft) -{ - /* multitaper parameters to copy */ - nw=parent.nw; - nseq=parent.nseq; - taperlen=parent.taperlen; - damp=parent.damp; + : FFTDeconOperator(parent), ScalarDecon(parent), tapers(parent.tapers), + noise(parent.noise), ao_fft(parent.ao_fft) { + /* multitaper parameters to copy */ + nw = parent.nw; + nseq = parent.nseq; + taperlen = parent.taperlen; + damp = parent.damp; } -int MultiTaperXcorDecon::read_metadata(const Metadata &md,bool refresh) -{ - try { - const string base_error("MultiTaperXcorDecon::read_metadata method: "); - int i,j,ii; - /* We use these temporaries to test for changes when we are not - initializing */ - int nfft_old,nseq_old, tl_old; - double nw_old; - if(refresh) - { - nfft_old=nfft; - nseq_old=nseq; - tl_old=taperlen; - nw_old=nw; - } - taperlen=ComputeTaperLength(md); - int nfft_from_win=ComputeFFTLength(md); - //window based nfft always overrides that extracted directly from md */ - if(nfft_from_win!=nfft) - { - this->change_size(nfft_from_win); - } - damp=md.get_double("damping_factor"); - nw=md.get_double("time_bandwidth_product"); - /* Wang originally had this as nw*2-2 but Park and Levin say - the maximum is nw*2-1 which we use here. P&L papers all use mw=2.5 - with K(seql here) of 3 */ - //seql=md.get_int("lower_dpss"); - nseq=md.get_int("number_tapers"); - int nseqtest=static_cast(2.0*nw); - if(nseq>nseqtest || (nseq<1)) - { - cerr << base_error << "(WARNING) Illegal value for number_of tapers parameter="<change_size(nfft_from_win); + } + damp = md.get_double("damping_factor"); + nw = md.get_double("time_bandwidth_product"); + /* Wang originally had this as nw*2-2 but Park and Levin say + the maximum is nw*2-1 which we use here. P&L papers all use mw=2.5 + with K(seql here) of 3 */ + // seql=md.get_int("lower_dpss"); + nseq = md.get_int("number_tapers"); + int nseqtest = static_cast(2.0 * nw); + if (nseq > nseqtest || (nseq < 1)) { + cerr << base_error + << "(WARNING) Illegal value for number_of tapers parameter=" << nseq + << endl + << "Resetting to maximum of 2*(time_bandwidth_product)=" << nseqtest + << endl; + nseq = nseqtest; + cerr << nseq << endl; + } + int seql = nseq - 1; + /* taperlen must be less than or equal nfft */ + /* old - this can not happen with algorithm change + if(taperlen>nfft) + throw MsPASSError(base_error + +"illegal taper_length parameter.\ntaper_length must + be less than or equal nfft computed from decon time window"); + */ + /* This is a bit ugly, but the finite set of parameters that change make + this the best approach I (glp) can see */ + bool parameters_changed(false); + if (refresh) { + if ((nfft != nfft_old) || (nseq != nseq_old) || (taperlen != tl_old) || + (nw != nw_old)) + parameters_changed = true; + } + /* Odd negative logic here. Idea is to always call this section + with a constructor, but bypass it when called in refresh mode + if we don't need to recompute the slepian functions */ + if ((!refresh) || parameters_changed) { + double *work(NULL); + work = new double[nseq * taperlen]; + /* This procedure allows selection of slepian tapers over a range + from seql to sequ. We alway swan the first nseq values so + set them as follows */ + seql = 0; + int sequ = nseq - 1; + dpss_calc(taperlen, nw, seql, sequ, work); + /* The tapers are stored in row order in work. We preserve that + here but use the dmatrix to store the values */ + tapers = dmatrix(nseq, taperlen); + for (i = 0, ii = 0; i < nseq; ++i) { + for (j = 0; j < taperlen; ++j) { + tapers(i, j) = work[ii]; + ++ii; } - //DEBUG - //cerr<< "Exiting constructor - damp="< &n) -{ - /* For this implementation we insist n be the same length - * as d (assumed taperlen) to avoid constant recomputing slepians. */ - if(n.size() == taperlen) - noise=n; - else - { - int nn=n.size(); - int k; - noise.clear(); - for(k=0; ktaperlen) nn=taperlen; - for(k=0; knfft) throw MsPASSError(base_error - + "Illegal input parameters. Vector of data received is larger than the fft buffer space allocated", - ErrorSeverity::Invalid); - /* The tapered data are stored in this vector of arrays */ - int i,j; - vector tdata; - int ntapers=tapers.rows(); - tdata.reserve(ntapers); - for(i=0; i +MultiTaperXcorDecon::taper_data(const vector &signal) { + const string base_error("taper_data procedure: "); + /* We put in this sanity check */ + if (signal.size() > nfft) + throw MsPASSError(base_error + + "Illegal input parameters. Vector of data received " + "is larger than the fft buffer space allocated", + ErrorSeverity::Invalid); + /* The tapered data are stored in this vector of arrays */ + int i, j; + vector tdata; + int ntapers = tapers.rows(); + tdata.reserve(ntapers); + for (i = 0; i < ntapers; ++i) { + double *work = new double[nfft]; + /* This will assure part of vector between end of + * data and nfft is zero padded */ + for (j = 0; j < nfft; ++j) + work[j] = 0.0; + for (j = 0; j < tapers.columns(); ++j) { + work[j] = tapers(i, j) * signal[j]; } - return tdata; + /* Force zero pads always */ + ComplexArray cwork(nfft, work); + tdata.push_back(cwork); + } + return tdata; } -void MultiTaperXcorDecon::process() -{ - //DEBUG - //cerr<< "Entering process method"< tdata; - tdata=taper_data(data); - /* Apply fft to each tapered data vector */ - for(i=0; i wdata; - wdata=taper_data(wavelet); - for(i=0; i ndata; - ndata=taper_data(noise); - for(i=0; i noise_spectrum(ndata[0].abs()); - for(i=1; i nwork(ndata[i].abs()); - for(j=0; j::iterator nptr; - double scale=damp/(static_cast(nseq)); - //DEBUG - //cerr << "scale computed from damp="< tdata; + tdata = taper_data(data); + /* Apply fft to each tapered data vector */ + for (i = 0; i < nseq; ++i) { + gsl_fft_complex_forward(tdata[i].ptr(), 1, nfft, wavetable, workspace); + } + // DEBUG + // cerr<< "Tapering wavelet vector"< wdata; + wdata = taper_data(wavelet); + for (i = 0; i < nseq; ++i) { + gsl_fft_complex_forward(wdata[i].ptr(), 1, nfft, wavetable, workspace); + } + /* And the noise data - although with noise we quickly turn to power spectrum + */ + vector ndata; + ndata = taper_data(noise); + for (i = 0; i < nseq; ++i) { + gsl_fft_complex_forward(ndata[i].ptr(), 1, nfft, wavetable, workspace); + } + vector noise_spectrum(ndata[0].abs()); + for (i = 1; i < nseq; ++i) { + vector nwork(ndata[i].abs()); + for (j = 0; j < ndata.size(); ++j) { + noise_spectrum[j] += nwork[j]; } - /* We put noise_spectrum data into ndata array with this constructor. We - then just add ComplexArray vectors in the decon calculation below */ - ComplexArray fdamp(noise_spectrum.size(),noise_spectrum); - /* Now we compute the deconvolved data using formulas in Park and Levin 2000 + } + /* normalize and add damping */ + vector::iterator nptr; + double scale = damp / (static_cast(nseq)); + // DEBUG + // cerr << "scale computed from damp="<df(0.01); //dt for test data - vector specwork(rf_fft.abs()); - for(i=0;i(i)<<" "<(i)<<" "<0) - { - for(int k=sample_shift; k>0; k--) - result.push_back(rf_fft[nfft-k].real()); - for(int k=0; kdf(0.01); //dt for test data + vector specwork(rf_fft.abs()); + for(i=0;i(i)<<" "<(i)<<" "< 0) { + for (int k = sample_shift; k > 0; k--) + result.push_back(rf_fft[nfft - k].real()); + for (int k = 0; k < data.size() - sample_shift; k++) + result.push_back(rf_fft[k].real()); + } else if (sample_shift == 0) { + for (int k = 0; k < data.size(); k++) + result.push_back(rf_fft[k].real()); + } else { + throw MsPASSError(base_error + "Coding error - trying to use an illegal " + "negative time shift parameter", + ErrorSeverity::Invalid); + } } -CoreTimeSeries MultiTaperXcorDecon::actual_output() -{ - try { - /* The ao_fft array contains the fft of the actual output wavelet. - * We do need to appy the shaping wavelet for consistency before - * converting it to the time domain.*/ - ao_fft=(*shapingwavelet.wavelet())*ao_fft; - gsl_fft_complex_inverse(ao_fft.ptr(),1,nfft,wavetable,workspace); - vector ao; - ao.reserve(nfft); - for(int k=0; k(ao); - CoreTimeSeries result(nfft); - /* Getting dt from here is unquestionably a flaw in the api, but will - retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ - double dt=this->shapingwavelet.sample_interval(); - result.set_t0(dt*(-(double)i0)); - result.set_dt(dt); - result.set_live(); - result.set_tref(TimeReferenceType::Relative); - result.set_npts(nfft); - for(int k=0;k ao; + ao.reserve(nfft); + for (int k = 0; k < ao_fft.size(); ++k) + ao.push_back(ao_fft[k].real()); + /* We always shift this wavelet to the center of the data vector. + We handle the time through the CoreTimeSeries object. */ + int i0 = nfft / 2; + ao = circular_shift(ao, i0); + ao = normalize(ao); + CoreTimeSeries result(nfft); + /* Getting dt from here is unquestionably a flaw in the api, but will + retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ + double dt = this->shapingwavelet.sample_interval(); + result.set_t0(dt * (-(double)i0)); + result.set_dt(dt); + result.set_live(); + result.set_tref(TimeReferenceType::Relative); + result.set_npts(nfft); + for (int k = 0; k < nfft; ++k) + result.s[k] = ao[k]; + return result; + } catch (...) { + throw; + }; } -CoreTimeSeries MultiTaperXcorDecon::inverse_wavelet(const double t0parent) -{ - try { - /* Getting dt from here is unquestionably a flaw in the api, but will - * retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ - double dt=this->shapingwavelet.sample_interval(); - return (this->FFTDeconOperator::FourierInverse(this->winv, - *shapingwavelet.wavelet(),dt,t0parent)); - } catch(...) { - throw; - }; +CoreTimeSeries MultiTaperXcorDecon::inverse_wavelet(const double t0parent) { + try { + /* Getting dt from here is unquestionably a flaw in the api, but will + * retain for now. Perhaps should a copy of dt in the ScalarDecon + * object. */ + double dt = this->shapingwavelet.sample_interval(); + return (this->FFTDeconOperator::FourierInverse( + this->winv, *shapingwavelet.wavelet(), dt, t0parent)); + } catch (...) { + throw; + }; } -CoreTimeSeries MultiTaperXcorDecon::inverse_wavelet() -{ - try{ +CoreTimeSeries MultiTaperXcorDecon::inverse_wavelet() { + try { return this->inverse_wavelet(0.0); - }catch(...){throw;}; + } catch (...) { + throw; + }; } -Metadata MultiTaperXcorDecon::QCMetrics() -{ +Metadata MultiTaperXcorDecon::QCMetrics() { /* Return only an empty Metadata container. Done as it is easier to maintain the code letting python do this work. This also anticipates new metrics being added which would be @@ -413,4 +392,4 @@ Metadata MultiTaperXcorDecon::QCMetrics() Metadata md; return md; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/ScalarDecon.cc b/cxx/src/lib/algorithms/deconvolution/ScalarDecon.cc index ffae4b33b..63154189f 100644 --- a/cxx/src/lib/algorithms/deconvolution/ScalarDecon.cc +++ b/cxx/src/lib/algorithms/deconvolution/ScalarDecon.cc @@ -1,73 +1,59 @@ #include "mspass/algorithms/deconvolution/ScalarDecon.h" -namespace mspass::algorithms::deconvolution -{ +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::utility; -ScalarDecon::ScalarDecon(const Metadata &md) : shapingwavelet(md) -{ - try { - /* This has to be defined or the shapingwavlet constructor will - * fail in the current implementation */ - int nfft=md.get_int("operator_nfft"); - wavelet.reserve(nfft); - data.reserve(nfft); - result.reserve(nfft); - } catch(...) { - throw; - }; -} -ScalarDecon::ScalarDecon(const vector& d, const vector& w) - : data(d),wavelet(w) -{ - result.reserve(data.size()); -} -ScalarDecon::ScalarDecon(const ScalarDecon& parent) - : data(parent.data), - wavelet(parent.wavelet), - result(parent.result), - shapingwavelet(parent.shapingwavelet) -{ -} -ScalarDecon& ScalarDecon::operator=(const ScalarDecon& parent) -{ - if(this!=&parent) - { - wavelet=parent.wavelet; - data=parent.data; - result=parent.result; - } - return *this; -} -int ScalarDecon::load(const vector &w,const vector &d) -{ - wavelet=w; - data=d; - result.clear(); - return 0; +ScalarDecon::ScalarDecon(const Metadata &md) : shapingwavelet(md) { + try { + /* This has to be defined or the shapingwavlet constructor will + * fail in the current implementation */ + int nfft = md.get_int("operator_nfft"); + wavelet.reserve(nfft); + data.reserve(nfft); + result.reserve(nfft); + } catch (...) { + throw; + }; +} +ScalarDecon::ScalarDecon(const vector &d, const vector &w) + : data(d), wavelet(w) { + result.reserve(data.size()); +} +ScalarDecon::ScalarDecon(const ScalarDecon &parent) + : data(parent.data), wavelet(parent.wavelet), result(parent.result), + shapingwavelet(parent.shapingwavelet) {} +ScalarDecon &ScalarDecon::operator=(const ScalarDecon &parent) { + if (this != &parent) { + wavelet = parent.wavelet; + data = parent.data; + result = parent.result; + } + return *this; +} +int ScalarDecon::load(const vector &w, const vector &d) { + wavelet = w; + data = d; + result.clear(); + return 0; } /* this an next method are normally called back to back. To assure stability we must have both call the clear method for result. Could do that in only loadwavelet, but that could produce funny bugs downsteam so we always clear for stability at a small cost */ -int ScalarDecon::loaddata(const vector &d) -{ - data=d; +int ScalarDecon::loaddata(const vector &d) { + data = d; result.clear(); return 0; } -int ScalarDecon::loadwavelet(const vector &w) -{ - wavelet=w; +int ScalarDecon::loadwavelet(const vector &w) { + wavelet = w; result.clear(); return 0; } -void ScalarDecon::changeparameter(const Metadata& md) -{ - shapingwavelet=ShapingWavelet(md); +void ScalarDecon::changeparameter(const Metadata &md) { + shapingwavelet = ShapingWavelet(md); } -void ScalarDecon::change_shaping_wavelet(const ShapingWavelet& nsw) -{ - shapingwavelet=nsw; +void ScalarDecon::change_shaping_wavelet(const ShapingWavelet &nsw) { + shapingwavelet = nsw; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/ShapingWavelet.cc b/cxx/src/lib/algorithms/deconvolution/ShapingWavelet.cc index 6dec37124..3d5111d22 100644 --- a/cxx/src/lib/algorithms/deconvolution/ShapingWavelet.cc +++ b/cxx/src/lib/algorithms/deconvolution/ShapingWavelet.cc @@ -1,306 +1,300 @@ -#include -#include -#include -#include -#include "misc/blas.h" -#include "mspass/utility/MsPASSError.h" -#include "mspass/seismic/CoreTimeSeries.h" #include "mspass/algorithms/deconvolution/ShapingWavelet.h" -#include "mspass/algorithms/deconvolution/wavelet.h" -#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" -#include "mspass/algorithms/amplitudes.h" +#include "misc/blas.h" #include "mspass/algorithms/Butterworth.h" -namespace mspass::algorithms::deconvolution -{ +#include "mspass/algorithms/amplitudes.h" +#include "mspass/algorithms/deconvolution/FFTDeconOperator.h" +#include "mspass/algorithms/deconvolution/wavelet.h" +#include "mspass/seismic/CoreTimeSeries.h" +#include "mspass/utility/MsPASSError.h" +#include +#include +#include +#include +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; using mspass::algorithms::amplitudes::normalize; -ShapingWavelet::ShapingWavelet(const Metadata& md, int nfftin) -{ - const string base_error("ShapingWavelet object constructor: "); - try { - /* We use this to allow a nfft to be set in md. A bit error prone - * we add a special error handler. */ - if(nfftin>0) - this->nfft=nfftin; - else - { - try { - nfft=md.get_int("operator_nfft"); - } catch(MetadataGetError& mderr) - { - throw MsPASSError(base_error - + "Called constructor with nfft=0 but parameter with key=operator_nfft is not in parameter file", - ErrorSeverity::Invalid); - } - } - /* these are workspaces used by gnu's fft algorithm. Other parts - * of this library cache them for efficiency, but this one we - * compute ever time the object is created and then discard it. */ - gsl_fft_complex_wavetable *wavetable; - gsl_fft_complex_workspace *workspace; - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); - string wavelettype=md.get_string("shaping_wavelet_type"); - wavelet_name=wavelettype; - dt=md.get_double("shaping_wavelet_dt"); - double *r; - if(wavelettype=="gaussian") - { - float fpeak=md.get_double("shaping_wavelet_frequency"); - //construct wavelet and fft - r=gaussian(fpeak,(float)dt,nfft); - w=ComplexArray(nfft,r); - gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); - delete [] r; - } - /* Note for CNR3CDecon the initial values on construction for - ricker or butterworth are irrelevant and wasted effort. We keep - them in this class because these forms are needed by the family of - scalar deconvolution algorithms */ - else if(wavelettype=="ricker") - { - float fpeak=(float)md.get_double("shaping_wavelet_frequency"); - //construct wavelet and fft - r=rickerwavelet(fpeak,(float)dt,nfft); -//DEBUG -//cerr << "Ricker shaping wavelet"<dt); - w=bwf.transfer_function(this->nfft); - } - /* This option requires a package to compute zero phase wavelets of - some specified type and bandwidth. Previous used antelope filters which - colides with open source goal of mspass. Another implementation of - TimeInvariantFilter and this could be restored. - */ - /* - else if(wavelettype=="filter_response") - { - string filterparam=md.get_string("filter"); - TimeInvariantFilter filt(filterparam); - TimeSeries dtmp(nfft); - dtmp.ns=nfft; - dtmp.dt=dt; - dtmp.live=true; - dtmp.t0=(-dt*static_cast(nfft/2)); - dtmp.tref=relative; - int i; - i=dtmp.sample_number(0.0); - dtmp.s[i]=1.0; // Delta function at 0 - filt.zerophase(dtmp); - // We need to shift the filter response now back to - // zero to avoid time shifts in output - dtmp.s=circular_shift(dtmp.s,nfft/2); - w=ComplexArray(dtmp.s.size(), &(dtmp.s[0])); - gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); - } - */ - else if((wavelettype=="slepian") || (wavelettype=="Slepian") ) - { - double tbp=md.get_double("time_bandwidth_product"); - double target_pulse_width=md.get_double("target_pulse_width"); - /* Sanity check on pulse width */ - if(target_pulse_width>(nfft/4) || (target_pulse_width(nfft))); - double *wtmp=slepian0(tbp,nwsize); - double *work=new double[nfft]; - for(int k=0;k 0) + this->nfft = nfftin; + else { + try { + nfft = md.get_int("operator_nfft"); + } catch (MetadataGetError &mderr) { + throw MsPASSError(base_error + + "Called constructor with nfft=0 but parameter " + "with key=operator_nfft is not in parameter file", + ErrorSeverity::Invalid); + } + } + /* these are workspaces used by gnu's fft algorithm. Other parts + * of this library cache them for efficiency, but this one we + * compute ever time the object is created and then discard it. */ + gsl_fft_complex_wavetable *wavetable; + gsl_fft_complex_workspace *workspace; + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + string wavelettype = md.get_string("shaping_wavelet_type"); + wavelet_name = wavelettype; + dt = md.get_double("shaping_wavelet_dt"); + double *r; + if (wavelettype == "gaussian") { + float fpeak = md.get_double("shaping_wavelet_frequency"); + // construct wavelet and fft + r = gaussian(fpeak, (float)dt, nfft); + w = ComplexArray(nfft, r); + gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); + delete[] r; + } + /* Note for CNR3CDecon the initial values on construction for + ricker or butterworth are irrelevant and wasted effort. We keep + them in this class because these forms are needed by the family of + scalar deconvolution algorithms */ + else if (wavelettype == "ricker") { + float fpeak = (float)md.get_double("shaping_wavelet_frequency"); + // construct wavelet and fft + r = rickerwavelet(fpeak, (float)dt, nfft); + // DEBUG + // cerr << "Ricker shaping wavelet"<dt); + w = bwf.transfer_function(this->nfft); + } + /* This option requires a package to compute zero phase wavelets of + some specified type and bandwidth. Previous used antelope filters which + colides with open source goal of mspass. Another implementation of + TimeInvariantFilter and this could be restored. + */ + /* + else if(wavelettype=="filter_response") { - throw; + string filterparam=md.get_string("filter"); + TimeInvariantFilter filt(filterparam); + TimeSeries dtmp(nfft); + dtmp.ns=nfft; + dtmp.dt=dt; + dtmp.live=true; + dtmp.t0=(-dt*static_cast(nfft/2)); + dtmp.tref=relative; + int i; + i=dtmp.sample_number(0.0); + dtmp.s[i]=1.0; // Delta function at 0 + filt.zerophase(dtmp); + // We need to shift the filter response now back to + // zero to avoid time shifts in output + dtmp.s=circular_shift(dtmp.s,nfft/2); + w=ComplexArray(dtmp.s.size(), &(dtmp.s[0])); + gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); + } + */ + else if ((wavelettype == "slepian") || (wavelettype == "Slepian")) { + double tbp = md.get_double("time_bandwidth_product"); + double target_pulse_width = md.get_double("target_pulse_width"); + /* Sanity check on pulse width */ + if (target_pulse_width > (nfft / 4) || (target_pulse_width < tbp)) { + stringstream ss; + ss << "ShapingWavelet Metadata constructor: bad input parameters for " + "Slepian shaping wavelet" + << endl + << "Specified target_pulse_width of " << target_pulse_width + << " samples" << endl + << "FFT buffer size=" << nfft + << " and time bandwidth product=" << tbp << endl + << "Pulse width should be small fraction of buffer size but ge than " + "tbp" + << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + double c = tbp / target_pulse_width; + int nwsize = round(c * (static_cast(nfft))); + double *wtmp = slepian0(tbp, nwsize); + double *work = new double[nfft]; + for (int k = 0; k < nfft; ++k) + work[k] = 0.0; + dcopy(nwsize, wtmp, 1, work, 1); + delete[] wtmp; + w = ComplexArray(nfft, work); + gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); + delete[] work; + } else if (wavelettype == "none") { + /* Prototype code issued an error in this condition, but we accept it + here as an option defined by none. We could do this by putting + all ones in the w array but using a delta function at zero lage + avoids scaling issues for little cost - this assumes this + object is created only occassionally and not millions of times */ + double *r = new double[nfft]; + for (int k = 0; k < nfft; ++k) + r[k] = 0.0; + r[0] = 1.0; + w = ComplexArray(nfft, r); + delete[] r; + } else { + throw MsPASSError( + base_error + "illegal value for shaping_wavelet_type=" + wavelettype, + ErrorSeverity::Invalid); } + gsl_fft_complex_wavetable_free(wavetable); + gsl_fft_complex_workspace_free(workspace); + df = 1.0 / (dt * ((double)nfft)); + } catch (MsPASSError &err) { + cerr << base_error + << "Something threw an unhandled MsPASSError with this message:" + << endl; + err.log_error(); + cerr << "This is a nonfatal bug that needs to be fixed" << endl; + throw err; + } catch (...) { + throw; + } } /* Shortcut for Ricker wavelet - does same thing as pf version but scaing sample interval. Note the use of the copy constructor to always make wavelet_name ricker in this case. */ ShapingWavelet::ShapingWavelet(const double fpeak, const double dtin, - const int n) : wavelet_name("ricker") -{ - nfft=n; - dt=dtin; - df=1.0/(dt*static_cast(n)); + const int n) + : wavelet_name("ricker") { + nfft = n; + dt = dtin; + df = 1.0 / (dt * static_cast(n)); double *r; - r=rickerwavelet((float)fpeak,(float)dt,nfft); - w=ComplexArray(nfft,r); + r = rickerwavelet((float)fpeak, (float)dt, nfft); + w = ComplexArray(nfft, r); gsl_fft_complex_wavetable *wavetable; gsl_fft_complex_workspace *workspace; - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); - gsl_fft_complex_wavetable_free (wavetable); - gsl_fft_complex_workspace_free (workspace); - delete [] r; + gsl_fft_complex_wavetable_free(wavetable); + gsl_fft_complex_workspace_free(workspace); + delete[] r; } ShapingWavelet::ShapingWavelet(const int npolelo, const double f3dblo, - const int npolehi, const double f3dbhi,const double dtin, const int n) - : wavelet_name("butterworth") -{ - nfft=n; - dt=dtin; - df=1.0/(dt*static_cast(n)); - Butterworth bwf(true,true,true,npolelo,f3dblo,npolehi,f3dbhi,dtin); - w=bwf.transfer_function(nfft); + const int npolehi, const double f3dbhi, + const double dtin, const int n) + : wavelet_name("butterworth") { + nfft = n; + dt = dtin; + df = 1.0 / (dt * static_cast(n)); + Butterworth bwf(true, true, true, npolelo, f3dblo, npolehi, f3dbhi, dtin); + w = bwf.transfer_function(nfft); } -ShapingWavelet::ShapingWavelet(const ShapingWavelet& parent) : w(parent.w) -{ - dt=parent.dt; - df=parent.df; - wavelet_name=parent.wavelet_name; +ShapingWavelet::ShapingWavelet(const ShapingWavelet &parent) : w(parent.w) { + dt = parent.dt; + df = parent.df; + wavelet_name = parent.wavelet_name; } -ShapingWavelet::ShapingWavelet(CoreTimeSeries d, int nfft) -{ - const string base_error("ShapingWavelet TimeSeries constructor: "); - wavelet_name=string("data"); - /* Silently handle this default condition that allows calling the - * constructor without the nfft argument */ - if(nfft<=0) nfft=d.npts(); - dt=d.dt(); - df=1.0/(dt*((double)nfft)); - /* This is prone to an off by one error */ - double t0; - if(nfft%2) - t0=-(double)(nfft/2) + 1; - else - t0=-(double)(nfft/2); - t0 *= dt; - /* A basic sanity check on d */ - if(d.time_is_UTC()) - throw MsPASSError(base_error - + "Shaping wavelet must be defined in relative time centered on 0", - ErrorSeverity::Invalid); - if( (d.endtime()(-t0)) ) - throw MsPASSError(base_error + "Input wavelet time span is illegal\n" - + "Wavelet must be centered on 0 reference time", - ErrorSeverity::Invalid); - /* Create a work vector an initialize it to zeros to make insertion - * algorithm easier */ - vector dwork; - dwork.reserve(nfft); - int i; - for(i=0; id.endtime()) break; - if( (iw>=0) && (iw (-t0))) + throw MsPASSError(base_error + "Input wavelet time span is illegal\n" + + "Wavelet must be centered on 0 reference time", + ErrorSeverity::Invalid); + /* Create a work vector an initialize it to zeros to make insertion + * algorithm easier */ + vector dwork; + dwork.reserve(nfft); + int i; + for (i = 0; i < nfft; ++i) + dwork.push_back(0.0); + /* We loop over + * we skip through d vector until we insert */ + for (i = 0; i < d.npts(); ++i) { + double t; + t = t0 + dt * ((double)i); + int iw = d.sample_number(t); + if (t > d.endtime()) + break; + if ((iw >= 0) && (iw < nfft)) + dwork[i] = d.s[iw]; + } + gsl_fft_complex_wavetable *wavetable; + gsl_fft_complex_workspace *workspace; + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + w = ComplexArray(nfft, &(dwork[0])); + gsl_fft_complex_forward(w.ptr(), 1, nfft, wavetable, workspace); + gsl_fft_complex_wavetable_free(wavetable); + gsl_fft_complex_workspace_free(workspace); } -ShapingWavelet& ShapingWavelet::operator=(const ShapingWavelet& parent) -{ - if(this != &parent) - { - w=parent.w; - dt=parent.dt; - df=parent.df; - wavelet_name=parent.wavelet_name; - } - return *this; +ShapingWavelet &ShapingWavelet::operator=(const ShapingWavelet &parent) { + if (this != &parent) { + w = parent.w; + dt = parent.dt; + df = parent.df; + wavelet_name = parent.wavelet_name; + } + return *this; } -CoreTimeSeries ShapingWavelet::impulse_response() -{ - try { - int nfft=w.size(); - gsl_fft_complex_wavetable *wavetable; - gsl_fft_complex_workspace *workspace; - wavetable = gsl_fft_complex_wavetable_alloc (nfft); - workspace = gsl_fft_complex_workspace_alloc (nfft); - /* We need to copy the current shaping wavelet or the inverse fft - * will make it invalid */ - ComplexArray iwf(w); - gsl_fft_complex_inverse(iwf.ptr(), 1, nfft, wavetable, workspace); - gsl_fft_complex_wavetable_free (wavetable); - gsl_fft_complex_workspace_free (workspace); - CoreTimeSeries result(nfft); +CoreTimeSeries ShapingWavelet::impulse_response() { + try { + int nfft = w.size(); + gsl_fft_complex_wavetable *wavetable; + gsl_fft_complex_workspace *workspace; + wavetable = gsl_fft_complex_wavetable_alloc(nfft); + workspace = gsl_fft_complex_workspace_alloc(nfft); + /* We need to copy the current shaping wavelet or the inverse fft + * will make it invalid */ + ComplexArray iwf(w); + gsl_fft_complex_inverse(iwf.ptr(), 1, nfft, wavetable, workspace); + gsl_fft_complex_wavetable_free(wavetable); + gsl_fft_complex_workspace_free(workspace); + CoreTimeSeries result(nfft); - result.set_tref(TimeReferenceType::Relative); - result.set_npts(nfft); - result.set_dt(dt); - result.set_t0(dt*(-(double)nfft/2)); - result.set_live(); - /* Unfold the fft output */ - int k,shift; - shift=nfft/2; - // Need this because constructor fills initially with nfft zeros and - // we use this approach to unfold the fft output - result.s.clear(); - for(k=0;k(result.s); - return result; - } catch(...) { - throw; - }; + result.set_tref(TimeReferenceType::Relative); + result.set_npts(nfft); + result.set_dt(dt); + result.set_t0(dt * (-(double)nfft / 2)); + result.set_live(); + /* Unfold the fft output */ + int k, shift; + shift = nfft / 2; + // Need this because constructor fills initially with nfft zeros and + // we use this approach to unfold the fft output + result.s.clear(); + for (k = 0; k < nfft; ++k) + result.s.push_back(iwf[k].real()); + result.s = circular_shift(result.s, shift); + result.s = normalize(result.s); + return result; + } catch (...) { + throw; + }; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/ThreeCSpike.cc b/cxx/src/lib/algorithms/deconvolution/ThreeCSpike.cc index da69aec8c..0ee69329a 100644 --- a/cxx/src/lib/algorithms/deconvolution/ThreeCSpike.cc +++ b/cxx/src/lib/algorithms/deconvolution/ThreeCSpike.cc @@ -1,37 +1,36 @@ /* This class is defined in this file because it is intimately linked to that operator. We put the C++ code here to make it easier to find. */ #include "mspass/algorithms/deconvolution/GeneralIterDecon.h" -namespace mspass::algorithms::deconvolution -{ +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::utility; -ThreeCSpike::ThreeCSpike(dmatrix& d, int k) -{ - try { - int i; - for(i=0; i<3; ++i) u[i]=d(i,k); - col=k; - for(i=0,amp=0.0; i<3; ++i) amp += u[i]*u[i]; - amp=sqrt(amp); - } catch(...) { - throw; - }; +ThreeCSpike::ThreeCSpike(dmatrix &d, int k) { + try { + int i; + for (i = 0; i < 3; ++i) + u[i] = d(i, k); + col = k; + for (i = 0, amp = 0.0; i < 3; ++i) + amp += u[i] * u[i]; + amp = sqrt(amp); + } catch (...) { + throw; + }; } -ThreeCSpike::ThreeCSpike(const ThreeCSpike& parent) -{ - col=parent.col; - amp=parent.amp; - for(int k=0; k<3; ++k)u[k]=parent.u[k]; +ThreeCSpike::ThreeCSpike(const ThreeCSpike &parent) { + col = parent.col; + amp = parent.amp; + for (int k = 0; k < 3; ++k) + u[k] = parent.u[k]; } -ThreeCSpike& ThreeCSpike::operator=(const ThreeCSpike& parent) -{ - if(this!=(&parent)) - { - col=parent.col; - amp=parent.amp; - for(int k=0; k<3; ++k)u[k]=parent.u[k]; - } - return *this; +ThreeCSpike &ThreeCSpike::operator=(const ThreeCSpike &parent) { + if (this != (&parent)) { + col = parent.col; + amp = parent.amp; + for (int k = 0; k < 3; ++k) + u[k] = parent.u[k]; + } + return *this; } -} // End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/WaterLevelDecon.cc b/cxx/src/lib/algorithms/deconvolution/WaterLevelDecon.cc index 3ed7e6c51..dea787352 100644 --- a/cxx/src/lib/algorithms/deconvolution/WaterLevelDecon.cc +++ b/cxx/src/lib/algorithms/deconvolution/WaterLevelDecon.cc @@ -1,196 +1,189 @@ -#include // Needed for DBL_EPSILON -#include "mspass/algorithms/amplitudes.h" #include "mspass/algorithms/deconvolution/WaterLevelDecon.h" -namespace mspass::algorithms::deconvolution -{ +#include "mspass/algorithms/amplitudes.h" +#include // Needed for DBL_EPSILON +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; using mspass::algorithms::amplitudes::normalize; WaterLevelDecon::WaterLevelDecon(const WaterLevelDecon &parent) - : FFTDeconOperator(parent),ScalarDecon(parent) -{ - wlv=parent.wlv; + : FFTDeconOperator(parent), ScalarDecon(parent) { + wlv = parent.wlv; } -int WaterLevelDecon::read_metadata(const Metadata &md) -{ - try { - const string base_error("SimpleLeastTaperDecon::read_metadata method: "); - int nfft_from_win=ComputeFFTLength(md); - //window based nfft always overrides that extracted directly from md */ - if(nfft_from_win!=FFTDeconOperator::nfft) - { - this->change_size(nfft_from_win); - } - wlv=md.get_double("water_level"); - shapingwavelet=ShapingWavelet(md,FFTDeconOperator::nfft); - return 0; - } catch(...) { - throw; - }; +int WaterLevelDecon::read_metadata(const Metadata &md) { + try { + const string base_error("SimpleLeastTaperDecon::read_metadata method: "); + int nfft_from_win = ComputeFFTLength(md); + // window based nfft always overrides that extracted directly from md */ + if (nfft_from_win != FFTDeconOperator::nfft) { + this->change_size(nfft_from_win); + } + wlv = md.get_double("water_level"); + shapingwavelet = ShapingWavelet(md, FFTDeconOperator::nfft); + return 0; + } catch (...) { + throw; + }; } /* This constructor is little more than a call to read_metadata for this opeator */ -WaterLevelDecon::WaterLevelDecon(const Metadata &md) - : FFTDeconOperator(md) -{ - try { - this->read_metadata(md); - } catch(...) { - throw; - } +WaterLevelDecon::WaterLevelDecon(const Metadata &md) : FFTDeconOperator(md) { + try { + this->read_metadata(md); + } catch (...) { + throw; + } } /* This method is really an alias for read_metadata for this operator */ -void WaterLevelDecon::changeparameter(const Metadata &md) -{ - try { - this->read_metadata(md); - } catch(...) { - throw; - }; +void WaterLevelDecon::changeparameter(const Metadata &md) { + try { + this->read_metadata(md); + } catch (...) { + throw; + }; } -WaterLevelDecon::WaterLevelDecon(const Metadata &md, - const vector &w,const vector &d) -{ - try { - this->read_metadata(md); - } catch(...) { - throw; - }; - wavelet=w; - data=d; +WaterLevelDecon::WaterLevelDecon(const Metadata &md, const vector &w, + const vector &d) { + try { + this->read_metadata(md); + } catch (...) { + throw; + }; + wavelet = w; + data = d; } -void WaterLevelDecon::process() -{ +void WaterLevelDecon::process() { - //apply fft to the input trace data - // data and wavelet sizes need to be zero padded if the are short - if(data.size()0) - { - for(int k=sample_shift; k>0; k--) - result.push_back(rf_fft[nfft-k].real()); - for(unsigned int k=0; k 0) { + for (int k = sample_shift; k > 0; k--) + result.push_back(rf_fft[nfft - k].real()); + for (unsigned int k = 0; k < data.size() - sample_shift; k++) + result.push_back(rf_fft[k].real()); + } else { + for (unsigned int k = 0; k < data.size(); k++) + result.push_back(rf_fft[k].real()); + } } -CoreTimeSeries WaterLevelDecon::actual_output() -{ - try { - ComplexArray W(nfft,&(wavelet[0])); - gsl_fft_complex_forward(W.ptr(),1,nfft,wavetable,workspace); - ComplexArray ao_fft; - ao_fft=winv*W; - /* We always apply the shaping wavelet - this perhaps should be optional - but probably better done with a none option for the shaping wavelet */ - ao_fft=(*shapingwavelet.wavelet())*ao_fft; - gsl_fft_complex_inverse(ao_fft.ptr(),1,nfft,wavetable,workspace); - vector ao; - ao.reserve(nfft); - for(unsigned int k=0; k(ao); - CoreTimeSeries result(nfft); - /* Getting dt from here is unquestionably a flaw in the api, but will - retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ - double dt=this->shapingwavelet.sample_interval(); - result.set_t0(-dt*((double)i0)); - result.set_dt(dt); - result.set_live(); - result.set_tref(TimeReferenceType::Relative); - result.set_npts(nfft); - for(int k=0;k ao; + ao.reserve(nfft); + for (unsigned int k = 0; k < ao_fft.size(); ++k) + ao.push_back(ao_fft[k].real()); + /* We always shift this wavelet to the center of the data vector. + We handle the time through the CoreTimeSeries object. */ + int i0 = nfft / 2; + ao = circular_shift(ao, i0); + ao = normalize(ao); + CoreTimeSeries result(nfft); + /* Getting dt from here is unquestionably a flaw in the api, but will + retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ + double dt = this->shapingwavelet.sample_interval(); + result.set_t0(-dt * ((double)i0)); + result.set_dt(dt); + result.set_live(); + result.set_tref(TimeReferenceType::Relative); + result.set_npts(nfft); + for (int k = 0; k < nfft; ++k) + result.s[k] = ao[k]; + ; + return result; + } catch (...) { + throw; + }; } -CoreTimeSeries WaterLevelDecon::inverse_wavelet(const double t0parent) -{ - try { - /* Getting dt from here is unquestionably a flaw in the api, but will - * retain for now. Perhaps should a copy of dt in the ScalarDecon object. */ - double dt=this->shapingwavelet.sample_interval(); - return (this->FFTDeconOperator::FourierInverse(this->winv, - *shapingwavelet.wavelet(),dt,t0parent)); - } catch(...) { - throw; - }; +CoreTimeSeries WaterLevelDecon::inverse_wavelet(const double t0parent) { + try { + /* Getting dt from here is unquestionably a flaw in the api, but will + * retain for now. Perhaps should a copy of dt in the ScalarDecon + * object. */ + double dt = this->shapingwavelet.sample_interval(); + return (this->FFTDeconOperator::FourierInverse( + this->winv, *shapingwavelet.wavelet(), dt, t0parent)); + } catch (...) { + throw; + }; } -CoreTimeSeries WaterLevelDecon::inverse_wavelet() -{ - try{ +CoreTimeSeries WaterLevelDecon::inverse_wavelet() { + try { return this->inverse_wavelet(0.0); - }catch(...){throw;}; + } catch (...) { + throw; + }; } -Metadata WaterLevelDecon::QCMetrics() -{ +Metadata WaterLevelDecon::QCMetrics() { /* Return only an empty Metadata container. Done as it is easier to maintain the code letting python do this work. This also anticipates new metrics being added which would be @@ -198,4 +191,4 @@ Metadata WaterLevelDecon::QCMetrics() Metadata md; return md; } -}//End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/circular_shift.cc b/cxx/src/lib/algorithms/deconvolution/circular_shift.cc index e7a267ccd..dbd433182 100644 --- a/cxx/src/lib/algorithms/deconvolution/circular_shift.cc +++ b/cxx/src/lib/algorithms/deconvolution/circular_shift.cc @@ -1,8 +1,7 @@ +#include "mspass/utility/MsPASSError.h" #include #include -#include "mspass/utility/MsPASSError.h" -namespace mspass::algorithms::deconvolution -{ +namespace mspass::algorithms::deconvolution { using namespace std; using namespace mspass::utility; @@ -22,40 +21,42 @@ can be achieved in the frequency by the standard linear phase shift theorem. \return - time shifted signal (same length as input d) */ -vector circular_shift(const vector& d,const int i0) -{ - /* a few basic sanity checks are useful to allow broader use */ - const string base_error("circular_shift procedure: "); - int nd(d.size()); - if(nd<=0) throw MsPASSError(base_error - + "Received empty data vector",ErrorSeverity::Invalid); - /* This one can be corrected - a negative shift is equivalent to this formula */ - int i0used; - if(i0<0) - i0used=nd+i0; - else - i0used=i0; - /* It is an error if i0 is still negative as it means we asked for a shift - * larger than nd. This could be handled, but to me it would scream a coding - * error */ - if(i0used<0) throw MsPASSError(base_error - + "Large negative shift exceeds length of data vector", - ErrorSeverity::Invalid); - if(i0used>=nd) throw MsPASSError(base_error - + "Large positive shift exceeds length of data vector", - ErrorSeverity::Invalid); - vector result; - result.reserve(nd); - int k,kk; - if(i0<0) - kk=nd+i0; - else - kk=i0; - for(k=0; k circular_shift(const vector &d, const int i0) { + /* a few basic sanity checks are useful to allow broader use */ + const string base_error("circular_shift procedure: "); + int nd(d.size()); + if (nd <= 0) + throw MsPASSError(base_error + "Received empty data vector", + ErrorSeverity::Invalid); + /* This one can be corrected - a negative shift is equivalent to this formula + */ + int i0used; + if (i0 < 0) + i0used = nd + i0; + else + i0used = i0; + /* It is an error if i0 is still negative as it means we asked for a shift + * larger than nd. This could be handled, but to me it would scream a coding + * error */ + if (i0used < 0) + throw MsPASSError(base_error + + "Large negative shift exceeds length of data vector", + ErrorSeverity::Invalid); + if (i0used >= nd) + throw MsPASSError(base_error + + "Large positive shift exceeds length of data vector", + ErrorSeverity::Invalid); + vector result; + result.reserve(nd); + int k, kk; + if (i0 < 0) + kk = nd + i0; + else + kk = i0; + for (k = 0; k < nd; ++k, ++kk) { + kk %= nd; + result.push_back(d[kk]); + } + return result; } -} //End namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/dpss.cc b/cxx/src/lib/algorithms/deconvolution/dpss.cc index 447a23da6..8922718ea 100644 --- a/cxx/src/lib/algorithms/deconvolution/dpss.cc +++ b/cxx/src/lib/algorithms/deconvolution/dpss.cc @@ -1,208 +1,208 @@ -//#include "perf.h" -#include -#include "misc/blas.h" +// #include "perf.h" #include "mspass/algorithms/deconvolution/dpss.h" -namespace mspass::algorithms::deconvolution -{ +#include "misc/blas.h" +#include +namespace mspass::algorithms::deconvolution { using namespace std; -void compute_energy_concentrations(double *h, int n, double NW, double *lambda, int nseq) { +void compute_energy_concentrations(double *h, int n, double NW, double *lambda, + int nseq) { - //kernel - double *kern= new double[n]; + // kernel + double *kern = new double[n]; - kern[0]=2.0*NW/n; - for (int ii=1; ii 0) + eig_iit(n_even, D, E, n_even - sequ_even, n_even - seql_even, eig_val, + &h[start_even], 2 * n); + + // ODD PROBLEM + D[n_even - 1] = dc - ec; + if (nseq_odd > 0) + eig_iit(n_odd, D, E, n_odd - sequ_odd, n_odd - seql_odd, + &eig_val[nseq_even], &h[start_odd], 2 * n); + + // reverse eigenvector order + double tmp; + for (int ii = 0; ii < floor(nseq / 2); ii++) { + for (int jj = 0; jj < n_even; jj++) { + tmp = h[(nseq - 1 - ii) * n + jj]; + h[(nseq - 1 - ii) * n + jj] = h[ii * n + jj]; + h[ii * n + jj] = tmp; } - - //central values for splitting - double dc=D[n_even-1], ec=E[n_even-1]; - - //EVEN PROBLEM - if (is_n_even) - D[n_even-1]=dc+ec; - else - E[n_odd-1]=sqrt(2)*E[n_odd-1]; - - int start_odd=0,start_even=0; - if (sequ%2==0) - start_odd=n; - else - start_even=n; - - //calculate eigenvectors - if (nseq_even>0) - eig_iit(n_even,D,E,n_even-sequ_even,n_even-seql_even,eig_val,&h[start_even],2*n); - - //ODD PROBLEM - D[n_even-1]=dc-ec; - if (nseq_odd>0) - eig_iit(n_odd,D,E,n_odd-sequ_odd,n_odd-seql_odd,&eig_val[nseq_even],&h[start_odd],2*n); - - //reverse eigenvector order - double tmp; - for (int ii=0; iimsg,30); -} -const char *ERR::getmsg() { - return msg; -} +ERR::ERR(const char *errmsg) { strncpy(msg, errmsg, 30); } +void ERR::getmsg(char *errmsg) { strncpy(errmsg, this->msg, 30); } +const char *ERR::getmsg() { return msg; } LAPACK_ERROR::LAPACK_ERROR(const char *errmsg) : ERR(errmsg) {}; -} //end namespace +} // namespace mspass::algorithms::deconvolution diff --git a/cxx/src/lib/algorithms/deconvolution/wavelet.cc b/cxx/src/lib/algorithms/deconvolution/wavelet.cc index 2989eafda..9cf5d42a3 100644 --- a/cxx/src/lib/algorithms/deconvolution/wavelet.cc +++ b/cxx/src/lib/algorithms/deconvolution/wavelet.cc @@ -1,54 +1,47 @@ -#include #include "mspass/algorithms/deconvolution/dpss.h" -namespace mspass::algorithms::deconvolution -{ +#include +namespace mspass::algorithms::deconvolution { using namespace std; - /* Return a vector of n doubles contain a ricker wavelet defined by center * frequence fpeak sampled at dt. Not checking is done to assure dt is * rational. IMPORTANT to note output is circular shifted to zero phase * so fft of result will not produce a time shift. */ -double *rickerwavelet(float fpeak, float dt, int n) -{ - //modified from http://toto-share.com/2011/05/cc-ricker-wavelets-code/ - int i, k; - int nw; - int nc; - double nw1, alpha, beta; +double *rickerwavelet(float fpeak, float dt, int n) { + // modified from http://toto-share.com/2011/05/cc-ricker-wavelets-code/ + int i, k; + int nw; + int nc; + double nw1, alpha, beta; - nw1 = 2.2/fpeak/dt; - nw = 2*floor(nw1/2)+1; - nc = floor(nw/2); - double *wricker=new double[n]; + nw1 = 2.2 / fpeak / dt; + nw = 2 * floor(nw1 / 2) + 1; + nc = floor(nw / 2); + double *wricker = new double[n]; - for (i=0; i #include "mspass/algorithms/TimeWindow.h" -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/SphericalCoordinate.h" -#include "mspass/seismic/Seismogram.h" #include "mspass/seismic/Ensemble.h" +#include "mspass/seismic/Seismogram.h" #include "mspass/seismic/keywords.h" -namespace mspass::algorithms -{ +#include "mspass/utility/MsPASSError.h" +#include "mspass/utility/SphericalCoordinate.h" +#include +namespace mspass::algorithms { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; @@ -19,11 +18,10 @@ This file was modified from code developed earlier in antelope contrib. Most changes are little more than name changes. Author: Gary L. Pavlis - Indiana University - pavlis@indiana.edu + Indiana University + pavlis@indiana.edu */ - /* This function converts a Seismogram from an absolute time standard to an arrival time reference frame using an arrival time stored in the metadata area of the object and referenced by a variable keyword. @@ -33,249 +31,236 @@ to the result. If the window is smaller a subset is returned and appropriate parameters are updated. Arguments: - tcsi - input data. Assumed to be in the reference frame that - matches the arrival time to be used to define time window - arrival_time_key - metadata keyword string used to extract the - time used to define time 0. i.e. this is the keyword - used to access a metadata field that defines the time - that will become time 0 o the result. - tw - TimeWindow in relative time units wrt arrival time defining - section of data to be extracted. Time 0 of the result - will be the arrival time. + tcsi - input data. Assumed to be in the reference frame that + matches the arrival time to be used to define time window + arrival_time_key - metadata keyword string used to extract the + time used to define time 0. i.e. this is the keyword + used to access a metadata field that defines the time + that will become time 0 o the result. + tw - TimeWindow in relative time units wrt arrival time defining + section of data to be extracted. Time 0 of the result + will be the arrival time. */ -shared_ptr ArrivalTimeReference(Seismogram& tcsi, - string arrival_time_key, - TimeWindow tw) -{ - double atime; - string base_error_message("ArrivalTimeReference: "); - try - { - atime = tcsi.get_double(arrival_time_key); - // Intentionally use the base class since the contents are discarded - // get_double currently would throw a MetadataGetError - } catch (MetadataGetError& mde) - { - throw MsPASSError(base_error_message - + arrival_time_key - + string(" not found in Seismogram object"), - ErrorSeverity::Invalid); - } - // We have to check this condition because ator will do nothing if - // time is already relative and this condition cannot be tolerated - // here as we have no idea what the time standard might be otherwise - if(tcsi.time_is_relative()) - throw MsPASSError(string("ArrivalTimeReference: ") - + string("received data in relative time units\n") - + string("Cannot proceed as timing is ambiguous"), - ErrorSeverity::Invalid); - // start with a clone of the original - shared_ptr tcso(new Seismogram(tcsi)); - tcso->ator(atime); // shifts to arrival time relative time reference - // Simply return a copy of traces marked dead - if(tcso->dead()) return(tcso); - /* this section does nothing if the time window requested exceeds the - data extent, BUT ensembles of such data will have irregular start and - end times. Presume all code will handle that case as the norm in - passive array data, but might be confusing if applied to active source - data. */ - if( (tw.start > tcso->t0()) || (tw.endtime(tcso->u.columns()-1) ) ) - { - int jstart, jend; - int ns_to_copy; - int i,j,jj; - jstart = tcso->sample_number(tw.start); - jend = tcso->sample_number(tw.end); - if(jstart<0) jstart=0; - if(jend>=tcso->u.columns()) jend = tcso->u.columns() - 1; - ns_to_copy = jend - jstart + 1; - // This is a null trace so mark it dead in this condition - if(ns_to_copy<0) - { - ns_to_copy=0; - tcso->kill(); - tcso->u=dmatrix(1,1); - tcso->set_npts(0); - } - else - { - // This is not the fastest way to do this, but it is - // clearer and the performance hit should not be serious - // old advice: make it work before you make it fast - // this is not needed in new api because set_npts does this - //tcso->u = dmatrix(3,ns_to_copy); - tcso->set_npts(ns_to_copy); - for(i=0; i<3; ++i) - for(j=0,jj=jstart; ju(i,j)=tcsi.u(i,jj); - //old api form - //tcso->t0 += (tcso->dt)*static_cast(jstart); - double newt0; - newt0=tcso->t0() + (tcso->dt())*static_cast(jstart); - tcso->set_t0(newt0); - /* Don't think the block below is needed for new api, but will retain this - code commented out until I know for sure glp-6/7/2020*/ - // start of old - // - // This is necessary to allow for a rtoa (relative - // to absolute time) conversion later. - // - /* - if(jstart>0) - { - double stime=atime+tcso->t0(); - tcso->put("time",stime); - // this one may not really be necessary - tcso->put("endtime",atime+tcso->endtime()); - } - end of old */ - } +shared_ptr +ArrivalTimeReference(Seismogram &tcsi, string arrival_time_key, TimeWindow tw) { + double atime; + string base_error_message("ArrivalTimeReference: "); + try { + atime = tcsi.get_double(arrival_time_key); + // Intentionally use the base class since the contents are discarded + // get_double currently would throw a MetadataGetError + } catch (MetadataGetError &mde) { + throw MsPASSError(base_error_message + arrival_time_key + + string(" not found in Seismogram object"), + ErrorSeverity::Invalid); + } + // We have to check this condition because ator will do nothing if + // time is already relative and this condition cannot be tolerated + // here as we have no idea what the time standard might be otherwise + if (tcsi.time_is_relative()) + throw MsPASSError(string("ArrivalTimeReference: ") + + string("received data in relative time units\n") + + string("Cannot proceed as timing is ambiguous"), + ErrorSeverity::Invalid); + // start with a clone of the original + shared_ptr tcso(new Seismogram(tcsi)); + tcso->ator(atime); // shifts to arrival time relative time reference + // Simply return a copy of traces marked dead + if (tcso->dead()) + return (tcso); + /* this section does nothing if the time window requested exceeds the + data extent, BUT ensembles of such data will have irregular start and + end times. Presume all code will handle that case as the norm in + passive array data, but might be confusing if applied to active source + data. */ + if ((tw.start > tcso->t0()) || (tw.end < tcso->time(tcso->u.columns() - 1))) { + int jstart, jend; + int ns_to_copy; + int i, j, jj; + jstart = tcso->sample_number(tw.start); + jend = tcso->sample_number(tw.end); + if (jstart < 0) + jstart = 0; + if (jend >= tcso->u.columns()) + jend = tcso->u.columns() - 1; + ns_to_copy = jend - jstart + 1; + // This is a null trace so mark it dead in this condition + if (ns_to_copy < 0) { + ns_to_copy = 0; + tcso->kill(); + tcso->u = dmatrix(1, 1); + tcso->set_npts(0); + } else { + // This is not the fastest way to do this, but it is + // clearer and the performance hit should not be serious + // old advice: make it work before you make it fast + // this is not needed in new api because set_npts does this + // tcso->u = dmatrix(3,ns_to_copy); + tcso->set_npts(ns_to_copy); + for (i = 0; i < 3; ++i) + for (j = 0, jj = jstart; j < ns_to_copy; ++j, ++jj) + tcso->u(i, j) = tcsi.u(i, jj); + // old api form + // tcso->t0 += (tcso->dt)*static_cast(jstart); + double newt0; + newt0 = tcso->t0() + (tcso->dt()) * static_cast(jstart); + tcso->set_t0(newt0); + /* Don't think the block below is needed for new api, but will retain this + code commented out until I know for sure glp-6/7/2020*/ + // start of old + // + // This is necessary to allow for a rtoa (relative + // to absolute time) conversion later. + // + /* + if(jstart>0) + { + double stime=atime+tcso->t0(); + tcso->put("time",stime); + // this one may not really be necessary + tcso->put("endtime",atime+tcso->endtime()); + } + end of old */ } - return(tcso); + } + return (tcso); } /* Similar function to above but processes an entire ensemble. The only special thing it does is handle exceptions. When the single object processing function throws an exception the error is printed and the object is simply not copied to the output ensemble */ -shared_ptr> ArrivalTimeReference(Ensemble& tcei, - string arrival_time_key, - TimeWindow tw) -{ - int nmembers=tcei.member.size(); - // use the special constructor to only clone the metadata and - // set aside slots for the new ensemble. - shared_ptr> - tceo(new Ensemble(dynamic_cast(tcei), - nmembers)); - tceo->member.reserve(nmembers); // reserve this many slots for efficiency - // We have to use a loop instead of for_each as I don't see how - // else to handle errors cleanly here. - vector::iterator indata; - for(indata=tcei.member.begin(); indata!=tcei.member.end(); ++indata) - { - try { - shared_ptr - tcs(ArrivalTimeReference(*indata,arrival_time_key,tw)); - tceo->member.push_back(*tcs); - } catch ( MsPASSError& serr) - { - serr.log_error(); - cerr << "This Seismogram was not copied to output ensemble"<> +ArrivalTimeReference(Ensemble &tcei, string arrival_time_key, + TimeWindow tw) { + int nmembers = tcei.member.size(); + // use the special constructor to only clone the metadata and + // set aside slots for the new ensemble. + shared_ptr> tceo( + new Ensemble(dynamic_cast(tcei), nmembers)); + tceo->member.reserve(nmembers); // reserve this many slots for efficiency + // We have to use a loop instead of for_each as I don't see how + // else to handle errors cleanly here. + vector::iterator indata; + for (indata = tcei.member.begin(); indata != tcei.member.end(); ++indata) { + try { + shared_ptr tcs( + ArrivalTimeReference(*indata, arrival_time_key, tw)); + tceo->member.push_back(*tcs); + } catch (MsPASSError &serr) { + serr.log_error(); + cerr << "This Seismogram was not copied to output ensemble" << endl; } - return(tceo); + } + return (tceo); } -/* This is a procedural form of one of the rotate method and is a bit redundant. */ -void HorizontalRotation(Seismogram& d, double phi) -{ - double tmatrix[3][3]; - double a,b; - a=cos(phi); - b=sin(phi); - tmatrix[0][0]=a; - tmatrix[1][0]=-b; - tmatrix[2][0]=0.0; - tmatrix[0][1]=b; - tmatrix[1][1]=a; - tmatrix[2][1]=0.0; - tmatrix[0][2]=0.0; - tmatrix[1][2]=0.0; - tmatrix[2][2]=1.0; - d.transform(tmatrix); +/* This is a procedural form of one of the rotate method and is a bit redundant. + */ +void HorizontalRotation(Seismogram &d, double phi) { + double tmatrix[3][3]; + double a, b; + a = cos(phi); + b = sin(phi); + tmatrix[0][0] = a; + tmatrix[1][0] = -b; + tmatrix[2][0] = 0.0; + tmatrix[0][1] = b; + tmatrix[1][1] = a; + tmatrix[2][1] = 0.0; + tmatrix[0][2] = 0.0; + tmatrix[1][2] = 0.0; + tmatrix[2][2] = 1.0; + d.transform(tmatrix); } -TimeSeries ExtractComponent(const Seismogram& tcs,const unsigned int component) -{ +TimeSeries ExtractComponent(const Seismogram &tcs, + const unsigned int component) { /* No need to test for negative because we use an unsigned */ - if(component>=3) throw MsPASSError("ExtractComponent: illegal component number - must be 0, 1, or 2", - ErrorSeverity::Invalid); + if (component >= 3) + throw MsPASSError( + "ExtractComponent: illegal component number - must be 0, 1, or 2", + ErrorSeverity::Invalid); /* Return a null seismogram if tcs is marked dead */ - if(tcs.dead()) return TimeSeries(); - try{ + if (tcs.dead()) + return TimeSeries(); + try { vector scomp; - for(size_t i=0;i(tcs), - dynamic_cast(tcs), - dynamic_cast(tcs), - scomp); + TimeSeries result(dynamic_cast(tcs), + dynamic_cast(tcs), + dynamic_cast(tcs), scomp); /* This section insert hang and vang. We use the SphericalCoordinate function to convert the unit vector stored in the rows of the tmatrix. */ - double nu[3],hang,vang; - if(tcs.cardinal()) - { + double nu[3], hang, vang; + if (tcs.cardinal()) { /* We handle this case under an assumption it is worth avoiding the cost of the trig functions needed to handle the not cardinal case */ - switch(component) - { - case 0: - hang=90.0; - vang=90.0; - break; - case 1: - hang=0.0; - vang=90.0; - break; - case 2: - hang=0.0; - vang=0.0; + switch (component) { + case 0: + hang = 90.0; + vang = 90.0; + break; + case 1: + hang = 0.0; + vang = 90.0; + break; + case 2: + hang = 0.0; + vang = 0.0; }; - } - else - { - dmatrix tm=tcs.get_transformation_matrix(); - for(size_t k=0;k<3;++k) - { - nu[k]=tm(component,k); + } else { + dmatrix tm = tcs.get_transformation_matrix(); + for (size_t k = 0; k < 3; ++k) { + nu[k] = tm(component, k); } SphericalCoordinate sc; - sc=UnitVectorToSpherical(nu); - vang=mspass::utility::deg(sc.theta); - hang=90.0-mspass::utility::deg(sc.phi); + sc = UnitVectorToSpherical(nu); + vang = mspass::utility::deg(sc.theta); + hang = 90.0 - mspass::utility::deg(sc.phi); } - result.put(SEISMICMD_hang,hang); - result.put(SEISMICMD_vang,vang); + result.put(SEISMICMD_hang, hang); + result.put(SEISMICMD_vang, vang); /*we hard code some ProcessingHistory here with the algid defining the component number extracted. This model should work for any algorithm that has only one argument that can be represented as a string. We do do nothing of the history is empty. */ - if(!result.is_empty()) - { + if (!result.is_empty()) { stringstream ss; - ss<<"component="< ExtractComponent(const Ensemble& d, - const unsigned int comp) -{ - //Using size_t = unsigned int means we don't need to check negative comp - if(comp>=3) - throw MsPASSError("ExtractComponent(Ensmble): illegal component number - must be 0,1, or 2", - ErrorSeverity::Invalid); - try{ +Ensemble ExtractComponent(const Ensemble &d, + const unsigned int comp) { + // Using size_t = unsigned int means we don't need to check negative comp + if (comp >= 3) + throw MsPASSError("ExtractComponent(Ensmble): illegal component number - " + "must be 0,1, or 2", + ErrorSeverity::Invalid); + try { Ensemble result; result.Metadata::operator=(d); result.member.reserve(d.member.size()); vector::const_iterator dptr; - for(dptr=d.member.begin();dptr!=d.member.end();++dptr) - { + for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) { TimeSeries dts; - dts=ExtractComponent(*dptr,comp); + dts = ExtractComponent(*dptr, comp); result.member.push_back(dts); } return result; - } catch(...){throw;}; + } catch (...) { + throw; + }; }; - -} // end mspass namespace encapsulation +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/slice_and_dice.cc b/cxx/src/lib/algorithms/slice_and_dice.cc index 4a9cdcab5..cf5bfa7a0 100644 --- a/cxx/src/lib/algorithms/slice_and_dice.cc +++ b/cxx/src/lib/algorithms/slice_and_dice.cc @@ -1,16 +1,16 @@ -#include -#include "mspass/utility/MsPASSError.h" #include "mspass/algorithms/TimeWindow.h" -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/keywords.h" -namespace mspass::algorithms -{ +#include "mspass/utility/MsPASSError.h" +#include +namespace mspass::algorithms { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; -/*! \brief Extracts a requested time window of data from a parent coreSeismogram object. +/*! \brief Extracts a requested time window of data from a parent coreSeismogram +object. It is common to need to extract a smaller segment of data from a larger time window of data. This function accomplishes this in a nifty method that @@ -20,59 +20,56 @@ handling time. \return new CoreSeismogram object derived from parent but windowed by input time window range. -\exception MsPASSError object if the requested time window is not inside data range +\exception MsPASSError object if the requested time window is not inside data +range \param parent is the larger CoreSeismogram object to be windowed \param tw defines the data range to be extracted from parent. */ -CoreSeismogram WindowData(const CoreSeismogram& parent, const TimeWindow& tw) -{ - // Always silently do nothing if marked dead - if(parent.dead()) - { - // return(CoreSeismogram()) doesn't work - // with g++. Have to us this long form - CoreSeismogram tmp; - return(tmp); - } - int is=parent.sample_number(tw.start); - /* This calculation was used in earlier versions but was found to - differ by 1 depending on the subsample timing of t0 of parent. the +CoreSeismogram WindowData(const CoreSeismogram &parent, const TimeWindow &tw) { + // Always silently do nothing if marked dead + if (parent.dead()) { + // return(CoreSeismogram()) doesn't work + // with g++. Have to us this long form + CoreSeismogram tmp; + return (tmp); + } + int is = parent.sample_number(tw.start); + /* This calculation was used in earlier versions but was found to + differ by 1 depending on the subsample timing of t0 of parent. the problem is that sample_number uses rounding which can produce that - effect due to a subtle interaction with tw.start and tw.end - relative to the sample grid. + effect due to a subtle interaction with tw.start and tw.end + relative to the sample grid. int ie=parent.sample_number(tw.end); */ - int outns,ie; - outns = round((tw.end-tw.start)/parent.dt()) + 1; + int outns, ie; + outns = round((tw.end - tw.start) / parent.dt()) + 1; ie = is + outns - 1; - if( (is<0) || (ie>parent.npts()) ) - { - ostringstream mess; - mess << "WindowData(CoreSeismogram): Window mismatch"< parent.npts())) { + ostringstream mess; + mess << "WindowData(CoreSeismogram): Window mismatch" << endl + << "Window start time=" << tw.start << " is sample number " << is + << endl + << "Window end time=" << tw.end << " is sample number " << ie << endl + << "Parent has " << parent.npts() << " samples" << endl; + throw MsPASSError(mess.str(), ErrorSeverity::Invalid); } - CoreSeismogram result(parent); - result.u=dmatrix(3,outns); - result.set_npts(outns); + CoreSeismogram result(parent); + result.u = dmatrix(3, outns); + result.set_npts(outns); /* Using the time method here preserves subsample timing.*/ - result.set_t0(parent.time(is)); + result.set_t0(parent.time(is)); // Perhaps should do this with blas or memcpy for efficiency // but this makes the algorithm much clearer - int i,ii,k; - for(i=is,ii=0;i<=ie && i((int)parent.npts())) ) - { - ostringstream mess; - mess << "WindowData(CoreTimeSeries): Window mismatch"< ((int)parent.npts()))) { + ostringstream mess; + mess << "WindowData(CoreTimeSeries): Window mismatch" << endl + << "Window start time=" << tw.start << " is sample number " << is + << endl + << "Window end time=" << tw.end << " is sample number " << ie << endl + << "Parent has " << parent.npts() << " samples" << endl; + throw MsPASSError(mess.str(), ErrorSeverity::Invalid); } - CoreTimeSeries result(parent); - result.s.reserve(outns); - result.set_npts(outns); + CoreTimeSeries result(parent); + result.s.reserve(outns); + result.set_npts(outns); /* Using the time method here preserves subsample timing.*/ - result.set_t0(parent.time(is)); - // Necessary to use the push_back method below or we get leading zeros - //result.s.clear(); - //for(int i=is;i<=ie && i=parent.npts()) ) - { - ostringstream mess; - mess << "WindowData(Seismogram): Window mismatch"<= parent.npts())) { + ostringstream mess; + mess << "WindowData(Seismogram): Window mismatch" << endl + << "Window start time=" << tw.start << " is sample number " << is + << endl + << "Window end time=" << tw.end << " is sample number " << ie << endl + << "Parent has " << parent.npts() << " samples" << endl; + /* Doing this full copy is a bit inefficient, but it avoids needing + to remove the const qualifier on parent.*/ + Seismogram dead_return(parent); + dead_return.kill(); + dead_return.elog.log_error("WindowData", mess.str(), + ErrorSeverity::Invalid); + /* In this case it is appropriate to clear the data array - this does + * that*/ + dead_return.set_npts(0); + return dead_return; } - /* MAINTENANCE ISSUE: The original implementation of this code used a copy constructor - here to initalize result. We realized that was very inefficient when - slicing down long input signals like data blocked in day files. - We converted to this algorithm BUT be warned it is somewhat fragile - as it depends on some side effects of the implementation of Seismogram - that could break it if the following assumptions changes: - 1. The constructor we use calls the set_npts method - 2. that constructor also syncs npts metadata - 3. The constructor initializes the dmatrix u to zeros with length - determined from the set_npts result. - Less important is we have to add the load_history call here or history - would be lost. Reason is the constructor uses CoreTimeSeries. */ - BasicTimeSeries btstmp(dynamic_cast(parent)); - btstmp.set_npts(outns); + /* MAINTENANCE ISSUE: The original implementation of this code used a copy + constructor here to initalize result. We realized that was very inefficient + when slicing down long input signals like data blocked in day files. We + converted to this algorithm BUT be warned it is somewhat fragile as it depends + on some side effects of the implementation of Seismogram that could break it + if the following assumptions changes: + 1. The constructor we use calls the set_npts method + 2. that constructor also syncs npts metadata + 3. The constructor initializes the dmatrix u to zeros with length + determined from the set_npts result. + Less important is we have to add the load_history call here or history + would be lost. Reason is the constructor uses CoreTimeSeries. */ + BasicTimeSeries btstmp(dynamic_cast(parent)); + btstmp.set_npts(outns); /* Using the time method here preserves subsample timing.*/ - btstmp.set_t0(parent.time(is)); - /* WARNING MAINTENANCE ISSUE: this is less than ideal fix for a problem - found when debugging the revision of this algorithm to improve its - performance May 2022. the constuctor called here assumes the - value of npts stored in Metadata is definitive creating an inconsistency - that led to seg faults. The following is a workaround that negates - some of the performance gain. We copy Metadata and then alter nsamp - in the copy before calling the constructor. The code that seg faulted - just did a dynamic cast to the input, which created the problem. - */ - Metadata mdtmp(dynamic_cast(parent)); - mdtmp.put_long(SEISMICMD_npts,outns); - Seismogram result(btstmp,mdtmp); + btstmp.set_t0(parent.time(is)); + /* WARNING MAINTENANCE ISSUE: this is less than ideal fix for a problem + found when debugging the revision of this algorithm to improve its + performance May 2022. the constuctor called here assumes the + value of npts stored in Metadata is definitive creating an inconsistency + that led to seg faults. The following is a workaround that negates + some of the performance gain. We copy Metadata and then alter nsamp + in the copy before calling the constructor. The code that seg faulted + just did a dynamic cast to the input, which created the problem. + */ + Metadata mdtmp(dynamic_cast(parent)); + mdtmp.put_long(SEISMICMD_npts, outns); + Seismogram result(btstmp, mdtmp); - // Perhaps should do this with blas or memcpy for efficiency + // Perhaps should do this with blas or memcpy for efficiency // but this makes the algorithm much clearer - int i,ii,k; - for(i=is,ii=0;i<=ie && i(parent)); - return result; + int i, ii, k; + for (i = is, ii = 0; i <= ie && i < parent.npts() && ii < outns; ++i, ++ii) + for (k = 0; k < 3; ++k) { + result.u(k, ii) = parent.u(k, i); + } + /* Necessary because the constructor we called will set the Seismogram + it creates dead. After filling in data we can mark it live */ + result.set_live(); + /*This dynamic_cast may not be necessary, but makes the api clear */ + result.load_history(dynamic_cast(parent)); + return result; } -/*! \brief Extracts a requested time window of data from a parent TimeSeries object. +/*! \brief Extracts a requested time window of data from a parent TimeSeries +object. It is common to need to extract a smaller segment of data from a larger time window of data. This function accomplishes this in a nifty method that @@ -243,79 +237,76 @@ handling time. \return new Seismgram object derived from parent but windowed by input time window range. -\exception MsPASSError object if the requested time window is not inside data range +\exception MsPASSError object if the requested time window is not inside data +range \param parent is the larger TimeSeries object to be windowed \param tw defines the data range to be extracted from parent. */ -TimeSeries WindowData(const TimeSeries& parent, const TimeWindow& tw) -{ - // Always silently do nothing if marked dead - if(parent.dead()) - { - // return(TimeSeries()) doesn't work - // with g++. Have to us this long form - TimeSeries tmp; - return(tmp); - } - int is=parent.sample_number(tw.start); - /* This calculation was used in earlier versions but was found to - differ by 1 depending on the subsample timing of t0 of parent. the +TimeSeries WindowData(const TimeSeries &parent, const TimeWindow &tw) { + // Always silently do nothing if marked dead + if (parent.dead()) { + // return(TimeSeries()) doesn't work + // with g++. Have to us this long form + TimeSeries tmp; + return (tmp); + } + int is = parent.sample_number(tw.start); + /* This calculation was used in earlier versions but was found to + differ by 1 depending on the subsample timing of t0 of parent. the problem is that sample_number uses rounding which can produce that - effect due to a subtle interaction with tw.start and tw.end - relative to the sample grid. + effect due to a subtle interaction with tw.start and tw.end + relative to the sample grid. int ie=parent.sample_number(tw.end); */ - int outns,ie; - outns = round((tw.end-tw.start)/parent.dt()) + 1; + int outns, ie; + outns = round((tw.end - tw.start) / parent.dt()) + 1; ie = is + outns - 1; - //Ridiculous (int) case to silence a bogus compiler warning - if( (is<0) || (ie>((int)parent.npts())) ) - { - ostringstream mess; - mess << "WindowData(TimeSeries): Window mismatch"< ((int)parent.npts()))) { + ostringstream mess; + mess << "WindowData(TimeSeries): Window mismatch" << endl + << "Window start time=" << tw.start << " is sample number " << is + << endl + << "Window end time=" << tw.end << " is sample number " << ie << endl + << "Parent has " << parent.npts() << " samples" << endl; + /* Making this copy is a bit inefficient, but necessary unless we + wanted to drop the const qualifier onparent*/ + TimeSeries dret(parent); + dret.kill(); + /* Here appropriate to clear the data array - no reason to carry + around baggages */ + dret.set_npts(0); + dret.elog.log_error("WindowData", mess.str(), ErrorSeverity::Invalid); + return dret; } - /* MAINTENANCE ISSUE: The original implementation of this code used a copy constructor - here to initalize result. We realized that was very inefficient when - slicing down long input signals like data blocked in day files. - We converted to this algorithm BUT be warned it is somewhat fragile - as it depends on some side effects of the implementation of TimeSeries - that could break it if the following assumptions changes: - 1. The constructor we use calls the set_npts method - 2. that constructor also syncs npts metadata - 3. The constructor initializes the std::vector to zeros with length - determined from the set_npts result. - Less important is we have to add the load_history call here or history - would be lost. Reason is the constructor uses CoreTimeSeries. */ + /* MAINTENANCE ISSUE: The original implementation of this code used a copy + constructor here to initalize result. We realized that was very inefficient + when slicing down long input signals like data blocked in day files. We + converted to this algorithm BUT be warned it is somewhat fragile as it depends + on some side effects of the implementation of TimeSeries that could break it + if the following assumptions changes: + 1. The constructor we use calls the set_npts method + 2. that constructor also syncs npts metadata + 3. The constructor initializes the std::vector to zeros with length + determined from the set_npts result. + Less important is we have to add the load_history call here or history + would be lost. Reason is the constructor uses CoreTimeSeries. */ - BasicTimeSeries btstmp(dynamic_cast(parent)); - btstmp.set_npts(outns); + BasicTimeSeries btstmp(dynamic_cast(parent)); + btstmp.set_npts(outns); /* Using the time method here preserves subsample timing.*/ - btstmp.set_t0(parent.time(is)); - TimeSeries result(btstmp,dynamic_cast(parent)); - /* That constuctor initalizes s to zeroes so we can copy directly - to the container without push_back. memcpy might buy a small performance - gain but would make this more fragile that it already is. */ - int i,ii; - for(i=is,ii=0;i<=ie && i(parent)); + /* That constuctor initalizes s to zeroes so we can copy directly + to the container without push_back. memcpy might buy a small performance + gain but would make this more fragile that it already is. */ + int i, ii; + for (i = is, ii = 0; i <= ie && i < parent.npts() && ii < outns; ++i, ++ii) result.s[ii] = parent.s[i]; - /*This dynamic_cast may not be necessary, but makes the api clear */ - result.load_history(dynamic_cast(parent)); + /*This dynamic_cast may not be necessary, but makes the api clear */ + result.load_history(dynamic_cast(parent)); - return(result); + return (result); } -} // end mspass namespace +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/snr.cc b/cxx/src/lib/algorithms/snr.cc index 102657306..03a13f972 100644 --- a/cxx/src/lib/algorithms/snr.cc +++ b/cxx/src/lib/algorithms/snr.cc @@ -1,163 +1,139 @@ -#include -#include "mspass/seismic/PowerSpectrum.h" #include "mspass/algorithms/amplitudes.h" -#include "mspass/utility/VectorStatistics.h" +#include "mspass/seismic/PowerSpectrum.h" #include "mspass/utility/Metadata.h" -namespace mspass::algorithms::amplitudes{ +#include "mspass/utility/VectorStatistics.h" +#include +namespace mspass::algorithms::amplitudes { using mspass::seismic::PowerSpectrum; using mspass::utility::Metadata; using mspass::utility::VectorStatistics; /* These are used to flag 0/0 and x/0 respectively */ -const double INDETERMINATE(-1.0),NOISE_FREE(9999999.9); +const double INDETERMINATE(-1.0), NOISE_FREE(9999999.9); /* This function once was a part of the CNR3CDecon.cc file, but it was moved here as it was found to have a more generic purpose - bandwidth estimation. It is used in snr python wrapper functions of mspass and in CNR3cDecon to estimate bandwidth from power spectrum estimates. */ -BandwidthData EstimateBandwidth(const double signal_df, - const PowerSpectrum& s, const PowerSpectrum& n, - const double snr_threshold, const double tbp,const double fhs, - const bool fix_high_edge_to_fhs) -{ +BandwidthData EstimateBandwidth(const double signal_df, const PowerSpectrum &s, + const PowerSpectrum &n, + const double snr_threshold, const double tbp, + const double fhs, + const bool fix_high_edge_to_fhs) { /* This number defines a scaling to correct for difference in window length for signal and noise windows. It assumes the noise process is stationary which pretty is pretty much essential for this entire algorithm to make sense anyway. */ - double window_length_correction=static_cast(s.nf())/static_cast(n.nf()); - /* Set the starting search points at low (based on noise tbp) and high (80% fny) - sides */ + double window_length_correction = + static_cast(s.nf()) / static_cast(n.nf()); + /* Set the starting search points at low (based on noise tbp) and high (80% + fny) sides */ double flow_start, fhigh_start; - flow_start=(n.df())*tbp; + flow_start = (n.df()) * tbp; /* Silently set to 80% of Nyquist if the passed value is negative or above nyquist - illegal values. Assume python wrappers can post warning if deemeed essential. This is a very harmless error so being silent is probably normally ok */ - if( (fhs<=0.0) || (fhs>s.Nyquist()) ) - fhigh_start=s.Nyquist()*0.8; + if ((fhs <= 0.0) || (fhs > s.Nyquist())) + fhigh_start = s.Nyquist() * 0.8; else fhigh_start = fhs; - double df_test_range=2.0*tbp*(n.df()); + double df_test_range = 2.0 * tbp * (n.df()); int s_range = s.sample_number(fhigh_start); BandwidthData result; /* First search from flow_start in increments of signal_df to find low edge.*/ double f, sigamp, namp, snrnow; double f_mark; - int istart=s.sample_number(flow_start); + int istart = s.sample_number(flow_start); bool searching(false); int i; - for(i=istart;i0.0) - snrnow=window_length_correction*sigamp/namp; - else - { - if(sigamp>0.0) - snrnow=NOISE_FREE; + sigamp = sqrt(s.spectrum[i]); + namp = n.power(f); + namp = sqrt(namp); + if (namp > 0.0) + snrnow = window_length_correction * sigamp / namp; + else { + if (sigamp > 0.0) + snrnow = NOISE_FREE; else snrnow = INDETERMINATE; } - if(snrnow>snr_threshold) - { - if(searching) - { - if((f-f_mark)>=df_test_range) - { - result.low_edge_f=f_mark; + if (snrnow > snr_threshold) { + if (searching) { + if ((f - f_mark) >= df_test_range) { + result.low_edge_f = f_mark; break; } + } else { + f_mark = f; + result.low_edge_snr = snrnow; + searching = true; } - else - { - f_mark=f; - result.low_edge_snr=snrnow; - searching=true; - } - } - else - { - if(searching) - { - searching=false; - f_mark=f; + } else { + if (searching) { + searching = false; + f_mark = f; } } } /* Return the zeroed result object if no data exceeded the snr threshold.*/ - if(i>=(s_range-1)) - { - result.low_edge_f=0.0; - result.high_edge_f=0.0; - result.low_edge_snr=0.0; - result.high_edge_snr=0.0; + if (i >= (s_range - 1)) { + result.low_edge_f = 0.0; + result.high_edge_f = 0.0; + result.low_edge_snr = 0.0; + result.high_edge_snr = 0.0; /* This is the most important one to set 0.0*/ - result.f_range=0.0; + result.f_range = 0.0; return result; } /* Now search from the high end to find upper band edge - same algorithm reversed direction. Note option to disable */ - if(fix_high_edge_to_fhs) - { + if (fix_high_edge_to_fhs) { result.high_edge_f = fhigh_start; - sigamp=sqrt(s.power(fhigh_start)); - namp=sqrt(n.power(fhigh_start)); - if(namp>0.0) - snrnow=window_length_correction*sigamp/namp; - else - { - if(sigamp>0.0) - snrnow=NOISE_FREE; + sigamp = sqrt(s.power(fhigh_start)); + namp = sqrt(n.power(fhigh_start)); + if (namp > 0.0) + snrnow = window_length_correction * sigamp / namp; + else { + if (sigamp > 0.0) + snrnow = NOISE_FREE; else snrnow = INDETERMINATE; } - result.high_edge_snr=snrnow; - } - else - { - searching=false; - istart=s.sample_number(fhigh_start); - for(i=istart;i>=0;--i) - { - f=s.frequency(i); - sigamp=sqrt(s.spectrum[i]); - namp=sqrt(n.power(f)); - if(namp>0.0) - snrnow=window_length_correction*sigamp/namp; - else - { - if(sigamp>0.0) - snrnow=NOISE_FREE; + result.high_edge_snr = snrnow; + } else { + searching = false; + istart = s.sample_number(fhigh_start); + for (i = istart; i >= 0; --i) { + f = s.frequency(i); + sigamp = sqrt(s.spectrum[i]); + namp = sqrt(n.power(f)); + if (namp > 0.0) + snrnow = window_length_correction * sigamp / namp; + else { + if (sigamp > 0.0) + snrnow = NOISE_FREE; else snrnow = INDETERMINATE; } - if(snrnow>snr_threshold) - { - if(searching) - { - if((f_mark-f)>=df_test_range) - { - result.high_edge_f=f_mark; + if (snrnow > snr_threshold) { + if (searching) { + if ((f_mark - f) >= df_test_range) { + result.high_edge_f = f_mark; break; } + } else { + f_mark = f; + result.high_edge_snr = snrnow; + searching = true; } - else - { - f_mark=f; - result.high_edge_snr=snrnow; - searching=true; - } - } - else - { - if(searching) - { - searching=false; - f_mark=f; + } else { + if (searching) { + searching = false; + f_mark = f; } } } @@ -165,56 +141,52 @@ BandwidthData EstimateBandwidth(const double signal_df, result.f_range = s.Nyquist() - s.f0(); return result; } -Metadata BandwidthStatistics(const PowerSpectrum& s, const PowerSpectrum& n, - const BandwidthData& bwd) -{ +Metadata BandwidthStatistics(const PowerSpectrum &s, const PowerSpectrum &n, + const BandwidthData &bwd) { /* As noted above this correction is needed for an irregular window size*/ - double window_length_correction=static_cast(s.nf())/static_cast(n.nf()); + double window_length_correction = + static_cast(s.nf()) / static_cast(n.nf()); Metadata result; /* the algorithm below will fail if either of these conditions is true so we trap that and return a null result. Caller must handle the null return correctly*/ - if( ( bwd.f_range <= 0.0 ) || ( (bwd.high_edge_f-bwd.low_edge_f) bandsnr; double f; - for(f=bwd.low_edge_f;f0.0) + if (noise_amp <= 0.0) { + if (signal_amp > 0.0) snr = NOISE_FREE; else snr = INDETERMINATE; - } - else - { - snr = window_length_correction*signal_amp/noise_amp; + } else { + snr = window_length_correction * signal_amp / noise_amp; } bandsnr.push_back(snr); } VectorStatistics stats(bandsnr); /* stats contains multiple methods that return other metrics but we only return typical box plot values */ - result.put_double("median_snr",stats.median()); - result.put_double("maximum_snr",stats.upper_bound()); - result.put_double("minimum_snr",stats.lower_bound()); - result.put_double("q1_4_snr",stats.q1_4()); - result.put_double("q3_4_snr",stats.q3_4()); - result.put_double("mean_snr",stats.mean()); - result.put_bool("stats_are_valid",true); + result.put_double("median_snr", stats.median()); + result.put_double("maximum_snr", stats.upper_bound()); + result.put_double("minimum_snr", stats.lower_bound()); + result.put_double("q1_4_snr", stats.q1_4()); + result.put_double("q3_4_snr", stats.q3_4()); + result.put_double("mean_snr", stats.mean()); + result.put_bool("stats_are_valid", true); return result; } -} // end namespace +} // namespace mspass::algorithms::amplitudes diff --git a/cxx/src/lib/algorithms/sparse_convolve.cc b/cxx/src/lib/algorithms/sparse_convolve.cc index 10d26ad08..0bd757da8 100644 --- a/cxx/src/lib/algorithms/sparse_convolve.cc +++ b/cxx/src/lib/algorithms/sparse_convolve.cc @@ -1,54 +1,55 @@ -//#include "perf.h" +// #include "perf.h" #include "misc/blas.h" -#include "mspass/utility/MsPASSError.h" #include "mspass/seismic/CoreSeismogram.h" #include "mspass/seismic/CoreTimeSeries.h" -namespace mspass::algorithms -{ +#include "mspass/utility/MsPASSError.h" +namespace mspass::algorithms { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; -CoreSeismogram sparse_convolve(const CoreTimeSeries& wavelet, - const CoreSeismogram& d) -{ - if( wavelet.time_is_UTC() || d.time_is_UTC() ) - throw MsPASSError(string("Error (convolve procedure): ") - + "both functions to be convolved must have " - + "relative time base",ErrorSeverity::Invalid); - CoreSeismogram out3c(d); - int nw=wavelet.npts(); - double *wptr; - wptr=const_cast(&(wavelet.s[0])); - /* Add a generous padding for out3c*/ - int nsout=d.npts()+2*nw; +CoreSeismogram sparse_convolve(const CoreTimeSeries &wavelet, + const CoreSeismogram &d) { + if (wavelet.time_is_UTC() || d.time_is_UTC()) + throw MsPASSError(string("Error (convolve procedure): ") + + "both functions to be convolved must have " + + "relative time base", + ErrorSeverity::Invalid); + CoreSeismogram out3c(d); + int nw = wavelet.npts(); + double *wptr; + wptr = const_cast(&(wavelet.s[0])); + /* Add a generous padding for out3c*/ + int nsout = d.npts() + 2 * nw; // These used to be necessary - now handled by set_npts - //out3c.u=dmatrix(3,nsout); - //out3c.u.zero(); - out3c.set_t0(d.t0() - (out3c.dt()*static_cast(wavelet.npts()))); - out3c.set_npts(nsout); - //out3c.t0=d.t0-(out3c.dt)*static_cast(wavelet.ns); - //out3c.ns=nsout; - /* oi is the position of the moving index position in out3c */ - int oi=out3c.sample_number(d.t0()); - /* si is the index to the point where the wavelet is to be inserted. offset by 0 of wavelet*/ - int si=oi-wavelet.sample_number(0.0); - if(si<0) throw MsPASSError("Error computed out3c index is less than 0 ", - ErrorSeverity::Invalid); - /* Intentionally do not check for stray indices as padding above - should guarantee no pointers fly outside the bounds of the data.*/ - int i,k; - for(i=0;i(out3c.u.get_address(0,si)); - double *dptr=d.u.get_address(0,i); - for(k=0;k<3;++k){ - if((*dptr)!=0.0){ - daxpy(nw,(*dptr),wptr,1,sptr,3); - } - ++sptr; - ++dptr; - } - } - return out3c; + // out3c.u=dmatrix(3,nsout); + // out3c.u.zero(); + out3c.set_t0(d.t0() - (out3c.dt() * static_cast(wavelet.npts()))); + out3c.set_npts(nsout); + // out3c.t0=d.t0-(out3c.dt)*static_cast(wavelet.ns); + // out3c.ns=nsout; + /* oi is the position of the moving index position in out3c */ + int oi = out3c.sample_number(d.t0()); + /* si is the index to the point where the wavelet is to be inserted. offset by + * 0 of wavelet*/ + int si = oi - wavelet.sample_number(0.0); + if (si < 0) + throw MsPASSError("Error computed out3c index is less than 0 ", + ErrorSeverity::Invalid); + /* Intentionally do not check for stray indices as padding above + should guarantee no pointers fly outside the bounds of the data.*/ + int i, k; + for (i = 0; i < d.npts(); ++i, ++si) { + double *sptr = const_cast(out3c.u.get_address(0, si)); + double *dptr = d.u.get_address(0, i); + for (k = 0; k < 3; ++k) { + if ((*dptr) != 0.0) { + daxpy(nw, (*dptr), wptr, 1, sptr, 3); + } + ++sptr; + ++dptr; + } + } + return out3c; } -} // End SEISPP namespace encapsulation +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/splicing.cc b/cxx/src/lib/algorithms/splicing.cc index 7c76f8fa2..a89186cba 100644 --- a/cxx/src/lib/algorithms/splicing.cc +++ b/cxx/src/lib/algorithms/splicing.cc @@ -1,25 +1,24 @@ -#include -#include -#include +#include "mspass/algorithms/algorithms.h" #include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/TimeSeriesWGaps.h" #include "mspass/utility/MsPASSError.h" -#include "mspass/algorithms/algorithms.h" -namespace mspass::algorithms -{ +#include +#include +#include +namespace mspass::algorithms { using namespace mspass::seismic; using namespace mspass::utility; using namespace std; using mspass::algorithms::WindowData; -/* A main idea of the functions in this file is to handle time - tears created by a range of possibilities. We have to handle - overlaps and gaps. Both are defined as existing when - there is a time mismatch larger than the following fraction - of a sample interval. i.e. all tests are done using a tolerance - of some variant of the sample interval in seconds time this - multiplier. -Note: if ever changed this constant should be consistent with +/* A main idea of the functions in this file is to handle time + tears created by a range of possibilities. We have to handle + overlaps and gaps. Both are defined as existing when + there is a time mismatch larger than the following fraction + of a sample interval. i.e. all tests are done using a tolerance + of some variant of the sample interval in seconds time this + multiplier. +Note: if ever changed this constant should be consistent with the same constant used by the minised indexing function,. */ const double TIME_TEAR_TOLERANCE(0.5); /*! File scope class to enscapsulate set of possible data problems. @@ -42,8 +41,7 @@ at present are: All attributes of this class are intentionally public as it should be thought of as a struct with convenient constructors. */ -class SegmentVectorProperties -{ +class SegmentVectorProperties { public: bool dt_constant; bool has_dead_components; @@ -62,11 +60,10 @@ class SegmentVectorProperties passed downstream. */ ErrorLogger elog; SegmentVectorProperties(); - SegmentVectorProperties(const std::vector& segments); - SegmentVectorProperties(const SegmentVectorProperties& parent); + SegmentVectorProperties(const std::vector &segments); + SegmentVectorProperties(const SegmentVectorProperties &parent); }; -SegmentVectorProperties::SegmentVectorProperties() : elog() -{ +SegmentVectorProperties::SegmentVectorProperties() : elog() { dt_constant = true; has_dead_components = false; is_sorted = true; @@ -74,14 +71,14 @@ SegmentVectorProperties::SegmentVectorProperties() : elog() has_gaps = false; number_live = 0; first_live = 0; - t0=0.0; - endtime=0.0; - dt=0.0; - spliced_nsamp=0; + t0 = 0.0; + endtime = 0.0; + dt = 0.0; + spliced_nsamp = 0; } -SegmentVectorProperties::SegmentVectorProperties(const SegmentVectorProperties& parent) - : elog(parent.elog) -{ +SegmentVectorProperties::SegmentVectorProperties( + const SegmentVectorProperties &parent) + : elog(parent.elog) { dt_constant = parent.dt_constant; has_dead_components = parent.has_dead_components; is_sorted = parent.is_sorted; @@ -89,33 +86,29 @@ SegmentVectorProperties::SegmentVectorProperties(const SegmentVectorProperties& has_gaps = parent.has_gaps; number_live = parent.number_live; first_live = parent.first_live; - t0=parent.t0; - endtime=parent.endtime; - dt=parent.dt; - spliced_nsamp=parent.spliced_nsamp; + t0 = parent.t0; + endtime = parent.endtime; + dt = parent.dt; + spliced_nsamp = parent.spliced_nsamp; } -SegmentVectorProperties::SegmentVectorProperties(const std::vector& segments) -{ +SegmentVectorProperties::SegmentVectorProperties( + const std::vector &segments) { /* Return a type conversion of the input when there is only one segment - nothing to splice in that case. Error if it is empty */ - if(segments.size() == 1) - { + if (segments.size() == 1) { dt_constant = true; has_dead_components = false; is_sorted = true; has_overlaps = false; has_gaps = false; - if(segments[0].live()) - { + if (segments[0].live()) { number_live = 1; first_live = 0; t0 = segments[0].t0(); - endtime=segments[0].endtime(); + endtime = segments[0].endtime(); dt = segments[0].dt(); this->spliced_nsamp = segments[0].npts(); - } - else - { + } else { number_live = 0; first_live = -1; t0 = 0.0; @@ -123,9 +116,7 @@ SegmentVectorProperties::SegmentVectorProperties(const std::vector& endtime = 0.0; this->spliced_nsamp = 0; } - } - else if(segments.size() == 0) - { + } else if (segments.size() == 0) { dt_constant = true; has_dead_components = false; is_sorted = true; @@ -137,29 +128,24 @@ SegmentVectorProperties::SegmentVectorProperties(const std::vector& dt = 0.0; endtime = 0.0; this->spliced_nsamp = 0; - } - else - { - double test_dt,dtfrac; + } else { + double test_dt, dtfrac; double first_t0, previous_t0, previous_endtime; /* This algorithm requires the test above that guarantees number of segments is more than 1. Some complexity to handle a dead member 0 is needed. */ bool this_is_first(true); - has_overlaps=false; - is_sorted = true; //initialization - has_dead_components=false; + has_overlaps = false; + is_sorted = true; // initialization + has_dead_components = false; /* Declare a sample mismatch if nondimensional sample interval mismatch is less than this constant. */ const double dt_fraction_mismatch(0.001); - for(size_t i=0;idt_constant = true; this->first_live = i; first_t0 = segments[i].t0(); @@ -170,63 +156,69 @@ SegmentVectorProperties::SegmentVectorProperties(const std::vector& this->dt = test_dt; this->t0 = first_t0; this->number_live = 1; - } - else - { + } else { /* This could be true multiple times if data were not sorted. Purpose of this boolean is to define a serious error condition that functions using this class must handle */ - if(segments[i].t0() < previous_t0) is_sorted = false; + if (segments[i].t0() < previous_t0) + is_sorted = false; /* Test for overlaps - note sign is important */ - if((previous_endtime + test_dt*TIME_TEAR_TOLERANCE) > segments[i].t0()) + if ((previous_endtime + test_dt * TIME_TEAR_TOLERANCE) > + segments[i].t0()) has_overlaps = true; /* test for gaps */ - if( (segments[i].t0() - previous_endtime - test_dt)/test_dt > TIME_TEAR_TOLERANCE) + if ((segments[i].t0() - previous_endtime - test_dt) / test_dt > + TIME_TEAR_TOLERANCE) has_gaps = true; /* Check for sample interval mismatch */ - dtfrac = fabs(segments[i].dt()-test_dt)/test_dt; - if(dtfrac > dt_fraction_mismatch) dt_constant = false; + dtfrac = fabs(segments[i].dt() - test_dt) / test_dt; + if (dtfrac > dt_fraction_mismatch) + dt_constant = false; previous_endtime = segments[i].endtime(); previous_t0 = segments[i].t0(); ++this->number_live; } } this->endtime = previous_endtime; - this->spliced_nsamp = lround((previous_endtime-this->t0)/this->dt); - ++this->spliced_nsamp; //nsamp is always one sample longer than intervals + this->spliced_nsamp = lround((previous_endtime - this->t0) / this->dt); + ++this->spliced_nsamp; // nsamp is always one sample longer than intervals } } -/*! Convenience operator to dump content of SegmentVectorProperties object. +/*! Convenience operator to dump content of SegmentVectorProperties object. -Writes a verbose dump of the content with labels. This overloaded -function is for debugging and will not be in the MsPASS python bindings. -In fact, neither is SegmentVectorProperties. -Note this does not need to be declared friend because all attributes of the +Writes a verbose dump of the content with labels. This overloaded +function is for debugging and will not be in the MsPASS python bindings. +In fact, neither is SegmentVectorProperties. +Note this does not need to be declared friend because all attributes of the class are declared public. */ -ostream& operator<<(ostream& os, SegmentVectorProperties& svp) -{ - const string sep("============================================================="); +ostream &operator<<(ostream &os, SegmentVectorProperties &svp) { + const string sep( + "============================================================="); os << sep << endl; - if(svp.dt_constant) - os << "Segments have constant sample interval"< null_vector; /* This is a bit of a weird/complicated construct using the WGaps constructor built from a TimeSeries. That initializes the gaps container to empty*/ - TimeSeriesWGaps result( TimeSeries( - dynamic_cast(segments[issues.first_live]), - dynamic_cast(segments[issues.first_live]), - ProcessingHistory(), - null_vector) ); + TimeSeriesWGaps result( + TimeSeries(dynamic_cast(segments[issues.first_live]), + dynamic_cast(segments[issues.first_live]), + ProcessingHistory(), null_vector)); - if(save_history) - { - std::vector inputs; + if (save_history) { + std::vector inputs; /* We assume history is initially empty - careful if the logic above changes */ - for(size_t i=0;i(&segments[i]); inputs.push_back(hptr); } @@ -336,72 +314,65 @@ TimeSeriesWGaps splice_segments(std::vector& segments, result.add_many_inputs(inputs); } - const long int MAX_DATA_VECTOR_LENGTH(100000000); //generous size allowance - if(issues.has_overlaps) - { + const long int MAX_DATA_VECTOR_LENGTH(100000000); // generous size allowance + if (issues.has_overlaps) { std::stringstream ss; ss << "Segments array has sections with overlaps of more than 1/2 sample" << std::endl - << "Preprocess your data to remove overlaps. This algorithm assumes overlaps were repaired previously." - < MAX_DATA_VECTOR_LENGTH) - { + } else if (issues.spliced_nsamp > MAX_DATA_VECTOR_LENGTH) { std::stringstream ss; - ss << "Computed vector length is huge="<=result.npts()) - { + if (ii >= result.npts()) { stringstream ss; - ss<<"splice_segments: computed sample index is outside merge data vector" - << endl - << "Computed index for segment number "<TIME_TEAR_TOLERANCE) - { + delta = (segments[i].t0() - issues.dt - previous_endtime) / issues.dt; + if (delta > TIME_TEAR_TOLERANCE) { TimeWindow gap; gap.start = previous_endtime; gap.end = segments[i].t0(); @@ -414,25 +385,25 @@ TimeSeriesWGaps splice_segments(std::vector& segments, return result; } /* helper for repair_overlaps. Acts a bit like numpy is_close. */ -bool samples_match(std::vector& v1, std::vector& v2) -{ +bool samples_match(std::vector &v1, std::vector &v2) { /* Use 32 bit float eps because no data at present has a greater precision than 24 bits. We scale by 10 to be a bit cautious. Better to return true than false for one or two sampls. 10 may be too large - this probably should be tested with data */ - const double SCALED_EPS(10.0*FLT_EPSILON); + const double SCALED_EPS(10.0 * FLT_EPSILON); /* Because of internal use we don't test if v1 and v2 are the same length but assume logic used to create them guarantees that is so. */ - std::vector::iterator v1ptr,v2ptr; - for(v1ptr=v1.begin(),v2ptr=v2.begin();v1ptr!=v1.end();++v1ptr,++v2ptr) - { + std::vector::iterator v1ptr, v2ptr; + for (v1ptr = v1.begin(), v2ptr = v2.begin(); v1ptr != v1.end(); + ++v1ptr, ++v2ptr) { double dtest; dtest = *v1ptr - *v2ptr; - dtest = fabs(dtest/(*v1ptr)); - //DEBUG - //cout << *v1ptr << " "<<*v2ptr<<" "<SCALED_EPS) return false; + dtest = fabs(dtest / (*v1ptr)); + // DEBUG + // cout << *v1ptr << " "<<*v2ptr<<" "< SCALED_EPS) + return false; } return true; } @@ -456,115 +427,97 @@ means there has been a time jump backward to create the apparent overlap. It is assumed that the segment killed had a timing problem. */ -std::vector repair_overlaps(std::vector& segments) -{ +std::vector repair_overlaps(std::vector &segments) { SegmentVectorProperties issues(segments); - //DEBUG + // DEBUG /* cout << "In repair_overlaps"< repaired_segments; int i_previous; i_previous = issues.first_live; - for(size_t i=issues.first_live+1;icontinue*/ - if(segments[i].live()) - { + if (segments[i].live()) { double ttest; - ttest = segments[i_previous].endtime() + segments[i].dt() - segments[i].t0(); - if(ttest<(TIME_TEAR_TOLERANCE*segments[i].dt())) - { + ttest = segments[i_previous].endtime() + segments[i].dt() - + segments[i].t0(); + if (ttest < (TIME_TEAR_TOLERANCE * segments[i].dt())) { repaired_segments.push_back(segments[i_previous]); i_previous = i; - } - else - { - //DEBUG - //cout << "Handling overlap with ttest="< vec1,vec2; + std::vector vec1, vec2; double tstart; size_t w_npts; tstart = segments[i].t0(); - for(size_t iw=segments[i_previous].sample_number(tstart); - iw repair_overlaps(std::vector& segments) /* Push the last live segment - defined by i_previous*/ repaired_segments.push_back(segments[i_previous]); return repaired_segments; - } - else - { + } else { return segments; } } -} +} // namespace mspass::algorithms diff --git a/cxx/src/lib/algorithms/tseries_helpers.cc b/cxx/src/lib/algorithms/tseries_helpers.cc index fa0e87b77..52525604d 100644 --- a/cxx/src/lib/algorithms/tseries_helpers.cc +++ b/cxx/src/lib/algorithms/tseries_helpers.cc @@ -1,10 +1,9 @@ -#include -#include "mspass/utility/MsPASSError.h" #include "mspass/algorithms/TimeWindow.h" -#include "mspass/seismic/TimeSeries.h" #include "mspass/seismic/Ensemble.h" -namespace mspass::algorithms -{ +#include "mspass/seismic/TimeSeries.h" +#include "mspass/utility/MsPASSError.h" +#include +namespace mspass::algorithms { using namespace std; using namespace mspass::seismic; using namespace mspass::utility; @@ -18,11 +17,10 @@ defined with an UTC time standard and convert them to an "arrival time reference frame" . See below for a description. Author: Gary L. Pavlis - Indiana University - pavlis@indiana.edu + Indiana University + pavlis@indiana.edu */ - /* This function converts a seismogram from an absolute time standard to an arrival time reference frame using an arrival time stored in the metadata area of the object and referenced by a variable keyword. @@ -32,95 +30,90 @@ to the result. If the window is smaller a subset is returned and appropriate parameters are updated. Arguments: - tcsi - input data. Assumed to be in the reference frame that - matches the arrival time to be used to define time window - arrival_time_key - metadata keyword string used to extract the - time used to define time 0. i.e. this is the keyword - used to access a metadata field that defines the time - that will become time 0 o the result. - tw - TimeWindow in relative time units wrt arrival time defining - section of data to be extracted. Time 0 of the result - will be the arrival time. + tcsi - input data. Assumed to be in the reference frame that + matches the arrival time to be used to define time window + arrival_time_key - metadata keyword string used to extract the + time used to define time 0. i.e. this is the keyword + used to access a metadata field that defines the time + that will become time 0 o the result. + tw - TimeWindow in relative time units wrt arrival time defining + section of data to be extracted. Time 0 of the result + will be the arrival time. */ -shared_ptr ArrivalTimeReference(TimeSeries& tcsi, - string arrival_time_key, - TimeWindow tw) -{ - double atime; - string base_error_message("ArrivalTimeReference: "); - try - { - atime = tcsi.get_double(arrival_time_key); - // Intentionally use the base class since the contents are discarded - // get_double currently would throw a MetadataGetError - } catch (MetadataGetError& mde) - { - throw MsPASSError(base_error_message - + arrival_time_key - + string(" not found in TimeSeries object"), - ErrorSeverity::Invalid); - } - // We have to check this condition because ator will do nothing if - // time is already relative and this condition cannot be tolerated - // here as we have no idea what the time standard might be otherwise - if(tcsi.time_is_relative()) - throw MsPASSError(string("ArrivalTimeReference: ") - + string("received data in relative time units\n") - + string("Cannot proceed as timing is ambiguous"), - ErrorSeverity::Invalid); +shared_ptr +ArrivalTimeReference(TimeSeries &tcsi, string arrival_time_key, TimeWindow tw) { + double atime; + string base_error_message("ArrivalTimeReference: "); + try { + atime = tcsi.get_double(arrival_time_key); + // Intentionally use the base class since the contents are discarded + // get_double currently would throw a MetadataGetError + } catch (MetadataGetError &mde) { + throw MsPASSError(base_error_message + arrival_time_key + + string(" not found in TimeSeries object"), + ErrorSeverity::Invalid); + } + // We have to check this condition because ator will do nothing if + // time is already relative and this condition cannot be tolerated + // here as we have no idea what the time standard might be otherwise + if (tcsi.time_is_relative()) + throw MsPASSError(string("ArrivalTimeReference: ") + + string("received data in relative time units\n") + + string("Cannot proceed as timing is ambiguous"), + ErrorSeverity::Invalid); - // start with a clone of the original - shared_ptr tcso(new TimeSeries(tcsi)); - tcso->ator(atime); // shifts to arrival time relative time reference + // start with a clone of the original + shared_ptr tcso(new TimeSeries(tcsi)); + tcso->ator(atime); // shifts to arrival time relative time reference - // Extracting a subset of the data is not needed when the requested - // time window encloses all the data - // Note an alternative approach is to pad with zeros and mark ends as - // a gap, but here I view ends as better treated with variable - // start and end times - if( (tw.start > tcso->t0()) || (tw.endtime(tcso->npts()-1) ) ) - { - int jstart, jend; - int ns_to_copy; - int j,jj; - jstart = tcso->sample_number(tw.start); - jend = tcso->sample_number(tw.end); - if(jstart<0) jstart=0; - if(jend>=tcso->npts()) jend = tcso->npts() - 1; - ns_to_copy = jend - jstart + 1; - if(ns_to_copy<=0) - { - tcso->kill(); - tcso->s.clear(); - return(tcso); - } - tcso->set_npts(ns_to_copy); - // This assumes set_npts initializes the s buffer so we use operator[] - // instead of push_back - for(j=0,jj=jstart; js[j]=tcsi.s[jj]; - /* This was the way t0 was set with the old api - tcso->t0 += (tcso->dt)*static_cast(jstart); + // Extracting a subset of the data is not needed when the requested + // time window encloses all the data + // Note an alternative approach is to pad with zeros and mark ends as + // a gap, but here I view ends as better treated with variable + // start and end times + if ((tw.start > tcso->t0()) || (tw.end < tcso->time(tcso->npts() - 1))) { + int jstart, jend; + int ns_to_copy; + int j, jj; + jstart = tcso->sample_number(tw.start); + jend = tcso->sample_number(tw.end); + if (jstart < 0) + jstart = 0; + if (jend >= tcso->npts()) + jend = tcso->npts() - 1; + ns_to_copy = jend - jstart + 1; + if (ns_to_copy <= 0) { + tcso->kill(); + tcso->s.clear(); + return (tcso); + } + tcso->set_npts(ns_to_copy); + // This assumes set_npts initializes the s buffer so we use operator[] + // instead of push_back + for (j = 0, jj = jstart; j < ns_to_copy; ++j, ++jj) + tcso->s[j] = tcsi.s[jj]; + /* This was the way t0 was set with the old api + tcso->t0 += (tcso->dt)*static_cast(jstart); - this is the new */ - double newt0; - newt0=tcso->t0() + (tcso->dt())*static_cast(jstart); - tcso->set_t0(newt0); - /* This same block of code appeared in the parallel function for - CoreSeismogram. I don't think this is necessary with the new api, but - as in that file I'm retaining it in case that is incorrect glp, 6/7/2020 - if(jstart>0) - { - double stime=atime+tcso->t0; - tcso->put("time",stime); - // this one may not really be necessary - tcso->put("endtime",atime+tcso->endtime()); - } - */ + this is the new */ + double newt0; + newt0 = tcso->t0() + (tcso->dt()) * static_cast(jstart); + tcso->set_t0(newt0); + /* This same block of code appeared in the parallel function for + CoreSeismogram. I don't think this is necessary with the new api, but + as in that file I'm retaining it in case that is incorrect glp, 6/7/2020 + if(jstart>0) + { + double stime=atime+tcso->t0; + tcso->put("time",stime); + // this one may not really be necessary + tcso->put("endtime",atime+tcso->endtime()); } - return(tcso); + */ + } + return (tcso); } /* Similar function to above but processes an entire ensemble. The only special thing it does is handle exceptions. When the single object @@ -131,39 +124,35 @@ This procedure is retained, but probably should be depricated to only be used on error logging children of TimeSeries that can post an error like the one handled here to an error log. Retained for now as this functionality is essential. */ -shared_ptr ArrivalTimeReference(TimeSeriesEnsemble& tcei, - string arrival_time_key, - TimeWindow tw) -{ - int nmembers=tcei.member.size(); - // use the special constructor to only clone the metadata and - // set aside slots for the new ensemble. - shared_ptr - tceo(new TimeSeriesEnsemble(dynamic_cast(tcei), - nmembers)); - tceo->member.reserve(nmembers); // reserve this many slots for efficiency - // We have to use a loop instead of for_each as I don't see how - // else to handle errors cleanly here. - vector::iterator indata; - shared_ptr tcs; - for(indata=tcei.member.begin(); indata!=tcei.member.end(); ++indata) - { - try { - tcs=ArrivalTimeReference(*indata, - arrival_time_key,tw); - tceo->member.push_back(*tcs); - } catch ( MsPASSError& serr) - { - tcs->kill(); - tceo->member.push_back(*tcs); - cerr << "Warning: ArrivalTimeReference threw this error for current seismogram"< ArrivalTimeReference(TimeSeriesEnsemble &tcei, + string arrival_time_key, + TimeWindow tw) { + int nmembers = tcei.member.size(); + // use the special constructor to only clone the metadata and + // set aside slots for the new ensemble. + shared_ptr tceo( + new TimeSeriesEnsemble(dynamic_cast(tcei), nmembers)); + tceo->member.reserve(nmembers); // reserve this many slots for efficiency + // We have to use a loop instead of for_each as I don't see how + // else to handle errors cleanly here. + vector::iterator indata; + shared_ptr tcs; + for (indata = tcei.member.begin(); indata != tcei.member.end(); ++indata) { + try { + tcs = ArrivalTimeReference(*indata, arrival_time_key, tw); + tceo->member.push_back(*tcs); + } catch (MsPASSError &serr) { + tcs->kill(); + tceo->member.push_back(*tcs); + cerr << "Warning: ArrivalTimeReference threw this error for current " + "seismogram" + << endl; + serr.log_error(); + cerr << "Seismogram was marked dead but copied to output Ensemble" + << endl; } - return(tceo); + } + return (tceo); } - -} // end mspass namespace encapsulation +} // namespace mspass::algorithms diff --git a/cxx/src/lib/io/fileio.cc b/cxx/src/lib/io/fileio.cc index 51c9a8333..3703b9c0c 100644 --- a/cxx/src/lib/io/fileio.cc +++ b/cxx/src/lib/io/fileio.cc @@ -1,20 +1,19 @@ +#include "mspass/io/fileio.h" +#include "mspass/seismic/Ensemble.h" +#include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/TimeSeries.h" +#include "mspass/seismic/keywords.h" +#include "mspass/utility/MsPASSError.h" #include -#include +#include #include #include -#include -#include "mspass/utility/MsPASSError.h" -#include "mspass/seismic/keywords.h" -#include "mspass/seismic/TimeSeries.h" -#include "mspass/seismic/Seismogram.h" -#include "mspass/seismic/Ensemble.h" -#include "mspass/io/fileio.h" -namespace mspass::io -{ -//using namespace mspass::io; +#include +namespace mspass::io { +// using namespace mspass::io; using namespace mspass::seismic; -using mspass::utility::MsPASSError; using mspass::utility::ErrorSeverity; +using mspass::utility::MsPASSError; using namespace std; /*This is a file scope function to allow the overlaoded fwrite_to_file @@ -27,55 +26,62 @@ function lockffile says that is no longer necessary and stdio is now tread safe - fopen creates an intrinsic lock that is not released until fclose is called. That is important for mspass as multiple threads writing to the same file can be expected to be common. */ -long int fwrite_sample_data(const string dir, const string dfile, double *dptr, const size_t nd) -{ - try{ - FILE *fp; - long int foff; - string fname; - if(dir.length()>0) - /* for expected context for use in python we will assume dir does not - have a trailing path separator so we always insert / */ - fname = dir + "/" + dfile; - else - /* Null as always in unix means use current directory*/ - fname=dfile; - if((fp=fopen(fname.c_str(),"a")) == NULL) - /* use the name of the overloaded parent instead of the actual function - intentional*/ - throw MsPASSError("fwrite_to_file: Open failed on file "+fname,ErrorSeverity::Invalid); - /* Both fseek and ftell can fail in weird circumstances, but I intentionally - do not trap that condition as if either have issues I am quite sure - the fwrite will fail */ - fseek(fp,0L,2); - foff = ftell(fp); - if( fwrite((void*)dptr,sizeof(double),nd,fp) != nd) - { - fclose(fp); - throw MsPASSError("fwrite_to_file: fwrite error while writing to file "+fname,ErrorSeverity::Invalid); - } - fclose(fp); - return foff; - }catch(...){throw;}; +long int fwrite_sample_data(const string dir, const string dfile, double *dptr, + const size_t nd) { + try { + FILE *fp; + long int foff; + string fname; + if (dir.length() > 0) + /* for expected context for use in python we will assume dir does not + have a trailing path separator so we always insert / */ + fname = dir + "/" + dfile; + else + /* Null as always in unix means use current directory*/ + fname = dfile; + if ((fp = fopen(fname.c_str(), "a")) == NULL) + /* use the name of the overloaded parent instead of the actual function - + * intentional*/ + throw MsPASSError("fwrite_to_file: Open failed on file " + fname, + ErrorSeverity::Invalid); + /* Both fseek and ftell can fail in weird circumstances, but I intentionally + do not trap that condition as if either have issues I am quite sure + the fwrite will fail */ + fseek(fp, 0L, 2); + foff = ftell(fp); + if (fwrite((void *)dptr, sizeof(double), nd, fp) != nd) { + fclose(fp); + throw MsPASSError("fwrite_to_file: fwrite error while writing to file " + + fname, + ErrorSeverity::Invalid); + } + fclose(fp); + return foff; + } catch (...) { + throw; + }; } /*! Write sample data for a TimeSeries to a file with fwrite. Always appends and returns foff of the position where fwrite wrote these data. Returns -1 if it receives a datum marked dead. */ -long int fwrite_to_file(TimeSeries& d,const string dir,const string dfile) -{ - if(d.dead()) return -1; - /* Using this function avoids repetitious code with Seismogram version. */ - long int foff; - try{ - foff = fwrite_sample_data(dir,dfile,d.s.data(),d.npts()); - }catch(...){throw;}; - /* We always set these 3 attributes in Metadata so they can be properly - saved to the database after a successful write. Repetitious with Seismogram - but a function to do this would be more confusing that helpful */ - d.put_string(SEISMICMD_dir,dir); - d.put_string(SEISMICMD_dfile,dfile); - d.put_long(SEISMICMD_foff,foff); - return(foff); +long int fwrite_to_file(TimeSeries &d, const string dir, const string dfile) { + if (d.dead()) + return -1; + /* Using this function avoids repetitious code with Seismogram version. */ + long int foff; + try { + foff = fwrite_sample_data(dir, dfile, d.s.data(), d.npts()); + } catch (...) { + throw; + }; + /* We always set these 3 attributes in Metadata so they can be properly + saved to the database after a successful write. Repetitious with Seismogram + but a function to do this would be more confusing that helpful */ + d.put_string(SEISMICMD_dir, dir); + d.put_string(SEISMICMD_dfile, dfile); + d.put_long(SEISMICMD_foff, foff); + return (foff); } /*! Write sample data for a Seismogram to a file with fwrite. Always appends and returns foff of the position where fwrite wrote these data. @@ -84,20 +90,22 @@ sample matrix. Returns -1 if it receives a datum marked dead. */ -long int fwrite_to_file(Seismogram& d, const string dir,const string dfile) -{ - if(d.dead()) return(-1); - /* Using this function avoids repetitious code with TimeSeries version. - Note use of 3*npts as the buffer size*/ - long int foff; - try{ - foff = fwrite_sample_data(dir,dfile,d.u.get_address(0,0),3*d.npts()); - }catch(...){throw;}; - d.put_string(SEISMICMD_dir,dir); - d.put_string(SEISMICMD_dfile,dfile); - d.put_long(SEISMICMD_foff,foff); +long int fwrite_to_file(Seismogram &d, const string dir, const string dfile) { + if (d.dead()) + return (-1); + /* Using this function avoids repetitious code with TimeSeries version. + Note use of 3*npts as the buffer size*/ + long int foff; + try { + foff = fwrite_sample_data(dir, dfile, d.u.get_address(0, 0), 3 * d.npts()); + } catch (...) { + throw; + }; + d.put_string(SEISMICMD_dir, dir); + d.put_string(SEISMICMD_dfile, dfile); + d.put_long(SEISMICMD_foff, foff); - return(foff); + return (foff); } /*! Write sample data for an Ensemble of TimeSeries to a single file. @@ -115,59 +123,65 @@ If entire ensemble is marked dead it will return an empty vector container. \param dfile file name for write */ -std::vector fwrite_to_file( - mspass::seismic::LoggingEnsemble& d, \ - const std::string dir, - const std::string dfile) -{ - try{ - FILE *fp; - vector foffs; - /* This will return an empty vector if the ensemble is marked dead - callers should handle this condition - but they normally shouldn't be calling this function if the entire ensemble is marked dead anyway.*/ - if(d.dead()) return(foffs); - string fname; - if(dir.length()>0) - /* for expected context for use in python we will assume dir does not - have a trailing path separator so we always insert / */ - fname = dir + "/" + dfile; - else - /* Null as always in unix means use current directory*/ - fname=dfile; - if((fp=fopen(fname.c_str(),"a")) == NULL) - /* use the name of the overloaded parent instead of the actual function - intentional*/ - throw MsPASSError("fwrite_to_file (TimeSeriesEnsemble): Open failed on file "+fname,ErrorSeverity::Invalid); - /* This guarantees appending - not essential since we open in "a" mode but - clearer */ - fseek(fp,0L,2); +std::vector +fwrite_to_file(mspass::seismic::LoggingEnsemble &d, + const std::string dir, const std::string dfile) { + try { + FILE *fp; + vector foffs; + /* This will return an empty vector if the ensemble is marked dead - callers + should handle this condition but they normally shouldn't be calling this + function if the entire ensemble is marked dead anyway.*/ + if (d.dead()) + return (foffs); + string fname; + if (dir.length() > 0) + /* for expected context for use in python we will assume dir does not + have a trailing path separator so we always insert / */ + fname = dir + "/" + dfile; + else + /* Null as always in unix means use current directory*/ + fname = dfile; + if ((fp = fopen(fname.c_str(), "a")) == NULL) + /* use the name of the overloaded parent instead of the actual function - + * intentional*/ + throw MsPASSError( + "fwrite_to_file (TimeSeriesEnsemble): Open failed on file " + fname, + ErrorSeverity::Invalid); + /* This guarantees appending - not essential since we open in "a" mode but + clearer */ + fseek(fp, 0L, 2); - for (int i = 0; i < d.member.size(); ++i) { - if(d.member[i].dead()) - foffs.push_back(-1); - else - { - long int foff = ftell(fp); - foffs.push_back(foff); - TimeSeries& t = d.member[i]; - if (fwrite((void *)t.s.data(), sizeof(double), t.npts(), fp) != t.npts()) - { - fclose(fp); - stringstream ss; - ss << "fwrite_to_file (TimeSeriesEnsemble): fwrite error while writing ensemble member " - << i << " to file="< +fwrite_to_file(mspass::seismic::LoggingEnsemble &d, + const std::string dir, const std::string dfile) { + try { + FILE *fp; + vector foffs; + if (d.dead()) + return (foffs); + string fname; + if (dir.length() > 0) + /* for expected context for use in python we will assume dir does not + have a trailing path separator so we always insert / */ + fname = dir + "/" + dfile; + else + /* Null as always in unix means use current directory*/ + fname = dfile; + if ((fp = fopen(fname.c_str(), "a")) == NULL) + /* use the name of the overloaded parent instead of the actual function - + * intentional*/ + throw MsPASSError( + "fwrite_to_file (SeismogramEnsemble): Open failed on file " + fname, + ErrorSeverity::Invalid); + /* This guarantees appending - not essential since we open in "a" mode but + clearer */ + fseek(fp, 0L, 2); - for (int i = 0; i < d.member.size(); ++i) { - if(d.member[i].dead()) - foffs.push_back(-1); - else - { - if(d.member[i].dead()) continue; - long int foff = ftell(fp); - foffs.push_back(foff); - Seismogram& t = d.member[i]; - if (fwrite((void *)t.u.get_address(0,0), sizeof(double), 3*t.npts(), fp) != 3*t.npts()) - { - fclose(fp); - stringstream ss; - ss << "fwrite_to_file (SeismogramEnsemble): fwrite error while writing ensemble member " - << i << " to file="<0) - { - if(fseek(fp,foff,SEEK_SET)) - { - fclose(fp); - throw MsPASSError("fread_data_from_file: fseek failure on file="+fname,ErrorSeverity::Invalid); - } - } - size_t retcount; - retcount = fread((void*)buffer,sizeof(double),nsamples,fp); - fclose(fp); - return retcount; +size_t fread_sample_data(double *buffer, const string dir, const string dfile, + const long int foff, const int nsamples) { + FILE *fp; + string fname; + if (dir.length() > 0) + /* for expected context for use in python we will assume dir does not + have a trailing path separator so we always insert / */ + fname = dir + "/" + dfile; + else + /* Null as always in unix means use current directory*/ + fname = dfile; + if ((fp = fopen(fname.c_str(), "r")) == NULL) + throw MsPASSError("fread_data_from_file: Open failed on file " + fname, + ErrorSeverity::Invalid); + if (foff > 0) { + if (fseek(fp, foff, SEEK_SET)) { + fclose(fp); + throw MsPASSError("fread_data_from_file: fseek failure on file=" + fname, + ErrorSeverity::Invalid); + } + } + size_t retcount; + retcount = fread((void *)buffer, sizeof(double), nsamples, fp); + fclose(fp); + return retcount; } -size_t fread_from_file(Seismogram& d,const string dir, const string dfile, - const long int foff) -{ - size_t ns_read; - try{ - ns_read = fread_sample_data(d.u.get_address(0,0),dir,dfile,foff,3*d.npts()); - return ns_read; - }catch(...){throw;}; +size_t fread_from_file(Seismogram &d, const string dir, const string dfile, + const long int foff) { + size_t ns_read; + try { + ns_read = fread_sample_data(d.u.get_address(0, 0), dir, dfile, foff, + 3 * d.npts()); + return ns_read; + } catch (...) { + throw; + }; } -size_t fread_from_file(TimeSeries& d,const string dir, const string dfile, - const long int foff) -{ - size_t ns_read; - try{ - ns_read = fread_sample_data(&(d.s[0]),dir,dfile,foff,d.npts()); - return ns_read; - }catch(...){throw;}; +size_t fread_from_file(TimeSeries &d, const string dir, const string dfile, + const long int foff) { + size_t ns_read; + try { + ns_read = fread_sample_data(&(d.s[0]), dir, dfile, foff, d.npts()); + return ns_read; + } catch (...) { + throw; + }; } -size_t fread_from_file(mspass::seismic::LoggingEnsemble &de, - const std::string dir, const std::string dfile, std::vector indexes) -{ - size_t ns_read_sum; - int n = indexes.size(); - FILE *fp; - string fname; - if(dir.length()>0) - /* for expected context for use in python we will assume dir does not - have a trailing path separator so we always insert / */ - fname = dir + "/" + dfile; - else - /* Null as always in unix means use current directory*/ - fname=dfile; - if((fp=fopen(fname.c_str(),"r")) == NULL) { - de.kill(); - stringstream ss; - ss << "can not open file in " << fname << endl; - de.elog.log_error(ss.str()); - return -1; - } +size_t fread_from_file( + mspass::seismic::LoggingEnsemble &de, + const std::string dir, const std::string dfile, + std::vector indexes) { + size_t ns_read_sum; + int n = indexes.size(); + FILE *fp; + string fname; + if (dir.length() > 0) + /* for expected context for use in python we will assume dir does not + have a trailing path separator so we always insert / */ + fname = dir + "/" + dfile; + else + /* Null as always in unix means use current directory*/ + fname = dfile; + if ((fp = fopen(fname.c_str(), "r")) == NULL) { + de.kill(); + stringstream ss; + ss << "can not open file in " << fname << endl; + de.elog.log_error(ss.str()); + return -1; + } - for (int ind = 0; ind < n; ++ind) { - size_t ns_read; - int i = indexes[ind]; - long int foff; - if (de.member[i].is_defined(SEISMICMD_foff)) - { - foff = de.member[i].get_long(SEISMICMD_foff); - } - else - { - de.member[i].kill(); - stringstream ss; - ss << "foff not defined for " << i << " member in ensemble" << endl; - de.member[i].elog.log_error(ss.str()); - continue; - } - try{ - if(fseek(fp,foff,SEEK_SET)) - { - fclose(fp); - de.member[i].kill(); - stringstream ss; - ss << "can not fseek in " << foff << endl; - de.member[i].elog.log_error(ss.str()); - continue; - } - ns_read = fread((void*)de.member[i].u.get_address(0, 0), sizeof(double), 3 * de.member[i].npts(), fp); - if (ns_read != 3 * de.member[i].npts()) - { - de.member[i].elog.log_error(string("read error: npts not equal")); - de.member[i].kill(); - } - else - { - de.member[i].set_live(); - } - ns_read_sum += ns_read; - }catch(...){throw;}; - } - fclose(fp); - de.set_live(); - return ns_read_sum; + for (int ind = 0; ind < n; ++ind) { + size_t ns_read; + int i = indexes[ind]; + long int foff; + if (de.member[i].is_defined(SEISMICMD_foff)) { + foff = de.member[i].get_long(SEISMICMD_foff); + } else { + de.member[i].kill(); + stringstream ss; + ss << "foff not defined for " << i << " member in ensemble" << endl; + de.member[i].elog.log_error(ss.str()); + continue; + } + try { + if (fseek(fp, foff, SEEK_SET)) { + fclose(fp); + de.member[i].kill(); + stringstream ss; + ss << "can not fseek in " << foff << endl; + de.member[i].elog.log_error(ss.str()); + continue; + } + ns_read = fread((void *)de.member[i].u.get_address(0, 0), sizeof(double), + 3 * de.member[i].npts(), fp); + if (ns_read != 3 * de.member[i].npts()) { + de.member[i].elog.log_error(string("read error: npts not equal")); + de.member[i].kill(); + } else { + de.member[i].set_live(); + } + ns_read_sum += ns_read; + } catch (...) { + throw; + }; + } + fclose(fp); + de.set_live(); + return ns_read_sum; } -size_t fread_from_file(mspass::seismic::LoggingEnsemble &de, - const std::string dir, const std::string dfile, std::vector indexes) +size_t fread_from_file( + mspass::seismic::LoggingEnsemble &de, + const std::string dir, const std::string dfile, + std::vector indexes) { - size_t ns_read_sum; - int n = indexes.size(); - FILE *fp; - string fname; - if(dir.length()>0) - /* for expected context for use in python we will assume dir does not - have a trailing path separator so we always insert / */ - fname = dir + "/" + dfile; - else - /* Null as always in unix means use current directory*/ - fname=dfile; - if((fp=fopen(fname.c_str(),"r")) == NULL) { - de.kill(); - stringstream ss; - ss << "can not open file in " << fname << endl; - de.elog.log_error(ss.str()); - return -1; - } - for (int ind = 0; ind < n; ++ind) { - size_t ns_read; - int i = indexes[ind]; - long int foff; - if (de.member[i].is_defined(SEISMICMD_foff)) - { - foff = de.member[i].get_long(SEISMICMD_foff); - } - else - { - de.member[i].kill(); - stringstream ss; - ss << "foff not defined for " << i << " member in ensemble" << endl; - de.member[i].elog.log_error(ss.str()); - continue; - } - try{ - if(fseek(fp,foff,SEEK_SET)) - { - fclose(fp); - de.member[i].kill(); - stringstream ss; - ss << "can not fseek in " << foff << endl; - de.member[i].elog.log_error(ss.str()); - continue; - } - ns_read = fread((void*)(&(de.member[i].s[0])), sizeof(double), de.member[i].npts(), fp); - if (ns_read != de.member[i].npts()) - { - de.member[i].elog.log_error(string("read error: npts not equal")); - de.member[i].kill(); - } - else - { - de.member[i].set_live(); - } - ns_read_sum += ns_read; - }catch(...){throw;}; - } - fclose(fp); - de.set_live(); - return ns_read_sum; + size_t ns_read_sum; + int n = indexes.size(); + FILE *fp; + string fname; + if (dir.length() > 0) + /* for expected context for use in python we will assume dir does not + have a trailing path separator so we always insert / */ + fname = dir + "/" + dfile; + else + /* Null as always in unix means use current directory*/ + fname = dfile; + if ((fp = fopen(fname.c_str(), "r")) == NULL) { + de.kill(); + stringstream ss; + ss << "can not open file in " << fname << endl; + de.elog.log_error(ss.str()); + return -1; + } + for (int ind = 0; ind < n; ++ind) { + size_t ns_read; + int i = indexes[ind]; + long int foff; + if (de.member[i].is_defined(SEISMICMD_foff)) { + foff = de.member[i].get_long(SEISMICMD_foff); + } else { + de.member[i].kill(); + stringstream ss; + ss << "foff not defined for " << i << " member in ensemble" << endl; + de.member[i].elog.log_error(ss.str()); + continue; + } + try { + if (fseek(fp, foff, SEEK_SET)) { + fclose(fp); + de.member[i].kill(); + stringstream ss; + ss << "can not fseek in " << foff << endl; + de.member[i].elog.log_error(ss.str()); + continue; + } + ns_read = fread((void *)(&(de.member[i].s[0])), sizeof(double), + de.member[i].npts(), fp); + if (ns_read != de.member[i].npts()) { + de.member[i].elog.log_error(string("read error: npts not equal")); + de.member[i].kill(); + } else { + de.member[i].set_live(); + } + ns_read_sum += ns_read; + } catch (...) { + throw; + }; + } + fclose(fp); + de.set_live(); + return ns_read_sum; } -} // Termination of namespace definitions +} // namespace mspass::io diff --git a/cxx/src/lib/io/mseed_file_indexer.cc b/cxx/src/lib/io/mseed_file_indexer.cc index e858c1799..ccc5bd5e5 100644 --- a/cxx/src/lib/io/mseed_file_indexer.cc +++ b/cxx/src/lib/io/mseed_file_indexer.cc @@ -1,170 +1,159 @@ #include +#include +#include +#include +#include #include #include #include +#include #include -#include #include -#include -#include -#include -#include #include "libmseed.h" #include "mspass/io/mseed_index.h" #include "mspass/utility/ErrorLogger.h" using namespace std; -namespace mspass::io -{ +namespace mspass::io { using namespace mspass::io; using mspass::utility::ErrorSeverity; -/*! Inline class used to make a cleaner interface the ugly libmseed function - for dealing with what they call an "sid". +/*! Inline class used to make a cleaner interface the ugly libmseed function + for dealing with what they call an "sid". */ -class MSEED_sid -{ - public: - MSEED_sid() - { - string s(""); - net=s; - sta=s; - chan=s; - loc=s; +class MSEED_sid { +public: + MSEED_sid() { + string s(""); + net = s; + sta = s; + chan = s; + loc = s; + } + MSEED_sid(const char *sid); + MSEED_sid(const MSEED_sid &parent) { + net = parent.net; + sta = parent.sta; + chan = parent.chan; + loc = parent.loc; + }; + string net; + string sta; + string chan; + string loc; + MSEED_sid &operator=(const MSEED_sid &parent) { + if (this != &parent) { + net = parent.net; + sta = parent.sta; + chan = parent.chan; + loc = parent.loc; } - MSEED_sid(const char *sid); - MSEED_sid(const MSEED_sid& parent) - { - net=parent.net; - sta=parent.sta; - chan=parent.chan; - loc=parent.loc; - }; - string net; - string sta; - string chan; - string loc; - MSEED_sid& operator=(const MSEED_sid& parent) - { - if(this != &parent) - { - net=parent.net; - sta=parent.sta; - chan=parent.chan; - loc=parent.loc; - } - return *this; - }; - bool operator!=(const MSEED_sid& other) const; - friend ostream& operator<<(ostream& os, MSEED_sid& self) - { - string sep(":"); - os << self.net - <(sid),net,sta,loc,chan)==0) - { + if (ms_sid2nslc(const_cast(sid), net, sta, loc, chan) == 0) { this->net = string(net); this->sta = string(sta); this->chan = string(chan); this->loc = string(loc); - } - else - { + } else { throw 1; } } -bool MSEED_sid::operator!=(const MSEED_sid& other) const -{ - if( this->net==other.net && this->sta==other.sta && this->chan==other.chan && this->loc==other.loc) +bool MSEED_sid::operator!=(const MSEED_sid &other) const { + if (this->net == other.net && this->sta == other.sta && + this->chan == other.chan && this->loc == other.loc) return false; else return true; }; /* Using this file scope typedef to avoid the absurdly complex syntax of an std::pair constructor with complex objects like this */ -typedef std::pair,mspass::utility::ErrorLogger> MSDINDEX_returntype; +typedef std::pair, mspass::utility::ErrorLogger> + MSDINDEX_returntype; thread_local std::string buffer; -/*! Internal function translates miniseed reader function return codes - to readable messages posted in mseed_file_indexer to ErrorLogger. +/*! Internal function translates miniseed reader function return codes + to readable messages posted in mseed_file_indexer to ErrorLogger. */ -std::string MS_code_to_message(int retcode) -{ - string message("Read error detected by libmseed reader function ms3_readmsr_r\n"); +std::string MS_code_to_message(int retcode) { + string message( + "Read error detected by libmseed reader function ms3_readmsr_r\n"); message += "File index will be empty or truncated\n"; - switch(retcode) - { - case MS_GENERROR: - message += "MS_GENERROR(-1) return - generic unspecified error"; - break; - case MS_NOTSEED: - message += "MS_NOTSEED(-2) return - Data not SEED"; - break; - case MS_WRONGLENGTH: - message += "MS_WRONGLENGTH(-3) return - Length of data read was not correct"; - break; - case MS_OUTOFRANGE: - message += "MS_OUTOFRANGE(-4) return - SEED record length out of range"; - break; - case MS_UNKNOWNFORMAT: - message += "MS_UNKNOWNFORMAT(-5) return - data encoding format value in packet is invalid"; - break; - case MS_STBADCOMPFLAG: - message += "MS_STBADCOMPFLAG(-6) return - compression flag value is invalid"; - break; - case MS_INVALIDCRC: - message += "MS_INVALIDCRC(-7) return - CRC value in packet is invalid"; - break; - default: - message += "Unknown return code - this should not happen and is likely a version skew problem"; + switch (retcode) { + case MS_GENERROR: + message += "MS_GENERROR(-1) return - generic unspecified error"; + break; + case MS_NOTSEED: + message += "MS_NOTSEED(-2) return - Data not SEED"; + break; + case MS_WRONGLENGTH: + message += + "MS_WRONGLENGTH(-3) return - Length of data read was not correct"; + break; + case MS_OUTOFRANGE: + message += "MS_OUTOFRANGE(-4) return - SEED record length out of range"; + break; + case MS_UNKNOWNFORMAT: + message += "MS_UNKNOWNFORMAT(-5) return - data encoding format value in " + "packet is invalid"; + break; + case MS_STBADCOMPFLAG: + message += + "MS_STBADCOMPFLAG(-6) return - compression flag value is invalid"; + break; + case MS_INVALIDCRC: + message += "MS_INVALIDCRC(-7) return - CRC value in packet is invalid"; + break; + default: + message += "Unknown return code - this should not happen and is likely a " + "version skew problem"; } return message; } -/*! \brief Indexing function using libmseed low level function ms3_readmsr_r. +/*! \brief Indexing function using libmseed low level function ms3_readmsr_r. * - * This function uses what has become the standard reader for miniseed from - * IRIS DMC called libmseed. It uses the low level C function ms3_readmsr_r + * This function uses what has become the standard reader for miniseed from + * IRIS DMC called libmseed. It uses the low level C function ms3_readmsr_r * to read an input file one packet at a time. It uses the version that - * is claimed to be thread safe. + * is claimed to be thread safe. * - * The complexities of seed can cause a number of problems. This version + * The complexities of seed can cause a number of problems. This version * tries to deal these complexities: - * 1. miniseed files are often produced by concatenation of data form multiple - * channel. Any change in station id returned by the function triggers a + * 1. miniseed files are often produced by concatenation of data form multiple + * channel. Any change in station id returned by the function triggers a * new index entry. * 2. Packet errors will force a new segment. - * 3. Time tears defined by either a jump or accumulated time mismatch of - * more than 1/2 sample will trigger a new segments. + * 3. Time tears defined by either a jump or accumulated time mismatch of + * more than 1/2 sample will trigger a new segments. * 4. Changes in sample interval trigger a new segments. That is - * actually implicit in point 1 with the "sid" because of the - * seed channel code naming convention. - * \return std::pair first is a vector of index data. second is - * an ErrorLogger object. Caller should test for empty vector - * that is a signal for a open failure or a file that probably isn't - * miniseed. The content of elog should always be tested as any - * errors there should be inspected/handled. + * actually implicit in point 1 with the "sid" because of the + * seed channel code naming convention. + * \return std::pair first is a vector of index data. second is + * an ErrorLogger object. Caller should test for empty vector + * that is a signal for a open failure or a file that probably isn't + * miniseed. The content of elog should always be tested as any + * errors there should be inspected/handled. * */ -MSDINDEX_returntype mseed_file_indexer(const string inputfile, - const bool segment_timetears,const bool Verbose) -{ +MSDINDEX_returntype mseed_file_indexer(const string inputfile, + const bool segment_timetears, + const bool Verbose) { const string function_name("mseed_file_indexer"); MS3Record *msr = 0; @@ -174,12 +163,12 @@ MSDINDEX_returntype mseed_file_indexer(const string inputfile, struct. The weird cleanup call at the end of the read loop calls the equivalent of a destructor.*/ MS3FileParam *msfp = NULL; - uint32_t flags = MSF_SKIPNOTDATA ; + uint32_t flags = MSF_SKIPNOTDATA; // int8_t ppackets = 0; int8_t verbose = 0; int retcode; - //char last_sid[128],current_sid[128]; - MSEED_sid last_sid,current_sid; + // char last_sid[128],current_sid[128]; + MSEED_sid last_sid, current_sid; /* This is used to define a time tear (gap). When the computed endtime of the previous packet read mismatches the starttime of the current packet we create a segment by defining a new index entry terminated at the time @@ -191,212 +180,205 @@ MSDINDEX_returntype mseed_file_indexer(const string inputfile, mspass::utility::ErrorLogger elog; /* Loop over the input file record by record */ - int64_t fpos=0; - uint64_t start_foff,nbytes; + int64_t fpos = 0; + uint64_t start_foff, nbytes; mseed_index ind; - /* These values have a different time standard structure than - * epoch times. These can only be compared with epoch times by + /* These values have a different time standard structure than + * epoch times. These can only be compared with epoch times by * calling the function MS_NSTIME2EPOCH */ nstime_t stime; - int64_t npts(0),current_npts,record_length(4096); - uint64_t number_packets_read(0),number_valid_packets(0); - double last_packet_samprate,last_packet_endtime,expected_starttime, last_dt; - /* These are used to handle long time series where the accumulated time - * from the start of a segment (many packets) gets inconsistent with + int64_t npts(0), current_npts, record_length(4096); + uint64_t number_packets_read(0), number_valid_packets(0); + double last_packet_samprate, last_packet_endtime, expected_starttime, last_dt; + /* These are used to handle long time series where the accumulated time + * from the start of a segment (many packets) gets inconsistent with * the next packet's starttime.*/ - double segment_starttime,computed_segment_starttime; - /* mseed stores time in an int (I think) this holds float + double segment_starttime, computed_segment_starttime; + /* mseed stores time in an int (I think) this holds float * conversions for current and last packet read.*/ - double current_epoch_stime,last_epoch_stime; + double current_epoch_stime, last_epoch_stime; /* loop break boolean */ bool data_available; /* It is not clear what verbose means in this function so we currently always turn it off. Note Verbose and verbose are different - a bit dangerous but that is what is for now. Also changed dec 2021: changed to thread safe version. Requires adding - msfp struct initialized as NULL. + msfp struct initialized as NULL. March 2024: changed from while to do-while loop. That improves the logic - because of the weird way this function works. Runs one packet at a time - but the read loads msr with the data in the packet. The do-while - loop allows the main loop to always act the same on each packet it - processes. Requires, however, an initialization and cleanup section + because of the weird way this function works. Runs one packet at a time + but the read loads msr with the data in the packet. The do-while + loop allows the main loop to always act the same on each packet it + processes. Requires, however, an initialization and cleanup section at top and after exiting the loop. */ - /* Although we don't use it this log initialization seems necessary as + /* Although we don't use it this log initialization seems necessary as libmseed functions will dogmatically use the facility */ - ms_rloginit (NULL, NULL, NULL, NULL, 10); - do - { - bool timetear_detected(false),sid_change_detected(false); - retcode = ms3_readmsr_r (&msfp,&msr, inputfile.c_str(), &fpos, NULL, - flags, verbose); - switch(retcode) - { - case MS_NOERROR: - try - { - current_sid=MSEED_sid(msr->sid); - }catch(...) - { - stringstream ss; - ss << "source id string="<starttime; - current_epoch_stime = MS_NSTIME2EPOCH(static_cast(stime)); - last_epoch_stime = current_epoch_stime; - last_packet_samprate = msr->samprate; - last_dt = 1.0/last_packet_samprate; - npts = msr->samplecnt; - last_packet_endtime = current_epoch_stime - + static_cast(npts-1)*last_dt; - segment_starttime = last_epoch_stime; - start_foff = 0; - /* Set this for the first packet and assume it is constant for the - whole file. I don't think seed technically requires this but - in practice it is alway the case. */ - record_length = msr->reclen; - } - else - { - stime = msr->starttime; - current_epoch_stime = MS_NSTIME2EPOCH(static_cast(stime)); - expected_starttime = last_packet_endtime + last_dt; - computed_segment_starttime = segment_starttime + last_dt*static_cast(npts); - if(current_sid!=last_sid) - sid_change_detected=true; - if(segment_timetears) - { - if( (fabs(current_epoch_stime-expected_starttime)>time_tear_tolerance) - || (fabs(current_epoch_stime-computed_segment_starttime)>time_tear_tolerance) ) - timetear_detected=true; - else - /* This explicit setting isn't essential but makes the logic clearer.*/ - timetear_detected=false; - } - - if( sid_change_detected || timetear_detected ) - { - nbytes=fpos-start_foff; - ind.net=last_sid.net; - ind.sta=last_sid.sta; - ind.chan=last_sid.chan; - ind.loc=last_sid.loc; - ind.foff=start_foff; - ind.nbytes=nbytes; - ind.starttime=segment_starttime; - ind.last_packet_time=last_epoch_stime; - ind.samprate=last_packet_samprate; - ind.npts=npts; - ind.endtime=ind.starttime + (static_cast(npts-1))/ind.samprate; - indexdata.push_back(ind); - /* Initate a new segment. Note the only difference if there was a decoding - * error is the index entry will be dropped. */ - last_sid = current_sid; - last_epoch_stime = current_epoch_stime; - last_packet_samprate = msr->samprate; - last_dt = 1.0/last_packet_samprate; - npts = msr->samplecnt; - last_packet_endtime = current_epoch_stime - + static_cast(npts-1)*last_dt; - segment_starttime = current_epoch_stime; - start_foff = fpos; - } + ms_rloginit(NULL, NULL, NULL, NULL, 10); + do { + bool timetear_detected(false), sid_change_detected(false); + retcode = ms3_readmsr_r(&msfp, &msr, inputfile.c_str(), &fpos, NULL, flags, + verbose); + switch (retcode) { + case MS_NOERROR: + try { + current_sid = MSEED_sid(msr->sid); + } catch (...) { + stringstream ss; + ss << "source id string=" << current_sid << " in packet number " + << number_packets_read << " of file " << inputfile + << " could not be decoded but reader did not flag an error" << endl + << "Segment break at this point is likely" << endl; + elog.log_error(function_name, ss.str(), ErrorSeverity::Complaint); + continue; + } + /* Land here for normal reads with no error return*/ + if (number_valid_packets == 0) { + /* Initializations needed for first packet in the file */ + last_sid = current_sid; + stime = msr->starttime; + current_epoch_stime = MS_NSTIME2EPOCH(static_cast(stime)); + last_epoch_stime = current_epoch_stime; + last_packet_samprate = msr->samprate; + last_dt = 1.0 / last_packet_samprate; + npts = msr->samplecnt; + last_packet_endtime = + current_epoch_stime + static_cast(npts - 1) * last_dt; + segment_starttime = last_epoch_stime; + start_foff = 0; + /* Set this for the first packet and assume it is constant for the + whole file. I don't think seed technically requires this but + in practice it is alway the case. */ + record_length = msr->reclen; + } else { + stime = msr->starttime; + current_epoch_stime = MS_NSTIME2EPOCH(static_cast(stime)); + expected_starttime = last_packet_endtime + last_dt; + computed_segment_starttime = + segment_starttime + last_dt * static_cast(npts); + if (current_sid != last_sid) + sid_change_detected = true; + if (segment_timetears) { + if ((fabs(current_epoch_stime - expected_starttime) > + time_tear_tolerance) || + (fabs(current_epoch_stime - computed_segment_starttime) > + time_tear_tolerance)) + timetear_detected = true; else - { - /* Packets without a break land here */ - last_sid = current_sid; - stime = msr->starttime; - last_epoch_stime = MS_NSTIME2EPOCH(static_cast(stime)); - last_packet_samprate = msr->samprate; - last_dt = 1.0/last_packet_samprate; - current_npts = msr->samplecnt; - npts += current_npts; - last_packet_endtime = current_epoch_stime - + static_cast(current_npts-1)*last_dt; - } + /* This explicit setting isn't essential but makes the logic + * clearer.*/ + timetear_detected = false; } - ++number_valid_packets; - data_available = true; - break; - case MS_ENDOFFILE: - if(number_valid_packets>0) - { - /* VERY IMPORTANT: reader handles data per packet. fpos is updated by - the reader but on EOF it is NOT updated. As a result we have to - add the record length or we drop the last packet from - the index. A too classic problem of use of a pointer to a pointer - in plain C. Furthermore had to save the record length earlier - as msr is a NULL pointer when EOF is returned */ - nbytes=fpos-start_foff + record_length; + + if (sid_change_detected || timetear_detected) { + nbytes = fpos - start_foff; ind.net = last_sid.net; ind.sta = last_sid.sta; ind.chan = last_sid.chan; ind.loc = last_sid.loc; - ind.foff=start_foff; - ind.nbytes=nbytes; - ind.starttime=segment_starttime; - ind.last_packet_time=last_epoch_stime; - ind.samprate=last_packet_samprate; - ind.npts=npts; - ind.endtime=ind.starttime + (static_cast(npts-1))/ind.samprate; + ind.foff = start_foff; + ind.nbytes = nbytes; + ind.starttime = segment_starttime; + ind.last_packet_time = last_epoch_stime; + ind.samprate = last_packet_samprate; + ind.npts = npts; + ind.endtime = + ind.starttime + (static_cast(npts - 1)) / ind.samprate; indexdata.push_back(ind); - data_available = false; - } - else - { - elog.log_error(function_name, - string("Hit end of file before reading any valid packets\nEmpty index"), - ErrorSeverity::Invalid); + /* Initate a new segment. Note the only difference if there was a + * decoding error is the index entry will be dropped. */ + last_sid = current_sid; + last_epoch_stime = current_epoch_stime; + last_packet_samprate = msr->samprate; + last_dt = 1.0 / last_packet_samprate; + npts = msr->samplecnt; + last_packet_endtime = + current_epoch_stime + static_cast(npts - 1) * last_dt; + segment_starttime = current_epoch_stime; + start_foff = fpos; + } else { + /* Packets without a break land here */ + last_sid = current_sid; + stime = msr->starttime; + last_epoch_stime = MS_NSTIME2EPOCH(static_cast(stime)); + last_packet_samprate = msr->samprate; + last_dt = 1.0 / last_packet_samprate; + current_npts = msr->samplecnt; + npts += current_npts; + last_packet_endtime = current_epoch_stime + + static_cast(current_npts - 1) * last_dt; } - break; - default: - /* All other error conditions end here. Function MS_code_to_message - translates to a rational error message return */ - string message=MS_code_to_message(retcode); - elog.log_error(function_name,message,ErrorSeverity::Complaint); - data_available = false; - /* If we land here we need to add a final index entry to salvage what - we can from this file. This is almost identical to the eof section - except here we intentionally drop the last packet assuming it - is the problem. */ - if(number_valid_packets>0) - { - // NOTE not the same as EOF section - nbytes=fpos-start_foff; - ind.net = last_sid.net; - ind.sta = last_sid.sta; - ind.chan = last_sid.chan; - ind.loc = last_sid.loc; - ind.foff=start_foff; - ind.nbytes=nbytes; - ind.starttime=segment_starttime; - ind.last_packet_time=last_epoch_stime; - ind.samprate=last_packet_samprate; - ind.npts=npts; - ind.endtime=ind.starttime + (static_cast(npts-1))/ind.samprate; - indexdata.push_back(ind); - data_available = false; - } + } + ++number_valid_packets; + data_available = true; + break; + case MS_ENDOFFILE: + if (number_valid_packets > 0) { + /* VERY IMPORTANT: reader handles data per packet. fpos is updated + by the reader but on EOF it is NOT updated. As a result we have to + add the record length or we drop the last packet from + the index. A too classic problem of use of a pointer to a pointer + in plain C. Furthermore had to save the record length earlier + as msr is a NULL pointer when EOF is returned */ + nbytes = fpos - start_foff + record_length; + ind.net = last_sid.net; + ind.sta = last_sid.sta; + ind.chan = last_sid.chan; + ind.loc = last_sid.loc; + ind.foff = start_foff; + ind.nbytes = nbytes; + ind.starttime = segment_starttime; + ind.last_packet_time = last_epoch_stime; + ind.samprate = last_packet_samprate; + ind.npts = npts; + ind.endtime = + ind.starttime + (static_cast(npts - 1)) / ind.samprate; + indexdata.push_back(ind); + data_available = false; + } else { + elog.log_error(function_name, + string("Hit end of file before reading any valid " + "packets\nEmpty index"), + ErrorSeverity::Invalid); + } + break; + default: + /* All other error conditions end here. Function MS_code_to_message + translates to a rational error message return */ + string message = MS_code_to_message(retcode); + elog.log_error(function_name, message, ErrorSeverity::Complaint); + data_available = false; + /* If we land here we need to add a final index entry to salvage what + we can from this file. This is almost identical to the eof section + except here we intentionally drop the last packet assuming it + is the problem. */ + if (number_valid_packets > 0) { + // NOTE not the same as EOF section + nbytes = fpos - start_foff; + ind.net = last_sid.net; + ind.sta = last_sid.sta; + ind.chan = last_sid.chan; + ind.loc = last_sid.loc; + ind.foff = start_foff; + ind.nbytes = nbytes; + ind.starttime = segment_starttime; + ind.last_packet_time = last_epoch_stime; + ind.samprate = last_packet_samprate; + ind.npts = npts; + ind.endtime = + ind.starttime + (static_cast(npts - 1)) / ind.samprate; + indexdata.push_back(ind); + data_available = false; + } }; ++number_packets_read; - }while(data_available); + } while (data_available); /* Make sure everything is cleaned up. Documentation says this is needed to invoke the plain C equivalent of a destructor.*/ - ms3_readmsr_r (&msfp, &msr, NULL, NULL, NULL, 0, 0); + ms3_readmsr_r(&msfp, &msr, NULL, NULL, NULL, 0, 0); buffer.clear(); - ms_rlog_emit(NULL,0,verbose); - return MSDINDEX_returntype(indexdata,elog); + ms_rlog_emit(NULL, 0, verbose); + return MSDINDEX_returntype(indexdata, elog); } } // End namespace mspass::io diff --git a/cxx/src/lib/io/mseed_index.cc b/cxx/src/lib/io/mseed_index.cc index 0c7b98e24..b9eb13c7e 100644 --- a/cxx/src/lib/io/mseed_index.cc +++ b/cxx/src/lib/io/mseed_index.cc @@ -1,26 +1,17 @@ +#include "mspass/io/mseed_index.h" #include #include -#include "mspass/io/mseed_index.h" -namespace mspass::io -{ +namespace mspass::io { using namespace mspass::io; -std::ostringstream& operator<< (std::ostringstream& ss,const mseed_index& ind) -{ - ss << ind.net <<" " - << ind.sta << " "; - if(ind.loc.size()>0) - ss << ind.loc<<" "; +std::ostringstream &operator<<(std::ostringstream &ss, const mseed_index &ind) { + ss << ind.net << " " << ind.sta << " "; + if (ind.loc.size() > 0) + ss << ind.loc << " "; else ss << "NULL "; - ss << ind.chan<<" " - << ind.foff<<" " - << ind.nbytes<<" " - << std::setprecision(20) - << ind.starttime<<" " - << ind.last_packet_time<<" " - << ind.endtime<<" " - << ind.samprate<<" " - << ind.npts; + ss << ind.chan << " " << ind.foff << " " << ind.nbytes << " " + << std::setprecision(20) << ind.starttime << " " << ind.last_packet_time + << " " << ind.endtime << " " << ind.samprate << " " << ind.npts; return ss; }; -} +} // namespace mspass::io diff --git a/cxx/src/lib/seismic/BasicTimeSeries.cc b/cxx/src/lib/seismic/BasicTimeSeries.cc index 96666f098..6f302cd96 100644 --- a/cxx/src/lib/seismic/BasicTimeSeries.cc +++ b/cxx/src/lib/seismic/BasicTimeSeries.cc @@ -1,108 +1,105 @@ /* This file contains member functions for a BasicTimeSeries object.*/ -#include #include "mspass/seismic/BasicTimeSeries.h" #include "mspass/utility/MsPASSError.h" +#include namespace mspass::seismic { using namespace std; using namespace mspass::utility; -void BasicTimeSeries::ator(double tshift) -{ - /* dead traces should to totally ignored */ - if(!(this->mlive)) return; - if(tref==TimeReferenceType::Relative) return; - t0shift=tshift; - mt0 -= tshift; - tref=TimeReferenceType::Relative; - t0shift_is_valid=true; +void BasicTimeSeries::ator(double tshift) { + /* dead traces should to totally ignored */ + if (!(this->mlive)) + return; + if (tref == TimeReferenceType::Relative) + return; + t0shift = tshift; + mt0 -= tshift; + tref = TimeReferenceType::Relative; + t0shift_is_valid = true; } // inverse of ator -- note minus becomes plus // everything else is nearly identical -void BasicTimeSeries::rtoa() -{ - /* dead traces should to totally ignored */ - if(!(this->mlive)) return; - const string base_error("BasicTimeSeries::rtoa() t0shift for conversion is not defined."); - if(tref==TimeReferenceType::UTC) return; - /* A rather odd test for a nonzero. We use 100 s assuming no active - * source data would use a shift longer than that unless it really did - * have an UTC time standard. Also assumes we'll never use data from - * the first 2 minutes of 1960.*/ - if(t0shift_is_valid || (t0shift>100.0) ) - { - mt0 += t0shift; - tref=TimeReferenceType::UTC; - t0shift_is_valid=false; - t0shift=0.0; - } - else - throw MsPASSError(base_error + "time shift to return to UTC time is not defined",ErrorSeverity::Invalid); +void BasicTimeSeries::rtoa() { + /* dead traces should to totally ignored */ + if (!(this->mlive)) + return; + const string base_error( + "BasicTimeSeries::rtoa() t0shift for conversion is not defined."); + if (tref == TimeReferenceType::UTC) + return; + /* A rather odd test for a nonzero. We use 100 s assuming no active + * source data would use a shift longer than that unless it really did + * have an UTC time standard. Also assumes we'll never use data from + * the first 2 minutes of 1960.*/ + if (t0shift_is_valid || (t0shift > 100.0)) { + mt0 += t0shift; + tref = TimeReferenceType::UTC; + t0shift_is_valid = false; + t0shift = 0.0; + } else + throw MsPASSError(base_error + + "time shift to return to UTC time is not defined", + ErrorSeverity::Invalid); } -BasicTimeSeries::BasicTimeSeries() -{ - mt0=0.0; - tref=TimeReferenceType::Relative; - mlive=false; - mdt=1.0; - nsamp=0; - t0shift=0.0; - t0shift_is_valid=false; +BasicTimeSeries::BasicTimeSeries() { + mt0 = 0.0; + tref = TimeReferenceType::Relative; + mlive = false; + mdt = 1.0; + nsamp = 0; + t0shift = 0.0; + t0shift_is_valid = false; } -BasicTimeSeries::BasicTimeSeries(const BasicTimeSeries& tsin) -{ - mt0=tsin.mt0; - tref=tsin.tref; - mlive=tsin.mlive; - mdt=tsin.mdt; - nsamp=tsin.nsamp; - t0shift=tsin.t0shift; - t0shift_is_valid=tsin.t0shift_is_valid; +BasicTimeSeries::BasicTimeSeries(const BasicTimeSeries &tsin) { + mt0 = tsin.mt0; + tref = tsin.tref; + mlive = tsin.mlive; + mdt = tsin.mdt; + nsamp = tsin.nsamp; + t0shift = tsin.t0shift; + t0shift_is_valid = tsin.t0shift_is_valid; } -BasicTimeSeries& BasicTimeSeries::operator=(const BasicTimeSeries& parent) -{ - if (this!=&parent) - { - mt0=parent.mt0; - tref=parent.tref; - mlive=parent.mlive; - mdt=parent.mdt; - nsamp=parent.nsamp; - t0shift=parent.t0shift; - t0shift_is_valid=parent.t0shift_is_valid; - } - return *this; +BasicTimeSeries &BasicTimeSeries::operator=(const BasicTimeSeries &parent) { + if (this != &parent) { + mt0 = parent.mt0; + tref = parent.tref; + mlive = parent.mlive; + mdt = parent.mdt; + nsamp = parent.nsamp; + t0shift = parent.t0shift; + t0shift_is_valid = parent.t0shift_is_valid; + } + return *this; } -void BasicTimeSeries::shift(double dt) -{ - try { - double oldt0shift=t0shift; - this->rtoa(); - this->ator(oldt0shift+dt); - } catch(...) { - throw; - }; +void BasicTimeSeries::shift(double dt) { + try { + double oldt0shift = t0shift; + this->rtoa(); + this->ator(oldt0shift + dt); + } catch (...) { + throw; + }; } -double BasicTimeSeries::time_reference() const -{ - const string base_error("BasicTimeSeries::time_reference method: "); - if(tref==TimeReferenceType::UTC) - throw MsPASSError(base_error - + "data have UTC time set so requesting the reference" - + " time make no sense - likely a coding error", - ErrorSeverity::Fatal); - if(t0shift_is_valid) - return(t0shift); - else - throw MsPASSError(base_error - + "cannot return time reference as it is marked invalid", - ErrorSeverity::Invalid); +double BasicTimeSeries::time_reference() const { + const string base_error("BasicTimeSeries::time_reference method: "); + if (tref == TimeReferenceType::UTC) + throw MsPASSError(base_error + + "data have UTC time set so requesting the reference" + + " time make no sense - likely a coding error", + ErrorSeverity::Fatal); + if (t0shift_is_valid) + return (t0shift); + else + throw MsPASSError( + base_error + "cannot return time reference as it is marked invalid", + ErrorSeverity::Invalid); } -std::vector BasicTimeSeries::time_axis() const -{ +std::vector BasicTimeSeries::time_axis() const { std::vector t; t.reserve(this->nsamp); - for(int i=0;imdt*static_cast(i)); + for (int i = 0; i < nsamp; ++i) + t.push_back(mt0 + this->mdt * static_cast(i)); return t; } -} // end mspass namespace encapsulation +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/CoreSeismogram.cc b/cxx/src/lib/seismic/CoreSeismogram.cc index 410cef0e3..3d596aa0c 100644 --- a/cxx/src/lib/seismic/CoreSeismogram.cc +++ b/cxx/src/lib/seismic/CoreSeismogram.cc @@ -1,49 +1,45 @@ -#include -#include -#include -#include -#include -#include +#include "mspass/seismic/CoreSeismogram.h" #include "misc/blas.h" #include "mspass/seismic/keywords.h" -#include "mspass/seismic/CoreSeismogram.h" #include "mspass/utility/MsPASSError.h" #include "mspass/utility/SphericalCoordinate.h" +#include +#include +#include +#include +#include +#include - -namespace mspass::seismic -{ +namespace mspass::seismic { using namespace std; using namespace mspass::utility; -namespace py=pybind11; +namespace py = pybind11; /* * Start with all the constructors. * -*/ + */ // // Default constructor for CoreSeismogram could be // done inline in seispp.h, but it is complication enough I put // it here // -CoreSeismogram::CoreSeismogram() : BasicTimeSeries(),Metadata() -{ +CoreSeismogram::CoreSeismogram() : BasicTimeSeries(), Metadata() { /* mlive and tref are set in BasicTimeSeries so we don't use putters for them here. These three initialize Metadata properly for these attributes*/ - this->set_dt(1.0); - this->set_t0(0.0); - this->set_npts(0); - components_are_orthogonal=true; - components_are_cardinal=true; - for(int i=0; i<3; ++i) - for(int j=0; j<3; ++j) - if(i==j) - tmatrix[i][i]=1.0; - else - tmatrix[i][j]=0.0; + this->set_dt(1.0); + this->set_t0(0.0); + this->set_npts(0); + components_are_orthogonal = true; + components_are_cardinal = true; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + if (i == j) + tmatrix[i][i] = 1.0; + else + tmatrix[i][j] = 0.0; } CoreSeismogram::CoreSeismogram(const size_t nsamples) - : BasicTimeSeries(),Metadata() -{ + : BasicTimeSeries(), Metadata() { /* IMPORTANT: this constructor assumes BasicTimeSeries initializes the equivalent of: set_dt(1.0) @@ -51,460 +47,444 @@ CoreSeismogram::CoreSeismogram(const size_t nsamples) set_tref(TimeReferenceType::Relative) this->kill() - i.e. marked dead */ - this->set_npts(nsamples); // Assume this is an allocator of the 3xnsamples matrix - components_are_orthogonal=true; - components_are_cardinal=true; - for(int i=0; i<3; ++i) - for(int j=0; j<3; ++j) - if(i==j) - tmatrix[i][i]=1.0; + this->set_npts( + nsamples); // Assume this is an allocator of the 3xnsamples matrix + components_are_orthogonal = true; + components_are_cardinal = true; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + if (i == j) + tmatrix[i][i] = 1.0; else - tmatrix[i][j]=0.0; + tmatrix[i][j] = 0.0; } -CoreSeismogram::CoreSeismogram(const CoreSeismogram& t3c) : - BasicTimeSeries(dynamic_cast(t3c)), - Metadata(dynamic_cast(t3c)), - u(t3c.u) -{ - int i,j; - components_are_orthogonal=t3c.components_are_orthogonal; - components_are_cardinal=t3c.components_are_cardinal; - for(i=0; i<3; ++i) - for(j=0; j<3; ++j) tmatrix[i][j]=t3c.tmatrix[i][j]; +CoreSeismogram::CoreSeismogram(const CoreSeismogram &t3c) + : BasicTimeSeries(dynamic_cast(t3c)), + Metadata(dynamic_cast(t3c)), u(t3c.u) { + int i, j; + components_are_orthogonal = t3c.components_are_orthogonal; + components_are_cardinal = t3c.components_are_cardinal; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + tmatrix[i][j] = t3c.tmatrix[i][j]; } -bool CoreSeismogram::tmatrix_is_cardinal() -{ - /* Test for 0 or 1 to 5 figures - safe but conservative for - float input although tmatrix is double */ - double scale(10000.0); - int itest(10000); - int i,j; - for(i=0;i<3;++i) - { - for(j=0;j<3;++j) - { - int ival=static_cast(tmatrix[i][j]*scale); - if(i==j) - { - if(ival!=itest) return false; - } - else - { - if(ival!=0) return false; - } - } +bool CoreSeismogram::tmatrix_is_cardinal() { + /* Test for 0 or 1 to 5 figures - safe but conservative for + float input although tmatrix is double */ + double scale(10000.0); + int itest(10000); + int i, j; + for (i = 0; i < 3; ++i) { + for (j = 0; j < 3; ++j) { + int ival = static_cast(tmatrix[i][j] * scale); + if (i == j) { + if (ival != itest) + return false; + } else { + if (ival != 0) + return false; + } } - return true; + } + return true; } -CoreSeismogram::CoreSeismogram(const Metadata& md, - const bool load_data) : Metadata(md) -{ - string dfile, dir; - long foff; - FILE *fp; - double *inbuffer; +CoreSeismogram::CoreSeismogram(const Metadata &md, const bool load_data) + : Metadata(md) { + string dfile, dir; + long foff; + FILE *fp; + double *inbuffer; - components_are_orthogonal=true; - mlive=false; - try { - /* Names used are from mspass defintions as of Jan 2020. - We don't need to call the set methods for these attributes as they - would add the overhead of setting delta, startime, and npts to the - same value passed. */ - this->mdt = this->get_double(SEISMICMD_dt); - this->mt0 = this->get_double(SEISMICMD_t0); - if(this->is_defined(SEISMICMD_time_standard)) - { - if(this->get_string(SEISMICMD_time_standard) == "UTC") - this->set_tref(TimeReferenceType::UTC); - else - { - this->set_tref(TimeReferenceType::Relative); - /* For now we can't post an error because this is CoreSeismogram - so elog is not defined. For now let this error be silent as it - is harmless */ - /* - this->elog.log_error("CoreSeismogram Metadata constructor", - SEISMICMD_time_standard+" attribute is not defined - set to Relative", - ErrorSeverity::Complaint); - */ - } - } - if(this->time_is_relative()) - { - /* It is not an error if a t0 shift is not defined and we are - in relative time. That is the norm for active source data. */ - if(this->is_defined(SEISMICMD_t0_shift)) - { - double t0shift=this->get_double(SEISMICMD_t0_shift); - this->force_t0_shift(t0shift); - } - } - /* This section is done specially to handle interaction with MongoDB. - We store tmatrix there as a python object so we use a get_any to fetch - it. Oct 22, 2021 added a bug fix to handle tmatrix not defined. - We will take a null (undefined) tmatrix stored in the database to - imply the data are cardinal and orthogonal (i.e. stardard geographic - coordinates in e,n,z order.)*/ - if(this->is_defined("tmatrix")) - { - this->set_transformation_matrix - (boost::any_cast(this->get_any("tmatrix"))); - components_are_cardinal=this->tmatrix_is_cardinal(); - if(components_are_cardinal) - components_are_orthogonal=true; - else - components_are_orthogonal=false; //May be wrong but cost is tiny - } - else - { - /* this might not be needed but best be explicit*/ - for(auto i=0;i<3;++i) - for(auto j=0;j<3;++j) this->tmatrix[i][j] = 0.0; - for(auto i=0;i<3;++i) this->tmatrix[i][i] = 1.0; - components_are_orthogonal = true; - components_are_cardinal = true; - } - /* We have to handle nsamp specially in the case when load_data - is false. To be consistent with TimeSeries we use a feature that - if the Metadata container does not define npts we default it. - In this case that means the default constructor for u and set - nsamp to 0 (via set_npts). */ - if(md.is_defined(SEISMICMD_npts)) - { - long int ns = md.get_long(SEISMICMD_npts); - this->set_npts(ns); /* note this is assumed to initialize u*/ - } - else - { - this->set_npts(0); - } - /* Note previous code had an else clause to to with the - following conditional. It used to zero the u matrix. - The call to set_npts above will always do that so that would - have been redundant and was removed June 2022*/ - if(load_data) - { - dir = this->get_string(SEISMICMD_dir); - dfile = this->get_string(SEISMICMD_dfile); - foff = this->get_long(SEISMICMD_foff); - string fname=dir+"/"+dfile; - if((fp=fopen(fname.c_str(),"r")) == NULL) - throw(MsPASSError(string("Open failure for file ")+fname, - ErrorSeverity::Invalid)); - if (foff>0)fseek(fp,foff,SEEK_SET); - /* The older seispp code allowed byte swapping here. For - efficiency we don't support that here and assume can do a - raw fread from the file and get valid data. If support for - other types is needed this will need to be extended. Here - we just point fread at the internal u array. */ - inbuffer = this->u.get_address(0,0); - unsigned int nt=3*this->nsamp; - if(fread((void *)(inbuffer),sizeof(double),nt,fp) - != nt ) - { - fclose(fp); - throw(MsPASSError(string("CoreSeismogram constructor: fread error on file ")+fname, - ErrorSeverity::Invalid)); - } - fclose(fp); - mlive=true; - } - } catch (MsPASSError& mpe) { - throw(mpe); - } catch (boost::bad_any_cast& be) { - throw(MsPASSError(string("CoreSeismogram constructor: tmatrix type is not recognized"), - ErrorSeverity::Invalid)); - } catch (...) { - throw; - }; + components_are_orthogonal = true; + mlive = false; + try { + /* Names used are from mspass defintions as of Jan 2020. + We don't need to call the set methods for these attributes as they + would add the overhead of setting delta, startime, and npts to the + same value passed. */ + this->mdt = this->get_double(SEISMICMD_dt); + this->mt0 = this->get_double(SEISMICMD_t0); + if (this->is_defined(SEISMICMD_time_standard)) { + if (this->get_string(SEISMICMD_time_standard) == "UTC") + this->set_tref(TimeReferenceType::UTC); + else { + this->set_tref(TimeReferenceType::Relative); + /* For now we can't post an error because this is CoreSeismogram + so elog is not defined. For now let this error be silent as it + is harmless */ + /* + this->elog.log_error("CoreSeismogram Metadata constructor", + SEISMICMD_time_standard+" attribute is not defined - set to Relative", + ErrorSeverity::Complaint); + */ + } + } + if (this->time_is_relative()) { + /* It is not an error if a t0 shift is not defined and we are + in relative time. That is the norm for active source data. */ + if (this->is_defined(SEISMICMD_t0_shift)) { + double t0shift = this->get_double(SEISMICMD_t0_shift); + this->force_t0_shift(t0shift); + } + } + /* This section is done specially to handle interaction with MongoDB. + We store tmatrix there as a python object so we use a get_any to fetch + it. Oct 22, 2021 added a bug fix to handle tmatrix not defined. + We will take a null (undefined) tmatrix stored in the database to + imply the data are cardinal and orthogonal (i.e. stardard geographic + coordinates in e,n,z order.)*/ + if (this->is_defined("tmatrix")) { + this->set_transformation_matrix( + boost::any_cast(this->get_any("tmatrix"))); + components_are_cardinal = this->tmatrix_is_cardinal(); + if (components_are_cardinal) + components_are_orthogonal = true; + else + components_are_orthogonal = false; // May be wrong but cost is tiny + } else { + /* this might not be needed but best be explicit*/ + for (auto i = 0; i < 3; ++i) + for (auto j = 0; j < 3; ++j) + this->tmatrix[i][j] = 0.0; + for (auto i = 0; i < 3; ++i) + this->tmatrix[i][i] = 1.0; + components_are_orthogonal = true; + components_are_cardinal = true; + } + /* We have to handle nsamp specially in the case when load_data + is false. To be consistent with TimeSeries we use a feature that + if the Metadata container does not define npts we default it. + In this case that means the default constructor for u and set + nsamp to 0 (via set_npts). */ + if (md.is_defined(SEISMICMD_npts)) { + long int ns = md.get_long(SEISMICMD_npts); + this->set_npts(ns); /* note this is assumed to initialize u*/ + } else { + this->set_npts(0); + } + /* Note previous code had an else clause to to with the + following conditional. It used to zero the u matrix. + The call to set_npts above will always do that so that would + have been redundant and was removed June 2022*/ + if (load_data) { + dir = this->get_string(SEISMICMD_dir); + dfile = this->get_string(SEISMICMD_dfile); + foff = this->get_long(SEISMICMD_foff); + string fname = dir + "/" + dfile; + if ((fp = fopen(fname.c_str(), "r")) == NULL) + throw(MsPASSError(string("Open failure for file ") + fname, + ErrorSeverity::Invalid)); + if (foff > 0) + fseek(fp, foff, SEEK_SET); + /* The older seispp code allowed byte swapping here. For + efficiency we don't support that here and assume can do a + raw fread from the file and get valid data. If support for + other types is needed this will need to be extended. Here + we just point fread at the internal u array. */ + inbuffer = this->u.get_address(0, 0); + unsigned int nt = 3 * this->nsamp; + if (fread((void *)(inbuffer), sizeof(double), nt, fp) != nt) { + fclose(fp); + throw(MsPASSError( + string("CoreSeismogram constructor: fread error on file ") + fname, + ErrorSeverity::Invalid)); + } + fclose(fp); + mlive = true; + } + } catch (MsPASSError &mpe) { + throw(mpe); + } catch (boost::bad_any_cast &be) { + throw(MsPASSError( + string("CoreSeismogram constructor: tmatrix type is not recognized"), + ErrorSeverity::Invalid)); + } catch (...) { + throw; + }; } -CoreSeismogram::CoreSeismogram(const vector& ts, - const unsigned int component_to_clone) - : BasicTimeSeries(dynamic_cast(ts[component_to_clone])), - Metadata(dynamic_cast(ts[component_to_clone])), - u() -{ - const string base_error("CoreSeismogram constructor from 3 Time Series: "); - int i,j; - /* This is needed in case nsamp does not match s.size(0) */ - int nstest = ts[component_to_clone].s.size(); - if(nsamp!=nstest) this->nsamp=nstest; - /* this method allocates u and sets the proper metadata for npts*/ - this->CoreSeismogram::set_npts(this->nsamp); - /* beware irregular sample rates, but don' be too machevelian. - Abort only if the mismatch is large defined as accumulated time - over data range of this constructor is less than half a sample */ - if( (ts[0].dt()!=ts[1].dt()) || (ts[1].dt()!=ts[2].dt()) ) - { - double ddtmag1=fabs(ts[0].dt()-ts[1].dt()); - double ddtmag2=fabs(ts[1].dt()-ts[2].dt()); - double ddtmag; - if(ddtmag1>ddtmag1) - ddtmag=ddtmag1; - else - ddtmag=ddtmag2; - ddtmag1=fabs(ts[0].dt()-ts[2].dt()); - if(ddtmag1>ddtmag) ddtmag=ddtmag1; - double ddtcum=ddtmag*((double)ts[0].s.size()); - if(ddtcum>(ts[0].dt())/2.0) - { - stringstream ss; - ss << base_error - << "Sample intervals of components are not consistent"< &ts, + const unsigned int component_to_clone) + : BasicTimeSeries( + dynamic_cast(ts[component_to_clone])), + Metadata(dynamic_cast(ts[component_to_clone])), u() { + const string base_error("CoreSeismogram constructor from 3 Time Series: "); + int i, j; + /* This is needed in case nsamp does not match s.size(0) */ + int nstest = ts[component_to_clone].s.size(); + if (nsamp != nstest) + this->nsamp = nstest; + /* this method allocates u and sets the proper metadata for npts*/ + this->CoreSeismogram::set_npts(this->nsamp); + /* beware irregular sample rates, but don' be too machevelian. + Abort only if the mismatch is large defined as accumulated time + over data range of this constructor is less than half a sample */ + if ((ts[0].dt() != ts[1].dt()) || (ts[1].dt() != ts[2].dt())) { + double ddtmag1 = fabs(ts[0].dt() - ts[1].dt()); + double ddtmag2 = fabs(ts[1].dt() - ts[2].dt()); + double ddtmag; + if (ddtmag1 > ddtmag1) + ddtmag = ddtmag1; + else + ddtmag = ddtmag2; + ddtmag1 = fabs(ts[0].dt() - ts[2].dt()); + if (ddtmag1 > ddtmag) + ddtmag = ddtmag1; + double ddtcum = ddtmag * ((double)ts[0].s.size()); + if (ddtcum > (ts[0].dt()) / 2.0) { + stringstream ss; + ss << base_error << "Sample intervals of components are not consistent" + << endl; + for (int ie = 0; ie < 3; ++ie) + ss << "Component " << ie << " dt=" << ts[ie].dt() << " "; + ss << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); } - // temporaries to hold component values - double t0_component[3]; - double hang[3]; - double vang[3]; - // Load up these temporary arrays inside this try block and arrange to - // throw an exception if required metadata are missing - try { - /* WARNING hang and vang attributes in Metadata - are always assumed to have been read from a database where they - were stored in degrees. We convert these to radians below to - compute the transformation matrix. + } + // temporaries to hold component values + double t0_component[3]; + double hang[3]; + double vang[3]; + // Load up these temporary arrays inside this try block and arrange to + // throw an exception if required metadata are missing + try { + /* WARNING hang and vang attributes in Metadata + are always assumed to have been read from a database where they + were stored in degrees. We convert these to radians below to + compute the transformation matrix. - Feb 2021: converted to use keywords.h definitions assuming these - came from the channel collection in MongoDB - Can be changed in - kewords.h for application outside mspass */ - hang[0]=ts[0].get_double(SEISMICMD_hang); - hang[1]=ts[1].get_double(SEISMICMD_hang); - hang[2]=ts[2].get_double(SEISMICMD_hang); - vang[0]=ts[0].get_double(SEISMICMD_vang); - vang[1]=ts[1].get_double(SEISMICMD_vang); - vang[2]=ts[2].get_double(SEISMICMD_vang); - } catch (MetadataGetError& mde) - { - stringstream ss; - ss << base_error - << "missing hang or vang variable in component TimeSeries objects received"<erase(SEISMICMD_hang); - this->erase(SEISMICMD_vang); - // These are loaded just for convenience - t0_component[0]=ts[0].t0(); - t0_component[1]=ts[1].t0(); - t0_component[2]=ts[2].t0(); + Feb 2021: converted to use keywords.h definitions assuming these + came from the channel collection in MongoDB - Can be changed in + kewords.h for application outside mspass */ + hang[0] = ts[0].get_double(SEISMICMD_hang); + hang[1] = ts[1].get_double(SEISMICMD_hang); + hang[2] = ts[2].get_double(SEISMICMD_hang); + vang[0] = ts[0].get_double(SEISMICMD_vang); + vang[1] = ts[1].get_double(SEISMICMD_vang); + vang[2] = ts[2].get_double(SEISMICMD_vang); + } catch (MetadataGetError &mde) { + stringstream ss; + ss << base_error + << "missing hang or vang variable in component TimeSeries objects " + "received" + << endl; + ss << "Message posted by Metadata::get_double: " << mde.what() << endl; + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + /* We couldn't get here if hang and vang were not set on comp 0 so + we don't test for that condition. We do need to clear hang and vang + from result here, however, as both attributes are meaningless + for a 3C seismogram */ + this->erase(SEISMICMD_hang); + this->erase(SEISMICMD_vang); + // These are loaded just for convenience + t0_component[0] = ts[0].t0(); + t0_component[1] = ts[1].t0(); + t0_component[2] = ts[2].t0(); - // Treat the normal case specially and avoid a bunch of work unless - // it is required - if( (ts[0].s.size()==ts[1].s.size()) && (ts[1].s.size()==ts[2].s.size()) - && (fabs( (t0_component[0]-t0_component[1])/dt() )<1.0) - && (fabs( (t0_component[1]-t0_component[2])/dt() )<1.0)) + // Treat the normal case specially and avoid a bunch of work unless + // it is required + if ((ts[0].s.size() == ts[1].s.size()) && + (ts[1].s.size() == ts[2].s.size()) && + (fabs((t0_component[0] - t0_component[1]) / dt()) < 1.0) && + (fabs((t0_component[1] - t0_component[2]) / dt()) < 1.0)) { + /* Older code had this. No longer needed with logic above that + calls set_npts. that method creates and initialized the u dmatrix*/ + // this->u=dmatrix(3,nsamp); + // Load data by a simple copy operation + /* This is a simple loop version + for(j=0;ju=dmatrix(3,nsamp); - // Load data by a simple copy operation - /* This is a simple loop version - for(j=0;ju(0,j)=ts[0].s[j]; - this->u(1,j)=ts[1].s[j]; - this->u(2,j)=ts[2].s[j]; - } - */ - // This is a vector version that I'll use because it will - // be faster albeit infinitely more obscure and - // intrinsically more dangerous - dcopy(nsamp,&(ts[0].s[0]),1,u.get_address(0,0),3); - dcopy(nsamp,&(ts[1].s[0]),1,u.get_address(1,0),3); - dcopy(nsamp,&(ts[2].s[0]),1,u.get_address(2,0),3); + this->u(0,j)=ts[0].s[j]; + this->u(1,j)=ts[1].s[j]; + this->u(2,j)=ts[2].s[j]; } - else - { - /*Land here if the start time or number of samples - is irregular. We cut the output to latest start time to earliest end time*/ - /* WARNING - debugging may be needed for this block. SEISPP versio of this - used gaps. Here we cut the output to match an irregularities. */ - double tsmax,temin; - tsmax=max(t0_component[0],t0_component[1]); - tsmax=max(tsmax,t0_component[2]); - temin=min(ts[0].endtime(),ts[1].endtime()); - temin=min(temin,ts[2].endtime()); - nstest=(int)round((temin-tsmax)/mdt); - if(nstest<=0) - throw MsPASSError(base_error - +"Irregular time windows of components have no overlap", - ErrorSeverity::Invalid); - else - this->CoreSeismogram::set_npts(nstest); - // Now load the data. Use the time and sample number methods - // to simplify process - double t; - t=tsmax; - this->set_t0(t); - double delta=this->dt(); - for(int ic=0; ic<3; ++ic) - { - t=this->t0(); - for(j=0; j=0) && (iu(ic,j)=ts[ic].s[i]; - t += delta; - } - } - } - /* Finally we need to set the transformation matrix. - This is a direct application of conversion of routines - in spherical coordinate procedures. They are procedural - routines, not objects so the code is procedural. */ - SphericalCoordinate scor; - double *nu; - // convert all the hang values to spherical coordinate phi - // (angle from postive east) from input assumed in degrees - // azimuth from north. At the same time convert vang to radians. - for(i=0; i<3; ++i) - { - hang[i]=mspass::utility::rad(90.0-hang[i]); - vang[i]=mspass::utility::rad(vang[i]); - } - for(i=0; i<3; ++i) - { - scor.phi=hang[i]; - scor.theta=vang[i]; - nu=SphericalToUnitVector(scor); - for(j=0; j<3; ++j)tmatrix[i][j]=nu[j]; - delete [] nu; - } - components_are_cardinal = this->tmatrix_is_cardinal(); - if(components_are_cardinal) - components_are_orthogonal=true; + // This is a vector version that I'll use because it will + // be faster albeit infinitely more obscure and + // intrinsically more dangerous + dcopy(nsamp, &(ts[0].s[0]), 1, u.get_address(0, 0), 3); + dcopy(nsamp, &(ts[1].s[0]), 1, u.get_address(1, 0), 3); + dcopy(nsamp, &(ts[2].s[0]), 1, u.get_address(2, 0), 3); + } else { + /*Land here if the start time or number of samples + is irregular. We cut the output to latest start time to earliest end time*/ + /* WARNING - debugging may be needed for this block. SEISPP versio of this + used gaps. Here we cut the output to match an irregularities. */ + double tsmax, temin; + tsmax = max(t0_component[0], t0_component[1]); + tsmax = max(tsmax, t0_component[2]); + temin = min(ts[0].endtime(), ts[1].endtime()); + temin = min(temin, ts[2].endtime()); + nstest = (int)round((temin - tsmax) / mdt); + if (nstest <= 0) + throw MsPASSError( + base_error + "Irregular time windows of components have no overlap", + ErrorSeverity::Invalid); else - components_are_orthogonal=false; - /* Last but not least set the datum live before returning */ - this->set_live(); + this->CoreSeismogram::set_npts(nstest); + // Now load the data. Use the time and sample number methods + // to simplify process + double t; + t = tsmax; + this->set_t0(t); + double delta = this->dt(); + for (int ic = 0; ic < 3; ++ic) { + t = this->t0(); + for (j = 0; j < ts[ic].s.size(); ++j) { + i = ts[ic].sample_number(t); + // silently do nothing if outside bounds. This + // perhaps should be an error as it shouldn't really + // happen with the above algorithm, but safety is good + if ((i >= 0) && (i < nsamp)) + this->u(ic, j) = ts[ic].s[i]; + t += delta; + } + } + } + /* Finally we need to set the transformation matrix. + This is a direct application of conversion of routines + in spherical coordinate procedures. They are procedural + routines, not objects so the code is procedural. + */ + SphericalCoordinate scor; + double *nu; + // convert all the hang values to spherical coordinate phi + // (angle from postive east) from input assumed in degrees + // azimuth from north. At the same time convert vang to radians. + for (i = 0; i < 3; ++i) { + hang[i] = mspass::utility::rad(90.0 - hang[i]); + vang[i] = mspass::utility::rad(vang[i]); + } + for (i = 0; i < 3; ++i) { + scor.phi = hang[i]; + scor.theta = vang[i]; + nu = SphericalToUnitVector(scor); + for (j = 0; j < 3; ++j) + tmatrix[i][j] = nu[j]; + delete[] nu; + } + components_are_cardinal = this->tmatrix_is_cardinal(); + if (components_are_cardinal) + components_are_orthogonal = true; + else + components_are_orthogonal = false; + /* Last but not least set the datum live before returning */ + this->set_live(); } // Note on usage in this group of functions. The rotation algorithms used here -// all key on the BLAS for speed. That is, a transformation matrix could be done -// by using the * operator between matrix objects. +// all key on the BLAS for speed. That is, a transformation matrix could be +// done by using the * operator between matrix objects. -void CoreSeismogram::rotate_to_standard() -{ - if( (u.size()[1]<=0) || this->dead()) return; // do nothing in these situations - double *work[3]; - int i,j; - if(components_are_cardinal) return; - /* We assume nsamp is the number of samples = number of columns in u - we don't - check here for efficiency */ - for(j=0; j<3; ++j) work[j]=new double[nsamp]; - if(components_are_orthogonal) - { - // - //Use a daxpy algorithm. tmatrix stores the - //forward transformation used to get current - //Use the transpose to get back - // - for(i=0; i<3; ++i) - { - // x has a stride of 3 because we store in fortran order in x - dcopy(nsamp,u.get_address(0,0),3,work[i],1); - dscal(nsamp,tmatrix[0][i],work[i],1); - daxpy(nsamp,tmatrix[1][i],u.get_address(1,0),3,work[i],1); - daxpy(nsamp,tmatrix[2][i],u.get_address(2,0),3,work[i],1); - } - for(i=0; i<3; ++i) dcopy(nsamp,work[i],1,u.get_address(i,0),3); +void CoreSeismogram::rotate_to_standard() { + if ((u.size()[1] <= 0) || this->dead()) + return; // do nothing in these situations + double *work[3]; + int i, j; + if (components_are_cardinal) + return; + /* We assume nsamp is the number of samples = number of columns in u - we + don't check here for efficiency */ + for (j = 0; j < 3; ++j) + work[j] = new double[nsamp]; + if (components_are_orthogonal) { + // + // Use a daxpy algorithm. tmatrix stores the + // forward transformation used to get current + // Use the transpose to get back + // + for (i = 0; i < 3; ++i) { + // x has a stride of 3 because we store in fortran order in x + dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1); + dscal(nsamp, tmatrix[0][i], work[i], 1); + daxpy(nsamp, tmatrix[1][i], u.get_address(1, 0), 3, work[i], 1); + daxpy(nsamp, tmatrix[2][i], u.get_address(2, 0), 3, work[i], 1); + } + for (i = 0; i < 3; ++i) + dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3); + } else { + // + // Enter here only when the transformation matrix is + // not orthogonal. We have to construct a fortran + // order matrix a to use LINPACK routine in sunperf/perf + // This could be done with the matrix template library + // but the overhead ain't worth it + // + double a[9]; + int ipivot[3]; + int info; + a[0] = tmatrix[0][0]; + a[1] = tmatrix[1][0]; + a[2] = tmatrix[2][0]; + a[3] = tmatrix[0][1]; + a[4] = tmatrix[1][1]; + a[5] = tmatrix[2][1]; + a[6] = tmatrix[0][2]; + a[7] = tmatrix[1][2]; + a[8] = tmatrix[2][2]; + // LAPACK routine with FORTRAN interface using pass by reference and + // pointers + int three(3); + dgetrf(three, three, a, three, ipivot, info); + if (info != 0) { + for (i = 0; i < 3; ++i) + delete[] work[i]; + throw(MsPASSError(string("rotate_to_standard: LU factorization of " + "transformation matrix failed"), + ErrorSeverity::Invalid)); + } + // inversion routine after factorization from lapack FORT$RAN interface + double awork[10]; // Larger than required but safety value small cost + int ldwork(10); + dgetri(three, a, three, ipivot, awork, ldwork, info); + // This is the openblas version + // info=LAPACKE_dgetri(LAPACK_COL_MAJOR,3,a,3,ipivot); + if (info != 0) { + for (i = 0; i < 3; ++i) + delete[] work[i]; + throw(MsPASSError(string("rotate_to_standard: LU factorization " + "inversion of transformation matrix failed"), + ErrorSeverity::Invalid)); } - else - { - // - //Enter here only when the transformation matrix is - //not orthogonal. We have to construct a fortran - //order matrix a to use LINPACK routine in sunperf/perf - //This could be done with the matrix template library - //but the overhead ain't worth it - // - double a[9]; - int ipivot[3]; - int info; - a[0] = tmatrix[0][0]; - a[1] = tmatrix[1][0]; - a[2] = tmatrix[2][0]; - a[3] = tmatrix[0][1]; - a[4] = tmatrix[1][1]; - a[5] = tmatrix[2][1]; - a[6] = tmatrix[0][2]; - a[7] = tmatrix[1][2]; - a[8] = tmatrix[2][2]; - //LAPACK routine with FORTRAN interface using pass by reference and pointers - int three(3); - dgetrf(three,three,a,three,ipivot,info); - if(info!=0) - { - for(i=0; i<3; ++i) delete [] work[i]; - throw(MsPASSError( - string("rotate_to_standard: LU factorization of transformation matrix failed"), - ErrorSeverity::Invalid)); - } - // inversion routine after factorization from lapack FORT$RAN interface - double awork[10]; //Larger than required but safety value small cost - int ldwork(10); - dgetri(three,a,three,ipivot,awork,ldwork,info); - // This is the openblas version - //info=LAPACKE_dgetri(LAPACK_COL_MAJOR,3,a,3,ipivot); - if(info!=0) - { - for(i=0; i<3; ++i) delete [] work[i]; - throw(MsPASSError( - string("rotate_to_standard: LU factorization inversion of transformation matrix failed"), - ErrorSeverity::Invalid)); - } - tmatrix[0][0] = a[0]; - tmatrix[1][0] = a[1]; - tmatrix[2][0] = a[2]; - tmatrix[0][1] = a[3]; - tmatrix[1][1] = a[4]; - tmatrix[2][1] = a[5]; - tmatrix[0][2] = a[6]; - tmatrix[1][2] = a[7]; - tmatrix[2][2] = a[8]; - /* The inverse is now in tmatrix so we reverse the - rows and columms from above loop */ + tmatrix[0][0] = a[0]; + tmatrix[1][0] = a[1]; + tmatrix[2][0] = a[2]; + tmatrix[0][1] = a[3]; + tmatrix[1][1] = a[4]; + tmatrix[2][1] = a[5]; + tmatrix[0][2] = a[6]; + tmatrix[1][2] = a[7]; + tmatrix[2][2] = a[8]; + /* The inverse is now in tmatrix so we reverse the + rows and columms from above loop */ - for(i=0; i<3; ++i) - { - dcopy(nsamp,u.get_address(0,0),3,work[i],1); - dscal(nsamp,tmatrix[i][0],work[i],1); - daxpy(nsamp,tmatrix[i][1],u.get_address(1,0),3,work[i],1); - daxpy(nsamp,tmatrix[i][2],u.get_address(2,0),3,work[i],1); - } - for(i=0; i<3; ++i) dcopy(nsamp,work[i],1,u.get_address(i,0),3); - components_are_orthogonal = true; + for (i = 0; i < 3; ++i) { + dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1); + dscal(nsamp, tmatrix[i][0], work[i], 1); + daxpy(nsamp, tmatrix[i][1], u.get_address(1, 0), 3, work[i], 1); + daxpy(nsamp, tmatrix[i][2], u.get_address(2, 0), 3, work[i], 1); } - // - //Have to set the transformation matrix to an identity now - // - for(i=0; i<3; ++i) - for(j=0; j<3; ++j) - if(i==j) - tmatrix[i][i]=1.0; - else - tmatrix[i][j]=0.0; + for (i = 0; i < 3; ++i) + dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3); + components_are_orthogonal = true; + } + // + // Have to set the transformation matrix to an identity now + // + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + if (i == j) + tmatrix[i][i] = 1.0; + else + tmatrix[i][j] = 0.0; - components_are_cardinal=true; - for(i=0; i<3; ++i) delete [] work[i]; + components_are_cardinal = true; + for (i = 0; i < 3; ++i) + delete[] work[i]; } - /* This routine takes a spherical coordinate vector that defines a given direction in space and returns a transformation matrix that should be viewed as a transformation to ray coordinates under an @@ -532,179 +512,182 @@ return an identity matrix because there is no way to determine a horizontal rotation direction. Arguments: - xsc - spherical coordinate structure defining unit vector used - to define the transform (radius is ignored). Angles - are assumed in radians. + xsc - spherical coordinate structure defining unit vector used + to define the transform (radius is ignored). Angles + are assumed in radians. Author: Gary L. Pavlis Written: Sept. 1999 Modified: Feb 2003 Original was plain C. Adapted to C++ for seismic processing */ -void CoreSeismogram::rotate(SphericalCoordinate& xsc) -{ - if( (u.size()[1]<=0) || dead()) return; // do nothing in these situations +void CoreSeismogram::rotate(SphericalCoordinate &xsc) { + if ((u.size()[1] <= 0) || dead()) + return; // do nothing in these situations - //Earlier version had a reset of the nsamp variable here - we need to trust - //that is correct here for efficiency. We the new API it would be hard - //to have that happen. without a serious blunder - int i; - double theta, phi; /* corrected angles after dealing with signs */ - double a,b,c,d; + // Earlier version had a reset of the nsamp variable here - we need to trust + // that is correct here for efficiency. We the new API it would be hard + // to have that happen. without a serious blunder + int i; + double theta, phi; /* corrected angles after dealing with signs */ + double a, b, c, d; - // - //Undo any previous transformations - // - this->rotate_to_standard(); - if(xsc.theta == M_PI) - { - //This will be left handed - tmatrix[2][2] = -1.0; - dscal(nsamp,-1.0,u.get_address(2,0),3); - return; - } + // + // Undo any previous transformations + // + this->rotate_to_standard(); + if (xsc.theta == M_PI) { + // This will be left handed + tmatrix[2][2] = -1.0; + dscal(nsamp, -1.0, u.get_address(2, 0), 3); + return; + } - if(xsc.theta < 0.0) - { - theta = -(xsc.theta); - phi = xsc.phi + M_PI; - if(phi > M_PI) phi -= (2.0*M_PI); - } - else if(xsc.theta > M_PI) - { - theta = xsc.theta - M_PI; - phi = xsc.phi + M_PI; - if(phi > M_PI) phi -= (2.0*M_PI); - } - else - { - theta = xsc.theta; - phi = xsc.phi; - } - /* Am using a formula here for azimuth with is pi/2 - phi*/ - double azimuth=M_PI_2-phi; - a = cos(azimuth); - b = sin(azimuth); - c = cos(theta); - d = sin(theta); + if (xsc.theta < 0.0) { + theta = -(xsc.theta); + phi = xsc.phi + M_PI; + if (phi > M_PI) + phi -= (2.0 * M_PI); + } else if (xsc.theta > M_PI) { + theta = xsc.theta - M_PI; + phi = xsc.phi + M_PI; + if (phi > M_PI) + phi -= (2.0 * M_PI); + } else { + theta = xsc.theta; + phi = xsc.phi; + } + /* Am using a formula here for azimuth with is pi/2 - phi*/ + double azimuth = M_PI_2 - phi; + a = cos(azimuth); + b = sin(azimuth); + c = cos(theta); + d = sin(theta); - tmatrix[0][0] = a; - tmatrix[1][0] = b*c; - tmatrix[2][0] = b*d; - tmatrix[0][1] = -b; - tmatrix[1][1] = a*c; - tmatrix[2][1] = a*d; - tmatrix[0][2] = 0.0; - tmatrix[1][2] = -d; - tmatrix[2][2] = c; + tmatrix[0][0] = a; + tmatrix[1][0] = b * c; + tmatrix[2][0] = b * d; + tmatrix[0][1] = -b; + tmatrix[1][1] = a * c; + tmatrix[2][1] = a * d; + tmatrix[0][2] = 0.0; + tmatrix[1][2] = -d; + tmatrix[2][2] = c; - /* Now multiply the data by this transformation matrix. */ - double *work[3]; - for(i=0; i<3; ++i)work[i] = new double[nsamp]; - for(i=0; i<3; ++i) - { - dcopy(nsamp,u.get_address(0,0),3,work[i],1); - dscal(nsamp,tmatrix[i][0],work[i],1); - daxpy(nsamp,tmatrix[i][1],u.get_address(1,0),3,work[i],1); - daxpy(nsamp,tmatrix[i][2],u.get_address(2,0),3,work[i],1); - } - for(i=0; i<3; ++i) dcopy(nsamp,work[i],1,u.get_address(i,0),3); - components_are_cardinal=false; - for(i=0; i<3; ++i) delete [] work[i]; + /* Now multiply the data by this transformation matrix. */ + double *work[3]; + for (i = 0; i < 3; ++i) + work[i] = new double[nsamp]; + for (i = 0; i < 3; ++i) { + dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1); + dscal(nsamp, tmatrix[i][0], work[i], 1); + daxpy(nsamp, tmatrix[i][1], u.get_address(1, 0), 3, work[i], 1); + daxpy(nsamp, tmatrix[i][2], u.get_address(2, 0), 3, work[i], 1); + } + for (i = 0; i < 3; ++i) + dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3); + components_are_cardinal = false; + for (i = 0; i < 3; ++i) + delete[] work[i]; } -void CoreSeismogram::rotate(const double nu[3]) -{ - if( (u.size()[1]<=0) || this->dead()) return; // do nothing in these situations - SphericalCoordinate xsc=UnitVectorToSpherical(nu); - this->rotate(xsc); +void CoreSeismogram::rotate(const double nu[3]) { + if ((u.size()[1] <= 0) || this->dead()) + return; // do nothing in these situations + SphericalCoordinate xsc = UnitVectorToSpherical(nu); + this->rotate(xsc); } /* simplified procedure to rotate only zonal angle by phi radians. Similar to above but using only azimuth angle AND doing a simple rotation in the horizontal plane. Efficient algorithm only - alters 0 and 1 components. + alters 0 and 1 components. Note sign is spherical coordinate form with phi positive anticlockwise. -Sign in rotate with spherical coordinate is different because phi is +Sign in rotate with spherical coordinate is different because phi is converted to azimuth. Note that is just the sign of b in the code.*/ -void CoreSeismogram::rotate(double phi) -{ - if( (u.size()[1]<=0) || dead()) return; // do nothing in these situations - int i,j,k; - double a,b; - a=cos(phi); - b=sin(phi); - double tmnew[3][3]; - tmnew[0][0] = a; - tmnew[1][0] = -b; - tmnew[2][0] = 0.0; - tmnew[0][1] = b; - tmnew[1][1] = a; - tmnew[2][1] = 0.0; - tmnew[0][2] = 0.0; - tmnew[1][2] = 0.0; - tmnew[2][2] = 1.0; +void CoreSeismogram::rotate(double phi) { + if ((u.size()[1] <= 0) || dead()) + return; // do nothing in these situations + int i, j, k; + double a, b; + a = cos(phi); + b = sin(phi); + double tmnew[3][3]; + tmnew[0][0] = a; + tmnew[1][0] = -b; + tmnew[2][0] = 0.0; + tmnew[0][1] = b; + tmnew[1][1] = a; + tmnew[2][1] = 0.0; + tmnew[0][2] = 0.0; + tmnew[1][2] = 0.0; + tmnew[2][2] = 1.0; - /* Now multiply the data by this transformation matrix. - Note trick in this i only goes to 2 because 3 component - is an identity.*/ - double *work[2]; - for(i=0; i<2; ++i)work[i] = new double[nsamp]; - for(i=0; i<2; ++i) - { - dcopy(nsamp,u.get_address(0,0),3,work[i],1); - dscal(nsamp,tmnew[i][0],work[i],1); - daxpy(nsamp,tmnew[i][1],u.get_address(1,0),3,work[i],1); + /* Now multiply the data by this transformation matrix. + Note trick in this i only goes to 2 because 3 component + is an identity.*/ + double *work[2]; + for (i = 0; i < 2; ++i) + work[i] = new double[nsamp]; + for (i = 0; i < 2; ++i) { + dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1); + dscal(nsamp, tmnew[i][0], work[i], 1); + daxpy(nsamp, tmnew[i][1], u.get_address(1, 0), 3, work[i], 1); + } + for (i = 0; i < 2; ++i) + dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3); + double tm_tmp[3][3]; + double prod; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) { + for (prod = 0.0, k = 0; k < 3; ++k) + prod += tmnew[i][k] * tmatrix[k][j]; + tm_tmp[i][j] = prod; } - for(i=0; i<2; ++i) dcopy(nsamp,work[i],1,u.get_address(i,0),3); - double tm_tmp[3][3]; - double prod; - for(i=0; i<3; ++i) - for(j=0; j<3; ++j) - { - for(prod=0.0,k=0; k<3; ++k) - prod+=tmnew[i][k]*tmatrix[k][j]; - tm_tmp[i][j]=prod; - } - for(i=0; i<3; ++i) - for(j=0; j<3; ++j)tmatrix[i][j]=tm_tmp[i][j]; - components_are_cardinal=false; - for(i=0; i<2; ++i) delete [] work[i]; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + tmatrix[i][j] = tm_tmp[i][j]; + components_are_cardinal = false; + for (i = 0; i < 2; ++i) + delete[] work[i]; } -void CoreSeismogram::transform(const double a[3][3]) -{ - if( (u.size()[1]<=0) || dead()) return; // do nothing in these situations - /* Older version had this - we need to trust ns is already u.columns(). */ - //size_t ns = u.size()[1]; - size_t i,j,k; - double *work[3]; - for(i=0; i<3; ++i) work[i] = new double[nsamp]; - for(i=0; i<3; ++i) - { - dcopy(nsamp,u.get_address(0,0),3,work[i],1); - dscal(nsamp,a[i][0],work[i],1); - daxpy(nsamp,a[i][1],u.get_address(1,0),3,work[i],1); - daxpy(nsamp,a[i][2],u.get_address(2,0),3,work[i],1); +void CoreSeismogram::transform(const double a[3][3]) { + if ((u.size()[1] <= 0) || dead()) + return; // do nothing in these situations + /* Older version had this - we need to trust ns is already u.columns(). */ + // size_t ns = u.size()[1]; + size_t i, j, k; + double *work[3]; + for (i = 0; i < 3; ++i) + work[i] = new double[nsamp]; + for (i = 0; i < 3; ++i) { + dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1); + dscal(nsamp, a[i][0], work[i], 1); + daxpy(nsamp, a[i][1], u.get_address(1, 0), 3, work[i], 1); + daxpy(nsamp, a[i][2], u.get_address(2, 0), 3, work[i], 1); + } + for (i = 0; i < 3; ++i) + dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3); + for (i = 0; i < 3; ++i) + delete[] work[i]; + /* Hand code this rather than use dmatrix or other library. + Probably dumb, but this is just a 3x3 system. This + is simply a multiply of a*tmatrix with result replacing + the internal tmatrix */ + double tmnew[3][3]; + double prod; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) { + for (prod = 0.0, k = 0; k < 3; ++k) + prod += a[i][k] * tmatrix[k][j]; + tmnew[i][j] = prod; } - for(i=0; i<3; ++i) dcopy(nsamp,work[i],1,u.get_address(i,0),3); - for(i=0; i<3; ++i) delete [] work[i]; - /* Hand code this rather than use dmatrix or other library. - Probably dumb, but this is just a 3x3 system. This - is simply a multiply of a*tmatrix with result replacing - the internal tmatrix */ - double tmnew[3][3]; - double prod; - for(i=0; i<3; ++i) - for(j=0; j<3; ++j) - { - for(prod=0.0,k=0; k<3; ++k) - prod+=a[i][k]*tmatrix[k][j]; - tmnew[i][j]=prod; - } - for(i=0; i<3; ++i) - for(j=0; j<3; ++j)tmatrix[i][j]=tmnew[i][j]; - components_are_cardinal = this->tmatrix_is_cardinal(); - /* Assume this method does not yield cartesian coordinate directions.*/ - if(!components_are_cardinal) components_are_orthogonal=false; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + tmatrix[i][j] = tmnew[i][j]; + components_are_cardinal = this->tmatrix_is_cardinal(); + /* Assume this method does not yield cartesian coordinate directions.*/ + if (!components_are_cardinal) + components_are_orthogonal = false; } /* This function computes and applies the free surface tranformaton matrix described by Kennett 1991. The result is a ray coordinate @@ -720,426 +703,408 @@ translation of m file from Michael Bostock. Author: Gary Pavlis */ -void CoreSeismogram::free_surface_transformation(SlownessVector uvec, - double a0, double b0) -{ - if( (u.size()[1]<=0) || dead()) return; // do nothing in these situations - double a02,b02,pslow,p2; - double qa,qb,vpz,vpr,vsr,vsz; - pslow=uvec.mag(); - // silently do nothing if magnitude of the slowness vector is 0 - // (vertical incidence) - if(pslowrotate(scor); + // First the horizonal rotation + SphericalCoordinate scor; + // rotation angle is - azimuth to put x2 (north in standard coord) + // in radial direction + scor.phi = atan2(uvec.uy, uvec.ux); + scor.theta = 0.0; + scor.radius = 1.0; + // after this transformation x1=transverse horizontal + // x2=radial horizonal, and x3 is still vertical + this->rotate(scor); - a02=a0*a0; - b02=b0*b0; - p2=pslow*pslow; - qa=sqrt((1.0/a02)-p2); - qb=sqrt((1.0/b02)-p2); - vpz=-(1.0-2.0*b02*p2)/(2.0*a0*qa); - vpr=pslow*b02/a0; - vsr=(1.0-2.0*b02*p2)/(2.0*b0*qb); - vsz=pslow*b0; - /* Now construct the transformation matrix - This is different from Bostock's original code - in sign and order. Also note this transformation - is not scaled to have a unit matrix norm so amplitudes - after the transformation are distorted. rotate_to_standard, - however, should still restore original data within roundoff - error if called on the result. */ - double fstran[3][3]; - fstran[0][0]=0.5; - fstran[0][1]=0.0; - fstran[0][2]=0.0; - fstran[1][0]=0.0; - fstran[1][1]=vsr; - fstran[1][2]=vpr; - fstran[2][0]=0.0; - fstran[2][1]=-vsz; - fstran[2][2]=-vpz; - this->transform(fstran); + a02 = a0 * a0; + b02 = b0 * b0; + p2 = pslow * pslow; + qa = sqrt((1.0 / a02) - p2); + qb = sqrt((1.0 / b02) - p2); + vpz = -(1.0 - 2.0 * b02 * p2) / (2.0 * a0 * qa); + vpr = pslow * b02 / a0; + vsr = (1.0 - 2.0 * b02 * p2) / (2.0 * b0 * qb); + vsz = pslow * b0; + /* Now construct the transformation matrix + This is different from Bostock's original code + in sign and order. Also note this transformation + is not scaled to have a unit matrix norm so amplitudes + after the transformation are distorted. rotate_to_standard, + however, should still restore original data within roundoff + error if called on the result. */ + double fstran[3][3]; + fstran[0][0] = 0.5; + fstran[0][1] = 0.0; + fstran[0][2] = 0.0; + fstran[1][0] = 0.0; + fstran[1][1] = vsr; + fstran[1][2] = vpr; + fstran[2][0] = 0.0; + fstran[2][1] = -vsz; + fstran[2][2] = -vpz; + this->transform(fstran); - components_are_cardinal=false; - components_are_orthogonal=false; + components_are_cardinal = false; + components_are_orthogonal = false; } -bool CoreSeismogram::set_transformation_matrix(const dmatrix& A) -{ - for(int i=0;i<3;++i) - for(int j=0;j<3;++j) tmatrix[i][j]=A(i,j); - py::list tmatrix_l; - for(int i=0;i<3;++i) - for(int j=0;j<3;++j) tmatrix_l.append(A(i, j)); - this->put_object(SEISMICMD_tmatrix, tmatrix_l); - bool cardinal; - cardinal=this->tmatrix_is_cardinal(); - if(cardinal) - { - components_are_cardinal=true; - components_are_orthogonal=true; - } - else - { - components_are_cardinal=false; - /* Not necessarily true, but small overhead cost*/ - components_are_orthogonal=false; - } - return components_are_cardinal; +bool CoreSeismogram::set_transformation_matrix(const dmatrix &A) { + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + tmatrix[i][j] = A(i, j); + py::list tmatrix_l; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + tmatrix_l.append(A(i, j)); + this->put_object(SEISMICMD_tmatrix, tmatrix_l); + bool cardinal; + cardinal = this->tmatrix_is_cardinal(); + if (cardinal) { + components_are_cardinal = true; + components_are_orthogonal = true; + } else { + components_are_cardinal = false; + /* Not necessarily true, but small overhead cost*/ + components_are_orthogonal = false; + } + return components_are_cardinal; } -bool CoreSeismogram::set_transformation_matrix(const double a[3][3]) -{ - for(int i=0;i<3;++i) - for(int j=0;j<3;++j) tmatrix[i][j]=a[i][j]; - py::list tmatrix_l; - for(int i=0;i<3;++i) - for(int j=0;j<3;++j) tmatrix_l.append(a[i][j]); - this->put_object(SEISMICMD_tmatrix, tmatrix_l); - bool cardinal; - cardinal=this->tmatrix_is_cardinal(); - if(cardinal) - { - components_are_cardinal=true; - components_are_orthogonal=true; - } - else - { - components_are_cardinal=false; - /* Not necessarily true, but small overhead cost*/ - components_are_orthogonal=false; - } - return components_are_cardinal; +bool CoreSeismogram::set_transformation_matrix(const double a[3][3]) { + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + tmatrix[i][j] = a[i][j]; + py::list tmatrix_l; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + tmatrix_l.append(a[i][j]); + this->put_object(SEISMICMD_tmatrix, tmatrix_l); + bool cardinal; + cardinal = this->tmatrix_is_cardinal(); + if (cardinal) { + components_are_cardinal = true; + components_are_orthogonal = true; + } else { + components_are_cardinal = false; + /* Not necessarily true, but small overhead cost*/ + components_are_orthogonal = false; + } + return components_are_cardinal; } -bool CoreSeismogram::set_transformation_matrix(py::object tmatrix_py) -{ - if(py::isinstance(tmatrix_py)) { - auto tmatrix_ary = tmatrix_py.cast>(); - py::buffer_info info = tmatrix_ary.request(); - if ((info.ndim == 2 && info.shape[0]*info.shape[1] == 9) || - (info.ndim == 1 && info.shape[0] == 9) - ) - return this->set_transformation_matrix(static_cast(info.ptr)); - else - throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a 3x3 matrix"), - ErrorSeverity::Invalid)); - } else if (py::isinstance(tmatrix_py)) { - auto tmatrix_ary = tmatrix_py.cast(); - if (tmatrix_ary.rows() != 3 || tmatrix_ary.columns() != 3) - throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a 3x3 matrix"), - ErrorSeverity::Invalid)); - return this->set_transformation_matrix(tmatrix_ary); - } else if (py::isinstance(tmatrix_py)) { - dmatrix tmatrix_ary(3,3); - double* ptr = tmatrix_ary.get_address(0,0); - if(py::len(tmatrix_py) == 9) { - int i = 0; - for (auto item : tmatrix_py) { - try{ - *(ptr+i) = item.cast(); - i++; - } catch (...) { - throw(MsPASSError(string("set_transformation_matrix: the elements of tmatrix should be float"), +bool CoreSeismogram::set_transformation_matrix(py::object tmatrix_py) { + if (py::isinstance(tmatrix_py)) { + auto tmatrix_ary = tmatrix_py.cast< + py::array_t>(); + py::buffer_info info = tmatrix_ary.request(); + if ((info.ndim == 2 && info.shape[0] * info.shape[1] == 9) || + (info.ndim == 1 && info.shape[0] == 9)) + return this->set_transformation_matrix( + static_cast(info.ptr)); + else + throw(MsPASSError( + string("set_transformation_matrix: tmatrix should be a 3x3 matrix"), + ErrorSeverity::Invalid)); + } else if (py::isinstance(tmatrix_py)) { + auto tmatrix_ary = tmatrix_py.cast(); + if (tmatrix_ary.rows() != 3 || tmatrix_ary.columns() != 3) + throw(MsPASSError( + string("set_transformation_matrix: tmatrix should be a 3x3 matrix"), + ErrorSeverity::Invalid)); + return this->set_transformation_matrix(tmatrix_ary); + } else if (py::isinstance(tmatrix_py)) { + dmatrix tmatrix_ary(3, 3); + double *ptr = tmatrix_ary.get_address(0, 0); + if (py::len(tmatrix_py) == 9) { + int i = 0; + for (auto item : tmatrix_py) { + try { + *(ptr + i) = item.cast(); + i++; + } catch (...) { + throw(MsPASSError(string("set_transformation_matrix: the elements of " + "tmatrix should be float"), ErrorSeverity::Invalid)); - } - } - } else if(py::len(tmatrix_py) == 3) { - int i = 0; - for (auto items : tmatrix_py) { - if(!py::isinstance(items)) - throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a 3x3 list of list"), + } + } + } else if (py::len(tmatrix_py) == 3) { + int i = 0; + for (auto items : tmatrix_py) { + if (!py::isinstance(items)) + throw(MsPASSError(string("set_transformation_matrix: tmatrix should " + "be a 3x3 list of list"), ErrorSeverity::Invalid)); - else if (py::len(items) != 3) - throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a 3x3 list of list"), + else if (py::len(items) != 3) + throw(MsPASSError(string("set_transformation_matrix: tmatrix should " + "be a 3x3 list of list"), ErrorSeverity::Invalid)); - else { - for (auto item : items) { - try{ - *(ptr+i) = item.cast(); - i++; - } catch (...) { - throw(MsPASSError(string("set_transformation_matrix: the elements of tmatrix should be float"), + else { + for (auto item : items) { + try { + *(ptr + i) = item.cast(); + i++; + } catch (...) { + throw(MsPASSError(string("set_transformation_matrix: the " + "elements of tmatrix should be float"), ErrorSeverity::Invalid)); - } - } - } } - } else { - throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a list of 9 floats or a 3x3 list of list"), - ErrorSeverity::Invalid)); + } } - return this->set_transformation_matrix(tr(tmatrix_ary)); + } } else { - throw(MsPASSError(string("set_transformation_matrix: tmatrix's type is not recognized"), - ErrorSeverity::Invalid)); + throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a " + "list of 9 floats or a 3x3 list of list"), + ErrorSeverity::Invalid)); } + return this->set_transformation_matrix(tr(tmatrix_ary)); + } else { + throw(MsPASSError( + string("set_transformation_matrix: tmatrix's type is not recognized"), + ErrorSeverity::Invalid)); + } } -CoreSeismogram& CoreSeismogram::operator=(const CoreSeismogram& seisin) -{ - if(this!=&seisin) - { - this->BasicTimeSeries::operator=(seisin); - this->Metadata::operator=(seisin); - components_are_orthogonal=seisin.components_are_orthogonal; - components_are_cardinal=seisin.components_are_cardinal; - for(int i=0; i<3; ++i) - { - for(int j=0; j<3; ++j) - { - tmatrix[i][j]=seisin.tmatrix[i][j]; - } - } - u=seisin.u; +CoreSeismogram &CoreSeismogram::operator=(const CoreSeismogram &seisin) { + if (this != &seisin) { + this->BasicTimeSeries::operator=(seisin); + this->Metadata::operator=(seisin); + components_are_orthogonal = seisin.components_are_orthogonal; + components_are_cardinal = seisin.components_are_cardinal; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + tmatrix[i][j] = seisin.tmatrix[i][j]; + } } - return(*this); + u = seisin.u; + } + return (*this); } -CoreSeismogram& CoreSeismogram::operator*=(const double scale) -{ +CoreSeismogram &CoreSeismogram::operator*=(const double scale) { /* do nothing to empty data or data marked dead*/ - if((this->npts()==0) || (this->dead())) return(*this); + if ((this->npts() == 0) || (this->dead())) + return (*this); /* We can use this dscal blas function because dmatrix puts all the data in a continguous block. Beware if there is am implementation change for the matrix data*/ - double *ptr=this->u.get_address(0,0); - dscal(3*this->npts(),scale,ptr,1); - return(*this); + double *ptr = this->u.get_address(0, 0); + dscal(3 * this->npts(), scale, ptr, 1); + return (*this); } -CoreSeismogram& CoreSeismogram::operator+=(const CoreSeismogram& d) -{ - int i,iend,jend; - size_t j,i0,j0; - // Silently do nothing if d or lhs is marked dead - if( d.dead() || (this->dead()) ) return(*this); - // Silently do nothing if d does not overlap with data to contain sum - if( (d.endtime()(this->endtime())) ) return(*this); - if(d.tref!=(this->tref)) - throw MsPASSError("CoreSeismogram += operator cannot handle data with inconsistent time base\n", - ErrorSeverity::Invalid); - /* this defines the range of left and right hand sides to be summed */ - i=d.sample_number(this->mt0); - if(i<0) - { - j0=this->sample_number(d.t0()); - i0=0; - } - else - { - j0=0; - i0=i; - } - iend=d.sample_number(this->endtime()); - jend=this->sample_number(d.endtime()); - if(iend>=(d.npts())) - { - iend=d.npts()-1; - } - if(jend>=this->npts()) - { - jend=this->npts()-1; - } - for(i=i0,j=j0; i<=iend && j<=jend; ++i,++j) - { - this->u(0,j)+=d.u(0,i); - this->u(1,j)+=d.u(1,i); - this->u(2,j)+=d.u(2,i); - } - return(*this); +CoreSeismogram &CoreSeismogram::operator+=(const CoreSeismogram &d) { + int i, iend, jend; + size_t j, i0, j0; + // Silently do nothing if d or lhs is marked dead + if (d.dead() || (this->dead())) + return (*this); + // Silently do nothing if d does not overlap with data to contain sum + if ((d.endtime() < mt0) || (d.mt0 > (this->endtime()))) + return (*this); + if (d.tref != (this->tref)) + throw MsPASSError("CoreSeismogram += operator cannot handle data with " + "inconsistent time base\n", + ErrorSeverity::Invalid); + /* this defines the range of left and right hand sides to be summed */ + i = d.sample_number(this->mt0); + if (i < 0) { + j0 = this->sample_number(d.t0()); + i0 = 0; + } else { + j0 = 0; + i0 = i; + } + iend = d.sample_number(this->endtime()); + jend = this->sample_number(d.endtime()); + if (iend >= (d.npts())) { + iend = d.npts() - 1; + } + if (jend >= this->npts()) { + jend = this->npts() - 1; + } + for (i = i0, j = j0; i <= iend && j <= jend; ++i, ++j) { + this->u(0, j) += d.u(0, i); + this->u(1, j) += d.u(1, i); + this->u(2, j) += d.u(2, i); + } + return (*this); } /* IMPORTANT: this code is absolutely identical to that for operator+= except the += in the last loop becomes -=. Any changes in operator+= must have exactly the same change here (other than a message with a tag to the function)*/ -CoreSeismogram& CoreSeismogram::operator-=(const CoreSeismogram& data) -{ - int i,iend,jend; - size_t j,i0,j0; - // Sun's compiler complains about const objects without this. - CoreSeismogram& d=const_cast(data); - // Silently do nothing if d is marked dead - if(!d.mlive) return(*this); - // Silently do nothing if d does not overlap with data to contain sum - if( (d.endtime()(this->endtime())) ) return(*this); - if(d.tref!=(this->tref)) - throw MsPASSError("CoreSeismogram += operator cannot handle data with inconsistent time base\n", - ErrorSeverity::Invalid); - /* this defines the range of left and right hand sides to be summed */ - i=d.sample_number(this->mt0); - if(i<0) - { - j0=this->sample_number(d.t0()); - i0=0; - } - else - { - j0=0; - i0=i; - } - iend=d.sample_number(this->endtime()); - jend=this->sample_number(d.endtime()); - if(iend>=(d.npts())) - { - iend=d.npts()-1; - } - if(jend>=this->npts()) - { - jend=this->npts()-1; - } - for(i=i0,j=j0; i<=iend && j<=jend; ++i,++j) - { - this->u(0,j)-=d.u(0,i); - this->u(1,j)-=d.u(1,i); - this->u(2,j)-=d.u(2,i); - } - return(*this); +CoreSeismogram &CoreSeismogram::operator-=(const CoreSeismogram &data) { + int i, iend, jend; + size_t j, i0, j0; + // Sun's compiler complains about const objects without this. + CoreSeismogram &d = const_cast(data); + // Silently do nothing if d is marked dead + if (!d.mlive) + return (*this); + // Silently do nothing if d does not overlap with data to contain sum + if ((d.endtime() < mt0) || (d.mt0 > (this->endtime()))) + return (*this); + if (d.tref != (this->tref)) + throw MsPASSError("CoreSeismogram += operator cannot handle data with " + "inconsistent time base\n", + ErrorSeverity::Invalid); + /* this defines the range of left and right hand sides to be summed */ + i = d.sample_number(this->mt0); + if (i < 0) { + j0 = this->sample_number(d.t0()); + i0 = 0; + } else { + j0 = 0; + i0 = i; + } + iend = d.sample_number(this->endtime()); + jend = this->sample_number(d.endtime()); + if (iend >= (d.npts())) { + iend = d.npts() - 1; + } + if (jend >= this->npts()) { + jend = this->npts() - 1; + } + for (i = i0, j = j0; i <= iend && j <= jend; ++i, ++j) { + this->u(0, j) -= d.u(0, i); + this->u(1, j) -= d.u(1, i); + this->u(2, j) -= d.u(2, i); + } + return (*this); } -const CoreSeismogram CoreSeismogram::operator+(const CoreSeismogram& other) const -{ +const CoreSeismogram +CoreSeismogram::operator+(const CoreSeismogram &other) const { CoreSeismogram result(*this); result += other; return result; } -const CoreSeismogram CoreSeismogram::operator-(const CoreSeismogram& other) const -{ +const CoreSeismogram +CoreSeismogram::operator-(const CoreSeismogram &other) const { CoreSeismogram result(*this); result -= other; return result; } -void CoreSeismogram::set_dt(const double sample_interval) -{ +void CoreSeismogram::set_dt(const double sample_interval) { this->BasicTimeSeries::set_dt(sample_interval); /* This is the unique name defined in the mspass schema - we always set it. */ - this->put(SEISMICMD_dt,sample_interval); + this->put(SEISMICMD_dt, sample_interval); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; /* Note these aren't set in keywords - aliases are flexible and this can allow another way to make these attribute names more flexible. */ aliases.insert("dt"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,sample_interval); + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, sample_interval); } } } -void CoreSeismogram::set_t0(const double t0in) -{ +void CoreSeismogram::set_t0(const double t0in) { this->BasicTimeSeries::set_t0(t0in); /* This is the unique name - we always set it. Pulled from keywords.h which should match the schema. aliases are hard coded not defined as keywords */ - this->put(SEISMICMD_t0,t0in); + this->put(SEISMICMD_t0, t0in); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; aliases.insert("t0"); aliases.insert("time"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,t0in); + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, t0in); } } } -void CoreSeismogram::set_npts(const size_t npts) -{ +void CoreSeismogram::set_npts(const size_t npts) { this->BasicTimeSeries::set_npts(npts); /* This is the unique name - we always set it. The weird cast is necessary to avoid type mismatch with unsigned. We use the name defined in keywords.h which we can always assume matches the schema for the unique name*/ - this->put(SEISMICMD_npts,(long int)npts); + this->put(SEISMICMD_npts, (long int)npts); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; aliases.insert("nsamp"); aliases.insert("wfdisc.nsamp"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,(long int)npts); + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, (long int)npts); } } /* this method has the further complication that npts sets the size of the data matrix. Here we resize the matrix and initialize it to 0s.*/ - if(npts==0) - { + if (npts == 0) { this->u = dmatrix(); - } - else - { - this->u=dmatrix(3,npts); + } else { + this->u = dmatrix(3, npts); this->u.zero(); } } -void CoreSeismogram::sync_npts() -{ - if(nsamp != this->u.columns()) { +void CoreSeismogram::sync_npts() { + if (nsamp != this->u.columns()) { this->BasicTimeSeries::set_npts(this->u.columns()); /* This is the unique name - we always set it. The weird cast is necessary to avoid type mismatch with unsigned. As above converted to keywords.h const string to make this easier to maintain*/ - this->put(SEISMICMD_npts,(long int)nsamp); + this->put(SEISMICMD_npts, (long int)nsamp); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; aliases.insert("nsamp"); aliases.insert("wfdisc.nsamp"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,(long int)nsamp); - } + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, (long int)nsamp); + } } } } -vector CoreSeismogram::operator[] (const int i)const -{ - try { - vector result; - result.reserve(3); - for(int k=0;k<3;++k) - result.push_back(this->u(k,i)); - return result; - }catch(...){throw;}; +vector CoreSeismogram::operator[](const int i) const { + try { + vector result; + result.reserve(3); + for (int k = 0; k < 3; ++k) + result.push_back(this->u(k, i)); + return result; + } catch (...) { + throw; + }; } -vector CoreSeismogram::operator[] (const double t) const -{ - try { - vector result; - int i=this->sample_number(t); - /* could test for a negative i and i too large but we assume - the dmatrix container will throw an exception if it resolves that way*/ - for(int k=0;k<3;++k) - result.push_back(this->u(k,i)); - return result; - }catch(...){throw;}; +vector CoreSeismogram::operator[](const double t) const { + try { + vector result; + int i = this->sample_number(t); + /* could test for a negative i and i too large but we assume + the dmatrix container will throw an exception if it resolves that way*/ + for (int k = 0; k < 3; ++k) + result.push_back(this->u(k, i)); + return result; + } catch (...) { + throw; + }; } -} // end namespace SEISPP +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/CoreTimeSeries.cc b/cxx/src/lib/seismic/CoreTimeSeries.cc index a7df79528..b4924e4fa 100644 --- a/cxx/src/lib/seismic/CoreTimeSeries.cc +++ b/cxx/src/lib/seismic/CoreTimeSeries.cc @@ -1,26 +1,24 @@ -#include +#include "mspass/seismic/CoreTimeSeries.h" #include "misc/blas.h" -#include "mspass/utility/MsPASSError.h" #include "mspass/seismic/keywords.h" -#include "mspass/seismic/CoreTimeSeries.h" #include "mspass/utility/Metadata.h" -namespace mspass::seismic -{ +#include "mspass/utility/MsPASSError.h" +#include +namespace mspass::seismic { using namespace std; using namespace mspass::utility; // // simple constructors for the CoreTimeSeries object are defined inline // in seispp.h. // -CoreTimeSeries::CoreTimeSeries() : BasicTimeSeries(), Metadata() -{ - s.reserve(0); - this->set_dt(1.0); - this->set_t0(0.0); - this->set_npts(0); +CoreTimeSeries::CoreTimeSeries() : BasicTimeSeries(), Metadata() { + s.reserve(0); + this->set_dt(1.0); + this->set_t0(0.0); + this->set_npts(0); } -CoreTimeSeries::CoreTimeSeries(const size_t nsin) : BasicTimeSeries(), Metadata() -{ +CoreTimeSeries::CoreTimeSeries(const size_t nsin) + : BasicTimeSeries(), Metadata() { /* IMPORTANT: this constructor assumes BasicTimeSeries initializes the equivalent of: set_dt(1.0) @@ -33,253 +31,223 @@ CoreTimeSeries::CoreTimeSeries(const size_t nsin) : BasicTimeSeries(), Metadata( this->CoreTimeSeries::set_npts(nsin); } - -CoreTimeSeries::CoreTimeSeries(const CoreTimeSeries& tsi) : - BasicTimeSeries(tsi), - Metadata(tsi) -{ - if(mlive) - { - s=tsi.s; - } - else if(tsi.s.size()>0) - { - /* This is needed to preserve the contents of data vector when something - marks the data dead, but one wants to restore it later. Classic example - is an interactive trace editor. Found mysterious errors can occur - without this features. */ - s=tsi.s; - } - /* Do nothing if the parent s is empty as std::vector will be properly - initialized*/ +CoreTimeSeries::CoreTimeSeries(const CoreTimeSeries &tsi) + : BasicTimeSeries(tsi), Metadata(tsi) { + if (mlive) { + s = tsi.s; + } else if (tsi.s.size() > 0) { + /* This is needed to preserve the contents of data vector when something + marks the data dead, but one wants to restore it later. Classic example + is an interactive trace editor. Found mysterious errors can occur + without this features. */ + s = tsi.s; + } + /* Do nothing if the parent s is empty as std::vector will be properly + initialized*/ } -CoreTimeSeries::CoreTimeSeries(const BasicTimeSeries& bd,const Metadata& md) - : BasicTimeSeries(bd), Metadata(md) -{ +CoreTimeSeries::CoreTimeSeries(const BasicTimeSeries &bd, const Metadata &md) + : BasicTimeSeries(bd), Metadata(md) { /* this assumes set_npts initializes the vector containers, s, to zeros AND that BasicTimeSeries constructor initializes ns (npts) to the value desired. */ this->CoreTimeSeries::set_npts(this->nsamp); } // standard assignment operator -CoreTimeSeries& CoreTimeSeries::operator=(const CoreTimeSeries& tsi) -{ - if(this!=&tsi) - { - this->BasicTimeSeries::operator=(tsi); - this->Metadata::operator=(tsi); - s=tsi.s; - } - return(*this); +CoreTimeSeries &CoreTimeSeries::operator=(const CoreTimeSeries &tsi) { + if (this != &tsi) { + this->BasicTimeSeries::operator=(tsi); + this->Metadata::operator=(tsi); + s = tsi.s; + } + return (*this); } /* Sum operator for CoreTimeSeries object */ -CoreTimeSeries& CoreTimeSeries::operator+=(const CoreTimeSeries& data) -{ - int i,iend,jend; - size_t i0; - size_t j,j0; - // Sun's compiler complains about const objects without this. - CoreTimeSeries& d=const_cast(data); - // Silently do nothing if d is marked dead - if(!d.mlive) return(*this); - // Silently do nothing if d does not overlap with data to contain sum - if( (d.endtime()(this->endtime())) ) return(*this); - if(d.tref!=(this->tref)) - throw MsPASSError("CoreTimeSeries += operator cannot handle data with inconsistent time base\n", - ErrorSeverity::Invalid); - /* this defines the range of left and right hand sides to be summed */ - i=d.sample_number(this->mt0); - if(i<0) - { - j0=this->sample_number(d.t0()); - i0=0; - } - else - { - j0=0; - i0=i; - } - iend=d.sample_number(this->endtime()); - jend=this->sample_number(d.endtime()); - if(iend>=(d.npts())) - { - iend=d.npts()-1; - } - if(jend>=this->npts()) - { - jend=this->npts()-1; - } - //cout << "i0="<mt0); + if (i < 0) { + j0 = this->sample_number(d.t0()); + i0 = 0; + } else { + j0 = 0; + i0 = i; + } + iend = d.sample_number(this->endtime()); + jend = this->sample_number(d.endtime()); + if (iend >= (d.npts())) { + iend = d.npts() - 1; + } + if (jend >= this->npts()) { + jend = this->npts() - 1; + } + // cout << "i0="<mt0); - if(i<0) - { - j0=this->sample_number(d.t0()); - i0=0; - } - else - { - j0=0; - i0=i; - } - iend=d.sample_number(this->endtime()); - jend=this->sample_number(d.endtime()); - if(iend>=(d.npts())) - { - iend=d.npts()-1; - } - if(jend>=this->npts()) - { - jend=this->npts()-1; - } - //cout << "i0="<mt0); + if (i < 0) { + j0 = this->sample_number(d.t0()); + i0 = 0; + } else { + j0 = 0; + i0 = i; + } + iend = d.sample_number(this->endtime()); + jend = this->sample_number(d.endtime()); + if (iend >= (d.npts())) { + iend = d.npts() - 1; + } + if (jend >= this->npts()) { + jend = this->npts() - 1; + } + // cout << "i0="<is_defined(*aptr)) - { - this->put(*aptr,sample_interval); + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, sample_interval); } } } -void CoreTimeSeries::set_t0(const double t0in) -{ +void CoreTimeSeries::set_t0(const double t0in) { this->BasicTimeSeries::set_t0(t0in); /* This is the unique name - we always set it. Changed Feb 2021 to use const string value defined in keywords.h*/ - this->put(SEISMICMD_t0,t0in); + this->put(SEISMICMD_t0, t0in); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; aliases.insert("t0"); aliases.insert("time"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,t0in); + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, t0in); } } } -void CoreTimeSeries::set_npts(const size_t npts) -{ +void CoreTimeSeries::set_npts(const size_t npts) { this->BasicTimeSeries::set_npts(npts); /* This is the unique name - we always set it. Cast is necessary to avoid type mismatch in python for unsigned. Changed Feb 2021 to use key defined in in keywords.h*/ - this->put(SEISMICMD_npts,(long int)npts); + this->put(SEISMICMD_npts, (long int)npts); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; aliases.insert("nsamp"); aliases.insert("wfdisc.nsamp"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,(long int)npts); + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, (long int)npts); } } /* this method has the further complication that npts sets the size of the data buffer. We clear it an initialize it to 0 to be consistent with how constructors handle this. */ - std::vector().swap(this->s); // Clear the memory allocation of s + std::vector().swap(this->s); // Clear the memory allocation of s this->s.reserve(npts); - for(size_t i=0;is.push_back(0.0); + for (size_t i = 0; i < npts; ++i) + this->s.push_back(0.0); } -void CoreTimeSeries::sync_npts() -{ - if(nsamp != this->s.size()) { +void CoreTimeSeries::sync_npts() { + if (nsamp != this->s.size()) { this->BasicTimeSeries::set_npts(this->s.size()); /* This is the unique name - we always set it. The weird cast is necessary to avoid type mismatch with unsigned Changed Feb 2021 to use key defined in keywords.h*/ - this->put(SEISMICMD_npts,(long int)nsamp); + this->put(SEISMICMD_npts, (long int)nsamp); /* these are hard coded aliases for sample_interval */ std::set aliases; std::set::iterator aptr; aliases.insert("nsamp"); aliases.insert("wfdisc.nsamp"); - for(aptr=aliases.begin();aptr!=aliases.end();++aptr) - { - if(this->is_defined(*aptr)) - { - this->put(*aptr,(long int)nsamp); - } + for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) { + if (this->is_defined(*aptr)) { + this->put(*aptr, (long int)nsamp); + } } } } -double CoreTimeSeries::operator[](size_t i) const -{ - if(!mlive) - throw MsPASSError(string("CoreTimeSeries operator[]: attempting to access data marked as dead"), - ErrorSeverity::Invalid); - if( (i<0) || (i>=s.size()) ) - { - throw MsPASSError( - string("CoreTimeSeries operator[]: request for sample outside range of data"), - ErrorSeverity::Invalid); - } - return(s[i]); +double CoreTimeSeries::operator[](size_t i) const { + if (!mlive) + throw MsPASSError(string("CoreTimeSeries operator[]: attempting to access " + "data marked as dead"), + ErrorSeverity::Invalid); + if ((i < 0) || (i >= s.size())) { + throw MsPASSError(string("CoreTimeSeries operator[]: request for sample " + "outside range of data"), + ErrorSeverity::Invalid); + } + return (s[i]); } -} // End MsPASS namespace declaration +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/DataGap.cc b/cxx/src/lib/seismic/DataGap.cc index 38c9d633d..290bbe51a 100644 --- a/cxx/src/lib/seismic/DataGap.cc +++ b/cxx/src/lib/seismic/DataGap.cc @@ -1,118 +1,108 @@ #include "mspass/seismic/DataGap.h" #include "mspass/algorithms/TimeWindow.h" -namespace mspass::seismic{ +namespace mspass::seismic { using namespace mspass::seismic; using namespace mspass::algorithms; -DataGap::DataGap(const std::list& twlist) -{ - for(auto twptr=twlist.begin();twptr!=twlist.end();++twptr) - gaps.insert(*twptr); +DataGap::DataGap(const std::list &twlist) { + for (auto twptr = twlist.begin(); twptr != twlist.end(); ++twptr) + gaps.insert(*twptr); } -void DataGap::add_gap(const TimeWindow tw) -{ +void DataGap::add_gap(const TimeWindow tw) { auto insert_result = this->gaps.insert(tw); - if(!insert_result.second) - { - /* We land here if tw overlaps or duplicates something already in - * the gaps container. first in this case contains an iterator to + if (!insert_result.second) { + /* We land here if tw overlaps or duplicates something already in + * the gaps container. first in this case contains an iterator to * the potential duplicate. We have to resolve which to keep*/ TimeWindow old_tw(*insert_result.first); TimeWindow new_tw; - if(tw.startold_tw.end) - new_tw.end=tw.end; + new_tw.start = old_tw.start; + if (tw.end > old_tw.end) + new_tw.end = tw.end; else - new_tw.end=old_tw.end; + new_tw.end = old_tw.end; gaps.erase(insert_result.first); gaps.insert(new_tw); } } -bool DataGap::is_gap(const double ttest) -{ - const double dt(0.001); // used to define small time interval smaller than most sampling - if(gaps.empty())return false; +bool DataGap::is_gap(const double ttest) { + const double dt( + 0.001); // used to define small time interval smaller than most sampling + if (gaps.empty()) + return false; TimeWindow twin; - twin.start = ttest - dt*0.5; - twin.end = ttest + dt*0.5; - if(gaps.find(twin)==gaps.end()) + twin.start = ttest - dt * 0.5; + twin.end = ttest + dt * 0.5; + if (gaps.find(twin) == gaps.end()) return false; else return true; } -bool DataGap::has_gap(const TimeWindow twin) -{ - if(gaps.empty())return false; - if(gaps.find(twin)==gaps.end()) - return(false); +bool DataGap::has_gap(const TimeWindow twin) { + if (gaps.empty()) + return false; + if (gaps.find(twin) == gaps.end()) + return (false); else - return(true); + return (true); } -std::list DataGap::get_gaps() const -{ +std::list DataGap::get_gaps() const { std::list result; - for(auto sptr=this->gaps.begin(); sptr!=this->gaps.end(); ++sptr) + for (auto sptr = this->gaps.begin(); sptr != this->gaps.end(); ++sptr) result.push_back(*sptr); return result; } -DataGap DataGap::subset(const TimeWindow tw) const -{ - /* This could be implemented with the set equal_range method +DataGap DataGap::subset(const TimeWindow tw) const { + /* This could be implemented with the set equal_range method * but these objects are expected to normally be very small * and it is a lot clearer what this algorithm does. * */ - DataGap result; - std::list gaplist = this->get_gaps(); - for(auto twptr= gaplist.begin();twptr!=gaplist.end();++twptr) - { - if( ((twptr->end)>tw.start) && ((twptr->start) gaplist = this->get_gaps(); + for (auto twptr = gaplist.begin(); twptr != gaplist.end(); ++twptr) { + if (((twptr->end) > tw.start) && ((twptr->start) < tw.end)) { + result.add_gap(*twptr); + } + } + return result; } -/* std::set iterators are always effectively const and the const +/* std::set iterators are always effectively const and the const * cannot be cast away. Hence, this algorithm is much more complex - * than I thought it would be. Have to make a copy of the gaps + * than I thought it would be. Have to make a copy of the gaps * container and edit the copy before then having to use the add_gap - * method to put in the edited value. If there were a more elegant - * way to handle the const problem this would go away. I think - * another element is the custom compare operator for this set - * container. Anyway this works and because in all known situations + * method to put in the edited value. If there were a more elegant + * way to handle the const problem this would go away. I think + * another element is the custom compare operator for this set + * container. Anyway this works and because in all known situations * the gaps set will be small this inefficiency is probably not important. * */ -void DataGap::translate_origin(double origin_time) -{ - std::set translated_gaps; - for(auto ptr=this->gaps.begin();ptr!=this->gaps.end();++ptr) - { +void DataGap::translate_origin(double origin_time) { + std::set translated_gaps; + for (auto ptr = this->gaps.begin(); ptr != this->gaps.end(); ++ptr) { TimeWindow tw(*ptr); tw.start -= origin_time; tw.end -= origin_time; translated_gaps.insert(tw); } this->clear_gaps(); - for(auto ptr=translated_gaps.begin();ptr!=translated_gaps.end();++ptr) - this->add_gap(*ptr); + for (auto ptr = translated_gaps.begin(); ptr != translated_gaps.end(); ++ptr) + this->add_gap(*ptr); } -DataGap& DataGap::operator=(const DataGap& parent) -{ - if(this!=(&parent)) - { - gaps=parent.gaps; - } - return *this; +DataGap &DataGap::operator=(const DataGap &parent) { + if (this != (&parent)) { + gaps = parent.gaps; + } + return *this; } -DataGap& DataGap::operator+=(const DataGap& parent) -{ - for(auto ptr=parent.gaps.begin();ptr!=parent.gaps.end();++ptr) this->add_gap(*ptr); +DataGap &DataGap::operator+=(const DataGap &parent) { + for (auto ptr = parent.gaps.begin(); ptr != parent.gaps.end(); ++ptr) + this->add_gap(*ptr); return *this; } -} // End namespace encapsulation +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/PowerSpectrum.cc b/cxx/src/lib/seismic/PowerSpectrum.cc index 62ca1aa02..736f4a9ac 100644 --- a/cxx/src/lib/seismic/PowerSpectrum.cc +++ b/cxx/src/lib/seismic/PowerSpectrum.cc @@ -1,113 +1,97 @@ -#include -#include -#include "mspass/seismic/BasicSpectrum.h" #include "mspass/seismic/PowerSpectrum.h" -namespace mspass::seismic -{ +#include "mspass/seismic/BasicSpectrum.h" +#include +#include +namespace mspass::seismic { using namespace std; using namespace mspass::utility; using namespace mspass::seismic; -PowerSpectrum::PowerSpectrum(): BasicSpectrum(),Metadata(), elog() -{ - spectrum_type=string("UNDEFINED"); +PowerSpectrum::PowerSpectrum() : BasicSpectrum(), Metadata(), elog() { + spectrum_type = string("UNDEFINED"); } -PowerSpectrum::PowerSpectrum(const PowerSpectrum& parent) - : BasicSpectrum(parent),Metadata(parent),elog(parent.elog) -{ - spectrum_type=parent.spectrum_type; - spectrum_type=parent.spectrum_type; - spectrum=parent.spectrum; - elog=parent.elog; +PowerSpectrum::PowerSpectrum(const PowerSpectrum &parent) + : BasicSpectrum(parent), Metadata(parent), elog(parent.elog) { + spectrum_type = parent.spectrum_type; + spectrum_type = parent.spectrum_type; + spectrum = parent.spectrum; + elog = parent.elog; } -PowerSpectrum& PowerSpectrum::operator=(const PowerSpectrum& parent) -{ - if(this!=(&parent)) - { +PowerSpectrum &PowerSpectrum::operator=(const PowerSpectrum &parent) { + if (this != (&parent)) { this->Metadata::operator=(parent); this->BasicSpectrum::operator=(parent); - spectrum_type=parent.spectrum_type; - spectrum=parent.spectrum; - elog=parent.elog; + spectrum_type = parent.spectrum_type; + spectrum = parent.spectrum; + elog = parent.elog; } return *this; } -PowerSpectrum& PowerSpectrum::operator+=(const PowerSpectrum& other) -{ +PowerSpectrum &PowerSpectrum::operator+=(const PowerSpectrum &other) { /* Allow self accumulation - double values */ - if(this==(&other)) - { - for(int k=0;knf();++k) spectrum[k]*=2.0; - } - else - { - if(this->nf() != other.nf()) - { + if (this == (&other)) { + for (int k = 0; k < this->nf(); ++k) + spectrum[k] *= 2.0; + } else { + if (this->nf() != other.nf()) { stringstream ss; - ss << "operator+=(accumulation) size mismatch of spectrum arrays"<kill(); - } - else if(this->f0() != other.f0()) - { + } else if (this->f0() != other.f0()) { stringstream ss; - ss << "operator+=(accumulation) f0 mismatch of spectrum estimates"<kill(); - } - else if(this->df() != other.df()) - { + } else if (this->df() != other.df()) { stringstream ss; - ss << "operator+=(accumulation) df mismatch of spectrum estimates"<kill(); - } - else - { - for(int k=0;knf();++k) + } else { + for (int k = 0; k < this->nf(); ++k) spectrum[k] += other.spectrum[k]; } } return *this; } -vector PowerSpectrum::amplitude() const -{ +vector PowerSpectrum::amplitude() const { vector result; result.reserve(spectrum.size()); - for(int k=0;k((f-this->f0val)/this->dfval); + filow = static_cast((f - this->f0val) / this->dfval); /* Force 0 at Nyquist and above - this allows simple interpolation in else */ - if(filow>=(spectrum.size()-1)) - return 0.0; - else - { - double slope=(spectrum[filow+1]-spectrum[filow])/this->dfval; - double flow=this->f0val+((double)filow)*this->dfval; - return spectrum[filow]+slope*(f-flow); + if (filow >= (spectrum.size() - 1)) + return 0.0; + else { + double slope = (spectrum[filow + 1] - spectrum[filow]) / this->dfval; + double flow = this->f0val + ((double)filow) * this->dfval; + return spectrum[filow] + slope * (f - flow); } } -std::vector PowerSpectrum::frequencies() const -{ +std::vector PowerSpectrum::frequencies() const { vector f; f.reserve(this->nf()); - for(auto i=0;inf();++i) f.push_back(this->f0val+this->dfval*static_cast(i)); + for (auto i = 0; i < this->nf(); ++i) + f.push_back(this->f0val + this->dfval * static_cast(i)); return f; } -} // End namespace +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/Seismogram.cc b/cxx/src/lib/seismic/Seismogram.cc index 071b09eab..0776efdfd 100644 --- a/cxx/src/lib/seismic/Seismogram.cc +++ b/cxx/src/lib/seismic/Seismogram.cc @@ -1,39 +1,35 @@ -#include -#include "mspass/seismic/keywords.h" #include "mspass/seismic/Seismogram.h" +#include "mspass/seismic/keywords.h" #include "mspass/utility/memory_constants.h" -namespace mspass::seismic -{ +#include +namespace mspass::seismic { using namespace std; using namespace mspass::utility; Seismogram::Seismogram(const size_t nsamples) - : CoreSeismogram(nsamples),ProcessingHistory() -{ -/* Note this constructor body needs no content. Just a wrapper for CoreSeismogram */ + : CoreSeismogram(nsamples), ProcessingHistory() { + /* Note this constructor body needs no content. Just a wrapper for + * CoreSeismogram */ } /* For some weird reason we can't call the parallel constructors for CoreSeismogram. Instead we have to call the constructors for the base class.*/ -Seismogram::Seismogram(const CoreSeismogram& d) - : CoreSeismogram(d),ProcessingHistory() -{ -} -Seismogram::Seismogram(const CoreSeismogram& d, const string alg) - : CoreSeismogram(d),ProcessingHistory() -{ +Seismogram::Seismogram(const CoreSeismogram &d) + : CoreSeismogram(d), ProcessingHistory() {} +Seismogram::Seismogram(const CoreSeismogram &d, const string alg) + : CoreSeismogram(d), ProcessingHistory() { /* Not sure this is a good idea, but will give each instance created by this constructor a uuid.*/ - string id=this->newid(); - this->ProcessingHistory::set_as_origin(alg,id,id,AtomicType::SEISMOGRAM,false); + string id = this->newid(); + this->ProcessingHistory::set_as_origin(alg, id, id, AtomicType::SEISMOGRAM, + false); this->ProcessingHistory::set_jobname(string("test")); this->ProcessingHistory::set_jobid(string("test")); } -Seismogram::Seismogram(const BasicTimeSeries& bts, const Metadata& md) - : CoreSeismogram(md,false),ProcessingHistory() -{ +Seismogram::Seismogram(const BasicTimeSeries &bts, const Metadata &md) + : CoreSeismogram(md, false), ProcessingHistory() { /* the contents of BasicTimeSeries passed will override anything set from Metadata in this section. Note also the very important use of this for the putters to assure proper resolution of the virtual methods */ - this->kill(); //set dead because buffer has invalid data + this->kill(); // set dead because buffer has invalid data this->set_t0(bts.t0()); this->set_tref(bts.timetype()); this->set_npts(bts.npts()); @@ -44,14 +40,11 @@ Seismogram::Seismogram(const BasicTimeSeries& bts, const Metadata& md) source data if this method were used to convert raw data. Careful if this is used in that context where the time reference is defined implicitly as shot time with no tie to an absolute time reference */ - if(bts.shifted()) - { + if (bts.shifted()) { double t0shift; - t0shift=bts.time_reference(); + t0shift = bts.time_reference(); this->force_t0_shift(t0shift); - } - else - { + } else { this->force_t0_shift(0.0); } } @@ -63,24 +56,24 @@ I couldn't get the dmatrix u to be allowed in the copy construct sequence - following the :. Minor performance hit duplicating a default construction of u before calling operator= on the last line of this constructor.*/ -Seismogram::Seismogram(const BasicTimeSeries& b, const Metadata& m, - const ProcessingHistory& his,const bool card, const bool ortho, - const dmatrix& tm, const dmatrix& uin) - : ProcessingHistory(his) -{ +Seismogram::Seismogram(const BasicTimeSeries &b, const Metadata &m, + const ProcessingHistory &his, const bool card, + const bool ortho, const dmatrix &tm, const dmatrix &uin) + : ProcessingHistory(his) { this->BasicTimeSeries::operator=(b); this->Metadata::operator=(m); - components_are_cardinal=card; - components_are_orthogonal=ortho; - int i,j; - for(i=0;i<3;++i) - for(j=0;j<3;++j) tmatrix[i][j]=tm(i,j); - this->u=uin; + components_are_cardinal = card; + components_are_orthogonal = ortho; + int i, j; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + tmatrix[i][j] = tm(i, j); + this->u = uin; } -Seismogram::Seismogram(const Metadata& md, const string jobname, - const string jobid, const string readername,const string algid) - : CoreSeismogram(md,true),ProcessingHistory() -{ +Seismogram::Seismogram(const Metadata &md, const string jobname, + const string jobid, const string readername, + const string algid) + : CoreSeismogram(md, true), ProcessingHistory() { const string algname("SeismogramMDConstructor"); this->set_jobname(jobname); this->set_jobid(jobid); @@ -90,54 +83,52 @@ Seismogram::Seismogram(const Metadata& md, const string jobname, the random number generator and post a complaint. */ string thisid; - try{ - string thisid=this->get_string(SEISMICMD_uuid); + try { + string thisid = this->get_string(SEISMICMD_uuid); this->set_id(thisid); - }catch(MsPASSError& merr) - { + } catch (MsPASSError &merr) { /* this sets the id to a random number based uuid */ - thisid=this->newid(); - this->elog.log_error(algname,"uuid not defined.\nSet by constructor to" - +thisid,ErrorSeverity::Complaint); + thisid = this->newid(); + this->elog.log_error(algname, + "uuid not defined.\nSet by constructor to" + thisid, + ErrorSeverity::Complaint); } bool mark_as_raw; - try{ - mark_as_raw=this->get_bool(SEISMICMD_rawdata); - } catch(MsPASSError& merr) - { - this->elog.log_error(algname,"rawdata boolean not found - default to false", - ErrorSeverity::Complaint); - mark_as_raw=false; + try { + mark_as_raw = this->get_bool(SEISMICMD_rawdata); + } catch (MsPASSError &merr) { + this->elog.log_error(algname, + "rawdata boolean not found - default to false", + ErrorSeverity::Complaint); + mark_as_raw = false; } - this->ProcessingHistory::set_as_origin(readername,algid,thisid, - AtomicType::SEISMOGRAM,mark_as_raw); + this->ProcessingHistory::set_as_origin(readername, algid, thisid, + AtomicType::SEISMOGRAM, mark_as_raw); } -Seismogram& Seismogram::operator=(const Seismogram& parent) -{ - if(this!=(&parent)) - { - this->CoreSeismogram::operator=(parent); - this->ProcessingHistory::operator=(parent); - } - return *this; +Seismogram &Seismogram::operator=(const Seismogram &parent) { + if (this != (&parent)) { + this->CoreSeismogram::operator=(parent); + this->ProcessingHistory::operator=(parent); + } + return *this; } -void Seismogram::load_history(const ProcessingHistory& h) -{ +void Seismogram::load_history(const ProcessingHistory &h) { this->ProcessingHistory::operator=(h); } -size_t Seismogram::memory_use() const -{ +size_t Seismogram::memory_use() const { size_t memory_estimate; memory_estimate = sizeof(Seismogram); /* data for a seismogram is 3 channels so 3*npts*/ - memory_estimate += sizeof(double)*3*this->npts(); + memory_estimate += sizeof(double) * 3 * this->npts(); /* We can only estimate the size of the Metadata container. These constants are defined in memory_constants.h */ - memory_estimate += memory_constants::MD_AVERAGE_SIZE*this->md.size(); - memory_estimate += memory_constants::KEY_AVERAGE_SIZE*this->changed_or_set.size(); + memory_estimate += memory_constants::MD_AVERAGE_SIZE * this->md.size(); + memory_estimate += + memory_constants::KEY_AVERAGE_SIZE * this->changed_or_set.size(); /* Similar for history and elog containers */ - memory_estimate += memory_constants::HISTORYDATA_AVERAGE_SIZE*this->nodes.size(); - memory_estimate += memory_constants::ELOG_AVERAGE_SIZE*this->elog.size(); + memory_estimate += + memory_constants::HISTORYDATA_AVERAGE_SIZE * this->nodes.size(); + memory_estimate += memory_constants::ELOG_AVERAGE_SIZE * this->elog.size(); return memory_estimate; } -}// end mspass namespace +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/SlownessVector.cc b/cxx/src/lib/seismic/SlownessVector.cc index 0a5b09794..f161442bf 100644 --- a/cxx/src/lib/seismic/SlownessVector.cc +++ b/cxx/src/lib/seismic/SlownessVector.cc @@ -1,95 +1,85 @@ +#include "mspass/seismic/SlownessVector.h" #include #include -#include "mspass/seismic/SlownessVector.h" -namespace mspass::seismic -{ -// these are trivial constructors and could be done inline, but +namespace mspass::seismic { +// these are trivial constructors and could be done inline, but // decided to put them here to keep things together. Learned this // lesson the hard way // -SlownessVector::SlownessVector() -{ - ux=0.0; - uy=0.0; - azimuth0=0.0; +SlownessVector::SlownessVector() { + ux = 0.0; + uy = 0.0; + azimuth0 = 0.0; } -SlownessVector::SlownessVector(const SlownessVector& old) -{ - ux=old.ux; - uy=old.uy; - azimuth0=old.azimuth0; +SlownessVector::SlownessVector(const SlownessVector &old) { + ux = old.ux; + uy = old.uy; + azimuth0 = old.azimuth0; } -SlownessVector::SlownessVector(const double ux0, const double uy0, const double az0) -{ - ux=ux0; - uy=uy0; - azimuth0=az0; +SlownessVector::SlownessVector(const double ux0, const double uy0, + const double az0) { + ux = ux0; + uy = uy0; + azimuth0 = az0; } -SlownessVector& SlownessVector::operator=(const SlownessVector& parent) -{ - if(this!=&parent) - { - ux=parent.ux; - uy=parent.uy; - azimuth0=parent.azimuth0; - } - return(*this); +SlownessVector &SlownessVector::operator=(const SlownessVector &parent) { + if (this != &parent) { + ux = parent.ux; + uy = parent.uy; + azimuth0 = parent.azimuth0; + } + return (*this); } -SlownessVector& SlownessVector::operator+=(const SlownessVector& other) -{ - ux+=other.ux; - uy+=other.uy; - return(*this); +SlownessVector &SlownessVector::operator+=(const SlownessVector &other) { + ux += other.ux; + uy += other.uy; + return (*this); } -SlownessVector& SlownessVector::operator-=(const SlownessVector& other) -{ - ux-=other.ux; - uy-=other.uy; - return(*this); +SlownessVector &SlownessVector::operator-=(const SlownessVector &other) { + ux -= other.ux; + uy -= other.uy; + return (*this); } -const SlownessVector SlownessVector::operator+(const SlownessVector& other) const { - SlownessVector result(*this); - result += other; - return result; +const SlownessVector +SlownessVector::operator+(const SlownessVector &other) const { + SlownessVector result(*this); + result += other; + return result; } -const SlownessVector SlownessVector::operator-(const SlownessVector& other) const { - SlownessVector result(*this); - result -= other; - return result; +const SlownessVector +SlownessVector::operator-(const SlownessVector &other) const { + SlownessVector result(*this); + result -= other; + return result; } // These could (and once were) inline, but decided that was poor // memory management -double SlownessVector::mag() const noexcept -{ - return(hypot(ux,uy)); +double SlownessVector::mag() const noexcept { return (hypot(ux, uy)); } +double SlownessVector::azimuth() const noexcept { + if (this->mag() <= FLT_EPSILON) + return (azimuth0); + double phi; + phi = M_PI_2 - atan2(uy, ux); + if (phi > M_PI) + return (phi - 2.0 * M_PI); + else if (phi < -M_PI) + return (phi + 2.0 * M_PI); + else + return (phi); } -double SlownessVector::azimuth() const noexcept -{ - if(this->mag() <= FLT_EPSILON) return(azimuth0); - double phi; - phi=M_PI_2-atan2(uy,ux); - if(phi>M_PI) - return(phi-2.0*M_PI); - else if(phi<-M_PI) - return(phi+2.0*M_PI); - else - return(phi); +double SlownessVector::baz() const noexcept { + double phi; + if (this->mag() <= FLT_EPSILON) + phi = M_PI - azimuth0; + else + phi = 3.0 * M_PI_2 - atan2(uy, ux); + if (phi > M_PI) + return (phi - 2.0 * M_PI); + else if (phi < -M_PI) + return (phi + 2.0 * M_PI); + else + return (phi); } -double SlownessVector::baz() const noexcept -{ - double phi; - if(this->mag() <= FLT_EPSILON) - phi=M_PI-azimuth0; - else - phi=3.0*M_PI_2-atan2(uy,ux); - if(phi>M_PI) - return(phi-2.0*M_PI); - else if(phi<-M_PI) - return(phi+2.0*M_PI); - else - return(phi); -} - -} // End namespace SEISPP +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/TimeSeries.cc b/cxx/src/lib/seismic/TimeSeries.cc index 0a7262e44..3853fa4ba 100644 --- a/cxx/src/lib/seismic/TimeSeries.cc +++ b/cxx/src/lib/seismic/TimeSeries.cc @@ -1,110 +1,101 @@ -#include "mspass/seismic/keywords.h" #include "mspass/seismic/TimeSeries.h" +#include "mspass/seismic/keywords.h" #include "mspass/utility/memory_constants.h" -namespace mspass::seismic -{ +namespace mspass::seismic { using namespace std; using namespace mspass::utility; using namespace mspass::seismic; -TimeSeries::TimeSeries(const CoreTimeSeries& d, const std::string alg) - : CoreTimeSeries(d),ProcessingHistory() -{ +TimeSeries::TimeSeries(const CoreTimeSeries &d, const std::string alg) + : CoreTimeSeries(d), ProcessingHistory() { /* Not sure this is a good idea, but will give each instance created by this constructor a uuid.*/ - string id=this->newid(); - this->ProcessingHistory::set_as_origin(alg,id,id,AtomicType::SEISMOGRAM,false); + string id = this->newid(); + this->ProcessingHistory::set_as_origin(alg, id, id, AtomicType::SEISMOGRAM, + false); this->ProcessingHistory::set_jobname(string("test")); this->ProcessingHistory::set_jobid(string("test")); } /* this is kind of a weird construct because the pieces are assembled out of the regular order of an object created by inheritance. I hope that does not cause problems. */ -TimeSeries::TimeSeries(const BasicTimeSeries& b, const Metadata& m, - const ProcessingHistory& his,const vector& d) - : CoreTimeSeries(b,m),ProcessingHistory(his) -{ - this->s=d; -} -TimeSeries::TimeSeries(const Metadata& md) : ProcessingHistory() -{ - mlive=false; - try { - this->Metadata::operator=(md); - /* Names used are from mspass defintions as of Jan 2020. - We don't need to call the set methods for these attributes as they - would add the overhead of setting delta, startime, and npts to the - same value passed. */ - this->mdt = this->get_double(SEISMICMD_dt); - this->mt0 = this->get_double(SEISMICMD_t0); - if(this->is_defined(SEISMICMD_time_standard)) - { - string tstd = this->get_string(SEISMICMD_time_standard); - if(tstd == "UTC") - this->set_tref(TimeReferenceType::UTC); - else if(tstd == "Relative") - this->set_tref(TimeReferenceType::Relative); - else - { - this->set_tref(TimeReferenceType::Relative); - this->elog.log_error("TimeSeries Metadata constructor", - SEISMICMD_time_standard+" attribute is not defined - set to Relative", - ErrorSeverity::Complaint); - } - } - if(this->time_is_relative()) - { - /* It is not an error if a t0 shift is not defined and we are - in relative time. That is the norm for active source data. */ - if(this->is_defined(SEISMICMD_t0_shift)) - { - double t0shift=this->get_double(SEISMICMD_t0_shift); - this->force_t0_shift(t0shift); - } - } - /* this default construct is needed to handle miniseed data in - MsPASS. It perhaps should generate a log message a information - but for now we do this silently. since the constructor returns - a result marked dead in all cases a default of 0 is sensible.*/ - long int ns; - if(md.is_defined(SEISMICMD_npts)) - { - ns = md.get_long(SEISMICMD_npts); - } - else - { - ns = 0; - } - /* this CoreTimeSeries method sets the npts attribute and - initializes the s buffer to all zeros */ - this->set_npts(ns); - }catch(...) {throw;}; +TimeSeries::TimeSeries(const BasicTimeSeries &b, const Metadata &m, + const ProcessingHistory &his, const vector &d) + : CoreTimeSeries(b, m), ProcessingHistory(his) { + this->s = d; } -TimeSeries& TimeSeries::operator=(const TimeSeries& parent) -{ - if(this!=(&parent)) - { - this->CoreTimeSeries::operator=(parent); - this->ProcessingHistory::operator=(parent); +TimeSeries::TimeSeries(const Metadata &md) : ProcessingHistory() { + mlive = false; + try { + this->Metadata::operator=(md); + /* Names used are from mspass defintions as of Jan 2020. + We don't need to call the set methods for these attributes as they + would add the overhead of setting delta, startime, and npts to the + same value passed. */ + this->mdt = this->get_double(SEISMICMD_dt); + this->mt0 = this->get_double(SEISMICMD_t0); + if (this->is_defined(SEISMICMD_time_standard)) { + string tstd = this->get_string(SEISMICMD_time_standard); + if (tstd == "UTC") + this->set_tref(TimeReferenceType::UTC); + else if (tstd == "Relative") + this->set_tref(TimeReferenceType::Relative); + else { + this->set_tref(TimeReferenceType::Relative); + this->elog.log_error("TimeSeries Metadata constructor", + SEISMICMD_time_standard + + " attribute is not defined - set to Relative", + ErrorSeverity::Complaint); + } } - return *this; + if (this->time_is_relative()) { + /* It is not an error if a t0 shift is not defined and we are + in relative time. That is the norm for active source data. */ + if (this->is_defined(SEISMICMD_t0_shift)) { + double t0shift = this->get_double(SEISMICMD_t0_shift); + this->force_t0_shift(t0shift); + } + } + /* this default construct is needed to handle miniseed data in + MsPASS. It perhaps should generate a log message a information + but for now we do this silently. since the constructor returns + a result marked dead in all cases a default of 0 is sensible.*/ + long int ns; + if (md.is_defined(SEISMICMD_npts)) { + ns = md.get_long(SEISMICMD_npts); + } else { + ns = 0; + } + /* this CoreTimeSeries method sets the npts attribute and + initializes the s buffer to all zeros */ + this->set_npts(ns); + } catch (...) { + throw; + }; +} +TimeSeries &TimeSeries::operator=(const TimeSeries &parent) { + if (this != (&parent)) { + this->CoreTimeSeries::operator=(parent); + this->ProcessingHistory::operator=(parent); + } + return *this; } -void TimeSeries::load_history(const ProcessingHistory& h) -{ +void TimeSeries::load_history(const ProcessingHistory &h) { this->ProcessingHistory::operator=(h); } -size_t TimeSeries::memory_use() const -{ +size_t TimeSeries::memory_use() const { size_t memory_estimate; memory_estimate = sizeof(TimeSeries); - memory_estimate += sizeof(double)*this->npts(); + memory_estimate += sizeof(double) * this->npts(); /* We can only estimate the size of the Metadata container. These constants are defined in memory_constants.h */ - memory_estimate += memory_constants::MD_AVERAGE_SIZE*this->md.size(); - memory_estimate += memory_constants::KEY_AVERAGE_SIZE*this->changed_or_set.size(); + memory_estimate += memory_constants::MD_AVERAGE_SIZE * this->md.size(); + memory_estimate += + memory_constants::KEY_AVERAGE_SIZE * this->changed_or_set.size(); /* Similar for history and elog containers */ - memory_estimate += memory_constants::HISTORYDATA_AVERAGE_SIZE*this->nodes.size(); - memory_estimate += memory_constants::ELOG_AVERAGE_SIZE*this->elog.size(); + memory_estimate += + memory_constants::HISTORYDATA_AVERAGE_SIZE * this->nodes.size(); + memory_estimate += memory_constants::ELOG_AVERAGE_SIZE * this->elog.size(); return memory_estimate; } -}// end mspass namespace +} // namespace mspass::seismic diff --git a/cxx/src/lib/seismic/TimeSeriesWGaps.cc b/cxx/src/lib/seismic/TimeSeriesWGaps.cc index 77407dd5e..89b757e6d 100644 --- a/cxx/src/lib/seismic/TimeSeriesWGaps.cc +++ b/cxx/src/lib/seismic/TimeSeriesWGaps.cc @@ -1,31 +1,30 @@ -#include -#include +#include "mspass/seismic/TimeSeriesWGaps.h" +#include "mspass/seismic/DataGap.h" #include "mspass/utility/MsPASSError.h" #include "mspass/utility/memory_constants.h" -#include "mspass/seismic/DataGap.h" -#include "mspass/seismic/TimeSeriesWGaps.h" - +#include +#include -namespace mspass::seismic{ +namespace mspass::seismic { using namespace mspass::utility; using namespace mspass::utility::memory_constants; using namespace mspass::seismic; using namespace mspass::algorithms; -void TimeSeriesWGaps::ator(const double tshift) -{ +void TimeSeriesWGaps::ator(const double tshift) { /* dead traces should to totally ignored */ - if(this->dead()) return; - if(tref==TimeReferenceType::Relative) return; - this->t0shift=tshift; + if (this->dead()) + return; + if (tref == TimeReferenceType::Relative) + return; + this->t0shift = tshift; this->mt0 -= tshift; // We have to shift all the gap windows definitions TimeWindow tw; - std::set shifted_gaps; - std::set ::iterator this_gap; - for(this_gap=gaps.begin();this_gap!=gaps.end();++this_gap) - { + std::set shifted_gaps; + std::set::iterator this_gap; + for (this_gap = gaps.begin(); this_gap != gaps.end(); ++this_gap) { tw.start = this_gap->start - tshift; tw.end = this_gap->end - tshift; shifted_gaps.insert(tw); @@ -38,134 +37,126 @@ void TimeSeriesWGaps::ator(const double tshift) not being defined for the set container. This loop is likely actually faster than having to call operator= anyway but why this fails is unknown. */ this->gaps.clear(); - for(this_gap=shifted_gaps.begin();this_gap!=shifted_gaps.end();++this_gap) - { + for (this_gap = shifted_gaps.begin(); this_gap != shifted_gaps.end(); + ++this_gap) { this->gaps.insert(*this_gap); } - this->tref=TimeReferenceType::Relative; - this->t0shift_is_valid=true; - + this->tref = TimeReferenceType::Relative; + this->t0shift_is_valid = true; } -void TimeSeriesWGaps::rtoa(const double tshift) -{ +void TimeSeriesWGaps::rtoa(const double tshift) { /* dead traces should to totally ignored */ - if(this->dead()) return; - if(tref==TimeReferenceType::UTC) return; + if (this->dead()) + return; + if (tref == TimeReferenceType::UTC) + return; this->mt0 += tshift; TimeWindow tw; - std::set shifted_gaps; - std::set ::iterator this_gap; - for(this_gap=gaps.begin();this_gap!=gaps.end();++this_gap) - { + std::set shifted_gaps; + std::set::iterator this_gap; + for (this_gap = gaps.begin(); this_gap != gaps.end(); ++this_gap) { tw.start = this_gap->start + tshift; tw.end = this_gap->end + tshift; shifted_gaps.insert(tw); } /* See related comment on why the line below will not compile */ - //this->gaps = shifted_gaps; + // this->gaps = shifted_gaps; this->gaps.clear(); - for(this_gap=shifted_gaps.begin();this_gap!=shifted_gaps.end();++this_gap) - { + for (this_gap = shifted_gaps.begin(); this_gap != shifted_gaps.end(); + ++this_gap) { this->gaps.insert(*this_gap); } - this->tref=TimeReferenceType::UTC; - this->t0shift_is_valid=true; + this->tref = TimeReferenceType::UTC; + this->t0shift_is_valid = true; } -void TimeSeriesWGaps::rtoa() -{ +void TimeSeriesWGaps::rtoa() { /* dead traces should to totally ignored */ - if(this->dead()) return; - const std::string errormess("TimeSeriesWGaps::rtoa() t0shift for conversion is not defined."); + if (this->dead()) + return; + const std::string errormess( + "TimeSeriesWGaps::rtoa() t0shift for conversion is not defined."); /* This perhaps should create a complaint message */ - if(tref==TimeReferenceType::UTC) return; + if (tref == TimeReferenceType::UTC) + return; /* A rather odd test for a nonzero. We use 100 s assuming no active - * source data would use a shift longer than that unless it really did - * have an absolute time standard. Also assumes we'll never use data from - * the first 2 minutes of 1960.*/ - if(t0shift_is_valid || (t0shift>100.0) ) - { + * source data would use a shift longer than that unless it really did + * have an absolute time standard. Also assumes we'll never use data from + * the first 2 minutes of 1960.*/ + if (t0shift_is_valid || (t0shift > 100.0)) { this->mt0 += this->t0shift; TimeWindow tw; - std::set shifted_gaps; - std::set ::iterator this_gap; - for(this_gap=gaps.begin();this_gap!=gaps.end();++this_gap) - { + std::set shifted_gaps; + std::set::iterator this_gap; + for (this_gap = gaps.begin(); this_gap != gaps.end(); ++this_gap) { tw.start = this_gap->start + t0shift; tw.end = this_gap->end + t0shift; shifted_gaps.insert(tw); } /* See related comment on why the line below will not compile */ - //this->gaps = shifted_gaps; + // this->gaps = shifted_gaps; this->gaps.clear(); - for(this_gap=shifted_gaps.begin();this_gap!=shifted_gaps.end();++this_gap) - { + for (this_gap = shifted_gaps.begin(); this_gap != shifted_gaps.end(); + ++this_gap) { this->gaps.insert(*this_gap); } - this->tref=TimeReferenceType::UTC; - t0shift_is_valid=false; - } - else - { + this->tref = TimeReferenceType::UTC; + t0shift_is_valid = false; + } else { this->kill(); - throw MsPASSError(errormess,ErrorSeverity::Invalid); + throw MsPASSError(errormess, ErrorSeverity::Invalid); } } -void TimeSeriesWGaps::shift(const double dt) -{ +void TimeSeriesWGaps::shift(const double dt) { /* This is the same test for valid t0shift used in BasicTimeSeries*/ - if(this->t0shift_is_valid || (this->t0shift>100.0) ) - { - double oldt0shift=this->t0shift; + if (this->t0shift_is_valid || (this->t0shift > 100.0)) { + double oldt0shift = this->t0shift; this->rtoa(); - this->ator(oldt0shift+dt); - } - else - { + this->ator(oldt0shift + dt); + } else { this->kill(); std::stringstream ss; - ss<<"TimeSeriesWGaps::shift: internal shift attributes is marked invalid" - <::iterator this_gap; +void TimeSeriesWGaps::zero_gaps() { + int i, istart, iend; + std::set::iterator this_gap; - for(this_gap=gaps.begin();this_gap!=gaps.end();++this_gap) - { - if(this_gap->end < this->mt0) continue; - if(this_gap->start > this->endtime()) continue; - if(this_gap->startmt0) + for (this_gap = gaps.begin(); this_gap != gaps.end(); ++this_gap) { + if (this_gap->end < this->mt0) + continue; + if (this_gap->start > this->endtime()) + continue; + if (this_gap->start < this->mt0) istart = 0; else - istart = round((this_gap->start-this->mt0)/this->mdt); - if((this_gap->end) > this->endtime()) + istart = round((this_gap->start - this->mt0) / this->mdt); + if ((this_gap->end) > this->endtime()) iend = this->nsamp - 1; else - iend = round((this_gap->end-mt0)/this->mdt); - for(i=istart;i<=iend;++i) this->s[i]=0.0; + iend = round((this_gap->end - mt0) / this->mdt); + for (i = istart; i <= iend; ++i) + this->s[i] = 0.0; } } -TimeSeriesWGaps& TimeSeriesWGaps::operator=(const TimeSeriesWGaps& parent) -{ - if(this!=(&parent)) - { - this->TimeSeries::operator=(parent); - this->gaps = parent.gaps; +TimeSeriesWGaps &TimeSeriesWGaps::operator=(const TimeSeriesWGaps &parent) { + if (this != (&parent)) { + this->TimeSeries::operator=(parent); + this->gaps = parent.gaps; } return *this; } -size_t TimeSeriesWGaps::memory_use() const -{ +size_t TimeSeriesWGaps::memory_use() const { size_t memory_estimate; memory_estimate = TimeSeries::memory_use(); - memory_estimate += DATA_GAP_AVERAGE_SIZE*gaps.size(); + memory_estimate += DATA_GAP_AVERAGE_SIZE * gaps.size(); return memory_estimate; } -}//End namespace mspass::seismic +} // End namespace mspass::seismic diff --git a/cxx/src/lib/utility/AntelopePf.cc b/cxx/src/lib/utility/AntelopePf.cc index a5df1b173..765966bb7 100644 --- a/cxx/src/lib/utility/AntelopePf.cc +++ b/cxx/src/lib/utility/AntelopePf.cc @@ -1,18 +1,25 @@ +#include "mspass/utility/AntelopePf.h" +#include "mspass/utility/MsPASSError.h" +#include "mspass/utility/utility.h" #include -#include -#include -#include -#include -#include #include +#include +#include #include -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/utility.h" -#include "mspass/utility/AntelopePf.h" +#include +#include +#include namespace mspass::utility { using namespace std; using namespace mspass::utility; -enum PfStyleInputType {PFMDSTRING, PFMDREAL, PFMDINT, PFMDBOOL, PFMDARR, PFMDTBL}; +enum PfStyleInputType { + PFMDSTRING, + PFMDREAL, + PFMDINT, + PFMDBOOL, + PFMDARR, + PFMDTBL +}; /* this is functionally similar to antelope yesno function, but in a more C++ style. int return code is the same: 0 is false, -1 for boolean true, and +1 for no match. @@ -23,13 +30,13 @@ enum PfStyleInputType {PFMDSTRING, PFMDREAL, PFMDINT, PFMDBOOL, PFMDARR, PFMDTBL to be used in another context while 0 is a very common parameter value. This feature should not cause harm anyway because all simple parameters get echoed to the string map anyway.*/ -int yesno(string s) -{ - if( (s=="yes") || (s=="ok") || (s=="y") || (s=="true") - || (s=="on") || (s=="t") ) return(-1); - if( (s=="no") || (s=="n") || (s=="false") || (s=="off") - || (s=="f") ) return(0); - return(1); +int yesno(string s) { + if ((s == "yes") || (s == "ok") || (s == "y") || (s == "true") || + (s == "on") || (s == "t")) + return (-1); + if ((s == "no") || (s == "n") || (s == "false") || (s == "off") || (s == "f")) + return (0); + return (1); } /* This internal helper tests an std::string to make a guess of the type. The algorithm is: @@ -41,420 +48,390 @@ int yesno(string s) The else clause defines a real as containing a ".", a "-", and/or a e/E. */ -PfStyleInputType arg_type(string token) -{ - const string arrtag("&Arr"); - const string tbltag("&Tbl"); - const string period("."); - const string expchars("eE"); - size_t found; - found=token.find(arrtag); - if(found!=string::npos) return(PFMDARR); - found=token.find(tbltag); - if(found!=string::npos) return(PFMDTBL); - if(yesno(token)!=1) return(PFMDBOOL); - int slen=token.size(); - int i; - bool found_an_e(false); - for(i=0; i alllines; + char inbuffer[512]; + while (inp.getline(inbuffer, 512)) { + const string white(" \n\t"); + // Probably should test for overflow right here - do not know how + string rawline(inbuffer); + if (rawline.find_first_not_of(white, 0) == string::npos) + continue; + if (is_comment_line(rawline)) + continue; + alllines.push_back(rawline); + } + try { + AntelopePf result(alllines); + return (result); + } catch (...) { + throw; + }; } /* This is a helper for the primary constructor below. It searches for closing curly bracket. It returns a count of the number of lines that define this block starting from the iterator first. */ -int find_end_block(list& alllines,list::iterator first) -{ - try { - int level(0); - int count; - list::iterator current,lastline; - lastline=alllines.end(); - for(count=0,current=first; current!=lastline; ++current) - { - ++count; - if(current->find("{")!=string::npos) ++level; - if(current->find("}")!=string::npos) --level; - if(level==0) break; - } - return(count); - } catch(...) { - throw; - }; +int find_end_block(list &alllines, list::iterator first) { + try { + int level(0); + int count; + list::iterator current, lastline; + lastline = alllines.end(); + for (count = 0, current = first; current != lastline; ++current) { + ++count; + if (current->find("{") != string::npos) + ++level; + if (current->find("}") != string::npos) + --level; + if (level == 0) + break; + } + return (count); + } catch (...) { + throw; + }; } -pair split_line(string s) -{ - const string white(" \t"); - const string terminators("\n#"); - size_t is,ie; - is=s.find_first_not_of(white,0); - ie=s.find_first_of(white,is); - string key; - key.assign(s,is,ie-is); - is=s.find_first_not_of(white,ie); - ie=s.find_first_of(terminators,is); - string val; - val.assign(s,is,ie-is); - pair result; - result.first=key; - result.second=val; - return result; +pair split_line(string s) { + const string white(" \t"); + const string terminators("\n#"); + size_t is, ie; + is = s.find_first_not_of(white, 0); + ie = s.find_first_of(white, is); + string key; + key.assign(s, is, ie - is); + is = s.find_first_not_of(white, ie); + ie = s.find_first_of(terminators, is); + string val; + val.assign(s, is, ie - is); + pair result; + result.first = key; + result.second = val; + return result; } /* Note this constructor is recursive. That is when there is a nest &Arr{ in a pf this constructor will call itself for each Arr block */ -AntelopePf::AntelopePf(list alllines) : Metadata() -{ - const string base_error("AntelopePf constructor: "); - list::iterator lptr,end_block; - list block; - string key,token2; - try { - for(lptr=alllines.begin(); lptr!=alllines.end(); ++lptr) - { - int i,ival; - int lines_this_block; - /* This is redundant, but cost is low for long term stability*/ - if(is_comment_line(*lptr)) continue; - /* Older version used a stringstream here but it would not - parse string attributes with embedded white space. This - revised algorithm will do that. returned pair has the - key as first and the string with the key trimmed in second. */ - /* - istringstream is(*lptr); - is>>key; - is>>token2; - */ - pair sl=split_line(*lptr); - key=sl.first; - token2=sl.second; - PfStyleInputType type_of_this_line=arg_type(token2); - switch(type_of_this_line) - { - /* Note all simple type variables are duplicated in - as strings. This is a failsafe mechanism used - because Metadata will always fall back to string - map if the type specific versions fail. Realize - if Metadata changes this will be wasted effort. */ - case PFMDSTRING: - this->put(key,token2); - break; - case PFMDREAL: - this->put(key,token2); - this->put(key,atof(token2.c_str())); - break; - case PFMDINT: - /* save ints as both string and int some string - values could be all digits. */ - this->put(key,token2); - this->put(key,atoi(token2.c_str())); - break; - case PFMDBOOL: - ival=yesno(token2); - if(ival!=0) - this->put(key,true); - else - this->put(key,false); - break; - case PFMDTBL: - case PFMDARR: - lines_this_block=find_end_block(alllines,lptr); - block.clear(); - /* Skips first and last lines in this loop. first with - if and last with termination condition. Note this - will cause problem if a curly back is not alone on - the last line */ - for(i=0; i<(lines_this_block-1); ++i,++lptr) - if(i>0)block.push_back(*lptr); - if(type_of_this_line==PFMDTBL) - pftbls.insert(pair >(key,block)); - else - { - AntelopePf thispfarr(block); - pfbranches.insert(pair - (key,thispfarr)); - } - break; - default: - throw AntelopePfError(base_error - +"Error parsing this line->" - +*lptr); - } +AntelopePf::AntelopePf(list alllines) : Metadata() { + const string base_error("AntelopePf constructor: "); + list::iterator lptr, end_block; + list block; + string key, token2; + try { + for (lptr = alllines.begin(); lptr != alllines.end(); ++lptr) { + int i, ival; + int lines_this_block; + /* This is redundant, but cost is low for long term stability*/ + if (is_comment_line(*lptr)) + continue; + /* Older version used a stringstream here but it would not + parse string attributes with embedded white space. This + revised algorithm will do that. returned pair has the + key as first and the string with the key trimmed in second. */ + /* + istringstream is(*lptr); + is>>key; + is>>token2; + */ + pair sl = split_line(*lptr); + key = sl.first; + token2 = sl.second; + PfStyleInputType type_of_this_line = arg_type(token2); + switch (type_of_this_line) { + /* Note all simple type variables are duplicated in + as strings. This is a failsafe mechanism used + because Metadata will always fall back to string + map if the type specific versions fail. Realize + if Metadata changes this will be wasted effort. */ + case PFMDSTRING: + this->put(key, token2); + break; + case PFMDREAL: + this->put(key, token2); + this->put(key, atof(token2.c_str())); + break; + case PFMDINT: + /* save ints as both string and int some string + values could be all digits. */ + this->put(key, token2); + this->put(key, atoi(token2.c_str())); + break; + case PFMDBOOL: + ival = yesno(token2); + if (ival != 0) + this->put(key, true); + else + this->put(key, false); + break; + case PFMDTBL: + case PFMDARR: + lines_this_block = find_end_block(alllines, lptr); + block.clear(); + /* Skips first and last lines in this loop. first with + if and last with termination condition. Note this + will cause problem if a curly back is not alone on + the last line */ + for (i = 0; i < (lines_this_block - 1); ++i, ++lptr) + if (i > 0) + block.push_back(*lptr); + if (type_of_this_line == PFMDTBL) + pftbls.insert(pair>(key, block)); + else { + AntelopePf thispfarr(block); + pfbranches.insert(pair(key, thispfarr)); } - } catch(...) { - throw; - }; + break; + default: + throw AntelopePfError(base_error + "Error parsing this line->" + *lptr); + } + } + } catch (...) { + throw; + }; } /* This is the private helper function for the PFPATH constructor */ -int AntelopePf::merge_pfmf(AntelopePf& m) -{ - try { - /* Downcast both the current object and m to Metadata and then we - * can use the += operator. Laziness stops me from making this method - * the += operator as that is more or less what it does. */ - Metadata *tptr=dynamic_cast(this); - Metadata *mmmd=dynamic_cast(&m); - *tptr += (*mmmd); - /* Now we accumulate any Arr and Tbl entries. */ - int count(0); - list akey,tkey; - akey=m.arr_keys(); - tkey=m.tbl_keys(); - list::iterator kptr; - for(kptr=tkey.begin(); kptr!=tkey.end(); ++kptr) - { - list t=m.get_tbl(*kptr); - this->pftbls[*kptr]=t; - ++count; - } - for(kptr=akey.begin(); kptr!=akey.end(); ++kptr) - { - AntelopePf pfv; - pfv=m.get_branch(*kptr); - this->pfbranches[*kptr]=pfv; - ++count; - } - return count; - } catch(...) { - throw; - }; +int AntelopePf::merge_pfmf(AntelopePf &m) { + try { + /* Downcast both the current object and m to Metadata and then we + * can use the += operator. Laziness stops me from making this method + * the += operator as that is more or less what it does. */ + Metadata *tptr = dynamic_cast(this); + Metadata *mmmd = dynamic_cast(&m); + *tptr += (*mmmd); + /* Now we accumulate any Arr and Tbl entries. */ + int count(0); + list akey, tkey; + akey = m.arr_keys(); + tkey = m.tbl_keys(); + list::iterator kptr; + for (kptr = tkey.begin(); kptr != tkey.end(); ++kptr) { + list t = m.get_tbl(*kptr); + this->pftbls[*kptr] = t; + ++count; + } + for (kptr = akey.begin(); kptr != akey.end(); ++kptr) { + AntelopePf pfv; + pfv = m.get_branch(*kptr); + this->pfbranches[*kptr] = pfv; + ++count; + } + return count; + } catch (...) { + throw; + }; } /* small helper - splits s into tokens and assures each result is is a pf file with the required .pf ending */ -list split_pfpath(string pfbase, char *s) -{ - /* make sure pfbase ends in .pf. If it doesn't, add it */ - string pftest(".pf"); - std::size_t found; - found=pfbase.find(pftest); - if(found==std::string::npos) pfbase+=pftest; - list pftmp; - char *p; - p=strtok(s,":"); - while(p!=NULL) - { - pftmp.push_back(string(p)); - p=strtok(NULL,":"); +list split_pfpath(string pfbase, char *s) { + /* make sure pfbase ends in .pf. If it doesn't, add it */ + string pftest(".pf"); + std::size_t found; + found = pfbase.find(pftest); + if (found == std::string::npos) + pfbase += pftest; + list pftmp; + char *p; + p = strtok(s, ":"); + while (p != NULL) { + pftmp.push_back(string(p)); + p = strtok(NULL, ":"); + } + list pfret; + list::iterator pfptr; + for (pfptr = pftmp.begin(); pfptr != pftmp.end(); ++pfptr) { + string fname; + fname = (*pfptr) + "/" + pfbase; + pfret.push_back(fname); + } + return pfret; +} +AntelopePf::AntelopePf(string pfbase) { + try { + list pffiles; + const std::string mspass_home_envname("MSPASS_HOME"); + char *base; + /* Note man page for getenv says explicitly the return of getenv should not + be touched - i.e. don't free it*/ + base = getenv(mspass_home_envname.c_str()); + if (base != NULL) { + pffiles.push_back(data_directory() + "/pf/" + pfbase); } - list pfret; - list::iterator pfptr; - for(pfptr=pftmp.begin(); pfptr!=pftmp.end(); ++pfptr) - { - string fname; - fname=(*pfptr)+"/"+pfbase; - pfret.push_back(fname); + const string envname("PFPATH"); + char *s = getenv(envname.c_str()); + if (s == NULL) { + pffiles.push_back(pfbase); } - return pfret; -} -AntelopePf::AntelopePf(string pfbase) -{ - try { - list pffiles; - const std::string mspass_home_envname("MSPASS_HOME"); - char *base; - /* Note man page for getenv says explicitly the return of getenv should not - be touched - i.e. don't free it*/ - base=getenv(mspass_home_envname.c_str()); - if(base!=NULL) - { - pffiles.push_back(data_directory()+"/pf/"+pfbase); - } - const string envname("PFPATH"); - char *s=getenv(envname.c_str()); - if(s==NULL) - { - pffiles.push_back(pfbase); - } - /* Test to see if pfbase is an absolute path - if so just use - * it and ignore the pfpath feature */ - else if(pfbase[0]=='/') - { - pffiles.push_back(pfbase); - } - else - { - pffiles=split_pfpath(pfbase,s); - } - list::iterator pfptr; - int nread; + /* Test to see if pfbase is an absolute path - if so just use + * it and ignore the pfpath feature */ + else if (pfbase[0] == '/') { + pffiles.push_back(pfbase); + } else { + pffiles = split_pfpath(pfbase, s); + } + list::iterator pfptr; + int nread; - for(nread=0,pfptr=pffiles.begin(); pfptr!=pffiles.end(); ++pfptr) - { - // Skip pf files that do not exist - if(access(pfptr->c_str(),R_OK)) continue; - if(nread==0) - { - *this=pfread(*pfptr); - } - else - { - AntelopePf pfnext=pfread(*pfptr); - this->merge_pfmf(pfnext); - } - ++nread; - } - if(nread==0) - { - if(s==NULL) - { - // This was necessary to avoid seg faults when s was a null pointer - // which happens when PFPATH is not set - throw MsPASSError(string("PFPATH is not defined and default pf=") - + pfbase + " was not found",ErrorSeverity::Invalid); - } - else - { - throw MsPASSError(string("PFPATH=")+s - +" had no pf files matching " + pfbase,ErrorSeverity::Invalid); - } - } - } catch(...) { - throw; - }; + for (nread = 0, pfptr = pffiles.begin(); pfptr != pffiles.end(); ++pfptr) { + // Skip pf files that do not exist + if (access(pfptr->c_str(), R_OK)) + continue; + if (nread == 0) { + *this = pfread(*pfptr); + } else { + AntelopePf pfnext = pfread(*pfptr); + this->merge_pfmf(pfnext); + } + ++nread; + } + if (nread == 0) { + if (s == NULL) { + // This was necessary to avoid seg faults when s was a null pointer + // which happens when PFPATH is not set + throw MsPASSError(string("PFPATH is not defined and default pf=") + + pfbase + " was not found", + ErrorSeverity::Invalid); + } else { + throw MsPASSError(string("PFPATH=") + s + " had no pf files matching " + + pfbase, + ErrorSeverity::Invalid); + } + } + } catch (...) { + throw; + }; } -AntelopePf::AntelopePf(const AntelopePf& parent) - : Metadata(parent) -{ - pftbls=parent.pftbls; - pfbranches=parent.pfbranches; +AntelopePf::AntelopePf(const AntelopePf &parent) : Metadata(parent) { + pftbls = parent.pftbls; + pfbranches = parent.pfbranches; } -list AntelopePf::get_tbl(const string key) const -{ - map >::const_iterator iptr; - iptr=pftbls.find(key); - if(iptr==pftbls.end()) throw AntelopePfError( - "get_tbl failed trying to find data for key="+key); - return(iptr->second); +list AntelopePf::get_tbl(const string key) const { + map>::const_iterator iptr; + iptr = pftbls.find(key); + if (iptr == pftbls.end()) + throw AntelopePfError("get_tbl failed trying to find data for key=" + key); + return (iptr->second); } -AntelopePf AntelopePf::get_branch(const string key) const -{ - map::const_iterator iptr; - iptr=pfbranches.find(key); - if(iptr==pfbranches.end()) throw AntelopePfError( - "get_branch failed trying to find data for key="+key); - return iptr->second; +AntelopePf AntelopePf::get_branch(const string key) const { + map::const_iterator iptr; + iptr = pfbranches.find(key); + if (iptr == pfbranches.end()) + throw AntelopePfError("get_branch failed trying to find data for key=" + + key); + return iptr->second; } -list AntelopePf::arr_keys() const -{ - map::const_iterator iptr; - list result; - for(iptr=pfbranches.begin(); iptr!=pfbranches.end(); ++iptr) - result.push_back((*iptr).first); - return result; +list AntelopePf::arr_keys() const { + map::const_iterator iptr; + list result; + for (iptr = pfbranches.begin(); iptr != pfbranches.end(); ++iptr) + result.push_back((*iptr).first); + return result; } -list AntelopePf::tbl_keys() const -{ - map >::const_iterator iptr; - list result; - for(iptr=pftbls.begin(); iptr!=pftbls.end(); ++iptr) - result.push_back((*iptr).first); - return(result); +list AntelopePf::tbl_keys() const { + map>::const_iterator iptr; + list result; + for (iptr = pftbls.begin(); iptr != pftbls.end(); ++iptr) + result.push_back((*iptr).first); + return (result); } -AntelopePf& AntelopePf::operator=(const AntelopePf& parent) -{ - if(this!=&parent) - { - /* This uses a trick to use operator = of the parent object */ - this->Metadata::operator=(parent); - pftbls=parent.pftbls; - pfbranches=parent.pfbranches; - } - return(*this); +AntelopePf &AntelopePf::operator=(const AntelopePf &parent) { + if (this != &parent) { + /* This uses a trick to use operator = of the parent object */ + this->Metadata::operator=(parent); + pftbls = parent.pftbls; + pfbranches = parent.pfbranches; + } + return (*this); } -Metadata AntelopePf::ConvertToMetadata() -{ - try { - Metadata result(dynamic_cast(*this)); - boost::any aTbl=this->pftbls; - //boost::any> aTbl(this->pftbl); - boost::any aArr=this->pfbranches; - result.put(pftbl_key,aTbl); - result.put(pfarr_key,aArr); - return result; - } catch(...) { - throw; - }; +Metadata AntelopePf::ConvertToMetadata() { + try { + Metadata result(dynamic_cast(*this)); + boost::any aTbl = this->pftbls; + // boost::any> aTbl(this->pftbl); + boost::any aArr = this->pfbranches; + result.put(pftbl_key, aTbl); + result.put(pfarr_key, aArr); + return result; + } catch (...) { + throw; + }; } -} // End mspass::utility Namespace declaration +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/AttributeCrossReference.cc b/cxx/src/lib/utility/AttributeCrossReference.cc index b430d7663..ea649a908 100644 --- a/cxx/src/lib/utility/AttributeCrossReference.cc +++ b/cxx/src/lib/utility/AttributeCrossReference.cc @@ -1,178 +1,159 @@ -#include #include "mspass/utility/AttributeCrossReference.h" +#include using namespace std; using namespace mspass::utility; namespace mspass::utility { -AttributeCrossReference::AttributeCrossReference(const string lines_to_parse) -{ - try{ +AttributeCrossReference::AttributeCrossReference(const string lines_to_parse) { + try { istringstream instrm(lines_to_parse); - do - { - string inkey,outkey; - string typestr; - instrm>>inkey; - // unfortunately this is the normal exit - if(instrm.eof()) break; - instrm>>outkey; - instrm>>typestr; - itoe.insert(pair(inkey,outkey)); - etoi.insert(pair(outkey,inkey)); - if(typestr=="int" || typestr=="INT" || typestr=="integer") - imdtypemap.insert(pair(inkey,MDtype::Integer)); - else if(typestr=="real" || typestr=="REAL" || typestr=="double") - imdtypemap.insert(pair(inkey,MDtype::Double)); - else if(typestr=="bool" || typestr=="BOOL" || typestr=="boolean") - imdtypemap.insert(pair(inkey,MDtype::Boolean)); - else if(typestr=="string" || typestr=="STRING") - imdtypemap.insert(pair(inkey,MDtype::String)); - else - { - /* Note in seispp this condition only logs and error and - * sets attribute invalid. Here we throw an exception as - * this would always be called on startup and should normally - * cause an abort */ - stringstream sserr; - sserr << "AttributeCrossReference string constructor: " - <<" Attribute with tag="<> inkey; + // unfortunately this is the normal exit + if (instrm.eof()) + break; + instrm >> outkey; + instrm >> typestr; + itoe.insert(pair(inkey, outkey)); + etoi.insert(pair(outkey, inkey)); + if (typestr == "int" || typestr == "INT" || typestr == "integer") + imdtypemap.insert(pair(inkey, MDtype::Integer)); + else if (typestr == "real" || typestr == "REAL" || typestr == "double") + imdtypemap.insert(pair(inkey, MDtype::Double)); + else if (typestr == "bool" || typestr == "BOOL" || typestr == "boolean") + imdtypemap.insert(pair(inkey, MDtype::Boolean)); + else if (typestr == "string" || typestr == "STRING") + imdtypemap.insert(pair(inkey, MDtype::String)); + else { + /* Note in seispp this condition only logs and error and + * sets attribute invalid. Here we throw an exception as + * this would always be called on startup and should normally + * cause an abort */ + stringstream sserr; + sserr << "AttributeCrossReference string constructor: " + << " Attribute with tag=" << inkey << " is tagged with an " + << "illegal type name=" << typestr << endl + << "Repair input data passed to this constructor" << endl; + throw MsPASSError(sserr.str()); + } - }while(!instrm.eof()); - }catch(...){throw;}; + } while (!instrm.eof()); + } catch (...) { + throw; + }; } -/*! This constructor was produced by a revisio of the previous to change +/*! This constructor was produced by a revisio of the previous to change * from a single string to a list container. */ -AttributeCrossReference::AttributeCrossReference(const list& lines) -{ - list::const_iterator lptr; - for(lptr=lines.begin();lptr!=lines.end();++lptr) - { - istringstream instrm(*lptr); - string inkey,outkey; - string typestr; - instrm>>inkey; - instrm>>outkey; - instrm>>typestr; - itoe.insert(pair(inkey,outkey)); - etoi.insert(pair(outkey,inkey)); - if(typestr=="int" || typestr=="INT" || typestr=="integer") - imdtypemap.insert(pair(inkey,MDtype::Integer)); - else if(typestr=="real" || typestr=="REAL" || typestr=="double") - imdtypemap.insert(pair(inkey,MDtype::Double)); - else if(typestr=="bool" || typestr=="BOOL" || typestr=="boolean") - imdtypemap.insert(pair(inkey,MDtype::Boolean)); - else if(typestr=="string" || typestr=="STRING") - imdtypemap.insert(pair(inkey,MDtype::String)); - else - { - stringstream sserr; - sserr << "AttributeCrossReference string constructor: " - <<" Attribute with tag="< &lines) { + list::const_iterator lptr; + for (lptr = lines.begin(); lptr != lines.end(); ++lptr) { + istringstream instrm(*lptr); + string inkey, outkey; + string typestr; + instrm >> inkey; + instrm >> outkey; + instrm >> typestr; + itoe.insert(pair(inkey, outkey)); + etoi.insert(pair(outkey, inkey)); + if (typestr == "int" || typestr == "INT" || typestr == "integer") + imdtypemap.insert(pair(inkey, MDtype::Integer)); + else if (typestr == "real" || typestr == "REAL" || typestr == "double") + imdtypemap.insert(pair(inkey, MDtype::Double)); + else if (typestr == "bool" || typestr == "BOOL" || typestr == "boolean") + imdtypemap.insert(pair(inkey, MDtype::Boolean)); + else if (typestr == "string" || typestr == "STRING") + imdtypemap.insert(pair(inkey, MDtype::String)); + else { + stringstream sserr; + sserr << "AttributeCrossReference string constructor: " + << " Attribute with tag=" << inkey << " is tagged with an " + << "illegal type name=" << typestr << endl + << "Repair input data passed to this constructor" << endl; + throw MsPASSError(sserr.str()); } + } } -AttributeCrossReference::AttributeCrossReference(const map int2ext, - const MetadataList& mdlist) -{ - MetadataList::const_iterator mdlptr; - for(mdlptr=mdlist.begin();mdlptr!=mdlist.end();++mdlptr) - { - imdtypemap.insert(pair(mdlptr->tag,mdlptr->mdt)); - } - itoe=int2ext; - map::iterator iptr; - /* This extracts each pair of the map and inverts them */ - for(iptr=itoe.begin();iptr!=itoe.end();++iptr) - etoi.insert(pair(iptr->second,iptr->first)); +AttributeCrossReference::AttributeCrossReference( + const map int2ext, const MetadataList &mdlist) { + MetadataList::const_iterator mdlptr; + for (mdlptr = mdlist.begin(); mdlptr != mdlist.end(); ++mdlptr) { + imdtypemap.insert(pair(mdlptr->tag, mdlptr->mdt)); + } + itoe = int2ext; + map::iterator iptr; + /* This extracts each pair of the map and inverts them */ + for (iptr = itoe.begin(); iptr != itoe.end(); ++iptr) + etoi.insert(pair(iptr->second, iptr->first)); } -AttributeCrossReference::AttributeCrossReference - (const AttributeCrossReference& parent) -{ - itoe=parent.itoe; - etoi=parent.etoi; - imdtypemap=parent.imdtypemap; +AttributeCrossReference::AttributeCrossReference( + const AttributeCrossReference &parent) { + itoe = parent.itoe; + etoi = parent.etoi; + imdtypemap = parent.imdtypemap; } -AttributeCrossReference& AttributeCrossReference::operator= - (const AttributeCrossReference& parent) -{ - if(this!=&parent) - { - itoe=parent.itoe; - etoi=parent.etoi; - imdtypemap=parent.imdtypemap; - } - return(*this); +AttributeCrossReference & +AttributeCrossReference::operator=(const AttributeCrossReference &parent) { + if (this != &parent) { + itoe = parent.itoe; + etoi = parent.etoi; + imdtypemap = parent.imdtypemap; + } + return (*this); } -string AttributeCrossReference::internal(const string key) const -{ - map::const_iterator iptr; - iptr=etoi.find(key); - if(iptr==etoi.end()) - throw MsPASSError(string("AttribureCrossReference::internal: ") - + "Cannot find attribute "+key - + " in external to internal namespace map"); - return(iptr->second); +string AttributeCrossReference::internal(const string key) const { + map::const_iterator iptr; + iptr = etoi.find(key); + if (iptr == etoi.end()) + throw MsPASSError(string("AttribureCrossReference::internal: ") + + "Cannot find attribute " + key + + " in external to internal namespace map"); + return (iptr->second); } -string AttributeCrossReference::external(const string key) const -{ - map::const_iterator iptr; - iptr=itoe.find(key); - if(iptr==itoe.end()) - throw MsPASSError(string("AttribureCrossReference::external: ") - + "Cannot find attribute "+key - + " in internal to external namespace map"); - return(iptr->second); +string AttributeCrossReference::external(const string key) const { + map::const_iterator iptr; + iptr = itoe.find(key); + if (iptr == itoe.end()) + throw MsPASSError(string("AttribureCrossReference::external: ") + + "Cannot find attribute " + key + + " in internal to external namespace map"); + return (iptr->second); } -MDtype AttributeCrossReference::type(const string key) const -{ - map::const_iterator iptr; - iptr=imdtypemap.find(key); - if(iptr==imdtypemap.end()) - throw MsPASSError(string("AttributeCrossReference::type: ") - + "Cannot find attribute "+key - + " in type definitions"); - return(iptr->second); +MDtype AttributeCrossReference::type(const string key) const { + map::const_iterator iptr; + iptr = imdtypemap.find(key); + if (iptr == imdtypemap.end()) + throw MsPASSError(string("AttributeCrossReference::type: ") + + "Cannot find attribute " + key + " in type definitions"); + return (iptr->second); } -int AttributeCrossReference::size() const -{ - // Assume the two maps are the same size - return(itoe.size()); +int AttributeCrossReference::size() const { + // Assume the two maps are the same size + return (itoe.size()); } -void AttributeCrossReference::put(const string i, const string e) -{ - itoe.insert(pair(i,e)); - etoi.insert(pair(e,i)); +void AttributeCrossReference::put(const string i, const string e) { + itoe.insert(pair(i, e)); + etoi.insert(pair(e, i)); } /* These two methods could use either the etoi or the itoe containers to fetch the appropriate keys, but we always use fetch the first of the pair as that will always yield a unique set that exactly matches the originating map. */ -set AttributeCrossReference::internal_names() const -{ - map::const_iterator mptr; +set AttributeCrossReference::internal_names() const { + map::const_iterator mptr; set keys; - for(mptr=itoe.begin();mptr!=itoe.end();++mptr) - { + for (mptr = itoe.begin(); mptr != itoe.end(); ++mptr) { keys.insert(mptr->first); } return keys; } -set AttributeCrossReference::external_names() const -{ - map::const_iterator mptr; +set AttributeCrossReference::external_names() const { + map::const_iterator mptr; set keys; - for(mptr=etoi.begin();mptr!=etoi.end();++mptr) - { + for (mptr = etoi.begin(); mptr != etoi.end(); ++mptr) { keys.insert(mptr->first); } return keys; } -} // end mspass::utility namespace encapsulation +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/AttributeMap.cc b/cxx/src/lib/utility/AttributeMap.cc index 2f06f3614..cfa7fbf2d 100644 --- a/cxx/src/lib/utility/AttributeMap.cc +++ b/cxx/src/lib/utility/AttributeMap.cc @@ -1,8 +1,8 @@ -#include -#include "mspass/utility/utility.h" -#include "mspass/utility/Metadata.h" #include "mspass/utility/AttributeMap.h" -namespace mspass::utility{ +#include "mspass/utility/Metadata.h" +#include "mspass/utility/utility.h" +#include +namespace mspass::utility { using namespace std; using namespace mspass::utility; // AttributeProperties encapsulate concepts about what a piece @@ -10,338 +10,329 @@ using namespace mspass::utility; // extracted from a relational database as the model. The // AttributeProperties object, however, should be more general // than this as it can support things like lists ala antelope tbls. -AttributeProperties::AttributeProperties() -{ - db_attribute_name="none"; - db_table_name=""; - internal_name="NULL"; - mdt=MDtype::String; +AttributeProperties::AttributeProperties() { + db_attribute_name = "none"; + db_table_name = ""; + internal_name = "NULL"; + mdt = MDtype::String; } -AttributeProperties::AttributeProperties(const string st) -{ - const string white(" \t\n"); - int current=0; - int end_current; - string mdtype_word; - string bool_word; - string stmp; // use temporary to allow string edits - const string emess("AttributeProperties(string) constructor failure: failure parsing the following string:\n"); +AttributeProperties::AttributeProperties(const string st) { + const string white(" \t\n"); + int current = 0; + int end_current; + string mdtype_word; + string bool_word; + string stmp; // use temporary to allow string edits + const string emess("AttributeProperties(string) constructor failure: " + "failure parsing the following string:\n"); - if(st.empty()) - throw MsPASSError(string("AttributeProperties constructor failure; Constructor was passed an empty string\n"), - ErrorSeverity::Invalid); + if (st.empty()) + throw MsPASSError(string("AttributeProperties constructor failure; " + "Constructor was passed an empty string\n"), + ErrorSeverity::Invalid); - // this should find first nonwhite position + // this should find first nonwhite position - // create a copy of st and remove any leading white - // space at the head of the string - stmp = st; - if((current=stmp.find_first_not_of(white,0)) != 0) - { - stmp.erase(0,current); - current = 0; - } - /* This series of throw statements assume the default for - ErrorSeverity is Invalid which means the constructor failed.*/ - end_current=stmp.find_first_of(white,current); - if(end_current<0) throw MsPASSError(string(emess+st)); - internal_name.assign(stmp,current,end_current-current); - current = stmp.find_first_not_of(white,end_current); - if(current<0) throw MsPASSError(string(emess+st)); - end_current=stmp.find_first_of(white,current); - if(end_current<0) throw MsPASSError(string(emess+st)); - db_attribute_name.assign(stmp,current,end_current-current); - current = stmp.find_first_not_of(white,end_current); - if(current<0) throw MsPASSError(string(emess+st)); - end_current=stmp.find_first_of(white,current); - if(end_current<0) throw MsPASSError(string(emess+st)); - db_table_name.assign(stmp,current,end_current-current); - current = stmp.find_first_not_of(white,end_current); - if(current<0) throw MsPASSError(string(emess+st)); - end_current=stmp.find_first_of(white,current); - if(end_current<0) end_current=stmp.length(); - mdtype_word.assign(stmp,current,end_current-current); - if(mdtype_word=="REAL" || mdtype_word=="real") - mdt=MDtype::Double; - else if(mdtype_word=="INT" || mdtype_word=="int" - || mdtype_word=="integer") - mdt = MDtype::Integer; - else if(mdtype_word=="STRING" || mdtype_word=="string") - mdt=MDtype::String; - else if(mdtype_word=="BOOL" || mdtype_word=="bool" - || mdtype_word=="BOOLEAN" || mdtype_word=="boolean") - mdt=MDtype::Boolean; - else - { - mdt = MDtype::Invalid; - throw MsPASSError(string("AttributeProperties(string) constructor: Unrecognized metadata type = ")+mdtype_word,ErrorSeverity::Invalid); - } - // optional is_key field. Defaulted false - is_key = false; + // create a copy of st and remove any leading white + // space at the head of the string + stmp = st; + if ((current = stmp.find_first_not_of(white, 0)) != 0) { + stmp.erase(0, current); + current = 0; + } + /* This series of throw statements assume the default for + ErrorSeverity is Invalid which means the constructor failed.*/ + end_current = stmp.find_first_of(white, current); + if (end_current < 0) + throw MsPASSError(string(emess + st)); + internal_name.assign(stmp, current, end_current - current); + current = stmp.find_first_not_of(white, end_current); + if (current < 0) + throw MsPASSError(string(emess + st)); + end_current = stmp.find_first_of(white, current); + if (end_current < 0) + throw MsPASSError(string(emess + st)); + db_attribute_name.assign(stmp, current, end_current - current); + current = stmp.find_first_not_of(white, end_current); + if (current < 0) + throw MsPASSError(string(emess + st)); + end_current = stmp.find_first_of(white, current); + if (end_current < 0) + throw MsPASSError(string(emess + st)); + db_table_name.assign(stmp, current, end_current - current); + current = stmp.find_first_not_of(white, end_current); + if (current < 0) + throw MsPASSError(string(emess + st)); + end_current = stmp.find_first_of(white, current); + if (end_current < 0) + end_current = stmp.length(); + mdtype_word.assign(stmp, current, end_current - current); + if (mdtype_word == "REAL" || mdtype_word == "real") + mdt = MDtype::Double; + else if (mdtype_word == "INT" || mdtype_word == "int" || + mdtype_word == "integer") + mdt = MDtype::Integer; + else if (mdtype_word == "STRING" || mdtype_word == "string") + mdt = MDtype::String; + else if (mdtype_word == "BOOL" || mdtype_word == "bool" || + mdtype_word == "BOOLEAN" || mdtype_word == "boolean") + mdt = MDtype::Boolean; + else { + mdt = MDtype::Invalid; + throw MsPASSError(string("AttributeProperties(string) constructor: " + "Unrecognized metadata type = ") + + mdtype_word, + ErrorSeverity::Invalid); + } + // optional is_key field. Defaulted false + is_key = false; - current = stmp.find_first_not_of(white,end_current); - if(current>=0) - { - end_current=stmp.find_first_of(white,current); - if(end_current<0) end_current=stmp.length(); - bool_word.assign(stmp,current,end_current-current); - if(bool_word=="yes" || bool_word=="true" - || bool_word=="TRUE") is_key=true; - } + current = stmp.find_first_not_of(white, end_current); + if (current >= 0) { + end_current = stmp.find_first_of(white, current); + if (end_current < 0) + end_current = stmp.length(); + bool_word.assign(stmp, current, end_current - current); + if (bool_word == "yes" || bool_word == "true" || bool_word == "TRUE") + is_key = true; + } } -AttributeProperties::AttributeProperties(const AttributeProperties& apin) -{ - db_attribute_name=apin.db_attribute_name; - db_table_name=apin.db_table_name; - internal_name=apin.internal_name; - mdt = apin.mdt; - is_key = apin.is_key; +AttributeProperties::AttributeProperties(const AttributeProperties &apin) { + db_attribute_name = apin.db_attribute_name; + db_table_name = apin.db_table_name; + internal_name = apin.internal_name; + mdt = apin.mdt; + is_key = apin.is_key; } -AttributeProperties& AttributeProperties::operator=(const AttributeProperties& apin) -{ - if(&apin==this)return(*this); - db_attribute_name=apin.db_attribute_name; - db_table_name=apin.db_table_name; - internal_name=apin.internal_name; - mdt = apin.mdt; - is_key = apin.is_key; - return(*this); +AttributeProperties & +AttributeProperties::operator=(const AttributeProperties &apin) { + if (&apin == this) + return (*this); + db_attribute_name = apin.db_attribute_name; + db_table_name = apin.db_table_name; + internal_name = apin.internal_name; + mdt = apin.mdt; + is_key = apin.is_key; + return (*this); } -string AttributeProperties::fully_qualified_name() const -{ - string result; - result=db_table_name + "." + db_attribute_name; - return(result); +string AttributeProperties::fully_qualified_name() const { + string result; + result = db_table_name + "." + db_attribute_name; + return (result); } -ostream& operator<<(ostream& ofs, const AttributeProperties& d) -{ - ofs< attbl=pfnested.get_tbl("Attributes"); - if(attbl.size()<=0) throw MsPASSError(base_error - + "Attributes table is empty. Is parameter file may be missing Attributes Tbl?"); - list::iterator tptr; - for(tptr=attbl.begin();tptr!=attbl.end();++tptr) - { - AttributeProperties ap(*tptr); - attributes[ap.internal_name]=ap; - } - list alias_tbl=pfnested.get_tbl("aliases"); - /* Assume the aliasmap container is propertly initialized in the (possible) - condition that the aliases section is empty. */ - for(tptr=alias_tbl.begin();tptr!=alias_tbl.end();++tptr) - { - string key,token; - istringstream in(*tptr); - in>>key; - if(key.length()<=0) - { - throw MsPASSError(base_error + "illegal line in aliases table ->" - + (*tptr) +"<-\nFirst token must be internal name key"); - } - /* We allow multiple aliases for the same internal name key. Hence - we have to parse this whole line */ - in>>token; - if(token.length()<=0) throw MsPASSError(base_error - + "Empty list of aliases in this input line\n" +(*tptr)); - list aliaslist; - while(!in.eof()) - { - aliaslist.push_back(token); - in>>token; - } - aliaslist.push_back(token); - aliasmap[key]=aliaslist; - } - }catch(...){throw;}; +AttributeMap::AttributeMap(const AntelopePf &pf, const string name) { + const string base_error("AttributeMap AntelopePf constructor: "); + try { + AntelopePf pfnested = pf.get_branch(name); + list attbl = pfnested.get_tbl("Attributes"); + if (attbl.size() <= 0) + throw MsPASSError(base_error + "Attributes table is empty. Is parameter " + "file may be missing Attributes Tbl?"); + list::iterator tptr; + for (tptr = attbl.begin(); tptr != attbl.end(); ++tptr) { + AttributeProperties ap(*tptr); + attributes[ap.internal_name] = ap; + } + list alias_tbl = pfnested.get_tbl("aliases"); + /* Assume the aliasmap container is propertly initialized in the (possible) + condition that the aliases section is empty. */ + for (tptr = alias_tbl.begin(); tptr != alias_tbl.end(); ++tptr) { + string key, token; + istringstream in(*tptr); + in >> key; + if (key.length() <= 0) { + throw MsPASSError(base_error + "illegal line in aliases table ->" + + (*tptr) + + "<-\nFirst token must be internal name key"); + } + /* We allow multiple aliases for the same internal name key. Hence + we have to parse this whole line */ + in >> token; + if (token.length() <= 0) + throw MsPASSError(base_error + + "Empty list of aliases in this input line\n" + + (*tptr)); + list aliaslist; + while (!in.eof()) { + aliaslist.push_back(token); + in >> token; + } + aliaslist.push_back(token); + aliasmap[key] = aliaslist; + } + } catch (...) { + throw; + }; } -AttributeMap::AttributeMap(const string mapname) -{ - const string base_error("AttributeMap constructor:"); - const string pfname("attribute_maps.pf"); - string datadir,pfdir,pffile; - try{ - datadir=mspass::utility::data_directory(); - pfdir=datadir+"/pf/"; - pffile=pfdir+pfname; - AntelopePf pfall_maps(pffile); - *this=AttributeMap(pfall_maps,mapname); - }catch(...){throw;}; +AttributeMap::AttributeMap(const string mapname) { + const string base_error("AttributeMap constructor:"); + const string pfname("attribute_maps.pf"); + string datadir, pfdir, pffile; + try { + datadir = mspass::utility::data_directory(); + pfdir = datadir + "/pf/"; + pffile = pfdir + pfname; + AntelopePf pfall_maps(pffile); + *this = AttributeMap(pfall_maps, mapname); + } catch (...) { + throw; + }; } /* The default constructor can be implemented as a special case of core constructor that specifies a specific schema. What is defined as default is hard coded here though */ -AttributeMap::AttributeMap() -{ - const string DEFAULT_SCHEMA_NAME("css3.0"); - try{ - (*this)=AttributeMap(DEFAULT_SCHEMA_NAME); - }catch(...){throw;}; +AttributeMap::AttributeMap() { + const string DEFAULT_SCHEMA_NAME("css3.0"); + try { + (*this) = AttributeMap(DEFAULT_SCHEMA_NAME); + } catch (...) { + throw; + }; } -AttributeMap& AttributeMap::operator=(const AttributeMap& am) -{ - if(this!=&am) - { - attributes = am.attributes; - aliasmap=am.aliasmap; - } - return (*this); +AttributeMap &AttributeMap::operator=(const AttributeMap &am) { + if (this != &am) { + attributes = am.attributes; + aliasmap = am.aliasmap; + } + return (*this); } -AttributeMap::AttributeMap(const AttributeMap& am) -{ - attributes = am.attributes; - aliasmap=am.aliasmap; +AttributeMap::AttributeMap(const AttributeMap &am) { + attributes = am.attributes; + aliasmap = am.aliasmap; } -bool AttributeMap::is_alias(const string key) const -{ - if(aliasmap.size()==0) return false; - if(aliasmap.find(key)==aliasmap.end()) return false; - return true; +bool AttributeMap::is_alias(const string key) const { + if (aliasmap.size() == 0) + return false; + if (aliasmap.find(key) == aliasmap.end()) + return false; + return true; } -bool AttributeMap::is_alias(const char *key) const -{ - return this->is_alias(string(key)); +bool AttributeMap::is_alias(const char *key) const { + return this->is_alias(string(key)); } -map AttributeMap::aliases(const string key) const -{ - map result; - /* reverse logic a bit odd, but cleanest solution */ - if(!this->is_alias(key)) - throw MsPASSError("AttributeMap::aliases method: " - + string("Attribute ") + key - + string(" is not defined as an alias") ); - else - { - map>::const_iterator aptr; - /* We don't need to test if this find works because is_alias - will not return true unless it is found */ - aptr=aliasmap.find(key); - list aml(aptr->second); - map::const_iterator amiter; - list::const_iterator listiter; - for(listiter=aml.begin();listiter!=aml.end();++listiter) - { - amiter=attributes.find(*listiter); - if(amiter==attributes.end()) - throw MsPASSError("AttributeMap::aliases method: " - + string("Attribute named ") - + (*listiter) - + string(" is not defined for this AttributeMap")); - /* We need to copy this AttributeProperty and - * change the internal_name to the alias name */ - AttributeProperties alias_property(amiter->second); - alias_property.internal_name=key; - result[amiter->second.db_table_name]=alias_property; - } - } - /* note this silently returns an empty list if key is not alias*/ - return(result); - +map AttributeMap::aliases(const string key) const { + map result; + /* reverse logic a bit odd, but cleanest solution */ + if (!this->is_alias(key)) + throw MsPASSError("AttributeMap::aliases method: " + string("Attribute ") + + key + string(" is not defined as an alias")); + else { + map>::const_iterator aptr; + /* We don't need to test if this find works because is_alias + will not return true unless it is found */ + aptr = aliasmap.find(key); + list aml(aptr->second); + map::const_iterator amiter; + list::const_iterator listiter; + for (listiter = aml.begin(); listiter != aml.end(); ++listiter) { + amiter = attributes.find(*listiter); + if (amiter == attributes.end()) + throw MsPASSError( + "AttributeMap::aliases method: " + string("Attribute named ") + + (*listiter) + string(" is not defined for this AttributeMap")); + /* We need to copy this AttributeProperty and + * change the internal_name to the alias name */ + AttributeProperties alias_property(amiter->second); + alias_property.internal_name = key; + result[amiter->second.db_table_name] = alias_property; + } + } + /* note this silently returns an empty list if key is not alias*/ + return (result); } -map AttributeMap::aliases(const char *key) const -{ - try{ - return this->aliases(string(key)); - }catch(...){throw;}; +map AttributeMap::aliases(const char *key) const { + try { + return this->aliases(string(key)); + } catch (...) { + throw; + }; } /* This code has very strong parallels to aliases because they do similar things even though they return very different results. */ -list AttributeMap::aliastables(const string key) const -{ - list result; - if(!this->is_alias(key)) - throw MsPASSError("AttributeMap::aliastables method: " - + string("Attribute ") + key - + string(" is not defined as an alias") ); - else - { - map>::const_iterator aliasptr; - aliasptr=aliasmap.find(key); - /* We don't have to test that aliasptr is valid because - the is_alias method test assures the find will yield - a valid iterator*/ - list aml(aliasptr->second); - map::const_iterator amiter; - list::iterator listiter; - for(listiter=aml.begin();listiter!=aml.end();++listiter) - { - amiter=attributes.find(*listiter); - if(amiter==attributes.end()) - throw MsPASSError("AttributeMap::aliastables method: " - + string("Attribute named ") - + (*listiter) - + string(" is not defined for this AttributeMap")); - result.push_back(amiter->second.db_table_name); - } - } - return(result); +list AttributeMap::aliastables(const string key) const { + list result; + if (!this->is_alias(key)) + throw MsPASSError( + "AttributeMap::aliastables method: " + string("Attribute ") + key + + string(" is not defined as an alias")); + else { + map>::const_iterator aliasptr; + aliasptr = aliasmap.find(key); + /* We don't have to test that aliasptr is valid because + the is_alias method test assures the find will yield + a valid iterator*/ + list aml(aliasptr->second); + map::const_iterator amiter; + list::iterator listiter; + for (listiter = aml.begin(); listiter != aml.end(); ++listiter) { + amiter = attributes.find(*listiter); + if (amiter == attributes.end()) + throw MsPASSError( + "AttributeMap::aliastables method: " + string("Attribute named ") + + (*listiter) + string(" is not defined for this AttributeMap")); + result.push_back(amiter->second.db_table_name); + } + } + return (result); } -list AttributeMap::aliastables(const char *key) const -{ - try{ - return this->aliastables(string(key)); - }catch(...){throw;}; +list AttributeMap::aliastables(const char *key) const { + try { + return this->aliastables(string(key)); + } catch (...) { + throw; + }; } -AttributeProperties AttributeMap::operator[](const std::string key) const -{ - map::const_iterator amptr; - amptr=attributes.find(key); - if(amptr==attributes.end()) - { - throw MsPASSError(string("AttributeMap::opertor[]: key=") - + key + " has not entry in this AttributeMap"); - } - else - { - return amptr->second; - } +AttributeProperties AttributeMap::operator[](const std::string key) const { + map::const_iterator amptr; + amptr = attributes.find(key); + if (amptr == attributes.end()) { + throw MsPASSError(string("AttributeMap::opertor[]: key=") + key + + " has not entry in this AttributeMap"); + } else { + return amptr->second; + } } -AttributeProperties AttributeMap::operator[](const char *key) const -{ - try{ - return this->operator[](string(key)); - }catch(...){throw;}; +AttributeProperties AttributeMap::operator[](const char *key) const { + try { + return this->operator[](string(key)); + } catch (...) { + throw; + }; } - -} // End mspass::utility Namespace declaration +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/ErrorLogger.cc b/cxx/src/lib/utility/ErrorLogger.cc index c1b8afb13..d31c2c7ff 100644 --- a/cxx/src/lib/utility/ErrorLogger.cc +++ b/cxx/src/lib/utility/ErrorLogger.cc @@ -1,100 +1,89 @@ #include "mspass/utility/ErrorLogger.h" using namespace mspass::utility; -namespace mspass::utility -{ +namespace mspass::utility { using namespace std; /* First code for the LogData class that are too long for inline */ LogData::LogData(const int jid, const std::string alg, - const mspass::utility::MsPASSError& merr) -{ - job_id=jid; - p_id=getpid(); - algorithm=alg; - message=merr.what(); - badness=merr.severity(); + const mspass::utility::MsPASSError &merr) { + job_id = jid; + p_id = getpid(); + algorithm = alg; + message = merr.what(); + badness = merr.severity(); } -LogData::LogData(const int jid, const std::string alg, const std::string msg, const mspass::utility::ErrorSeverity lvl) -{ - job_id=jid; - p_id=getpid(); - algorithm=alg; - message=msg; - badness=lvl; +LogData::LogData(const int jid, const std::string alg, const std::string msg, + const mspass::utility::ErrorSeverity lvl) { + job_id = jid; + p_id = getpid(); + algorithm = alg; + message = msg; + badness = lvl; } -ostream& operator<<(ostream& ofs, LogData& ld) -{ - switch(ld.badness) - { - case ErrorSeverity::Fatal: - ofs<<"Fatal "; - break; - case ErrorSeverity::Invalid: - ofs<<"Invalid "; - break; - case ErrorSeverity::Complaint: - ofs<<"Complaint "; - break; - case ErrorSeverity::Debug: - ofs<<"Debug "; - break; - case ErrorSeverity::Informational: - ofs<<"Informational "; - break; - default: - ofs<<"Undefined"<allmessages.push_back(*lptr); } } return *this; } -int ErrorLogger::log_error(const mspass::utility::MsPASSError& merr) -{ - LogData thislog(this->job_id,string("MsPASSError"),merr); +int ErrorLogger::log_error(const mspass::utility::MsPASSError &merr) { + LogData thislog(this->job_id, string("MsPASSError"), merr); allmessages.push_back(thislog); return allmessages.size(); } -int ErrorLogger::log_error(const std::string alg, const std::string mess, - const mspass::utility::ErrorSeverity level=ErrorSeverity::Invalid) -{ - LogData thislog(this->job_id,alg,mess,level); +int ErrorLogger::log_error( + const std::string alg, const std::string mess, + const mspass::utility::ErrorSeverity level = ErrorSeverity::Invalid) { + LogData thislog(this->job_id, alg, mess, level); allmessages.push_back(thislog); return allmessages.size(); } -int ErrorLogger::log_verbose(const std::string alg,const std::string mess) -{ +int ErrorLogger::log_verbose(const std::string alg, const std::string mess) { int count; - count=this->log_error(alg,mess,ErrorSeverity::Informational); + count = this->log_error(alg, mess, ErrorSeverity::Informational); return count; }; /* This method needs to return a list of the highest ranking @@ -104,41 +93,44 @@ equivalence of an enum with an ordered list of ints. Hence, we have a bit of an ugly algorithm here. The algorithm is a memory hog and would be a bad idea if the log got huge, but for the indended use in mspass that should never happen. User beware if transported though - glp 9/28/2019.*/ -list ErrorLogger::worst_errors() const -{ +list ErrorLogger::worst_errors() const { /* Return immediately if the messages container is empty. */ - if(allmessages.size()<=0) return allmessages; - list flist,ivlist,slist,clist,dlist,ilist; + if (allmessages.size() <= 0) + return allmessages; + list flist, ivlist, slist, clist, dlist, ilist; list::const_iterator aptr; - for(aptr=allmessages.begin();aptr!=allmessages.end();++aptr) - { - switch(aptr->badness) - { - case ErrorSeverity::Fatal: - flist.push_back(*aptr); - break; - case ErrorSeverity::Invalid: - ivlist.push_back(*aptr); - break; - case ErrorSeverity::Suspect: - slist.push_back(*aptr); - break; - case ErrorSeverity::Complaint: - clist.push_back(*aptr); - break; - case ErrorSeverity::Debug: - dlist.push_back(*aptr); - break; - case ErrorSeverity::Informational: - default: - ilist.push_back(*aptr); + for (aptr = allmessages.begin(); aptr != allmessages.end(); ++aptr) { + switch (aptr->badness) { + case ErrorSeverity::Fatal: + flist.push_back(*aptr); + break; + case ErrorSeverity::Invalid: + ivlist.push_back(*aptr); + break; + case ErrorSeverity::Suspect: + slist.push_back(*aptr); + break; + case ErrorSeverity::Complaint: + clist.push_back(*aptr); + break; + case ErrorSeverity::Debug: + dlist.push_back(*aptr); + break; + case ErrorSeverity::Informational: + default: + ilist.push_back(*aptr); }; } - if(flist.size()>0) return flist; - if(ivlist.size()>0) return ivlist; - if(slist.size()>0) return slist; - if(clist.size()>0) return clist; - if(dlist.size()>0) return dlist; + if (flist.size() > 0) + return flist; + if (ivlist.size() > 0) + return ivlist; + if (slist.size() > 0) + return slist; + if (clist.size() > 0) + return clist; + if (dlist.size() > 0) + return dlist; return ilist; } -} +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/Metadata.cc b/cxx/src/lib/utility/Metadata.cc index 073b86452..1148294a2 100644 --- a/cxx/src/lib/utility/Metadata.cc +++ b/cxx/src/lib/utility/Metadata.cc @@ -1,335 +1,290 @@ -#include -#include -#include -#include "misc/base64.h" #include "mspass/utility/Metadata.h" +#include "misc/base64.h" #include "mspass/utility/MsPASSError.h" -namespace mspass::utility -{ +#include +#include +#include +namespace mspass::utility { using namespace std; -Metadata::Metadata(ifstream& ifs, const string form) -{ - try{ +Metadata::Metadata(ifstream &ifs, const string form) { + try { char linebuffer[256]; - while(ifs.getline(linebuffer,128)) - { - string s1,s2,s3; + while (ifs.getline(linebuffer, 128)) { + string s1, s2, s3; boost::any a; stringstream ss(linebuffer); - ss>>s1; - ss>>s2; - ss>>s3; - if(s3=="real") - { + ss >> s1; + ss >> s2; + ss >> s3; + if (s3 == "real") { double dval; - dval=atof(s2.c_str()); - a=dval; - md[s1]=a; + dval = atof(s2.c_str()); + a = dval; + md[s1] = a; changed_or_set.insert(s1); - } - else if(s3=="integer") - { + } else if (s3 == "integer") { long ival; - ival=atol(s2.c_str()); - a=ival; - md[s1]=a; + ival = atol(s2.c_str()); + a = ival; + md[s1] = a; changed_or_set.insert(s1); - } - else if(s3=="string") - { + } else if (s3 == "string") { string sval; - a=sval; - md[s1]=a; + a = sval; + md[s1] = a; changed_or_set.insert(s1); - } - else if(s3=="bool") - { + } else if (s3 == "bool") { bool bval; - if( (s2=="TRUE")||(s2=="true")||(s2=="1") ) - bval=true; + if ((s2 == "TRUE") || (s2 == "true") || (s2 == "1")) + bval = true; else - bval=false; - a=bval; - md[s1]=a; + bval = false; + a = bval; + md[s1] = a; changed_or_set.insert(s1); - } - else - { + } else { stringstream sserr; - sserr << "Metadata file constructor: Illegal type specification for key=" - << s1<<" with a value field of "<::const_iterator mptr; - mptr=md.find(key); - if(mptr!=md.end()) - { +Metadata::Metadata(const Metadata &parent) + : md(parent.md), changed_or_set(parent.changed_or_set) {} +bool Metadata::is_defined(const string key) const noexcept { + map::const_iterator mptr; + mptr = md.find(key); + if (mptr != md.end()) { return true; - } - else - { + } else { return false; } } void Metadata::append_chain(const std::string key, const std::string val, - const std::string separator) -{ - if(this->is_defined(key)) - { - string typ=this->type(key); - if(typ.find("string")==string::npos) - throw MsPASSError("Metadata::append_chain: data for key=" - + key + " is not string type but "+typ - + "\nMust be string type to define a valid chain", - ErrorSeverity::Invalid); - string sval=this->get_string(key); + const std::string separator) { + if (this->is_defined(key)) { + string typ = this->type(key); + if (typ.find("string") == string::npos) + throw MsPASSError("Metadata::append_chain: data for key=" + key + + " is not string type but " + typ + + "\nMust be string type to define a valid chain", + ErrorSeverity::Invalid); + string sval = this->get_string(key); sval += separator; sval += val; - this->put(key,sval); - } - else - { - this->put(key,val); + this->put(key, sval); + } else { + this->put(key, val); } changed_or_set.insert(key); } -Metadata& Metadata::operator=(const Metadata& parent) -{ - if(this!=(&parent)) - { - md=parent.md; - changed_or_set=parent.changed_or_set; +Metadata &Metadata::operator=(const Metadata &parent) { + if (this != (&parent)) { + md = parent.md; + changed_or_set = parent.changed_or_set; } return *this; } -Metadata& Metadata::operator+=(const Metadata& rhs) noexcept -{ - if(this!=(&rhs)) - { +Metadata &Metadata::operator+=(const Metadata &rhs) noexcept { + if (this != (&rhs)) { /* We depend here upon the map container replacing values associated with - existing keys. We mark all entries changes anyway. This is a vastly simpler - algorithm than the old SEISPP::Metadata. */ - map::const_iterator rhsptr; - for(rhsptr=rhs.md.begin();rhsptr!=rhs.md.end();++rhsptr) - { - md[rhsptr->first]=rhsptr->second; + existing keys. We mark all entries changes anyway. This is a vastly + simpler algorithm than the old SEISPP::Metadata. */ + map::const_iterator rhsptr; + for (rhsptr = rhs.md.begin(); rhsptr != rhs.md.end(); ++rhsptr) { + md[rhsptr->first] = rhsptr->second; changed_or_set.insert(rhsptr->first); } } return *this; } -const Metadata Metadata::operator+(const Metadata& other) const -{ +const Metadata Metadata::operator+(const Metadata &other) const { Metadata result(*this); result += other; return result; } -set Metadata::keys() const noexcept -{ +set Metadata::keys() const noexcept { set result; - map::const_iterator mptr; - for(mptr=md.begin();mptr!=md.end();++mptr) - { - string key(mptr->first);\ + map::const_iterator mptr; + for (mptr = md.begin(); mptr != md.end(); ++mptr) { + string key(mptr->first); result.insert(key); } return result; } -void Metadata::erase(const std::string key) -{ - map::iterator iptr; - iptr=md.find(key); - if(iptr!=md.end()) +void Metadata::erase(const std::string key) { + map::iterator iptr; + iptr = md.find(key); + if (iptr != md.end()) md.erase(iptr); /* Also need to modify this set if the key is found there */ set::iterator sptr; - sptr=changed_or_set.find(key); - if(sptr!=changed_or_set.end()) - changed_or_set.erase(sptr); + sptr = changed_or_set.find(key); + if (sptr != changed_or_set.end()) + changed_or_set.erase(sptr); } -std::size_t Metadata::size() const noexcept -{ - return md.size(); -} -std::map::const_iterator Metadata::begin() const noexcept -{ +std::size_t Metadata::size() const noexcept { return md.size(); } +std::map::const_iterator Metadata::begin() const noexcept { return md.begin(); } -std::map::const_iterator Metadata::end() const noexcept -{ +std::map::const_iterator Metadata::end() const noexcept { return md.end(); } /* Helper returns demangled name using boost demangle. */ -string demangled_name(const boost::any a) -{ - try{ - const std::type_info &ti = a.type(); - const char *rawname=ti.name(); - string pretty_name(boost::core::demangle(rawname)); - return pretty_name; - }catch(...){throw;}; +string demangled_name(const boost::any a) { + try { + const std::type_info &ti = a.type(); + const char *rawname = ti.name(); + string pretty_name(boost::core::demangle(rawname)); + return pretty_name; + } catch (...) { + throw; + }; } -std::string Metadata::type(const string key) const -{ - try{ - boost::any a=this->get_any(key); - return demangled_name(a); - } - catch(...){throw;}; +std::string Metadata::type(const string key) const { + try { + boost::any a = this->get_any(key); + return demangled_name(a); + } catch (...) { + throw; + }; } /* friend operator */ -ostringstream& operator<<(ostringstream& os, const Metadata& m) -{ - try{ - map::const_iterator mdptr; - for(mdptr=m.md.begin();mdptr!=m.md.end();++mdptr) - { - /* Only handle simple types for now. Issue an error message to - * cerr for other types */ - int ival; - long lval; - double dval; - float fval; - string sval; - bool bval; - pybind11::object poval; - boost::any a=mdptr->second; - /* A relic retained to help remember this construct*/ - //const std::type_info &ti = a.type(); - string pretty_name=demangled_name(a); - /*WARNING: potential future maintance issue. - * Currently the demangling process is not standardized and the - * boost code used here does not return a name that is at all - * pretty for string data. This crude approach just tests for - * the keyword basic_string embedded in the long name. This works - * for now, but could create problems if and when this anomaly - * evolves away. */ - string sname("string"); - if(pretty_name.find("basic_string")==std::string::npos) - sname=pretty_name; - //os<first.c_str(), mdptr->first.size())<<" "<first << " "<(a); - os<(a); - os<(a); - os<(a); - os<(a); - os<(a); - /* - string code = misc::base64_encode(sval.c_str(), sval.size()); - os<(a); - pybind11::gil_scoped_acquire acquire; - pybind11::module pickle = pybind11::module::import("pickle"); - pybind11::module base64 = pybind11::module::import("base64"); - pybind11::object dumps = pickle.attr("dumps"); - pybind11::object b64encode = base64.attr("b64encode"); - /* The following in Python will be base64.b64encode(pickle.dumps(poval)).decode() - * The complexity is to ensure the bytes string to be valid UTF-8 */ - pybind11::object pyStr = b64encode(dumps(poval)).attr("decode")(); - os<()<::const_iterator mdptr; + for (mdptr = m.md.begin(); mdptr != m.md.end(); ++mdptr) { + /* Only handle simple types for now. Issue an error message to + * cerr for other types */ + int ival; + long lval; + double dval; + float fval; + string sval; + bool bval; + pybind11::object poval; + boost::any a = mdptr->second; + /* A relic retained to help remember this construct*/ + // const std::type_info &ti = a.type(); + string pretty_name = demangled_name(a); + /*WARNING: potential future maintance issue. + * Currently the demangling process is not standardized and the + * boost code used here does not return a name that is at all + * pretty for string data. This crude approach just tests for + * the keyword basic_string embedded in the long name. This works + * for now, but could create problems if and when this anomaly + * evolves away. */ + string sname("string"); + if (pretty_name.find("basic_string") == std::string::npos) + sname = pretty_name; + // os<first.c_str(), mdptr->first.size())<<" + // "<first << " " << sname << " "; + try { + if (sname == "int") { + ival = boost::any_cast(a); + os << ival << endl; + } else if (sname == "long") { + lval = boost::any_cast(a); + os << lval << endl; + } else if (sname == "double") { + dval = boost::any_cast(a); + os << dval << endl; + } else if (sname == "float") { + fval = boost::any_cast(a); + os << fval << endl; + } else if (sname == "bool") { + bval = boost::any_cast(a); + os << bval << endl; + } else if (sname == "string") { + sval = boost::any_cast(a); + /* + string code = misc::base64_encode(sval.c_str(), sval.size()); + os<(a); + pybind11::gil_scoped_acquire acquire; + pybind11::module pickle = pybind11::module::import("pickle"); + pybind11::module base64 = pybind11::module::import("base64"); + pybind11::object dumps = pickle.attr("dumps"); + pybind11::object b64encode = base64.attr("b64encode"); + /* The following in Python will be + * base64.b64encode(pickle.dumps(poval)).decode() The complexity is to + * ensure the bytes string to be valid UTF-8 */ + pybind11::object pyStr = b64encode(dumps(poval)).attr("decode")(); + os << pyStr.cast() << endl; + pybind11::gil_scoped_release release; + } else { + os << "NONPRINTABLE" << endl; } + } catch (boost::bad_any_cast &e) { + os << "BAD_ANY_CAST_ERROR" << endl; + } } return os; - }catch(...){throw;}; + } catch (...) { + throw; + }; } -/* This function is implemented with pybind11 so it should only be called under python. */ -pybind11::object serialize_metadata_py(const Metadata &md) -{ +/* This function is implemented with pybind11 so it should only be called under + * python. */ +pybind11::object serialize_metadata_py(const Metadata &md) { pybind11::gil_scoped_acquire acquire; - try{ + try { pybind11::dict md_dict = pybind11::cast(md); pybind11::set changed_or_set = pybind11::cast(md.changed_or_set); pybind11::module pickle = pybind11::module::import("pickle"); pybind11::object dumps = pickle.attr("dumps"); - pybind11::object md_dump = dumps(pybind11::make_tuple(md_dict, changed_or_set)); + pybind11::object md_dump = + dumps(pybind11::make_tuple(md_dict, changed_or_set)); pybind11::gil_scoped_release release; return md_dump; - }catch(...){pybind11::gil_scoped_release release;throw;}; + } catch (...) { + pybind11::gil_scoped_release release; + throw; + }; } /* This is the reverse of serialize_metadata also using pybind11. */ -Metadata restore_serialized_metadata_py(const pybind11::object &s) -{ +Metadata restore_serialized_metadata_py(const pybind11::object &s) { pybind11::gil_scoped_acquire acquire; - try - { + try { pybind11::module pickle = pybind11::module::import("pickle"); pybind11::object loads = pickle.attr("loads"); pybind11::tuple md_dump = loads(s); pybind11::dict md_dict = md_dump[0]; - pybind11::object md_py = pybind11::module_::import("mspasspy.ccore.utility").attr("Metadata")(md_dict); + pybind11::object md_py = pybind11::module_::import("mspasspy.ccore.utility") + .attr("Metadata")(md_dict); Metadata md = md_py.cast(); md.changed_or_set = md_dump[1].cast>(); pybind11::gil_scoped_release release; return md; - } - catch (...) - { + } catch (...) { pybind11::gil_scoped_release release; throw; }; } -/* New method added Apr 2020 to change key assigned to a value - used for aliass*/ -void Metadata::change_key(const string oldkey, const string newkey) -{ - map::iterator mdptr; - mdptr=md.find(oldkey); +/* New method added Apr 2020 to change key assigned to a value - used for + * aliass*/ +void Metadata::change_key(const string oldkey, const string newkey) { + map::iterator mdptr; + mdptr = md.find(oldkey); /* We silently do nothing if old is not found */ - if(mdptr!=md.end()) - { + if (mdptr != md.end()) { md.insert_or_assign(newkey, mdptr->second); md.erase(mdptr); } } -} // End mspass::utility Namespace block +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/MetadataDefinitions.cc b/cxx/src/lib/utility/MetadataDefinitions.cc index 7daf2008b..3fc122b89 100644 --- a/cxx/src/lib/utility/MetadataDefinitions.cc +++ b/cxx/src/lib/utility/MetadataDefinitions.cc @@ -1,264 +1,250 @@ +#include "mspass/utility/MetadataDefinitions.h" +#include "mspass/utility/AntelopePf.h" +#include "mspass/utility/MsPASSError.h" +#include "mspass/utility/utility.h" +#include "yaml-cpp/yaml.h" #include #include #include #include -#include "mspass/utility/utility.h" -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/AntelopePf.h" -#include "yaml-cpp/yaml.h" -#include "mspass/utility/MetadataDefinitions.h" const std::string DefaultSchemaName("mspass"); -namespace mspass::utility{ +namespace mspass::utility { using namespace mspass::utility; using namespace std; -MetadataDefinitions::MetadataDefinitions(const std::string mdname) -{ - try{ - /* silent try to recover if the user adds .yaml to mdname*/ - std::size_t ipos; //size_t seems essential here for this to work -weird - string name_to_use; - ipos=mdname.find(".yaml"); - if(ipos==std::string::npos) - { - name_to_use=mdname; - } - /* We throw an exception if the name is a path with / characters*/ - else if(mdname.find("/")!=std::string::npos) - { - throw MsPASSError("MetadataDefinitions: name passed seems to be a full path name that is not allowed\nReceived this: " - +mdname,ErrorSeverity::Invalid); - } - else - { - name_to_use.assign(mdname,0,ipos); - } - string datadir=mspass::utility::data_directory(); - string path; - path=datadir+"/yaml/"+name_to_use+".yaml"; - MetadataDefinitions tmp(path,MDDefFormat::YAML); - *this=tmp; - }catch(...){throw;}; - +MetadataDefinitions::MetadataDefinitions(const std::string mdname) { + try { + /* silent try to recover if the user adds .yaml to mdname*/ + std::size_t ipos; // size_t seems essential here for this to work -weird + string name_to_use; + ipos = mdname.find(".yaml"); + if (ipos == std::string::npos) { + name_to_use = mdname; + } + /* We throw an exception if the name is a path with / characters*/ + else if (mdname.find("/") != std::string::npos) { + throw MsPASSError("MetadataDefinitions: name passed seems to be a full " + "path name that is not allowed\nReceived this: " + + mdname, + ErrorSeverity::Invalid); + } else { + name_to_use.assign(mdname, 0, ipos); + } + string datadir = mspass::utility::data_directory(); + string path; + path = datadir + "/yaml/" + name_to_use + ".yaml"; + MetadataDefinitions tmp(path, MDDefFormat::YAML); + *this = tmp; + } catch (...) { + throw; + }; } /* The unparameterized constructor is almost like the single string constructor * loading a frozen file name. The only difference is not needing to worry * about user errors.*/ -MetadataDefinitions::MetadataDefinitions() -{ - try{ - string datadir=mspass::utility::data_directory(); - string path; - path=datadir+"/yaml/"+DefaultSchemaName+".yaml"; - MetadataDefinitions tmp(path,MDDefFormat::YAML); - *this=tmp; - }catch(...){throw;}; +MetadataDefinitions::MetadataDefinitions() { + try { + string datadir = mspass::utility::data_directory(); + string path; + path = datadir + "/yaml/" + DefaultSchemaName + ".yaml"; + MetadataDefinitions tmp(path, MDDefFormat::YAML); + *this = tmp; + } catch (...) { + throw; + }; } -MetadataDefinitions::MetadataDefinitions(const string fname,const MDDefFormat mdf) -{ - try{ - switch(mdf) - { - case MDDefFormat::YAML: - this->yaml_reader(fname); - break; - case MDDefFormat::PF: - this->pfreader(fname); - break; - default: - throw MsPASSError("MetadataDefinitions file constructor: illegal format specification"); +MetadataDefinitions::MetadataDefinitions(const string fname, + const MDDefFormat mdf) { + try { + switch (mdf) { + case MDDefFormat::YAML: + this->yaml_reader(fname); + break; + case MDDefFormat::PF: + this->pfreader(fname); + break; + default: + throw MsPASSError("MetadataDefinitions file constructor: illegal " + "format specification"); }; - }catch(...){throw;}; + } catch (...) { + throw; + }; } -MetadataDefinitions::MetadataDefinitions(const MetadataDefinitions& parent) - : tmap(parent.tmap),cmap(parent.cmap), - aliasmap(parent.aliasmap), - alias_xref(parent.alias_xref),roset(parent.roset), - unique_id_data(parent.unique_id_data) -{} -bool MetadataDefinitions::is_defined(const std::string key) const noexcept -{ +MetadataDefinitions::MetadataDefinitions(const MetadataDefinitions &parent) + : tmap(parent.tmap), cmap(parent.cmap), aliasmap(parent.aliasmap), + alias_xref(parent.alias_xref), roset(parent.roset), + unique_id_data(parent.unique_id_data) {} +bool MetadataDefinitions::is_defined(const std::string key) const noexcept { /* test type map because concept can be empty for a key */ - map::const_iterator tptr; - tptr=tmap.find(key); - if(tptr!=tmap.end()) - { + map::const_iterator tptr; + tptr = tmap.find(key); + if (tptr != tmap.end()) { return true; - } - else - { - pair unr; + } else { + pair unr; /* A bit weird to catch an exception as a way to test for a false, but the way the api currently is defined requires this. */ - try{ - unr=this->unique_name(key); + try { + unr = this->unique_name(key); return true; - }catch(MsPASSError& mderr) - { + } catch (MsPASSError &mderr) { return false; } } } -std::string MetadataDefinitions::concept(const std::string key) const -{ +std::string MetadataDefinitions::concept(const std::string key) const { const string base_error("MetadataDefinitions::concept: "); - map::const_iterator cptr; - cptr=cmap.find(key); - if(cptr==cmap.end()) - { + map::const_iterator cptr; + cptr = cmap.find(key); + if (cptr == cmap.end()) { stringstream ss; - ss<second; } -mspass::utility::MDtype MetadataDefinitions::type(const std::string key) const -{ +mspass::utility::MDtype MetadataDefinitions::type(const std::string key) const { const string base_error("MetadataDefinitions::type: "); - map::const_iterator tptr; - if(this->is_alias(key)) + map::const_iterator tptr; + if (this->is_alias(key)) return this->unique_name(key).second; - tptr=tmap.find(key); - if(tptr==tmap.end()) - { + tptr = tmap.find(key); + if (tptr == tmap.end()) { stringstream ss; - ss<second; } -void MetadataDefinitions::add(const std::string key, const std::string concept_, const MDtype mdt) -{ - cmap[key]=concept_; - tmap[key]=mdt; +void MetadataDefinitions::add(const std::string key, const std::string concept_, + const MDtype mdt) { + cmap[key] = concept_; + tmap[key] = mdt; } -bool MetadataDefinitions::has_alias(const std::string key) const -{ - multimap::const_iterator aptr; - aptr=aliasmap.find(key); - if(aptr==aliasmap.end()) +bool MetadataDefinitions::has_alias(const std::string key) const { + multimap::const_iterator aptr; + aptr = aliasmap.find(key); + if (aptr == aliasmap.end()) return false; else return true; } -list MetadataDefinitions::aliases(const std::string key) const -{ +list MetadataDefinitions::aliases(const std::string key) const { list result; - multimap::const_iterator aptr; - aptr=aliasmap.find(key); - if(aptr==aliasmap.end()) return result; //return an empty list when not found - std::pair::const_iterator, - multimap::const_iterator> rng; - rng=aliasmap.equal_range(key); - /* Obscure iterator loop over return of equal_range. See multimap documentation*/ - for(aptr=rng.first;aptr!=rng.second;++aptr) - { + multimap::const_iterator aptr; + aptr = aliasmap.find(key); + if (aptr == aliasmap.end()) + return result; // return an empty list when not found + std::pair::const_iterator, + multimap::const_iterator> + rng; + rng = aliasmap.equal_range(key); + /* Obscure iterator loop over return of equal_range. See multimap + * documentation*/ + for (aptr = rng.first; aptr != rng.second; ++aptr) { result.push_back(aptr->second); } return result; } -void MetadataDefinitions::add_alias(const std::string key, const std::string aliasname) -{ +void MetadataDefinitions::add_alias(const std::string key, + const std::string aliasname) { /* We could use operator[] but this is more bombproof if not as clear */ - aliasmap.insert(std::pair(key,aliasname)); - alias_xref.insert(std::pair(aliasname,key)); + aliasmap.insert(std::pair(key, aliasname)); + alias_xref.insert(std::pair(aliasname, key)); } -bool MetadataDefinitions::is_alias(const std::string key) const -{ - map::const_iterator mptr; - mptr=alias_xref.find(key); - if(mptr==alias_xref.end()) - return false; +bool MetadataDefinitions::is_alias(const std::string key) const { + map::const_iterator mptr; + mptr = alias_xref.find(key); + if (mptr == alias_xref.end()) + return false; else - return true; + return true; } -std::pair MetadataDefinitions::unique_name - (const string aliasname) const -{ - const string base_error("MetadataDefinitions::unique_name: "); - map::const_iterator aptr; - aptr=alias_xref.find(aliasname); - if(aptr==alias_xref.end()) - { - throw MsPASSError(base_error+"alias name=" - + aliasname + " is not defined",ErrorSeverity::Invalid); - } - else - { - /* We do not assume the key returned from alias_xref resolves. Small - cost for a stability gain*/ - string kname=aptr->second; - map::const_iterator tptr; - tptr=tmap.find(kname); - if(tptr==tmap.end()) throw MsPASSError(base_error+"alias name="+aliasname - + " has no matching entry in alias tables\n" - + "SETUP ERROR - FIX CONFIGURATION FILES", - ErrorSeverity::Fatal); - return(pair(kname,tptr->second)); - } +std::pair +MetadataDefinitions::unique_name(const string aliasname) const { + const string base_error("MetadataDefinitions::unique_name: "); + map::const_iterator aptr; + aptr = alias_xref.find(aliasname); + if (aptr == alias_xref.end()) { + throw MsPASSError(base_error + "alias name=" + aliasname + + " is not defined", + ErrorSeverity::Invalid); + } else { + /* We do not assume the key returned from alias_xref resolves. Small + cost for a stability gain*/ + string kname = aptr->second; + map::const_iterator tptr; + tptr = tmap.find(kname); + if (tptr == tmap.end()) + throw MsPASSError(base_error + "alias name=" + aliasname + + " has no matching entry in alias tables\n" + + "SETUP ERROR - FIX CONFIGURATION FILES", + ErrorSeverity::Fatal); + return (pair(kname, tptr->second)); + } } -std::list MetadataDefinitions::keys() const -{ +std::list MetadataDefinitions::keys() const { /* assume tmap and cmap have the same keys*/ std::list result; - map::const_iterator tptr; - for(tptr=tmap.cbegin();tptr!=tmap.cend();++tptr) - { + map::const_iterator tptr; + for (tptr = tmap.cbegin(); tptr != tmap.cend(); ++tptr) { result.push_back(tptr->first); } return result; } -MetadataDefinitions& MetadataDefinitions::operator=(const MetadataDefinitions& parent) -{ - if(this!=&parent) - { - tmap=parent.tmap; - cmap=parent.cmap; - aliasmap=parent.aliasmap; - alias_xref=parent.alias_xref; - roset=parent.roset; - unique_id_data=parent.unique_id_data; +MetadataDefinitions & +MetadataDefinitions::operator=(const MetadataDefinitions &parent) { + if (this != &parent) { + tmap = parent.tmap; + cmap = parent.cmap; + aliasmap = parent.aliasmap; + alias_xref = parent.alias_xref; + roset = parent.roset; + unique_id_data = parent.unique_id_data; } return *this; } -MetadataDefinitions& MetadataDefinitions::operator+=(const MetadataDefinitions& other) -{ - if(this==&other) return *this; - list kvals=other.keys(); +MetadataDefinitions & +MetadataDefinitions::operator+=(const MetadataDefinitions &other) { + if (this == &other) + return *this; + list kvals = other.keys(); list::iterator kptr; - for(kptr=kvals.begin();kptr!=kvals.end();++kptr) - { - MDtype mdt=other.type(*kptr); - /* Note this will silently overwrite previous if the key was already present*/ - this->tmap[*kptr]=mdt; - try{ - string cother=other.concept(*kptr); - this->cmap[*kptr]=cother; - }catch(MsPASSError& merr) - { - //Assume the only error here comes from concept methode failing - cerr << "MetadataDefinitions operator+= (Warning): concept description is missing for key=" - <<*kptr<tmap[*kptr] = mdt; + try { + string cother = other.concept(*kptr); + this->cmap[*kptr] = cother; + } catch (MsPASSError &merr) { + // Assume the only error here comes from concept methode failing + cerr << "MetadataDefinitions operator+= (Warning): concept description " + "is missing for key=" + << *kptr << endl + << "Error will be ignored" << endl; } - /* The alias_xref is a multimap so we just append other data - no such thing as duplicates*/ - map::const_iterator aptr; - for(aptr=other.alias_xref.begin();aptr!=other.alias_xref.end();++aptr) - { + /* The alias_xref is a multimap so we just append other data - no such thing + * as duplicates*/ + map::const_iterator aptr; + for (aptr = other.alias_xref.begin(); aptr != other.alias_xref.end(); + ++aptr) { this->alias_xref.insert(*aptr); } /* These behave like the type map above - we silently replace any entry that was present before. i.e. other overrides */ set::const_iterator sptr; - for(sptr=other.roset.begin();sptr!=roset.end();++sptr) - { + for (sptr = other.roset.begin(); sptr != roset.end(); ++sptr) { this->roset.insert(*sptr); } - map>::const_iterator uptr; - for(uptr=other.unique_id_data.begin();uptr!=other.unique_id_data.end();++uptr) - { + map>::const_iterator uptr; + for (uptr = other.unique_id_data.begin(); + uptr != other.unique_id_data.end(); ++uptr) { this->unique_id_data.insert(*uptr); } } @@ -268,115 +254,93 @@ MetadataDefinitions& MetadataDefinitions::operator+=(const MetadataDefinitions& keys is those for readonly. That inverts the logic of this function. i.e. roset contains keys of attributes marked readonly and this function is the not of that logic. */ -bool MetadataDefinitions::writeable(const string key) const -{ +bool MetadataDefinitions::writeable(const string key) const { set::const_iterator roptr; - roptr=roset.find(key); - if(roptr==roset.end()) + roptr = roset.find(key); + if (roptr == roset.end()) return true; - else if(this->is_alias(key)) - { + else if (this->is_alias(key)) { /* roset only contains unique key entries. This checks any possible aliases. */ - pair kp; + pair kp; /* unique_name method returns an exception if the key is not defined. We avoid that and silently return false if that happens, although that situation will likely create downstream problems. */ - try{ - kp=this->unique_name(key); - }catch(MsPASSError &mderr) - { + try { + kp = this->unique_name(key); + } catch (MsPASSError &mderr) { cerr << "MetadataDefinitions::writeable method (WARNING): Requested key " - << key<<" is undefined and is not a registered alias"<writeable(key))); +bool MetadataDefinitions::readonly(const string key) const { + return (!(this->writeable(key))); } -void MetadataDefinitions::set_readonly(const string key) -{ +void MetadataDefinitions::set_readonly(const string key) { /* Silently return if key is already set as readonly */ - if(roset.find(key)==roset.end()) - { + if (roset.find(key) == roset.end()) { roset.insert(key); } } -void MetadataDefinitions::set_writeable(const string key) -{ +void MetadataDefinitions::set_writeable(const string key) { set::const_iterator roptr; - roptr=roset.find(key); + roptr = roset.find(key); /* Here we silently do nothing if the key is not in roset */ - if(roptr!=roset.end()) - { + if (roptr != roset.end()) { roset.erase(roptr); } } -bool MetadataDefinitions::is_normalized(const string key) const -{ - if(unique_id_data.find(key)!=unique_id_data.end()) +bool MetadataDefinitions::is_normalized(const string key) const { + if (unique_id_data.find(key) != unique_id_data.end()) return true; else return false; } /* The normalization is stored with the 0 element of the tuple being the table(collection) name and the 1 element being the key for the id needed */ -string MetadataDefinitions::unique_id_key(const string key) const -{ - map>::const_iterator uidptr; - uidptr=unique_id_data.find(key); +string MetadataDefinitions::unique_id_key(const string key) const { + map>::const_iterator uidptr; + uidptr = unique_id_data.find(key); - if(uidptr!=unique_id_data.end()) - { - return(get<1>(uidptr->second)); - } - else - { + if (uidptr != unique_id_data.end()) { + return (get<1>(uidptr->second)); + } else { return (string("")); } } -string MetadataDefinitions::collection(const string key) const -{ - map>::const_iterator uidptr; - uidptr=unique_id_data.find(key); - if(uidptr==unique_id_data.end()) - { +string MetadataDefinitions::collection(const string key) const { + map>::const_iterator uidptr; + uidptr = unique_id_data.find(key); + if (uidptr == unique_id_data.end()) { return (string("")); - } - else - { - return(get<0>(uidptr->second)); + } else { + return (get<0>(uidptr->second)); } } -std::pair MetadataDefinitions::normalize_data(const string key) const -{ - map>::const_iterator uidptr; - uidptr=unique_id_data.find(key); - if(uidptr==unique_id_data.end()) - { - throw MsPASSError("MetadataDefinitions::normalize_data: key=" - + key + " has no normalization data"); - } - else - { - pair result; - result.first=get<0>(uidptr->second); - result.second=get<1>(uidptr->second); +std::pair +MetadataDefinitions::normalize_data(const string key) const { + map>::const_iterator uidptr; + uidptr = unique_id_data.find(key); + if (uidptr == unique_id_data.end()) { + throw MsPASSError("MetadataDefinitions::normalize_data: key=" + key + + " has no normalization data"); + } else { + pair result; + result.first = get<0>(uidptr->second); + result.second = get<1>(uidptr->second); return result; } } @@ -384,12 +348,10 @@ std::pair MetadataDefinitions::normalize_data(const str /* This function takes a list of lines form a tbl in pf, adds a newline at end of each element, and appends to to a master returning one string with newlines marking the list item boundaries. */ -std::string list_to_1str(list& l) -{ +std::string list_to_1str(list &l) { list::iterator lptr; string result; - for(lptr=l.begin();lptr!=l.end();++lptr) - { + for (lptr = l.begin(); lptr != l.end(); ++lptr) { result += (*lptr); result += "\n"; } @@ -398,164 +360,152 @@ std::string list_to_1str(list& l) /* Small helper used by parsers. Note tstr can't be const because it is altered, but because it is called by value I don't think the caller would be modified copy could be modified. */ -MDtype str2mdt(string tstr) -{ +MDtype str2mdt(string tstr) { transform(tstr.begin(), tstr.end(), tstr.begin(), ::tolower); MDtype mdt_this; - if( (tstr=="real") || (tstr=="float") || (tstr=="real32") ) - mdt_this=MDtype::Real32; - else if(tstr=="int32") - mdt_this=MDtype::Int32; - else if((tstr=="real64") || (tstr=="double")) - mdt_this=MDtype::Double; + if ((tstr == "real") || (tstr == "float") || (tstr == "real32")) + mdt_this = MDtype::Real32; + else if (tstr == "int32") + mdt_this = MDtype::Int32; + else if ((tstr == "real64") || (tstr == "double")) + mdt_this = MDtype::Double; /* in the 64 bit world we default int and integer to int64 */ - else if((tstr=="long") || (tstr=="int64") || (tstr=="int") || (tstr=="integer") ) - mdt_this=MDtype::Int64; - else if(tstr=="string") - mdt_this=MDtype::String; - else if((tstr=="boolean") || (tstr=="Boolean") || (tstr=="bool") ) - mdt_this=MDtype::Boolean; - else if((tstr=="double_array") ) - mdt_this=MDtype::Double_Array; + else if ((tstr == "long") || (tstr == "int64") || (tstr == "int") || + (tstr == "integer")) + mdt_this = MDtype::Int64; + else if (tstr == "string") + mdt_this = MDtype::String; + else if ((tstr == "boolean") || (tstr == "Boolean") || (tstr == "bool")) + mdt_this = MDtype::Boolean; + else if ((tstr == "double_array")) + mdt_this = MDtype::Double_Array; else - throw MsPASSError("MetadataDefinitions::pfreader: type value=" - + tstr+" not recognized"); + throw MsPASSError("MetadataDefinitions::pfreader: type value=" + tstr + + " not recognized"); return mdt_this; } /* Private methods */ -void MetadataDefinitions::pfreader(const string pfname) -{ - try{ +void MetadataDefinitions::pfreader(const string pfname) { + try { /* Most of these may throw a MsPASSError. Any failure for this class is bad so we just do one catch at the end. A program is expected to - normally create this thing near the start of execution so aborting on failure - is the expected norm */ + normally create this thing near the start of execution so aborting on + failure is the expected norm */ AntelopePf pf(pfname); - list akeys=pf.arr_keys(); - list::iterator kptr,aptr; - for(kptr=akeys.begin();kptr!=akeys.end();++kptr) - { + list akeys = pf.arr_keys(); + list::iterator kptr, aptr; + for (kptr = akeys.begin(); kptr != akeys.end(); ++kptr) { AntelopePf pfb(pf.get_branch(*kptr)); list con_list; - con_list=pfb.get_tbl("concept"); - string con_str=list_to_1str(con_list); - cmap[*kptr]=con_str; - string tstr=pfb.get_string("type"); - MDtype mdt_this=str2mdt(tstr); - tmap[*kptr]=mdt_this; + con_list = pfb.get_tbl("concept"); + string con_str = list_to_1str(con_list); + cmap[*kptr] = con_str; + string tstr = pfb.get_string("type"); + MDtype mdt_this = str2mdt(tstr); + tmap[*kptr] = mdt_this; /* parse aliases as a tbl linked to this key */ - list alist=pfb.get_tbl("aliases"); - for(aptr=alist.begin();aptr!=alist.end();++aptr) - { - this->add_alias(*kptr,*aptr); + list alist = pfb.get_tbl("aliases"); + for (aptr = alist.begin(); aptr != alist.end(); ++aptr) { + this->add_alias(*kptr, *aptr); } } - }catch(...){throw;}; + } catch (...) { + throw; + }; } -void MetadataDefinitions::yaml_reader(const string fname) -{ - try{ - YAML::Node outer=YAML::LoadFile(fname.c_str()); +void MetadataDefinitions::yaml_reader(const string fname) { + try { + YAML::Node outer = YAML::LoadFile(fname.c_str()); /* The structure of yaml file is a map with a group key * for each piece. We ignore the group key here and use it * only to make the file more human readable. */ - //const YAML::Node& attributes=outer["Attributes"]; - for(YAML::const_iterator it=outer.begin();it!=outer.end();++it) - { - string group_key=it->first.as(); - const YAML::Node& attributes=outer[group_key]; - unsigned int natt=attributes.size(); - unsigned int i; - for(i=0;i(); - string concept=attributes[i]["concept"].as(); - cmap[key]=concept; - string styp=attributes[i]["type"].as(); - MDtype mdt_this=str2mdt(styp); - tmap[key]=mdt_this; - /* Aliases is optional - this skips parsing aliases if key is missing*/ - if(attributes[i]["aliases"]) - { - string str=attributes[i]["aliases"].as(); - if(str.size()>0) - { - /* using strtok which will alter the string contents so we have - to copy it first*/ - char *s=strdup(str.c_str()); - string delim(" ,"); // allow either spaces or commas as delimiters - char *p=strtok(s,delim.c_str()); - while(p!=NULL){ - this->add_alias(key,string(p));; - p=strtok(NULL,delim.c_str()); //strtok oddity of NULL meaning use last position + // const YAML::Node& attributes=outer["Attributes"]; + for (YAML::const_iterator it = outer.begin(); it != outer.end(); ++it) { + string group_key = it->first.as(); + const YAML::Node &attributes = outer[group_key]; + unsigned int natt = attributes.size(); + unsigned int i; + for (i = 0; i < natt; ++i) { + string key; + key = attributes[i]["name"].as(); + string concept = attributes[i]["concept"].as(); + cmap[key] = concept; + string styp = attributes[i]["type"].as(); + MDtype mdt_this = str2mdt(styp); + tmap[key] = mdt_this; + /* Aliases is optional - this skips parsing aliases if key is missing*/ + if (attributes[i]["aliases"]) { + string str = attributes[i]["aliases"].as(); + if (str.size() > 0) { + /* using strtok which will alter the string contents so we have + to copy it first*/ + char *s = strdup(str.c_str()); + string delim(" ,"); // allow either spaces or commas as delimiters + char *p = strtok(s, delim.c_str()); + while (p != NULL) { + this->add_alias(key, string(p)); + ; + p = strtok(NULL, delim.c_str()); // strtok oddity of NULL meaning + // use last position } - free(s); + free(s); } } /* We parse this set of parameters only when readonly is set */ - if(attributes[i]["readonly"]) - { - string str=attributes[i]["readonly"].as(); + if (attributes[i]["readonly"]) { + string str = attributes[i]["readonly"].as(); /* Break out of this if tagged false - user error handled silently. */ - if(str=="false" || str=="False") continue; + if (str == "false" || str == "False") + continue; roset.insert(key); string uid, tbl; - uid=attributes[i]["unique_id"].as(); - tbl=attributes[i]["collection"].as(); - std::tuple entry(std::make_tuple(tbl,uid)); - unique_id_data[key]=entry; + uid = attributes[i]["unique_id"].as(); + tbl = attributes[i]["collection"].as(); + std::tuple entry(std::make_tuple(tbl, uid)); + unique_id_data[key] = entry; } } } - }catch(YAML::Exception& eyaml) - { + } catch (YAML::Exception &eyaml) { /* Rethrow these as a MsPASSError */ - throw MsPASSError(eyaml.what(),ErrorSeverity::Invalid); - } - catch(...){throw;}; + throw MsPASSError(eyaml.what(), ErrorSeverity::Invalid); + } catch (...) { + throw; + }; } /* New methods added April 2020 to improve support for aliases*/ -std::list MetadataDefinitions::apply_aliases - (Metadata& d, const std::list aliaslist) -{ +std::list +MetadataDefinitions::apply_aliases(Metadata &d, + const std::list aliaslist) { list failures; list::const_iterator aptr; - std::pair nmpair; - for(aptr=aliaslist.begin();aptr!=aliaslist.end();++aptr) - { - if(this->is_alias(*aptr)) - { - nmpair=this->unique_name(*aptr); - string ukey=nmpair.first; + std::pair nmpair; + for (aptr = aliaslist.begin(); aptr != aliaslist.end(); ++aptr) { + if (this->is_alias(*aptr)) { + nmpair = this->unique_name(*aptr); + string ukey = nmpair.first; /* Silently skip any aliases not defined. That assures that if the name had already been changed to this alias it will be prserved. */ - if(d.is_defined(ukey)) - { - d.change_key(ukey,*aptr); + if (d.is_defined(ukey)) { + d.change_key(ukey, *aptr); } - } - else - { + } else { failures.push_back(*aptr); } } return failures; } -void MetadataDefinitions::clear_aliases(Metadata& d) -{ +void MetadataDefinitions::clear_aliases(Metadata &d) { std::set keys; - keys=d.keys(); + keys = d.keys(); std::set::iterator kptr; - std::pair nmpair; - for (kptr=keys.begin();kptr!=keys.end();++kptr) - { - if(this->is_alias(*kptr)) - { - nmpair=this->unique_name(*kptr); - string ukey=nmpair.first; - d.change_key(*kptr,ukey); + std::pair nmpair; + for (kptr = keys.begin(); kptr != keys.end(); ++kptr) { + if (this->is_alias(*kptr)) { + nmpair = this->unique_name(*kptr); + string ukey = nmpair.first; + d.change_key(*kptr, ukey); } } } -} +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/MsPASSError.cc b/cxx/src/lib/utility/MsPASSError.cc index 0b403f7ea..db562c397 100644 --- a/cxx/src/lib/utility/MsPASSError.cc +++ b/cxx/src/lib/utility/MsPASSError.cc @@ -1,62 +1,52 @@ +#include "mspass/utility/MsPASSError.h" #include #include -#include "mspass/utility/MsPASSError.h" -namespace mspass::utility{ +namespace mspass::utility { using namespace std; /* These are two useful helper functions for MsPASSError constructors and for inverse needed for python wrappers - there is no way currently with pybind11 to have a custom exception class have access to members other than the "what" method that overrides std::exception */ -ErrorSeverity string2severity(const string howbad) -{ +ErrorSeverity string2severity(const string howbad) { ErrorSeverity badness; string s(howbad); - if(s=="Fatal"){ - badness=ErrorSeverity::Fatal; - } - else if (s=="Invalid"){ - badness=ErrorSeverity::Invalid; - } - else if (s=="Suspect"){ - badness=ErrorSeverity::Suspect; - } - else if (s=="Complaint"){ - badness=ErrorSeverity::Complaint; - } - else if (s=="Debug"){ - badness=ErrorSeverity::Debug; - } - else if (s=="Informational"){ - badness=ErrorSeverity::Informational; - } - else - { - /* Default to Fatal in this case as this should only happen in - a debug situation. */ - badness=ErrorSeverity::Fatal; - } + if (s == "Fatal") { + badness = ErrorSeverity::Fatal; + } else if (s == "Invalid") { + badness = ErrorSeverity::Invalid; + } else if (s == "Suspect") { + badness = ErrorSeverity::Suspect; + } else if (s == "Complaint") { + badness = ErrorSeverity::Complaint; + } else if (s == "Debug") { + badness = ErrorSeverity::Debug; + } else if (s == "Informational") { + badness = ErrorSeverity::Informational; + } else { + /* Default to Fatal in this case as this should only happen in + a debug situation. */ + badness = ErrorSeverity::Fatal; + } return badness; } /* inverse of string2severity */ -string severity2string(const mspass::utility::ErrorSeverity es) -{ - switch(es) - { - case mspass::utility::ErrorSeverity::Fatal: - return string("Fatal"); - case mspass::utility::ErrorSeverity::Invalid: - return string("Invalid"); - case mspass::utility::ErrorSeverity::Suspect: - return string("Suspect"); - case mspass::utility::ErrorSeverity::Complaint: - return string("Complaint"); - case mspass::utility::ErrorSeverity::Debug: - return string("Debug"); - case mspass::utility::ErrorSeverity::Informational: - return string("Informational"); - default: - return string("Fatal"); +string severity2string(const mspass::utility::ErrorSeverity es) { + switch (es) { + case mspass::utility::ErrorSeverity::Fatal: + return string("Fatal"); + case mspass::utility::ErrorSeverity::Invalid: + return string("Invalid"); + case mspass::utility::ErrorSeverity::Suspect: + return string("Suspect"); + case mspass::utility::ErrorSeverity::Complaint: + return string("Complaint"); + case mspass::utility::ErrorSeverity::Debug: + return string("Debug"); + case mspass::utility::ErrorSeverity::Informational: + return string("Informational"); + default: + return string("Fatal"); }; } /* This set of functions are used to provide the patch for pybind11 handling @@ -66,21 +56,21 @@ posts a string representation of the severity level at the end of the message string of a what return. Use this set of functions to test the return of "what" to convert to tests for levels of badness. */ -string parse_message_error_severity(const mspass::utility::MsPASSError& err) -{ - string s=err.what(); +string parse_message_error_severity(const mspass::utility::MsPASSError &err) { + string s = err.what(); size_t fpos; - fpos=s.find_last_of(":"); + fpos = s.find_last_of(":"); /* Return Invalid if the message has no colon - maybe should be fatal*/ - if(fpos==string::npos) return string("Invalid"); - string badness_str=s.substr(fpos+1); + if (fpos == string::npos) + return string("Invalid"); + string badness_str = s.substr(fpos + 1); /* Similarly be careful of a terminating : */ - if(badness_str.empty()) return string("Invalid"); + if (badness_str.empty()) + return string("Invalid"); return badness_str; } -ErrorSeverity message_error_severity(const mspass::utility::MsPASSError& err) -{ - string str=parse_message_error_severity(err); +ErrorSeverity message_error_severity(const mspass::utility::MsPASSError &err) { + string str = parse_message_error_severity(err); return string2severity(str); } @@ -88,11 +78,11 @@ ErrorSeverity message_error_severity(const mspass::utility::MsPASSError& err) Hope is that err will be a pointer to a real MsPASSError and the compiled C++ code will handle the conversion. Problem will occur if err is a copy created by python.*/ -bool error_says_data_bad(const mspass::utility::MsPASSError& err) -{ - if( (err.severity() == ErrorSeverity::Fatal) - || (err.severity() == ErrorSeverity::Invalid)) return true; +bool error_says_data_bad(const mspass::utility::MsPASSError &err) { + if ((err.severity() == ErrorSeverity::Fatal) || + (err.severity() == ErrorSeverity::Invalid)) + return true; else - return false; + return false; } -} // End namespace +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/ProcessManager.cc b/cxx/src/lib/utility/ProcessManager.cc index f12221a22..12fdc9fc7 100644 --- a/cxx/src/lib/utility/ProcessManager.cc +++ b/cxx/src/lib/utility/ProcessManager.cc @@ -1,82 +1,71 @@ -#include -#include -#include +#include "mspass/utility/ProcessManager.h" +#include "mspass/utility/MsPASSError.h" #include #include -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/ProcessManager.h" +#include +#include +#include using namespace std; -namespace mspass::utility{ -ProcessManager::ProcessManager() : jobnm() -{ - job_uuid=gen(); -}; -ProcessManager::ProcessManager(string fname) -{ +namespace mspass::utility { +ProcessManager::ProcessManager() : jobnm() { job_uuid = gen(); }; +ProcessManager::ProcessManager(string fname) { const string base_error("ProcessManager file constructor: "); - try{ - job_uuid=gen(); + try { + job_uuid = gen(); ifstream ifs; - ifs.open(fname.c_str(),std::ifstream::in); - if(!ifs.is_open()) - throw MsPASSError(base_error+"fopen failed for file="+fname, - ErrorSeverity::Fatal); + ifs.open(fname.c_str(), std::ifstream::in); + if (!ifs.is_open()) + throw MsPASSError(base_error + "fopen failed for file=" + fname, + ErrorSeverity::Fatal); /* job names needs to be the first line */ ifs >> jobnm; /* Now we loop over lines with algorithm name and and idstring */ char inp[128]; - while(ifs.getline(inp,128)) - { - map>::iterator aptr; + while (ifs.getline(inp, 128)) { + map>::iterator aptr; stringstream ss(inp); - string algnm,idin,inptyp,outtyp; + string algnm, idin, inptyp, outtyp; ss >> algnm; ss >> idin; ss >> inptyp; ss >> outtyp; - AlgorithmDefinition thisalg(algnm,inptyp,outtyp,idin); - aptr=algs.find(algnm); - if(aptr==algs.end()) - { + AlgorithmDefinition thisalg(algnm, inptyp, outtyp, idin); + aptr = algs.find(algnm); + if (aptr == algs.end()) { vector temp; temp.push_back(thisalg); - algs.insert(pair>(algnm,temp)); - } - else - { + algs.insert(pair>(algnm, temp)); + } else { aptr->second.push_back(thisalg); } } - }catch(...){throw;}; + } catch (...) { + throw; + }; }; AlgorithmDefinition ProcessManager::algorithm(const string name, - const size_t instance) const -{ + const size_t instance) const { size_t i; - map>::const_iterator aptr; - aptr=algs.find(name); - if(aptr==algs.end()) - { + map>::const_iterator aptr; + aptr = algs.find(name); + if (aptr == algs.end()) { /* If the name is not registered the map we don't want this method to throw an error. Instead we return the name with the id field set to UNDEFINED*/ - return AlgorithmDefinition(name,"UNDEFINED","UNDEFINED","UNDEFINED"); - } - else - { - i=instance; - if(instance>=(aptr->second.size())) - { + return AlgorithmDefinition(name, "UNDEFINED", "UNDEFINED", "UNDEFINED"); + } else { + i = instance; + if (instance >= (aptr->second.size())) { cerr << "ProcessManager::algorithm(Warning): " - << "algorithm="<second.size() - 1; + << "algorithm=" << name + << " was defined but requested instance=" << instance + << " exceeds number of defined instances=" << aptr->second.size() + << endl + << "Set to id for instance=" << aptr->second.size() - 1 << endl + << "History data may be invalid" << endl; + i = aptr->second.size() - 1; } } return aptr->second[i]; } -} //End mspass namespace +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/ProcessingHistory.cc b/cxx/src/lib/utility/ProcessingHistory.cc index def284738..3178b77e5 100644 --- a/cxx/src/lib/utility/ProcessingHistory.cc +++ b/cxx/src/lib/utility/ProcessingHistory.cc @@ -1,154 +1,142 @@ +#include "mspass/utility/ProcessingHistory.h" +#include "mspass/utility/MsPASSError.h" +#include +#include #include #include -#include -#include #include -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/ProcessingHistory.h" using namespace std; -namespace mspass::utility{ +namespace mspass::utility { /* This is an internal function that returns a string description of the ProcessingStatus enum class */ -string status_to_words(const ProcessingStatus status) -{ +string status_to_words(const ProcessingStatus status) { string word; - switch(status) - { - case ProcessingStatus::RAW: - word=string("RAW"); - break; - case ProcessingStatus::ORIGIN: - word=string("ORIGIN"); - break; - case ProcessingStatus::VOLATILE: - word=string("VOLATILE"); - break; - case ProcessingStatus::SAVED: - word=string("SAVED"); - break; - default: - word=string("UNDEFINED"); + switch (status) { + case ProcessingStatus::RAW: + word = string("RAW"); + break; + case ProcessingStatus::ORIGIN: + word = string("ORIGIN"); + break; + case ProcessingStatus::VOLATILE: + word = string("VOLATILE"); + break; + case ProcessingStatus::SAVED: + word = string("SAVED"); + break; + default: + word = string("UNDEFINED"); }; return word; } /* Start of NodeData implementations - all of these could have probably been defaulted, but defined here for clarity. Default constructor definitely does something different from default */ -NodeData::NodeData() -{ - status=ProcessingStatus::UNDEFINED; - uuid="UNDEFINED"; - type=AtomicType::UNDEFINED; - stage=-1; //Invalid value could be used as a hint of uninitialized data -} -NodeData::NodeData(const NodeData& parent) - : uuid(parent.uuid),algorithm(parent.algorithm),algid(parent.algid) -{ - status=parent.status; - type=parent.type; - stage=parent.stage; -} -NodeData& NodeData::operator=(const NodeData& parent) -{ - if(&parent != this) - { - status=parent.status; - type=parent.type; - stage=parent.stage; - uuid=parent.uuid; - algorithm=parent.algorithm; - algid=parent.algid; +NodeData::NodeData() { + status = ProcessingStatus::UNDEFINED; + uuid = "UNDEFINED"; + type = AtomicType::UNDEFINED; + stage = -1; // Invalid value could be used as a hint of uninitialized data +} +NodeData::NodeData(const NodeData &parent) + : uuid(parent.uuid), algorithm(parent.algorithm), algid(parent.algid) { + status = parent.status; + type = parent.type; + stage = parent.stage; +} +NodeData &NodeData::operator=(const NodeData &parent) { + if (&parent != this) { + status = parent.status; + type = parent.type; + stage = parent.stage; + uuid = parent.uuid; + algorithm = parent.algorithm; + algid = parent.algid; } return *this; } -bool NodeData::operator==(const NodeData& other) -{ +bool NodeData::operator==(const NodeData &other) { /* Not sure if this will generate the most compact code. Alternative is a string of && conditionsals. The advantage here is these were organized by a guess of which would most likely yield a false */ - if( (this->algorithm) != (other.algorithm) ) return false; - if( (this->uuid) != (other.uuid) ) return false; - if( (this->status) != (other.status) ) return false; - if( (this->type) != (other.type) ) return false; - if( (this->stage) != (other.stage) ) return false; - if( (this->algid) != (other.algid) ) return false; + if ((this->algorithm) != (other.algorithm)) + return false; + if ((this->uuid) != (other.uuid)) + return false; + if ((this->status) != (other.status)) + return false; + if ((this->type) != (other.type)) + return false; + if ((this->stage) != (other.stage)) + return false; + if ((this->algid) != (other.algid)) + return false; return true; } -bool NodeData::operator!=(const NodeData& other) -{ - return !((*this)==other); -} +bool NodeData::operator!=(const NodeData &other) { return !((*this) == other); } /* Start of ProcessingHistory code. */ /* Note all constructors need to define the head of the chain as undefined. That assures valid initialization and is needed to assure everything behaves if history is not ignored. */ -ProcessingHistory::ProcessingHistory():elog() -{ - current_status=ProcessingStatus::UNDEFINED; - current_id="UNDEFINED"; - current_stage=-1; //illegal value that could be used as signal for uninitalized - mytype=AtomicType::UNDEFINED; - algorithm="UNDEFINED"; - algid="UNDEFINED"; +ProcessingHistory::ProcessingHistory() : elog() { + current_status = ProcessingStatus::UNDEFINED; + current_id = "UNDEFINED"; + current_stage = + -1; // illegal value that could be used as signal for uninitalized + mytype = AtomicType::UNDEFINED; + algorithm = "UNDEFINED"; + algid = "UNDEFINED"; } ProcessingHistory::ProcessingHistory(const string jobnm, const string jid) - : BasicProcessingHistory(jobnm,jid),elog() -{ - current_status=ProcessingStatus::UNDEFINED; - current_id="UNDEFINED"; - current_stage=-1; //illegal value that could be used as signal for uninitalized - mytype=AtomicType::UNDEFINED; - algorithm="UNDEFINED"; - algid="UNDEFINED"; -} -ProcessingHistory::ProcessingHistory(const ProcessingHistory& parent) - : BasicProcessingHistory(parent),elog(parent.elog),nodes(parent.nodes), - algorithm(parent.algorithm),algid(parent.algid) -{ - current_status=parent.current_status; - current_id=parent.current_id; - current_stage=parent.current_stage; - mytype=parent.mytype; -} -bool ProcessingHistory::is_empty() const -{ - if( (current_status==ProcessingStatus::UNDEFINED) - && (nodes.empty()) )return true; + : BasicProcessingHistory(jobnm, jid), elog() { + current_status = ProcessingStatus::UNDEFINED; + current_id = "UNDEFINED"; + current_stage = + -1; // illegal value that could be used as signal for uninitalized + mytype = AtomicType::UNDEFINED; + algorithm = "UNDEFINED"; + algid = "UNDEFINED"; +} +ProcessingHistory::ProcessingHistory(const ProcessingHistory &parent) + : BasicProcessingHistory(parent), elog(parent.elog), nodes(parent.nodes), + algorithm(parent.algorithm), algid(parent.algid) { + current_status = parent.current_status; + current_id = parent.current_id; + current_stage = parent.current_stage; + mytype = parent.mytype; +} +bool ProcessingHistory::is_empty() const { + if ((current_status == ProcessingStatus::UNDEFINED) && (nodes.empty())) + return true; return false; } -bool ProcessingHistory::is_raw() const -{ - if(current_status==ProcessingStatus::RAW) +bool ProcessingHistory::is_raw() const { + if (current_status == ProcessingStatus::RAW) return true; else return false; } -bool ProcessingHistory::is_origin() const -{ - if(current_status==ProcessingStatus::RAW || current_status==ProcessingStatus::ORIGIN) +bool ProcessingHistory::is_origin() const { + if (current_status == ProcessingStatus::RAW || + current_status == ProcessingStatus::ORIGIN) return true; else return false; } -bool ProcessingHistory::is_volatile() const -{ - if(current_status==ProcessingStatus::VOLATILE) +bool ProcessingHistory::is_volatile() const { + if (current_status == ProcessingStatus::VOLATILE) return true; else return false; } -bool ProcessingHistory::is_saved() const -{ - if(current_status==ProcessingStatus::SAVED) +bool ProcessingHistory::is_saved() const { + if (current_status == ProcessingStatus::SAVED) return true; else return false; } -size_t ProcessingHistory::number_of_stages() -{ - return current_stage; -} +size_t ProcessingHistory::number_of_stages() { return current_stage; } /* the next set of methods are the primary methdods for managing the history data. A key implementation detail is when data marked current is pushed to @@ -158,75 +146,69 @@ all the methods named "map" something. A corollary is that when an object is an origin the multimaps must be empty. */ /* Note we don't distinguish raw and origin here - rec must define it one way or the other. */ -void ProcessingHistory::set_as_origin(const string alg,const string algid_in, - const string uuid,const AtomicType typ, bool define_as_raw) -{ +void ProcessingHistory::set_as_origin(const string alg, const string algid_in, + const string uuid, const AtomicType typ, + bool define_as_raw) { const string base_error("ProcessingHistory::set_as_origin: "); - if( nodes.size()>0 ) - { - elog.log_error(alg+":"+algid_in, - base_error + "Illegal usage. History chain was not empty. Calling clear method and continuing", - ErrorSeverity::Complaint); + if (nodes.size() > 0) { + elog.log_error(alg + ":" + algid_in, + base_error + "Illegal usage. History chain was not empty. " + " Calling clear method and continuing", + ErrorSeverity::Complaint); this->clear(); } - if(define_as_raw) - { - current_status=ProcessingStatus::RAW; + if (define_as_raw) { + current_status = ProcessingStatus::RAW; + } else { + current_status = ProcessingStatus::ORIGIN; } - else - { - current_status=ProcessingStatus::ORIGIN; - } - algorithm=alg; - algid=algid_in; - current_id=uuid; - mytype=typ; + algorithm = alg; + algid = algid_in; + current_id = uuid; + mytype = typ; /* Origin/raw are always defined as stage 0 even after a save. */ - current_stage=0; -} -string ProcessingHistory::new_ensemble_process(const string alg,const string algid_in, - const AtomicType typ,const vector parents, - const bool create_newid) -{ - if(create_newid) - { + current_stage = 0; +} +string ProcessingHistory::new_ensemble_process( + const string alg, const string algid_in, const AtomicType typ, + const vector parents, const bool create_newid) { + if (create_newid) { this->newid(); } /* We need to clear the tree contents because all the parents will branch from this. Hence, we have to put the node data into an empty container */ this->clear(); - algorithm=alg; - algid=algid_in; - mytype=typ; + algorithm = alg; + algid = algid_in; + mytype = typ; /* Initialize current stage but assume it will be updated as max of parents below */ - current_stage=0; - multimap::const_iterator nptr,nl,nu; + current_stage = 0; + multimap::const_iterator nptr, nl, nu; size_t i; /* current_stage can be ambiguous from multiple inputs. We define the current stage from a reduce as the largest stage value found in all inputs. Note we only test the stage value at the head for each parent */ int max_stage(0); - for(i=0;iis_empty()) - { + for (i = 0; i < parents.size(); ++i) { + if (parents[i]->is_empty()) { stringstream ss; - ss << "Vector member number "< parent_node_data(parents[i]->get_nodes()); + multimap parent_node_data(parents[i]->get_nodes()); /* We also have to get the head data with this method now */ - NodeData nd=parents[i]->current_nodedata(); - if(nd.stage>max_stage) max_stage=nd.stage; - for(nptr=parent_node_data.begin();nptr!=parent_node_data.end();++nptr) - { + NodeData nd = parents[i]->current_nodedata(); + if (nd.stage > max_stage) + max_stage = nd.stage; + for (nptr = parent_node_data.begin(); nptr != parent_node_data.end(); + ++nptr) { /*Adding to nodes multimap has a complication. It is possible in some situations to have duplicate node data coming from different inputs. The method we use to reconstruct the processing history tree @@ -235,109 +217,95 @@ string ProcessingHistory::new_ensemble_process(const string alg,const string alg if the number of values with a common key is large for either this or parent[i]*/ string key(nptr->first); - if(this->nodes.count(key)>0) - { - nl=this->nodes.lower_bound(key); - nu=this->nodes.upper_bound(key); - for(auto ptr=nl;ptr!=nu;++ptr) - { + if (this->nodes.count(key) > 0) { + nl = this->nodes.lower_bound(key); + nu = this->nodes.upper_bound(key); + for (auto ptr = nl; ptr != nu; ++ptr) { NodeData ndtest(ptr->second); - if(ndtest != (nptr->second)) - { + if (ndtest != (nptr->second)) { this->nodes.insert(*nptr); } } - } - else - { + } else { /* No problem just inserting a node if there were no previous entries*/ this->nodes.insert(*nptr); } } /* Also insert the head data */ - pair pnd(current_id,nd); + pair pnd(current_id, nd); this->nodes.insert(pnd); } - current_stage=max_stage; + current_stage = max_stage; /* Now reset the current contents to make it the base of the history tree. Be careful of uninitialized current_stage*/ - if(current_stage>=0) + if (current_stage >= 0) ++current_stage; - else - { + else { elog.log_error("ProcessingHistory::new_ensemble_process", - "current_stage for none of the parents was initialized\nImproper usage will create an invalid history chain that may cause downstream problems", - ErrorSeverity::Complaint); - current_stage=0; + "current_stage for none of the parents was " + "initialized\nImproper usage will create an invalid history " + "chain that may cause downstream problems", + ErrorSeverity::Complaint); + current_stage = 0; } - algorithm=alg; - algid=algid_in; + algorithm = alg; + algid = algid_in; // note this is output type - inputs can be variable and defined by nodes - mytype=typ; - current_status=ProcessingStatus::VOLATILE; + mytype = typ; + current_status = ProcessingStatus::VOLATILE; return current_id; } -/* Companion to new_ensemble_process that appends the history of one datum to the -multimap containers. It does not alter the current values the new_ensemble_process method -MUST have been called before calling this method or the history chain will -become corrupted.*/ -void ProcessingHistory::add_one_input(const ProcessingHistory& data_to_add) -{ +/* Companion to new_ensemble_process that appends the history of one datum to +the multimap containers. It does not alter the current values the +new_ensemble_process method MUST have been called before calling this method or +the history chain will become corrupted.*/ +void ProcessingHistory::add_one_input(const ProcessingHistory &data_to_add) { - if(data_to_add.is_empty()) - { + if (data_to_add.is_empty()) { stringstream ss; - ss<<"Data with uuid="<::iterator nptr; - multimap newhistory = data_to_add.get_nodes(); - multimap::iterator nl,nu; + ss << "Data with uuid=" << data_to_add.id() << " has an empty history chain" + << endl + << "At best this will leave ProcessingHistory incomplete" << endl; + elog.log_error("ProcessingHistory::add_one_input", ss.str(), + ErrorSeverity::Complaint); + } else { + multimap::iterator nptr; + multimap newhistory = data_to_add.get_nodes(); + multimap::iterator nl, nu; /* As above this one needs check for duplicates and only add - a node if the data are unique. This is simple compared to new_ensemble_process - because we just have to check one object's history at a time. */ - for(nptr=newhistory.begin();nptr!=newhistory.end();++nptr) - { + a node if the data are unique. This is simple compared to + new_ensemble_process because we just have to check one object's history at a + time. */ + for (nptr = newhistory.begin(); nptr != newhistory.end(); ++nptr) { string key(nptr->first); - if(this->nodes.count(key)>0) - { - nl=this->nodes.lower_bound(key); - nu=this->nodes.upper_bound(key); - for(auto ptr=nl;ptr!=nu;++ptr) - { + if (this->nodes.count(key) > 0) { + nl = this->nodes.lower_bound(key); + nu = this->nodes.upper_bound(key); + for (auto ptr = nl; ptr != nu; ++ptr) { NodeData ndtest(ptr->second); - if(ndtest != (nptr->second)) - { + if (ndtest != (nptr->second)) { this->nodes.insert(*nptr); } } - } - else - { + } else { this->nodes.insert(*nptr); } } /* Don't forget head node data*/ - NodeData nd=data_to_add.current_nodedata(); - NodeData ndhere=this->current_nodedata(); - pair pnd(current_id,nd); + NodeData nd = data_to_add.current_nodedata(); + NodeData ndhere = this->current_nodedata(); + pair pnd(current_id, nd); this->nodes.insert(pnd); } } /* This one also doesn't change the current contents because it is just a front end to a loop calling add_one_input for each vector component */ -void ProcessingHistory::add_many_inputs(const vector& d) -{ - vector::const_iterator dptr; - for(dptr=d.begin();dptr!=d.end();++dptr) - { +void ProcessingHistory::add_many_inputs(const vector &d) { + vector::const_iterator dptr; + for (dptr = d.begin(); dptr != d.end(); ++dptr) { ProcessingHistory *ptr; - ptr=(*dptr); + ptr = (*dptr); this->add_one_input(*ptr); } } @@ -347,86 +315,93 @@ Be careful not to mix that up with the uuid of the parent. There are two overloaded versions of this method. */ -string ProcessingHistory::new_map(const string alg,const string algid_in, - const AtomicType typ, const ProcessingStatus newstatus) -{ - if(this->is_empty()) - { +string ProcessingHistory::new_map(const string alg, const string algid_in, + const AtomicType typ, + const ProcessingStatus newstatus) { + if (this->is_empty()) { stringstream ss; ss << "Attempt to call this method on an empty history chain for uuid=" - << this->id()<id() << endl + << "Cannot preserve history for algorithm=" << alg + << " with id=" << algid << endl; + elog.log_error("ProcessingHistory::new_map", ss.str(), + ErrorSeverity::Complaint); return current_id; } /* In this case we have to push current data to the history chain */ NodeData nd; - nd=this->current_nodedata(); - /* We always need a new id here for this object we are handling as the child */ - current_id=this->newid(); + nd = this->current_nodedata(); + /* We always need a new id here for this object we are handling as the child + */ + current_id = this->newid(); /* The new id is now the key to link back to previous record so we insert nd with the new key to define that link */ - pair pn(current_id,nd); + pair pn(current_id, nd); this->nodes.insert(pn); - algorithm=alg; - algid=algid_in; - current_status=newstatus; //Probably should default in include file to VOLATILE - if(current_stage>=0) + algorithm = alg; + algid = algid_in; + current_status = + newstatus; // Probably should default in include file to VOLATILE + if (current_stage >= 0) ++current_stage; - else - { - elog.log_error("ProcessingHistory::new_map", - "current_stage on entry had not been initialized\nImproper usage will create an invalid history chain that may cause downstream problems", - ErrorSeverity::Complaint); - current_stage=0; + else { + elog.log_error( + "ProcessingHistory::new_map", + "current_stage on entry had not been initialized\nImproper usage will " + "create an invalid history chain that may cause downstream problems", + ErrorSeverity::Complaint); + current_stage = 0; } - mytype=typ; + mytype = typ; return current_id; } -string ProcessingHistory::new_map(const string alg,const string algid_in, - const AtomicType typ,const ProcessingHistory& copy_to_clone, - const ProcessingStatus newstatus) -{ +string ProcessingHistory::new_map(const string alg, const string algid_in, + const AtomicType typ, + const ProcessingHistory ©_to_clone, + const ProcessingStatus newstatus) { /* We must be sure the chain is empty before we push the clone's data there*/ this->clear(); /* this works because get_nodes pushes the current data to the nodes multimap. We intentionally do not test for an empty nodes map assuming one wouldn't call this without knowing that was necessary. That may be an incorrect assumption, but will use it until proven otherwise*/ - nodes=copy_to_clone.get_nodes(); + nodes = copy_to_clone.get_nodes(); NodeData nd; - nd=this->current_nodedata(); - /* We always need a new id here for this object we are handling as the child */ - current_id=this->newid(); - pair pn(current_id,nd); + nd = this->current_nodedata(); + /* We always need a new id here for this object we are handling as the child + */ + current_id = this->newid(); + pair pn(current_id, nd); this->nodes.insert(pn); - algorithm=alg; - algid=algid_in; - current_status=newstatus; //Probably should default in include file to VOLATILE - if(current_stage>=0) + algorithm = alg; + algid = algid_in; + current_status = + newstatus; // Probably should default in include file to VOLATILE + if (current_stage >= 0) ++current_stage; - else - { - elog.log_error("ProcessingHistory::new_map", - "current_stage on entry had not been initialized\nImproper usage will create an invalid history chain that may cause downstream problems", - ErrorSeverity::Complaint); - current_stage=0; + else { + elog.log_error( + "ProcessingHistory::new_map", + "current_stage on entry had not been initialized\nImproper usage will " + "create an invalid history chain that may cause downstream problems", + ErrorSeverity::Complaint); + current_stage = 0; } - mytype=typ; + mytype = typ; return current_id; } /* Note we always trust that the parent history data is ok in this case assuming this would only be called immediately after a save.*/ -string ProcessingHistory::map_as_saved(const string alg,const string algid_in, - const AtomicType typ) -{ - if(this->is_empty()) - { +string ProcessingHistory::map_as_saved(const string alg, const string algid_in, + const AtomicType typ) { + if (this->is_empty()) { stringstream ss; ss << "Attempt to call this method on an empty history chain for uuid=" - << this->id()<id() << endl + << "Cannot preserve history for writer=" << alg << " with id=" << algid + << endl; + elog.log_error("ProcessingHistory::map_as_saved", ss.str(), + ErrorSeverity::Complaint); return current_id; } /* This is essentially pushing current data to the end of the history chain @@ -434,7 +409,7 @@ string ProcessingHistory::map_as_saved(const string alg,const string algid_in, We use a fixed keyword defined in ProcessingHistory.h assuming saves are always a one-to-one operation (definition of atomic really)*/ NodeData nd(this->current_nodedata()); - pair pn(SAVED_ID_KEY,nd); + pair pn(SAVED_ID_KEY, nd); this->nodes.insert(pn); /* Now we reset current to define it as the saver. Then calls to the getters for the multimap will properly insert this data as the end of the @@ -442,89 +417,79 @@ string ProcessingHistory::map_as_saved(const string alg,const string algid_in, I don't think that will cause an ambiguity, but it might be better to just create a new one here - will do it this way unless that proves a problem as the equality of the two might be a useful test for other purposes */ - algorithm=alg; - algid=algid_in; - current_status=ProcessingStatus::SAVED; - current_id=SAVED_ID_KEY; - if(current_stage>=0) + algorithm = alg; + algid = algid_in; + current_status = ProcessingStatus::SAVED; + current_id = SAVED_ID_KEY; + if (current_stage >= 0) ++current_stage; - else - { - elog.log_error("ProcessingHistory::map_as_saved", - "current_stage on entry had not been initialized\nImproper usage will create an invalid history chain that may cause downstream problems", - ErrorSeverity::Complaint); - current_stage=0; + else { + elog.log_error( + "ProcessingHistory::map_as_saved", + "current_stage on entry had not been initialized\nImproper usage will " + "create an invalid history chain that may cause downstream problems", + ErrorSeverity::Complaint); + current_stage = 0; } - mytype=typ; + mytype = typ; return current_id; } /* Merge in the history nodes from another. */ -void ProcessingHistory::merge(const ProcessingHistory& data_to_add) -{ +void ProcessingHistory::merge(const ProcessingHistory &data_to_add) { - if(data_to_add.is_empty()) - { + if (data_to_add.is_empty()) { stringstream ss; - ss<<"Data with uuid="<::iterator nptr; - multimap newhistory = data_to_add.get_nodes(); - multimap::iterator nl,nu; - for(nptr=newhistory.begin();nptr!=newhistory.end();++nptr) - { + ss << "Data with uuid=" << data_to_add.id() << " has an empty history chain" + << endl + << "At best this will leave ProcessingHistory incomplete" << endl; + elog.log_error("ProcessingHistory::merge", ss.str(), + ErrorSeverity::Complaint); + } else { + multimap::iterator nptr; + multimap newhistory = data_to_add.get_nodes(); + multimap::iterator nl, nu; + for (nptr = newhistory.begin(); nptr != newhistory.end(); ++nptr) { string key(nptr->first); /* if the data_to_add's key matches its current id, we merge all the nodes under the current id of *this. */ - if(key == data_to_add.current_id) - { + if (key == data_to_add.current_id) { this->nodes.insert(std::make_pair(this->current_id, nptr->second)); - } - else if(this->nodes.count(key)>0) - { - nl=this->nodes.lower_bound(key); - nu=this->nodes.upper_bound(key); - for(auto ptr=nl;ptr!=nu;++ptr) - { + } else if (this->nodes.count(key) > 0) { + nl = this->nodes.lower_bound(key); + nu = this->nodes.upper_bound(key); + for (auto ptr = nl; ptr != nu; ++ptr) { NodeData ndtest(ptr->second); - if(ndtest != (nptr->second)) - { + if (ndtest != (nptr->second)) { this->nodes.insert(*nptr); } } - } - else - { + } else { this->nodes.insert(*nptr); } } } } -void ProcessingHistory::accumulate(const string algin,const string algidin, - const AtomicType typ,const ProcessingHistory& ni) -{ +void ProcessingHistory::accumulate(const string algin, const string algidin, + const AtomicType typ, + const ProcessingHistory &ni) { ProcessingHistory newinput(ni); - if((newinput.algorithm != algin) || (newinput.algid != algidin) - || (newinput.jid != newinput.jobid()) || (newinput.jnm != newinput.jobname())) - { + if ((newinput.algorithm != algin) || (newinput.algid != algidin) || + (newinput.jid != newinput.jobid()) || + (newinput.jnm != newinput.jobname())) { NodeData nd; - nd=newinput.current_nodedata(); + nd = newinput.current_nodedata(); newinput.newid(); - pair pn(newinput.current_id,nd); + pair pn(newinput.current_id, nd); newinput.nodes.insert(pn); - newinput.jid=newinput.jobid(); - newinput.jnm=newinput.jobname(); - newinput.algorithm=algin; - newinput.algid=algidin; - newinput.current_status=ProcessingStatus::VOLATILE; - newinput.current_stage=nd.stage+1; - newinput.mytype=typ; + newinput.jid = newinput.jobid(); + newinput.jnm = newinput.jobname(); + newinput.algorithm = algin; + newinput.algid = algidin; + newinput.current_status = ProcessingStatus::VOLATILE; + newinput.current_stage = nd.stage + 1; + newinput.mytype = typ; } /* We have to detect an initialization condition without losing the stored history. There are two conditions we need to handle. First, @@ -535,57 +500,54 @@ void ProcessingHistory::accumulate(const string algin,const string algidin, preserve the history. For the is_empty logic: we just copy the newinput's history and add make its current node data the connection backward - i.e. we have to make a new uuid and add an entry. */ - if(this->is_empty()) - { + if (this->is_empty()) { this->newid(); - nodes=ni.get_nodes(); + nodes = ni.get_nodes(); NodeData nd; - nd=ni.current_nodedata(); - pair pn(current_id,nd); + nd = ni.current_nodedata(); + pair pn(current_id, nd); this->nodes.insert(pn); this->set_jobid(ni.jobid()); this->set_jobname(ni.jobname()); - algorithm=algin; - algid=algidin; - current_status=ProcessingStatus::VOLATILE; - current_stage=nd.stage+1; - mytype=typ; + algorithm = algin; + algid = algidin; + current_status = ProcessingStatus::VOLATILE; + current_stage = nd.stage + 1; + mytype = typ; } /* This is the condition for a left hand side that is not empty but not yet initialized. We detect this condition by a mismatch in all the unique names and ids that mark the current process define this reduce operation*/ - else if((this->algorithm != algin) || (this->algid != algidin) - || (this->jid != newinput.jobid()) || (this->jnm != newinput.jobname())) - { + else if ((this->algorithm != algin) || (this->algid != algidin) || + (this->jid != newinput.jobid()) || + (this->jnm != newinput.jobname())) { /* This is similar to the block above, but the key difference here is we - have to push this's history data to convert it's current data to define an input. - That means getting a new uuid and pushing current node data to the nodes map - as an input */ + have to push this's history data to convert it's current data to define an + input. That means getting a new uuid and pushing current node data to the + nodes map as an input */ NodeData nd; - nd=this->current_nodedata(); + nd = this->current_nodedata(); this->newid(); - pair pn(current_id,nd); + pair pn(current_id, nd); this->nodes.insert(pn); - this->jid=newinput.jobid(); - this->jnm=newinput.jobname(); - this->algorithm=algin; - this->algid=algidin; - this->current_status=ProcessingStatus::VOLATILE; - this->current_stage=nd.stage+1; - this->mytype=typ; + this->jid = newinput.jobid(); + this->jnm = newinput.jobname(); + this->algorithm = algin; + this->algid = algidin; + this->current_status = ProcessingStatus::VOLATILE; + this->current_stage = nd.stage + 1; + this->mytype = typ; this->merge(newinput); - } - else - { + } else { this->merge(newinput); } } -string ProcessingHistory::clean_accumulate_uuids() -{ +string ProcessingHistory::clean_accumulate_uuids() { /* Return undefined immediately if the history chain is empty */ - if(this->is_empty()) return string("UNDEFINED"); - NodeData ndthis=this->current_nodedata(); + if (this->is_empty()) + return string("UNDEFINED"); + NodeData ndthis = this->current_nodedata(); string alg(ndthis.algorithm); string algidtest(ndthis.algid); /* The algorithm here finds all entries for which algorithm is alg and @@ -596,31 +558,28 @@ string ProcessingHistory::clean_accumulate_uuids() /* this approach of pushing iterators to this list that match seemed to be the only way I could make this work correctly. Not sure why, but the added cost over handling this correctly in the loops is small. */ - std::list::iterator> need_to_erase; - for(auto nptr=this->nodes.begin();nptr!=this->nodes.end();++nptr) - { + std::list::iterator> need_to_erase; + for (auto nptr = this->nodes.begin(); nptr != this->nodes.end(); ++nptr) { /* this copy operation is somewhat inefficient, but the cost is small compared to how obscure the code will look if we directly manipulate the second value */ NodeData nd(nptr->second); - /* this depends upon the distinction between set and multiset. i.e. an insert - of a duplicate does nothing*/ - if((alg==nd.algorithm) && (algidtest==nd.algid)) - { + /* this depends upon the distinction between set and multiset. i.e. an + insert of a duplicate does nothing*/ + if ((alg == nd.algorithm) && (algidtest == nd.algid)) { matching_ids.insert(nd.uuid); need_to_erase.push_back(nptr); } } // handle no match situation gracefully - if(matching_ids.empty()) + if (matching_ids.empty()) return string("UNDEFINED"); /* Nothing more to do but return the uuid if there is only one*/ - if(matching_ids.size()==1) + if (matching_ids.size() == 1) return *(matching_ids.begin()); - else - { - for(auto sptr=need_to_erase.begin();sptr!=need_to_erase.end();++sptr) - { + else { + for (auto sptr = need_to_erase.begin(); sptr != need_to_erase.end(); + ++sptr) { nodes.erase(*sptr); } need_to_erase.clear(); @@ -629,37 +588,32 @@ string ProcessingHistory::clean_accumulate_uuids() and change all the others. This operation works ONLY because in a multimap erase only invalidates the iterator it points to and others remain valid. */ - string master_uuid=ndthis.uuid; - for(auto sptr=matching_ids.begin();sptr!=matching_ids.end();++sptr) - { + string master_uuid = ndthis.uuid; + for (auto sptr = matching_ids.begin(); sptr != matching_ids.end(); ++sptr) { /* Note this test is necessary to stip the master_uuid - no else needed*/ - if((*sptr)!=master_uuid) - { - multimap::iterator nl,nu; - nl=this->nodes.lower_bound(*sptr); - nu=this->nodes.upper_bound(*sptr); - for(auto nptr=nl;nptr!=nu;++nptr) - { + if ((*sptr) != master_uuid) { + multimap::iterator nl, nu; + nl = this->nodes.lower_bound(*sptr); + nu = this->nodes.upper_bound(*sptr); + for (auto nptr = nl; nptr != nu; ++nptr) { NodeData nd; - nd=(nptr->second); + nd = (nptr->second); need_to_erase.push_back(nptr); - nodes.insert(pair(master_uuid,nd)); + nodes.insert(pair(master_uuid, nd)); } } } - for(auto sptr=need_to_erase.begin();sptr!=need_to_erase.end();++sptr) - { + for (auto sptr = need_to_erase.begin(); sptr != need_to_erase.end(); ++sptr) { nodes.erase(*sptr); } return master_uuid; } -multimap ProcessingHistory::get_nodes() const -{ +multimap ProcessingHistory::get_nodes() const { /* Return empty map if it has no data - necessary or the logic below will insert an empty head to the chain. */ - if(this->is_empty()) - return nodes; // a way to return an empty container + if (this->is_empty()) + return nodes; // a way to return an empty container /* This is wrong, I think, but retained to test before removing. remove this once current idea is confirmed. Note if that proves true we can also remove the two lines above as they do @@ -674,83 +628,73 @@ multimap ProcessingHistory::get_nodes() const */ return nodes; } -void ProcessingHistory::clear() -{ +void ProcessingHistory::clear() { nodes.clear(); - current_status=ProcessingStatus::UNDEFINED; - current_stage=0; - mytype=AtomicType::UNDEFINED; - algorithm="UNDEFINED"; - algid="UNDEFINED"; + current_status = ProcessingStatus::UNDEFINED; + current_stage = 0; + mytype = AtomicType::UNDEFINED; + algorithm = "UNDEFINED"; + algid = "UNDEFINED"; } /* This is really just a wrapper around the count method. We do it because it is an implementation detail to use a multimap in this form */ -int ProcessingHistory::number_inputs(const string testuuid) const -{ +int ProcessingHistory::number_inputs(const string testuuid) const { // Return result is int to mesh better with python even though // count returns size_t - int n=nodes.count(testuuid); + int n = nodes.count(testuuid); return n; } -int ProcessingHistory::number_inputs() const -{ +int ProcessingHistory::number_inputs() const { return this->number_inputs(current_id); } -string ProcessingHistory::newid() -{ +string ProcessingHistory::newid() { boost::uuids::random_generator gen; boost::uuids::uuid uuidval; - uuidval=gen(); - this->current_id=boost::uuids::to_string(uuidval); + uuidval = gen(); + this->current_id = boost::uuids::to_string(uuidval); return current_id; } -void ProcessingHistory::set_id(const string newid) -{ - this->current_id=newid; -} -NodeData ProcessingHistory::current_nodedata() const -{ +void ProcessingHistory::set_id(const string newid) { this->current_id = newid; } +NodeData ProcessingHistory::current_nodedata() const { NodeData nd; - nd.status=current_status; - nd.uuid=current_id; - nd.type=mytype; - nd.stage=current_stage; - nd.algorithm=algorithm; - nd.algid=algid; + nd.status = current_status; + nd.uuid = current_id; + nd.type = mytype; + nd.stage = current_stage; + nd.algorithm = algorithm; + nd.algid = algid; return nd; } -list ProcessingHistory::inputs(const std::string id_to_find) const -{ +list ProcessingHistory::inputs(const std::string id_to_find) const { list result; // Return empty list immediately if key not found - if(nodes.count(id_to_find)<=0) return result; + if (nodes.count(id_to_find) <= 0) + return result; /* Note these have to be const_iterators because method is tagged const*/ - multimap::const_iterator upper,lower; - lower=nodes.lower_bound(id_to_find); - upper=nodes.upper_bound(id_to_find); - multimap::const_iterator mptr; - for(mptr=lower;mptr!=upper;++mptr) - { + multimap::const_iterator upper, lower; + lower = nodes.lower_bound(id_to_find); + upper = nodes.upper_bound(id_to_find); + multimap::const_iterator mptr; + for (mptr = lower; mptr != upper; ++mptr) { result.push_back(mptr->second); } return result; }; -ProcessingHistory& ProcessingHistory::operator=(const ProcessingHistory& parent) -{ - if(this!=(&parent)) - { +ProcessingHistory & +ProcessingHistory::operator=(const ProcessingHistory &parent) { + if (this != (&parent)) { this->BasicProcessingHistory::operator=(parent); - nodes=parent.nodes; - current_status=parent.current_status; - current_id=parent.current_id; - current_stage=parent.current_stage; - mytype=parent.mytype; - algorithm=parent.algorithm; - algid=parent.algid; - elog=parent.elog; + nodes = parent.nodes; + current_status = parent.current_status; + current_id = parent.current_id; + current_stage = parent.current_stage; + mytype = parent.mytype; + algorithm = parent.algorithm; + algid = parent.algid; + elog = parent.elog; } return *this; } @@ -760,15 +704,13 @@ ProcessingHistory. They were made functions to reduce unnecessary baggage in the low level ProcessingHistory object that is a base class of all atomic data in mspass */ /* This is used for sorting tuple in set below */ -typedef std::tuple Algdata; -class sort_by_stage -{ +typedef std::tuple Algdata; +class sort_by_stage { public: - bool operator()(const Algdata A, const Algdata B) const - { - int i=std::get<0>(A); - int j=std::get<0>(B); - return i(A); + int j = std::get<0>(B); + return i < j; }; }; @@ -778,28 +720,25 @@ only returned a list of names. The order of the tuple returned is: stage : algorithm : algid Note the list is sorted into ascending order by stage*/ -list algorithm_history(const ProcessingHistory& h) -{ +list algorithm_history(const ProcessingHistory &h) { /* We use this set container to sort out unique combinations of the tuple of 3 pieces of NodeData that form the ouput. */ std::set algset; - multimap hmap=h.get_nodes(); - multimap::iterator mptr; - for(mptr=hmap.begin();mptr!=hmap.end();++mptr) - { - NodeData n=mptr->second; //created only to make this more readable - Algdata work(n.stage,n.algorithm,n.algid); + multimap hmap = h.get_nodes(); + multimap::iterator mptr; + for (mptr = hmap.begin(); mptr != hmap.end(); ++mptr) { + NodeData n = mptr->second; // created only to make this more readable + Algdata work(n.stage, n.algorithm, n.algid); /* Intentionally ignore the return of insert. We expect it to return true and false for different elements */ algset.insert(work); } /* This sort is creating a mysterious compilation so will temporarily disable it to work on testing main class */ - //std::sort(algset.begin(),algset.end(),sort_by_stage); + // std::sort(algset.begin(),algset.end(),sort_by_stage); list result; set::iterator aptr; - for(aptr=algset.begin();aptr!=algset.end();++aptr) - { + for (aptr = algset.begin(); aptr != algset.end(); ++aptr) { result.push_back(*aptr); } return result; @@ -810,18 +749,17 @@ match alg and algid. The original method had a different name (data_processed_by) that only made sense if the function were a member. This function does the same thing but has a different name that hopefully is closer to describing what it does */ -list algorithm_outputs(const ProcessingHistory& h, const string alg, - const string aid) -{ +list algorithm_outputs(const ProcessingHistory &h, const string alg, + const string aid) { list result; - multimap hmap=h.get_nodes(); - multimap::iterator hptr; - for(hptr=hmap.begin();hptr!=hmap.end();++hptr) - { - NodeData n=hptr->second; - if( (alg==n.algorithm) && (aid==n.algid)) result.push_back(hptr->first); + multimap hmap = h.get_nodes(); + multimap::iterator hptr; + for (hptr = hmap.begin(); hptr != hmap.end(); ++hptr) { + NodeData n = hptr->second; + if ((alg == n.algorithm) && (aid == n.algid)) + result.push_back(hptr->first); } return result; } -}//End mspass namespace encapsulation +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/SphericalCoordinate.cc b/cxx/src/lib/utility/SphericalCoordinate.cc index 0fee9fb9c..d6b77296e 100644 --- a/cxx/src/lib/utility/SphericalCoordinate.cc +++ b/cxx/src/lib/utility/SphericalCoordinate.cc @@ -1,58 +1,50 @@ +#include "mspass/utility/SphericalCoordinate.h" #include #include -#include "mspass/utility/SphericalCoordinate.h" -namespace mspass::utility -{ +namespace mspass::utility { /* This routine takes a 3-d unit vector, nu, and converts it -to a SphericalCoordinate structure which is returned. The +to a SphericalCoordinate structure which is returned. The input coordinates are assume to be standard, right handed cartesian coordinates in 1,2,3 order */ -SphericalCoordinate UnitVectorToSpherical(const double nu[3]) -{ - SphericalCoordinate xsc; +SphericalCoordinate UnitVectorToSpherical(const double nu[3]) { + SphericalCoordinate xsc; - xsc.radius = 1.0; - xsc.theta = acos(nu[2]); - if(hypot(nu[0],nu[1]) #include "mspass/utility/MsPASSError.h" -namespace mspass::utility{ +#include +namespace mspass::utility { /* Standardizes top level directory for mspass */ -std::string data_directory() -{ - const std::string mspass_home_envname("MSPASS_HOME"); - char *base; - /* Note man page for getenv says explicitly the return of getenv should not - be touched - i.e. don't free it*/ - base=getenv(mspass_home_envname.c_str()); - if(base==NULL)throw MsPASSError(std::string("maspass_data_directory procedure: ") - + "required environmental variable="+mspass_home_envname+" is not set"); - std::string datadir; - datadir=std::string(base)+"/data"; - return datadir; -} +std::string data_directory() { + const std::string mspass_home_envname("MSPASS_HOME"); + char *base; + /* Note man page for getenv says explicitly the return of getenv should not + be touched - i.e. don't free it*/ + base = getenv(mspass_home_envname.c_str()); + if (base == NULL) + throw MsPASSError(std::string("maspass_data_directory procedure: ") + + "required environmental variable=" + mspass_home_envname + + " is not set"); + std::string datadir; + datadir = std::string(base) + "/data"; + return datadir; } - +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/dmatrix.cc b/cxx/src/lib/utility/dmatrix.cc index dadc2422b..5aac136b6 100644 --- a/cxx/src/lib/utility/dmatrix.cc +++ b/cxx/src/lib/utility/dmatrix.cc @@ -1,26 +1,23 @@ -#include -#include "misc/blas.h" #include "mspass/utility/dmatrix.h" +#include "misc/blas.h" +#include -namespace mspass::utility{ +namespace mspass::utility { using namespace std; -dmatrix::dmatrix() -{ - nrr=0; - ncc=0; - length=0; +dmatrix::dmatrix() { + nrr = 0; + ncc = 0; + length = 0; ary.reserve(0); } -dmatrix::dmatrix(const size_t nr, const size_t nc) -{ - nrr=nr; - ncc=nc; - length=nr*nc; - if(length<1) - { - length=1; - nrr=ncc=0; +dmatrix::dmatrix(const size_t nr, const size_t nc) { + nrr = nr; + ncc = nc; + length = nr * nc; + if (length < 1) { + length = 1; + nrr = ncc = 0; } /* This std::vector method allocates space so zero method can just * use indexing. */ @@ -28,250 +25,228 @@ dmatrix::dmatrix(const size_t nr, const size_t nc) this->zero(); } -dmatrix::dmatrix(const dmatrix& other) - { - nrr=other.nrr; - ncc=other.ncc; - length=other.length; - ary=other.ary; - } +dmatrix::dmatrix(const dmatrix &other) { + nrr = other.nrr; + ncc = other.ncc; + length = other.length; + ary = other.ary; +} -dmatrix::~dmatrix() -{ -//if(ary!=NULL) delete [] ary; +dmatrix::~dmatrix() { + // if(ary!=NULL) delete [] ary; } -double dmatrix::operator()(const size_t rowindex, const size_t colindex) const -{ - int out_of_range=0; - if (rowindex>=nrr) out_of_range=1; - if (rowindex<0) out_of_range=1; - if (colindex>=ncc) out_of_range=1; - if (colindex<0) out_of_range=1; +double dmatrix::operator()(const size_t rowindex, const size_t colindex) const { + int out_of_range = 0; + if (rowindex >= nrr) + out_of_range = 1; + if (rowindex < 0) + out_of_range = 1; + if (colindex >= ncc) + out_of_range = 1; + if (colindex < 0) + out_of_range = 1; if (out_of_range) - throw dmatrix_index_error(nrr,ncc,rowindex,colindex); - double result=ary[rowindex+(nrr)*(colindex)]; + throw dmatrix_index_error(nrr, ncc, rowindex, colindex); + double result = ary[rowindex + (nrr) * (colindex)]; return result; } -double& dmatrix::operator()(const size_t rowindex, const size_t colindex) -{ - int out_of_range=0; - if (rowindex>=nrr) out_of_range=1; - if (rowindex<0) out_of_range=1; - if (colindex>=ncc) out_of_range=1; - if (colindex<0) out_of_range=1; +double &dmatrix::operator()(const size_t rowindex, const size_t colindex) { + int out_of_range = 0; + if (rowindex >= nrr) + out_of_range = 1; + if (rowindex < 0) + out_of_range = 1; + if (colindex >= ncc) + out_of_range = 1; + if (colindex < 0) + out_of_range = 1; if (out_of_range) - throw dmatrix_index_error(nrr,ncc,rowindex,colindex); - return (ary[rowindex+(nrr)*(colindex)]); + throw dmatrix_index_error(nrr, ncc, rowindex, colindex); + return (ary[rowindex + (nrr) * (colindex)]); } // -// subtle difference here. This one returns a pointer to the +// subtle difference here. This one returns a pointer to the // requested element // -double* dmatrix::get_address(size_t rowindex, size_t colindex) const -{ +double *dmatrix::get_address(size_t rowindex, size_t colindex) const { double *ptr; - int out_of_range=0; - if (rowindex>=nrr) out_of_range=1; - if (rowindex<0) out_of_range=1; - if (colindex>=ncc) out_of_range=1; - if (colindex<0) out_of_range=1; + int out_of_range = 0; + if (rowindex >= nrr) + out_of_range = 1; + if (rowindex < 0) + out_of_range = 1; + if (colindex >= ncc) + out_of_range = 1; + if (colindex < 0) + out_of_range = 1; if (out_of_range) - throw dmatrix_index_error(nrr,ncc,rowindex,colindex); + throw dmatrix_index_error(nrr, ncc, rowindex, colindex); /* This is somewhat contradictory to the const qualifier of the method but not really. const in that position implies the method itself does not alter the object, but a pointer that is not const is always something that allows data to be changed*/ - ptr=const_cast(&(ary[rowindex+(nrr)*(colindex)])); - return(ptr); + ptr = const_cast(&(ary[rowindex + (nrr) * (colindex)])); + return (ptr); } -dmatrix& dmatrix::operator=(const dmatrix& other) -{ - if(&other!=this) - { - ncc=other.ncc; - nrr=other.nrr; - length=other.length; - ary=other.ary; - } - return *this; +dmatrix &dmatrix::operator=(const dmatrix &other) { + if (&other != this) { + ncc = other.ncc; + nrr = other.nrr; + length = other.length; + ary = other.ary; + } + return *this; } -dmatrix& dmatrix::operator+=(const dmatrix& other) -{ +dmatrix &dmatrix::operator+=(const dmatrix &other) { size_t i; - if ((nrr!=other.nrr)||(length!=other.length)) + if ((nrr != other.nrr) || (length != other.length)) throw dmatrix_size_error(nrr, ncc, other.nrr, other.length); - for(i=0;i(x1).get_address(i,0); - bptr=const_cast(b).get_address(0,j); - /* This temporary seems necessary */ - double *dptr; - dptr=prod.get_address(i,j); - *dptr=ddot(x1.columns(),x1ptr,x1.rows(),bptr,1); - } - return prod; +dmatrix dmatrix::operator+(const dmatrix &other) const { + try { + dmatrix result(*this); + result += other; + return result; + } catch (...) { + throw; + }; } - -dmatrix operator*(const double& x, const dmatrix &zx) noexcept -{ +dmatrix dmatrix::operator-(const dmatrix &other) const { + try { + dmatrix result(*this); + result -= other; + return result; + } catch (...) { + throw; + }; +} + +dmatrix operator*(const dmatrix &x1, const dmatrix &b) { + size_t i, j; + /* The computed length in last arg to the error object is a relic*/ + if (x1.columns() != b.rows()) + throw dmatrix_size_error(x1.rows(), x1.columns(), b.rows(), + b.rows() * b.columns()); + dmatrix prod(x1.rows(), b.columns()); + for (i = 0; i < x1.rows(); i++) + for (j = 0; j < b.columns(); j++) { + double *x1ptr, *bptr; + x1ptr = const_cast(x1).get_address(i, 0); + bptr = const_cast(b).get_address(0, j); + /* This temporary seems necessary */ + double *dptr; + dptr = prod.get_address(i, j); + *dptr = ddot(x1.columns(), x1ptr, x1.rows(), bptr, 1); + } + return prod; +} + +dmatrix operator*(const double &x, const dmatrix &zx) noexcept { size_t i; - dmatrix tempmat(zx.rows(),zx.columns()); - size_t lenary=zx.rows()*zx.columns(); - double *zptr,*dptr; - zptr=const_cast(zx).get_address(0,0); - dptr=tempmat.get_address(0,0); - for(i=0;i(zx).get_address(0, 0); + dptr = tempmat.get_address(0, 0); + for (i = 0; i < lenary; ++i) { + (*dptr) = x * (*zptr); + ++dptr; + ++zptr; } return tempmat; } -dmatrix dmatrix::operator* (double x) const noexcept -{ - double *ptr; - dmatrix result(*this); - ptr=result.get_address(0,0); - dscal(length,x,ptr,1); - return result; +dmatrix dmatrix::operator*(double x) const noexcept { + double *ptr; + dmatrix result(*this); + ptr = result.get_address(0, 0); + dscal(length, x, ptr, 1); + return result; } - - - -dmatrix tr(const dmatrix& x1) noexcept -{ - size_t i,j; - dmatrix temp(x1.columns(),x1.rows()); - for(i=0; i dmatrix::size() const -{ - vector sz; +vector dmatrix::size() const { + vector sz; sz.push_back(nrr); sz.push_back(ncc); - return(sz); + return (sz); } // simpler versions of same -size_t dmatrix::rows() const -{ - return(nrr); -} -size_t dmatrix::columns() const -{ - return(ncc); -} +size_t dmatrix::rows() const { return (nrr); } +size_t dmatrix::columns() const { return (ncc); } // vector methods -dvector& dvector::operator=(const dvector& other) -{ - if(this != &other) - { - ncc=1; - nrr=other.nrr; - length=other.length; - ary=other.ary; - } - return *this; +dvector &dvector::operator=(const dvector &other) { + if (this != &other) { + ncc = 1; + nrr = other.nrr; + length = other.length; + ary = other.ary; + } + return *this; } -dvector::dvector(const dvector& other) -{ - ncc=1; - nrr=other.nrr; - length=other.length; - ary=other.ary; +dvector::dvector(const dvector &other) { + ncc = 1; + nrr = other.nrr; + length = other.length; + ary = other.ary; } -double &dvector::operator()(const size_t rowindex) -{ - if (rowindex>=nrr) - throw dmatrix_index_error(nrr,1,rowindex,1); +double &dvector::operator()(const size_t rowindex) { + if (rowindex >= nrr) + throw dmatrix_index_error(nrr, 1, rowindex, 1); return (ary[rowindex]); -} -dvector operator*(const dmatrix& x1,const dvector& b) -{ - size_t i; - size_t nrx1=x1.rows(); - size_t ncx1=x1.columns(); - size_t nrb=const_cast(b).rows(); - if(ncx1!=nrb) - throw dmatrix_size_error(nrx1, ncx1, nrb, 1); - dvector prod(nrx1); - for(i=0;i(x1).get_address(i,0),nrx1, - const_cast(b).get_address(0,0),1); - return prod; } -} // end mspass namespace +dvector operator*(const dmatrix &x1, const dvector &b) { + size_t i; + size_t nrx1 = x1.rows(); + size_t ncx1 = x1.columns(); + size_t nrb = const_cast(b).rows(); + if (ncx1 != nrb) + throw dmatrix_size_error(nrx1, ncx1, nrb, 1); + dvector prod(nrx1); + for (i = 0; i < nrx1; i++) + prod(i) = ddot(nrb, const_cast(x1).get_address(i, 0), nrx1, + const_cast(b).get_address(0, 0), 1); + return prod; +} +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/metadata_helpers.cc b/cxx/src/lib/utility/metadata_helpers.cc index a171e514a..e12ba8ede 100644 --- a/cxx/src/lib/utility/metadata_helpers.cc +++ b/cxx/src/lib/utility/metadata_helpers.cc @@ -1,124 +1,120 @@ -#include "mspass/utility/MsPASSError.h" -#include "mspass/utility/Metadata.h" #include "mspass/utility/AntelopePf.h" +#include "mspass/utility/Metadata.h" +#include "mspass/utility/MsPASSError.h" using namespace std; using namespace mspass::utility; -namespace mspass::utility -{ -MetadataList get_mdlist(const AntelopePf& m,const string tag) -{ - try { - MetadataList mdl; - Metadata_typedef mdt; - string mdname, mdtype; - list t=m.get_tbl(tag); - list::iterator tptr; - for(tptr=t.begin();tptr!=t.end();++tptr) - { - /* This loop is identical to above in pfget_mdlist - except for the next line*/ - istringstream instr(*tptr); - instr >> mdname; - instr >> mdtype; - mdt.tag = mdname; - if(mdtype=="real" || mdtype=="REAL" || mdtype=="float" - || mdtype=="FLOAT" || mdtype=="Real32") - mdt.mdt = MDtype::Real; - else if(mdtype=="double" || mdtype=="Real64" || mdtype=="real64") - mdt.mdt = MDtype::Double; - else if(mdtype=="int" || mdtype=="INT" || mdtype=="integer") - mdt.mdt = MDtype::Integer; - else if(mdtype=="string" || mdtype=="STRING") - mdt.mdt = MDtype::String; - else if(mdtype=="boolean" || mdtype=="BOOLEAN") - mdt.mdt = MDtype::Boolean; - else - { - stringstream ss; - ss << "get_mdlist: Error in mdlist specification type name defined by key = " - << mdtype << " was not recognized."< t = m.get_tbl(tag); + list::iterator tptr; + for (tptr = t.begin(); tptr != t.end(); ++tptr) { + /* This loop is identical to above in pfget_mdlist + except for the next line*/ + istringstream instr(*tptr); + instr >> mdname; + instr >> mdtype; + mdt.tag = mdname; + if (mdtype == "real" || mdtype == "REAL" || mdtype == "float" || + mdtype == "FLOAT" || mdtype == "Real32") + mdt.mdt = MDtype::Real; + else if (mdtype == "double" || mdtype == "Real64" || mdtype == "real64") + mdt.mdt = MDtype::Double; + else if (mdtype == "int" || mdtype == "INT" || mdtype == "integer") + mdt.mdt = MDtype::Integer; + else if (mdtype == "string" || mdtype == "STRING") + mdt.mdt = MDtype::String; + else if (mdtype == "boolean" || mdtype == "BOOLEAN") + mdt.mdt = MDtype::Boolean; + else { + stringstream ss; + ss << "get_mdlist: Error in mdlist specification type name defined by " + "key = " + << mdtype << " was not recognized." << endl + << "Listed with attribute name=" << mdname << endl + << "List is not valid." << endl; + /* We mark this invalid, but it will probably normally be a fatal error + because this procedure would normally be calld on startup */ + throw MsPASSError(ss.str(), ErrorSeverity::Invalid); + } + mdl.push_back(mdt); + } + return (mdl); + } catch (...) { + throw; + }; } -int copy_selected_metadata(const Metadata& mdin, Metadata& mdout, - const MetadataList& mdlist) -{ - MetadataList::const_iterator mdti; - int count; +int copy_selected_metadata(const Metadata &mdin, Metadata &mdout, + const MetadataList &mdlist) { + MetadataList::const_iterator mdti; + int count; - for(mdti=mdlist.begin(),count=0;mdti!=mdlist.end();++mdti,++count) - { - MDtype mdtest; - float f; - double r; - int ii; - long iv; - string s; - bool b; + for (mdti = mdlist.begin(), count = 0; mdti != mdlist.end(); + ++mdti, ++count) { + MDtype mdtest; + float f; + double r; + int ii; + long iv; + string s; + bool b; - mdtest = mdti->mdt; - try - { - switch(mdtest) - { - case MDtype::Real: - case MDtype::Real32: - f=mdin.get(mdti->tag); - mdout.put(mdti->tag,f); - ++count; - break; - case MDtype::Double: - case MDtype::Real64: - r=mdin.get(mdti->tag); - mdout.put(mdti->tag,r); - ++count; - break; - case MDtype::Integer: - case MDtype::Int32: - ii=mdin.get(mdti->tag); - mdout.put(mdti->tag,ii); - ++count; - break; - case MDtype::Long: - case MDtype::Int64: - iv=mdin.get(mdti->tag); - mdout.put(mdti->tag,iv); - ++count; - break; - case MDtype::String: - s=mdin.get(mdti->tag); - mdout.put(mdti->tag,s); - ++count; - break; - case MDtype::Boolean: - b=mdin.get(mdti->tag); - mdout.put(mdti->tag,b); - ++count; - break; - case MDtype::Invalid: - // silently skip values marked as invalid - break; - default: - throw MsPASSError(string("copy_selected_metadata: ") - + " was passed illegal type definition\n" - + string("This indicates a coding error that must be fixed\n") - + string("If caller does not exit on this error, expect a less graceful abort"), - ErrorSeverity::Fatal); - }; - } catch( ... ) - { - throw; - } + mdtest = mdti->mdt; + try { + switch (mdtest) { + case MDtype::Real: + case MDtype::Real32: + f = mdin.get(mdti->tag); + mdout.put(mdti->tag, f); + ++count; + break; + case MDtype::Double: + case MDtype::Real64: + r = mdin.get(mdti->tag); + mdout.put(mdti->tag, r); + ++count; + break; + case MDtype::Integer: + case MDtype::Int32: + ii = mdin.get(mdti->tag); + mdout.put(mdti->tag, ii); + ++count; + break; + case MDtype::Long: + case MDtype::Int64: + iv = mdin.get(mdti->tag); + mdout.put(mdti->tag, iv); + ++count; + break; + case MDtype::String: + s = mdin.get(mdti->tag); + mdout.put(mdti->tag, s); + ++count; + break; + case MDtype::Boolean: + b = mdin.get(mdti->tag); + mdout.put(mdti->tag, b); + ++count; + break; + case MDtype::Invalid: + // silently skip values marked as invalid + break; + default: + throw MsPASSError( + string("copy_selected_metadata: ") + + " was passed illegal type definition\n" + + string("This indicates a coding error that must be fixed\n") + + string("If caller does not exit on this error, expect a less " + "graceful abort"), + ErrorSeverity::Fatal); + }; + } catch (...) { + throw; } - return count; + } + return count; } -} // Termination of namespace mspass::utility definitions - +} // namespace mspass::utility diff --git a/cxx/src/lib/utility/normalize.cc b/cxx/src/lib/utility/normalize.cc index 155f7c5da..de086e7d6 100644 --- a/cxx/src/lib/utility/normalize.cc +++ b/cxx/src/lib/utility/normalize.cc @@ -1,47 +1,44 @@ -#include +#include "misc/blas.h" #include -#include +#include #include -#include "misc/blas.h" -namespace mspass::utility{ +#include +namespace mspass::utility { using namespace std; -vector normalize_rows(const dmatrix& d) -{ - int nrows=d.rows(); - int ncols=d.columns(); - double nrm,scl; +vector normalize_rows(const dmatrix &d) { + int nrows = d.rows(); + int ncols = d.columns(); + double nrm, scl; vector allnrms; allnrms.reserve(nrows); - for(int i=0;i normalize_columns(const dmatrix& d) -{ - int nrows=d.rows(); - int ncols=d.columns(); - double nrm,scl; +vector normalize_columns(const dmatrix &d) { + int nrows = d.rows(); + int ncols = d.columns(); + double nrm, scl; vector allnrms; allnrms.reserve(ncols); - for(int i=0;i