MsPASS C++ API  2.4.1.dev4+g92330b7a
Defines the C++ API for MsPASS
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Protected Attributes | List of all members
mspass::seismic::CoreSeismogram Class Reference

Vector (three-component) seismogram data object. More...

#include <CoreSeismogram.h>

Inheritance diagram for mspass::seismic::CoreSeismogram:
Inheritance graph
[legend]
Collaboration diagram for mspass::seismic::CoreSeismogram:
Collaboration graph
[legend]

Public Member Functions

 CoreSeismogram ()
 
 CoreSeismogram (const size_t nsamples)
 
 CoreSeismogram (const std::vector< mspass::seismic::CoreTimeSeries > &ts, const unsigned int component_to_clone=0)
 
 CoreSeismogram (const mspass::utility::Metadata &md, const bool load_data=true)
 Construct from Metadata definition that includes data path.
 
 CoreSeismogram (const CoreSeismogram &)
 
void set_dt (const double sample_interval)
 Set the sample interval.
 
void set_npts (const size_t npts)
 Set the number of samples attribute for data.
 
void sync_npts ()
 Sync the number of samples attribute with actual data size.
 
void set_t0 (const double t0in)
 Set the data start time.
 
CoreSeismogramoperator= (const CoreSeismogram &)
 
CoreSeismogramoperator+= (const CoreSeismogram &d)
 Summation operator.
 
const CoreSeismogram operator+ (const CoreSeismogram &other) const
 
CoreSeismogramoperator*= (const double)
 
CoreSeismogramoperator-= (const CoreSeismogram &d)
 Subtraction operator.
 
const CoreSeismogram operator- (const CoreSeismogram &other) const
 
std::vector< doubleoperator[] (const int sample) const
 
std::vector< doubleoperator[] (const double time) const
 Overloaded version of operator[] for time.
 
virtual ~CoreSeismogram ()
 
void rotate_to_standard ()
 
void rotate (mspass::utility::SphericalCoordinate &sc)
 
void rotate (const double nu[3])
 
void rotate (const double phi)
 Rotate horizontals by a simple angle in degrees.
 
void transform (const double a[3][3])
 
void free_surface_transformation (const mspass::seismic::SlownessVector u, const double vp0, const double vs0)
 
mspass::utility::dmatrix get_transformation_matrix () const
 
bool set_transformation_matrix (const mspass::utility::dmatrix &A)
 Define the transformaton matrix.
 
bool set_transformation_matrix (const double a[3][3])
 Define the transformaton matrix with a C style 3x3 matrix.
 
bool set_transformation_matrix (pybind11::object a)
 Define the transformaton matrix with a python object.
 
bool cardinal () const
 
bool orthogonal () const
 
double endtime () const noexcept
 
- Public Member Functions inherited from mspass::seismic::BasicTimeSeries
 BasicTimeSeries ()
 
 BasicTimeSeries (const BasicTimeSeries &)
 
virtual ~BasicTimeSeries ()
 Virtual destructor.
 
double time (const int i) const
 
int sample_number (double t) const
 
double endtime () const noexcept
 
bool shifted () const
 
double get_t0shift () const
 
double time_reference () const
 
void force_t0_shift (const double t)
 Force a t0 shift value on data.
 
virtual void ator (const double tshift)
 
virtual void rtoa ()
 
virtual void shift (const double dt)
 
bool live () const
 
bool dead () const
 
void kill ()
 
void set_live ()
 
double dt () const
 
bool time_is_UTC () const
 
bool time_is_relative () const
 
TimeReferenceType timetype () const
 
double samprate () const
 
size_t npts () const
 
double t0 () const
 
std::vector< double > time_axis () const
 
void set_tref (const TimeReferenceType newtref)
 Force the time standard.
 
BasicTimeSeriesoperator= (const BasicTimeSeries &parent)
 

Public Attributes

mspass::utility::dmatrix u
 

Protected Attributes

bool components_are_orthogonal
 
bool components_are_cardinal
 
double tmatrix [3][3]
 
- Protected Attributes inherited from mspass::seismic::BasicTimeSeries
bool mlive
 
double mdt
 
double mt0
 
size_t nsamp
 
TimeReferenceType tref
 
bool t0shift_is_valid
 
double t0shift
 

Detailed Description

Vector (three-component) seismogram data object.

A three-component seismogram is a common concept in seismology. The concept used here is that a three-component seismogram is a time series with a 3-vector as the data at each time step. As a result the data are stored internally as a matrix with row defining the component number (C indexing 0,1,2) and the column defining the time variable. The object inherits common concepts of a time series through the BasicTimeSeries object. Auxiliary parameters are defined for the object through inheritance of a Metadata object.

Author
Gary L. Pavlis

Constructor & Destructor Documentation

◆ CoreSeismogram() [1/5]

mspass::seismic::CoreSeismogram::CoreSeismogram ( )

Default constructor.

Sets ns to zero and builds an empty data matrix. The live variable in BasicTimeSeries is also set false.

27 /* mlive and tref are set in BasicTimeSeries so we don't use putters for
28 them here. These three initialize Metadata properly for these attributes*/
29 this->set_dt(1.0);
30 this->set_t0(0.0);
31 this->set_npts(0);
34 for (int i = 0; i < 3; ++i)
35 for (int j = 0; j < 3; ++j)
36 if (i == j)
37 tmatrix[i][i] = 1.0;
38 else
39 tmatrix[i][j] = 0.0;
40}
BasicTimeSeries()
Definition BasicTimeSeries.cc:45
double tmatrix[3][3]
Definition CoreSeismogram.h:528
void set_dt(const double sample_interval)
Set the sample interval.
Definition CoreSeismogram.cc:1007
void set_t0(const double t0in)
Set the data start time.
Definition CoreSeismogram.cc:1023
bool components_are_orthogonal
Definition CoreSeismogram.h:511
void set_npts(const size_t npts)
Set the number of samples attribute for data.
Definition CoreSeismogram.cc:1040
bool components_are_cardinal
Definition CoreSeismogram.h:520
Metadata()
Definition Metadata.h:105
T get(const std::string key) const
Definition Metadata.h:477

References components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), set_dt(), set_npts(), set_t0(), and tmatrix.

◆ CoreSeismogram() [2/5]

mspass::seismic::CoreSeismogram::CoreSeismogram ( const size_t  nsamples)

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.

Parameters
nsamplesnumber of samples expected for holding data.
43 /* IMPORTANT: this constructor assumes BasicTimeSeries initializes the
44 equivalent of:
45 set_dt(1.0)
46 set_t0(0.0)
47 set_tref(TimeReferenceType::Relative)
48 this->kill() - i.e. marked dead
49 */
50 this->set_npts(
51 nsamples); // Assume this is an allocator of the 3xnsamples matrix
54 for (int i = 0; i < 3; ++i)
55 for (int j = 0; j < 3; ++j)
56 if (i == j)
57 tmatrix[i][i] = 1.0;
58 else
59 tmatrix[i][j] = 0.0;
60}

References components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), set_npts(), and tmatrix.

◆ CoreSeismogram() [3/5]

mspass::seismic::CoreSeismogram::CoreSeismogram ( const std::vector< mspass::seismic::CoreTimeSeries > &  ts,
const unsigned int  component_to_clone = 0 
)

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.

Exceptions
SeisppErrorexception can be throw for a variety of serious problems.
Parameters
tsvector 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.
component_to_clonethe 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.
211 dynamic_cast<const BasicTimeSeries &>(ts[component_to_clone])),
212 Metadata(dynamic_cast<const Metadata &>(ts[component_to_clone])), u() {
213 const string base_error("CoreSeismogram constructor from 3 Time Series: ");
214 int i, j;
215 /* This is needed in case nsamp does not match s.size(0) */
216 int nstest = ts[component_to_clone].s.size();
217 if (nsamp != nstest)
218 this->nsamp = nstest;
219 /* this method allocates u and sets the proper metadata for npts*/
220 this->CoreSeismogram::set_npts(this->nsamp);
221 /* beware irregular sample rates, but don' be too machevelian.
222 Abort only if the mismatch is large defined as accumulated time
223 over data range of this constructor is less than half a sample */
224 if ((ts[0].dt() != ts[1].dt()) || (ts[1].dt() != ts[2].dt())) {
225 double ddtmag1 = fabs(ts[0].dt() - ts[1].dt());
226 double ddtmag2 = fabs(ts[1].dt() - ts[2].dt());
227 double ddtmag;
228 if (ddtmag1 > ddtmag1)
229 ddtmag = ddtmag1;
230 else
231 ddtmag = ddtmag2;
232 ddtmag1 = fabs(ts[0].dt() - ts[2].dt());
233 if (ddtmag1 > ddtmag)
234 ddtmag = ddtmag1;
235 double ddtcum = ddtmag * ((double)ts[0].s.size());
236 if (ddtcum > (ts[0].dt()) / 2.0) {
237 stringstream ss;
238 ss << base_error << "Sample intervals of components are not consistent"
239 << endl;
240 for (int ie = 0; ie < 3; ++ie)
241 ss << "Component " << ie << " dt=" << ts[ie].dt() << " ";
242 ss << endl;
243 throw MsPASSError(ss.str(), ErrorSeverity::Invalid);
244 }
245 }
246 // temporaries to hold component values
247 double t0_component[3];
248 double hang[3];
249 double vang[3];
250 // Load up these temporary arrays inside this try block and arrange to
251 // throw an exception if required metadata are missing
252 try {
253 /* WARNING hang and vang attributes in Metadata
254 are always assumed to have been read from a database where they
255 were stored in degrees. We convert these to radians below to
256 compute the transformation matrix.
257
258 Feb 2021: converted to use keywords.h definitions assuming these
259 came from the channel collection in MongoDB - Can be changed in
260 kewords.h for application outside mspass */
261 hang[0] = ts[0].get_double(SEISMICMD_hang);
262 hang[1] = ts[1].get_double(SEISMICMD_hang);
263 hang[2] = ts[2].get_double(SEISMICMD_hang);
264 vang[0] = ts[0].get_double(SEISMICMD_vang);
265 vang[1] = ts[1].get_double(SEISMICMD_vang);
266 vang[2] = ts[2].get_double(SEISMICMD_vang);
267 } catch (MetadataGetError &mde) {
268 stringstream ss;
269 ss << base_error
270 << "missing hang or vang variable in component TimeSeries objects "
271 "received"
272 << endl;
273 ss << "Message posted by Metadata::get_double: " << mde.what() << endl;
274 throw MsPASSError(ss.str(), ErrorSeverity::Invalid);
275 }
276 /* We couldn't get here if hang and vang were not set on comp 0 so
277 we don't test for that condition. We do need to clear hang and vang
278 from result here, however, as both attributes are meaningless
279 for a 3C seismogram */
280 this->erase(SEISMICMD_hang);
281 this->erase(SEISMICMD_vang);
282 // These are loaded just for convenience
283 t0_component[0] = ts[0].t0();
284 t0_component[1] = ts[1].t0();
285 t0_component[2] = ts[2].t0();
286
287 // Treat the normal case specially and avoid a bunch of work unless
288 // it is required
289 if ((ts[0].s.size() == ts[1].s.size()) &&
290 (ts[1].s.size() == ts[2].s.size()) &&
291 (fabs((t0_component[0] - t0_component[1]) / dt()) < 1.0) &&
292 (fabs((t0_component[1] - t0_component[2]) / dt()) < 1.0)) {
293 /* Older code had this. No longer needed with logic above that
294 calls set_npts. that method creates and initialized the u dmatrix*/
295 // this->u=dmatrix(3,nsamp);
296 // Load data by a simple copy operation
297 /* This is a simple loop version
298 for(j=0;j<nsamp;++nsamp)
299 {
300 this->u(0,j)=ts[0].s[j];
301 this->u(1,j)=ts[1].s[j];
302 this->u(2,j)=ts[2].s[j];
303 }
304 */
305 // This is a vector version that I'll use because it will
306 // be faster albeit infinitely more obscure and
307 // intrinsically more dangerous
308 dcopy(nsamp, &(ts[0].s[0]), 1, u.get_address(0, 0), 3);
309 dcopy(nsamp, &(ts[1].s[0]), 1, u.get_address(1, 0), 3);
310 dcopy(nsamp, &(ts[2].s[0]), 1, u.get_address(2, 0), 3);
311 } else {
312 /*Land here if the start time or number of samples
313 is irregular. We cut the output to latest start time to earliest end time*/
314 /* WARNING - debugging may be needed for this block. SEISPP versio of this
315 used gaps. Here we cut the output to match an irregularities. */
316 double tsmax, temin;
319 temin = min(ts[0].endtime(), ts[1].endtime());
320 temin = min(temin, ts[2].endtime());
321 nstest = (int)round((temin - tsmax) / mdt);
322 if (nstest <= 0)
323 throw MsPASSError(
324 base_error + "Irregular time windows of components have no overlap",
325 ErrorSeverity::Invalid);
326 else
327 this->CoreSeismogram::set_npts(nstest);
328 // Now load the data. Use the time and sample number methods
329 // to simplify process
330 double t;
331 t = tsmax;
332 this->set_t0(t);
333 double delta = this->dt();
334 for (int ic = 0; ic < 3; ++ic) {
335 t = this->t0();
336 for (j = 0; j < ts[ic].s.size(); ++j) {
337 i = ts[ic].sample_number(t);
338 // silently do nothing if outside bounds. This
339 // perhaps should be an error as it shouldn't really
340 // happen with the above algorithm, but safety is good
341 if ((i >= 0) && (i < nsamp))
342 this->u(ic, j) = ts[ic].s[i];
343 t += delta;
344 }
345 }
346 }
347 /* Finally we need to set the transformation matrix.
348 This is a direct application of conversion of routines
349 in spherical coordinate procedures. They are procedural
350 routines, not objects so the code is procedural.
351 */
352 SphericalCoordinate scor;
353 double *nu;
354 // convert all the hang values to spherical coordinate phi
355 // (angle from postive east) from input assumed in degrees
356 // azimuth from north. At the same time convert vang to radians.
357 for (i = 0; i < 3; ++i) {
358 hang[i] = mspass::utility::rad(90.0 - hang[i]);
359 vang[i] = mspass::utility::rad(vang[i]);
360 }
361 for (i = 0; i < 3; ++i) {
362 scor.phi = hang[i];
363 scor.theta = vang[i];
364 nu = SphericalToUnitVector(scor);
365 for (j = 0; j < 3; ++j)
366 tmatrix[i][j] = nu[j];
367 delete[] nu;
368 }
369 components_are_cardinal = this->tmatrix_is_cardinal();
372 else
374 /* Last but not least set the datum live before returning */
375 this->set_live();
376}
size_t nsamp
Definition BasicTimeSeries.h:257
void set_live()
Definition BasicTimeSeries.h:151
double t0() const
Definition BasicTimeSeries.h:176
double mdt
Definition BasicTimeSeries.h:249
double dt() const
Definition BasicTimeSeries.h:153
mspass::utility::dmatrix u
Definition CoreSeismogram.h:52
double endtime() const noexcept
Definition CoreSeismogram.h:496
void erase(const std::string key)
Definition Metadata.cc:495
double * get_address(size_t r, size_t c) const
Get a pointer to the location of a matrix component.
Definition dmatrix.cc:72
const std::string SEISMICMD_vang("channel_vang")
const std::string SEISMICMD_hang("channel_hang")

References components_are_cardinal, components_are_orthogonal, mspass::seismic::BasicTimeSeries::dt(), endtime(), mspass::utility::Metadata::erase(), mspass::utility::Metadata::get(), mspass::utility::dmatrix::get_address(), mspass::seismic::BasicTimeSeries::mdt, mspass::seismic::BasicTimeSeries::nsamp, mspass::seismic::SEISMICMD_hang(), mspass::seismic::SEISMICMD_vang(), mspass::seismic::BasicTimeSeries::set_live(), set_npts(), set_t0(), mspass::seismic::BasicTimeSeries::t0(), tmatrix, and u.

◆ CoreSeismogram() [4/5]

mspass::seismic::CoreSeismogram::CoreSeismogram ( const mspass::utility::Metadata md,
const bool  load_data = true 
)

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.

Parameters
mdis the Metadata used for the construction.
load_dataif 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.
Exceptions
Willthrow a MsPASSError if required metadata are missing.
93 : Metadata(md) {
94 string dfile, dir;
95 long foff;
96 FILE *fp;
97 double *inbuffer;
98
100 mlive = false;
101 try {
102 /* Names used are from mspass defintions as of Jan 2020.
103 We don't need to call the set methods for these attributes as they
104 would add the overhead of setting delta, startime, and npts to the
105 same value passed. */
106 this->mdt = this->get_double(SEISMICMD_dt);
107 this->mt0 = this->get_double(SEISMICMD_t0);
109 if (this->get_string(SEISMICMD_time_standard) == "UTC")
111 else {
113 /* For now we can't post an error because this is CoreSeismogram
114 so elog is not defined. For now let this error be silent as it
115 is harmless */
116 /*
117 this->elog.log_error("CoreSeismogram Metadata constructor",
118 SEISMICMD_time_standard+" attribute is not defined - set to Relative",
119 ErrorSeverity::Complaint);
120 */
121 }
122 }
123 if (this->time_is_relative()) {
124 /* It is not an error if a t0 shift is not defined and we are
125 in relative time. That is the norm for active source data. */
126 if (this->is_defined(SEISMICMD_t0_shift)) {
127 double t0shift = this->get_double(SEISMICMD_t0_shift);
128 this->force_t0_shift(t0shift);
129 }
130 }
131 /* This section is done specially to handle interaction with MongoDB.
132 We store tmatrix there as a python object so we use a get_any to fetch
133 it. Oct 22, 2021 added a bug fix to handle tmatrix not defined.
134 We will take a null (undefined) tmatrix stored in the database to
135 imply the data are cardinal and orthogonal (i.e. stardard geographic
136 coordinates in e,n,z order.)*/
137 if (this->is_defined("tmatrix")) {
139 boost::any_cast<py::object>(this->get_any("tmatrix")));
140 components_are_cardinal = this->tmatrix_is_cardinal();
143 else
144 components_are_orthogonal = false; // May be wrong but cost is tiny
145 } else {
146 /* this might not be needed but best be explicit*/
147 for (auto i = 0; i < 3; ++i)
148 for (auto j = 0; j < 3; ++j)
149 this->tmatrix[i][j] = 0.0;
150 for (auto i = 0; i < 3; ++i)
151 this->tmatrix[i][i] = 1.0;
154 }
155 /* We have to handle nsamp specially in the case when load_data
156 is false. To be consistent with TimeSeries we use a feature that
157 if the Metadata container does not define npts we default it.
158 In this case that means the default constructor for u and set
159 nsamp to 0 (via set_npts). */
160 if (md.is_defined(SEISMICMD_npts)) {
161 long int ns = md.get_long(SEISMICMD_npts);
162 this->set_npts(ns); /* note this is assumed to initialize u*/
163 } else {
164 this->set_npts(0);
165 }
166 /* Note previous code had an else clause to to with the
167 following conditional. It used to zero the u matrix.
168 The call to set_npts above will always do that so that would
169 have been redundant and was removed June 2022*/
170 if (load_data) {
171 dir = this->get_string(SEISMICMD_dir);
172 dfile = this->get_string(SEISMICMD_dfile);
173 foff = this->get_long(SEISMICMD_foff);
174 string fname = dir + "/" + dfile;
175 if ((fp = fopen(fname.c_str(), "r")) == NULL)
176 throw(MsPASSError(string("Open failure for file ") + fname,
177 ErrorSeverity::Invalid));
178 if (foff > 0)
179 fseek(fp, foff, SEEK_SET);
180 /* The older seispp code allowed byte swapping here. For
181 efficiency we don't support that here and assume can do a
182 raw fread from the file and get valid data. If support for
183 other types is needed this will need to be extended. Here
184 we just point fread at the internal u array. */
185 inbuffer = this->u.get_address(0, 0);
186 unsigned int nt = 3 * this->nsamp;
187 if (fread((void *)(inbuffer), sizeof(double), nt, fp) != nt) {
188 fclose(fp);
189 throw(MsPASSError(
190 string("CoreSeismogram constructor: fread error on file ") + fname,
191 ErrorSeverity::Invalid));
192 }
193 fclose(fp);
194 mlive = true;
195 }
196 } catch (MsPASSError &mpe) {
197 throw(mpe);
198 } catch (boost::bad_any_cast &be) {
199 throw(MsPASSError(
200 string("CoreSeismogram constructor: tmatrix type is not recognized"),
201 ErrorSeverity::Invalid));
202 } catch (...) {
203 throw;
204 };
205}
bool mlive
Definition BasicTimeSeries.h:245
bool time_is_relative() const
Definition BasicTimeSeries.h:162
void force_t0_shift(const double t)
Force a t0 shift value on data.
Definition BasicTimeSeries.h:109
void set_tref(const TimeReferenceType newtref)
Force the time standard.
Definition BasicTimeSeries.h:235
double t0shift
Definition BasicTimeSeries.h:274
double mt0
Definition BasicTimeSeries.h:253
bool set_transformation_matrix(const mspass::utility::dmatrix &A)
Define the transformaton matrix.
Definition CoreSeismogram.cc:776
bool is_defined(const std::string key) const noexcept
Definition Metadata.cc:342
long get_long(const std::string key) const
Definition Metadata.cc:379
std::map< std::string, boost::any > md
Definition Metadata.h:473
boost::any get_any(const std::string key) const
Definition Metadata.h:256
std::string get_string(const std::string key) const override
Definition Metadata.h:189
double get_double(const std::string key) const override
Definition Metadata.cc:352
const std::string SEISMICMD_t0_shift("starttime_shift")
const std::string SEISMICMD_dfile("dfile")
const std::string SEISMICMD_foff("foff")
const std::string SEISMICMD_t0("starttime")
const std::string SEISMICMD_dt("delta")
const std::string SEISMICMD_time_standard("time_standard")
const std::string SEISMICMD_dir("dir")
const std::string SEISMICMD_npts("npts")

References components_are_cardinal, components_are_orthogonal, mspass::seismic::BasicTimeSeries::force_t0_shift(), mspass::utility::Metadata::get(), mspass::utility::dmatrix::get_address(), mspass::utility::Metadata::get_any(), mspass::utility::Metadata::get_double(), mspass::utility::Metadata::get_long(), mspass::utility::Metadata::get_string(), mspass::utility::Metadata::is_defined(), mspass::utility::Metadata::md, mspass::seismic::BasicTimeSeries::mdt, mspass::seismic::BasicTimeSeries::mlive, mspass::seismic::BasicTimeSeries::mt0, mspass::seismic::BasicTimeSeries::nsamp, mspass::seismic::Relative, mspass::seismic::SEISMICMD_dfile(), mspass::seismic::SEISMICMD_dir(), mspass::seismic::SEISMICMD_dt(), mspass::seismic::SEISMICMD_foff(), mspass::seismic::SEISMICMD_npts(), mspass::seismic::SEISMICMD_t0(), mspass::seismic::SEISMICMD_t0_shift(), mspass::seismic::SEISMICMD_time_standard(), set_npts(), set_transformation_matrix(), mspass::seismic::BasicTimeSeries::set_tref(), mspass::seismic::BasicTimeSeries::t0shift, mspass::seismic::BasicTimeSeries::time_is_relative(), tmatrix, u, and mspass::seismic::UTC.

◆ CoreSeismogram() [5/5]

mspass::seismic::CoreSeismogram::CoreSeismogram ( const CoreSeismogram t3c)

Standard copy constructor.

63 : BasicTimeSeries(dynamic_cast<const BasicTimeSeries &>(t3c)),
64 Metadata(dynamic_cast<const Metadata &>(t3c)), u(t3c.u) {
65 int i, j;
66 components_are_orthogonal = t3c.components_are_orthogonal;
67 components_are_cardinal = t3c.components_are_cardinal;
68 for (i = 0; i < 3; ++i)
69 for (j = 0; j < 3; ++j)
70 tmatrix[i][j] = t3c.tmatrix[i][j];
71}

References components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), and tmatrix.

◆ ~CoreSeismogram()

virtual mspass::seismic::CoreSeismogram::~CoreSeismogram ( )
inlinevirtual

Standard destructor.

302{};

Member Function Documentation

◆ cardinal()

bool mspass::seismic::CoreSeismogram::cardinal ( ) const
inline

Returns true of components are cardinal.

489{ return components_are_cardinal; };

References components_are_cardinal.

◆ endtime()

double mspass::seismic::CoreSeismogram::endtime ( ) const
inlinenoexcept

Returns the end time (time associated with last data sample) of this data object.

496 {
497 return (mt0 + mdt * static_cast<double>(u.columns() - 1));
498 };
size_t columns() const
Definition dmatrix.cc:216

References mspass::utility::dmatrix::columns(), mspass::seismic::BasicTimeSeries::mdt, mspass::seismic::BasicTimeSeries::mt0, and u.

◆ free_surface_transformation()

void mspass::seismic::CoreSeismogram::free_surface_transformation ( const mspass::seismic::SlownessVector  u,
const double  vp0,
const double  vs0 
)

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.

Parameters
uslowness vector off the incident wavefield
vp0Surface P wave velocity
vs0Surface S wave velocity.
708 {
709 if ((u.size()[1] <= 0) || dead())
710 return; // do nothing in these situations
711 double a02, b02, pslow, p2;
712 double qa, qb, vpz, vpr, vsr, vsz;
713 pslow = uvec.mag();
714 // silently do nothing if magnitude of the slowness vector is 0
715 // (vertical incidence)
716 if (pslow < DBL_EPSILON)
717 return;
718 // Can't handle evanescent waves with this operator
719 double vapparent = 1.0 / pslow;
720 if (vapparent < a0 || vapparent < b0) {
721 stringstream ss;
722 ss << "CoreSeismogram::free_surface_transformation method: illegal input"
723 << endl
724 << "Apparent velocity defined by input slowness vector=" << vapparent
725 << endl
726 << "Smaller than specified surface P velocity=" << a0
727 << " or S velocity=" << b0 << endl
728 << "That implies evanescent waves that violate the assumption of this "
729 "operator"
730 << endl;
731 throw MsPASSError(ss.str(), ErrorSeverity::Invalid);
732 }
733
734 // First the horizonal rotation
735 SphericalCoordinate scor;
736 // rotation angle is - azimuth to put x2 (north in standard coord)
737 // in radial direction
738 scor.phi = atan2(uvec.uy, uvec.ux);
739 scor.theta = 0.0;
740 scor.radius = 1.0;
741 // after this transformation x1=transverse horizontal
742 // x2=radial horizonal, and x3 is still vertical
743 this->rotate(scor);
744
745 a02 = a0 * a0;
746 b02 = b0 * b0;
747 p2 = pslow * pslow;
748 qa = sqrt((1.0 / a02) - p2);
749 qb = sqrt((1.0 / b02) - p2);
750 vpz = -(1.0 - 2.0 * b02 * p2) / (2.0 * a0 * qa);
751 vpr = pslow * b02 / a0;
752 vsr = (1.0 - 2.0 * b02 * p2) / (2.0 * b0 * qb);
753 vsz = pslow * b0;
754 /* Now construct the transformation matrix
755 This is different from Bostock's original code
756 in sign and order. Also note this transformation
757 is not scaled to have a unit matrix norm so amplitudes
758 after the transformation are distorted. rotate_to_standard,
759 however, should still restore original data within roundoff
760 error if called on the result. */
761 double fstran[3][3];
762 fstran[0][0] = 0.5;
763 fstran[0][1] = 0.0;
764 fstran[0][2] = 0.0;
765 fstran[1][0] = 0.0;
766 fstran[1][1] = vsr;
767 fstran[1][2] = vpr;
768 fstran[2][0] = 0.0;
769 fstran[2][1] = -vsz;
770 fstran[2][2] = -vpz;
771 this->transform(fstran);
772
775}
bool dead() const
Definition BasicTimeSeries.h:145
void rotate(mspass::utility::SphericalCoordinate &sc)
Definition CoreSeismogram.cc:525
void transform(const double a[3][3])
Definition CoreSeismogram.cc:654
std::vector< size_t > size() const
Return a vector with 2 elements giving the size.
Definition dmatrix.cc:207

References components_are_cardinal, components_are_orthogonal, mspass::seismic::BasicTimeSeries::dead(), mspass::utility::Metadata::get(), mspass::utility::SphericalCoordinate::phi, rotate(), mspass::utility::dmatrix::size(), transform(), and u.

◆ get_transformation_matrix()

mspass::utility::dmatrix mspass::seismic::CoreSeismogram::get_transformation_matrix ( ) const
inline

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).

Returns
3x3 transformation matrix.
439 {
440 mspass::utility::dmatrix result(3, 3);
441 for (int i = 0; i < 3; ++i)
442 for (int j = 0; j < 3; ++j)
443 result(i, j) = tmatrix[i][j];
444 return result;
445 };
Lightweight, simple matrix object.
Definition dmatrix.h:104

References tmatrix.

◆ operator*=()

Multiply data by a scalar.

904 {
905 /* do nothing to empty data or data marked dead*/
906 if ((this->npts() == 0) || (this->dead()))
907 return (*this);
908 /* We can use this dscal blas function because dmatrix puts all the data
909 in a continguous block. Beware if there is am implementation change for
910 the matrix data*/
911 double *ptr = this->u.get_address(0, 0);
912 dscal(3 * this->npts(), scale, ptr, 1);
913 return (*this);
914}
size_t npts() const
Definition BasicTimeSeries.h:173

References mspass::seismic::BasicTimeSeries::dead(), mspass::utility::dmatrix::get_address(), mspass::seismic::BasicTimeSeries::npts(), and u.

◆ operator+()

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.

996 {
997 CoreSeismogram result(*this);
998 result += other;
999 return result;
1000}
CoreSeismogram()
Definition CoreSeismogram.cc:26

References mspass::utility::Metadata::get().

◆ operator+=()

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.

Parameters
dis other signal to add to this.
Exceptions
MsPASSErrorcan be thrown if lhs and rhs do not have matching time standards.
915 {
916 int i, iend, jend;
917 size_t j, i0, j0;
918 // Silently do nothing if d or lhs is marked dead
919 if (d.dead() || (this->dead()))
920 return (*this);
921 // Silently do nothing if d does not overlap with data to contain sum
922 if ((d.endtime() < mt0) || (d.mt0 > (this->endtime())))
923 return (*this);
924 if (d.tref != (this->tref))
925 throw MsPASSError("CoreSeismogram += operator cannot handle data with "
926 "inconsistent time base\n",
927 ErrorSeverity::Invalid);
928 /* this defines the range of left and right hand sides to be summed */
929 i = d.sample_number(this->mt0);
930 if (i < 0) {
931 j0 = this->sample_number(d.t0());
932 i0 = 0;
933 } else {
934 j0 = 0;
935 i0 = i;
936 }
937 iend = d.sample_number(this->endtime());
938 jend = this->sample_number(d.endtime());
939 if (iend >= (d.npts())) {
940 iend = d.npts() - 1;
941 }
942 if (jend >= this->npts()) {
943 jend = this->npts() - 1;
944 }
945 for (i = i0, j = j0; i <= iend && j <= jend; ++i, ++j) {
946 this->u(0, j) += d.u(0, i);
947 this->u(1, j) += d.u(1, i);
948 this->u(2, j) += d.u(2, i);
949 }
950 return (*this);
951}
int sample_number(double t) const
Definition BasicTimeSeries.h:72

References mspass::seismic::BasicTimeSeries::dead(), endtime(), mspass::utility::Metadata::get(), mspass::seismic::BasicTimeSeries::mt0, mspass::seismic::BasicTimeSeries::npts(), mspass::seismic::BasicTimeSeries::sample_number(), mspass::seismic::BasicTimeSeries::t0(), mspass::seismic::BasicTimeSeries::tref, and u.

◆ operator-()

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).

1002 {
1003 CoreSeismogram result(*this);
1004 result -= other;
1005 return result;
1006}

References mspass::utility::Metadata::get().

◆ operator-=()

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.

Parameters
dis other signal to subract from this.
Exceptions
MsPASSErrorcan be thrown if lhs and rhs do not have matching time standards.
956 {
957 int i, iend, jend;
958 size_t j, i0, j0;
959 // Sun's compiler complains about const objects without this.
960 CoreSeismogram &d = const_cast<CoreSeismogram &>(data);
961 // Silently do nothing if d is marked dead
962 if (!d.mlive)
963 return (*this);
964 // Silently do nothing if d does not overlap with data to contain sum
965 if ((d.endtime() < mt0) || (d.mt0 > (this->endtime())))
966 return (*this);
967 if (d.tref != (this->tref))
968 throw MsPASSError("CoreSeismogram += operator cannot handle data with "
969 "inconsistent time base\n",
970 ErrorSeverity::Invalid);
971 /* this defines the range of left and right hand sides to be summed */
972 i = d.sample_number(this->mt0);
973 if (i < 0) {
974 j0 = this->sample_number(d.t0());
975 i0 = 0;
976 } else {
977 j0 = 0;
978 i0 = i;
979 }
980 iend = d.sample_number(this->endtime());
981 jend = this->sample_number(d.endtime());
982 if (iend >= (d.npts())) {
983 iend = d.npts() - 1;
984 }
985 if (jend >= this->npts()) {
986 jend = this->npts() - 1;
987 }
988 for (i = i0, j = j0; i <= iend && j <= jend; ++i, ++j) {
989 this->u(0, j) -= d.u(0, i);
990 this->u(1, j) -= d.u(1, i);
991 this->u(2, j) -= d.u(2, i);
992 }
993 return (*this);
994}

References endtime(), mspass::utility::Metadata::get(), mspass::seismic::BasicTimeSeries::mlive, mspass::seismic::BasicTimeSeries::mt0, mspass::seismic::BasicTimeSeries::npts(), mspass::seismic::BasicTimeSeries::sample_number(), mspass::seismic::BasicTimeSeries::t0(), mspass::seismic::BasicTimeSeries::tref, and u.

◆ operator=()

Standard assignment operator.

889 {
890 if (this != &seisin) {
893 components_are_orthogonal = seisin.components_are_orthogonal;
894 components_are_cardinal = seisin.components_are_cardinal;
895 for (int i = 0; i < 3; ++i) {
896 for (int j = 0; j < 3; ++j) {
897 tmatrix[i][j] = seisin.tmatrix[i][j];
898 }
899 }
900 u = seisin.u;
901 }
902 return (*this);
903}
BasicTimeSeries & operator=(const BasicTimeSeries &parent)
Definition BasicTimeSeries.cc:63
Metadata & operator=(const Metadata &mdold)
Definition Metadata.cc:460

References components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), mspass::seismic::BasicTimeSeries::operator=(), mspass::utility::Metadata::operator=(), tmatrix, and u.

◆ operator[]() [1/2]

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.

Parameters
timeis the time of the requested sample
Returns
3 vector of data samples at requested time
Exceptions
MsPASSErrorwill be thrown if the time is outside the data range.
1098 {
1099 try {
1100 vector<double> result;
1101 int i = this->sample_number(t);
1102 /* could test for a negative i and i too large but we assume
1103 the dmatrix container will throw an exception if it resolves that way*/
1104 for (int k = 0; k < 3; ++k)
1105 result.push_back(this->u(k, i));
1106 return result;
1107 } catch (...) {
1108 throw;
1109 };
1110}

References mspass::utility::Metadata::get(), and mspass::seismic::BasicTimeSeries::sample_number().

◆ operator[]() [2/2]

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.

Parameters
sampleis the sample number requested (must be in range or an exception will be thrown)
Exceptions
MsPASSErrorif 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.
Returns
std::vector containing a 3 vector of the samples at requested sample number
1087 {
1088 try {
1089 vector<double> result;
1090 result.reserve(3);
1091 for (int k = 0; k < 3; ++k)
1092 result.push_back(this->u(k, i));
1093 return result;
1094 } catch (...) {
1095 throw;
1096 };
1097}

References mspass::utility::Metadata::get().

◆ orthogonal()

bool mspass::seismic::CoreSeismogram::orthogonal ( ) const
inline

Return true if the components are orthogonal.

491{ return components_are_orthogonal; };

References components_are_orthogonal.

◆ rotate() [1/3]

void mspass::seismic::CoreSeismogram::rotate ( const double  nu[3])

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

Parameters
nudefines direction of x3 direction (longitudinal) as a unit vector with three components.
594 {
595 if ((u.size()[1] <= 0) || this->dead())
596 return; // do nothing in these situations
597 SphericalCoordinate xsc = UnitVectorToSpherical(nu);
598 this->rotate(xsc);
599}

References mspass::utility::Metadata::get(), rotate(), mspass::utility::dmatrix::size(), and u.

◆ rotate() [2/3]

void mspass::seismic::CoreSeismogram::rotate ( const double  phi)

Rotate horizontals by a simple angle in degrees.

A common transformation in 3C processing is a rotation of the
horizontal components by an angle.  This leaves the vertical
(assumed here x3) unaltered.   This routine rotates the horizontals
by angle phi using with positive phi counterclockwise as in
polar coordinates and the azimuth angle of spherical coordinates.

Note this transformation is cummulative.  i.e. this transformation
is cumulative.  The internal transformation matrix will be updated.
This is a useful feature for things like incremental horizontal
rotation in rotational angle grid searches.

\param phi rotation angle around x3 axis in counterclockwise
  direction (in radians).
608 {
609 if ((u.size()[1] <= 0) || dead())
610 return; // do nothing in these situations
611 int i, j, k;
612 double a, b;
613 a = cos(phi);
614 b = sin(phi);
615 double tmnew[3][3];
616 tmnew[0][0] = a;
617 tmnew[1][0] = -b;
618 tmnew[2][0] = 0.0;
619 tmnew[0][1] = b;
620 tmnew[1][1] = a;
621 tmnew[2][1] = 0.0;
622 tmnew[0][2] = 0.0;
623 tmnew[1][2] = 0.0;
624 tmnew[2][2] = 1.0;
625
626 /* Now multiply the data by this transformation matrix.
627 Note trick in this i only goes to 2 because 3 component
628 is an identity.*/
629 double *work[2];
630 for (i = 0; i < 2; ++i)
631 work[i] = new double[nsamp];
632 for (i = 0; i < 2; ++i) {
633 dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1);
634 dscal(nsamp, tmnew[i][0], work[i], 1);
635 daxpy(nsamp, tmnew[i][1], u.get_address(1, 0), 3, work[i], 1);
636 }
637 for (i = 0; i < 2; ++i)
638 dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3);
639 double tm_tmp[3][3];
640 double prod;
641 for (i = 0; i < 3; ++i)
642 for (j = 0; j < 3; ++j) {
643 for (prod = 0.0, k = 0; k < 3; ++k)
644 prod += tmnew[i][k] * tmatrix[k][j];
645 tm_tmp[i][j] = prod;
646 }
647 for (i = 0; i < 3; ++i)
648 for (j = 0; j < 3; ++j)
649 tmatrix[i][j] = tm_tmp[i][j];
651 for (i = 0; i < 2; ++i)
652 delete[] work[i];
653}

References components_are_cardinal, mspass::seismic::BasicTimeSeries::dead(), mspass::utility::Metadata::get(), mspass::utility::dmatrix::get_address(), mspass::seismic::BasicTimeSeries::nsamp, mspass::utility::dmatrix::size(), tmatrix, and u.

◆ rotate() [3/3]

void mspass::seismic::CoreSeismogram::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. 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.

Parameters
scdefines final x3 direction (longitudinal) in a spherical coordinate structure.
525 {
526 if ((u.size()[1] <= 0) || dead())
527 return; // do nothing in these situations
528
529 // Earlier version had a reset of the nsamp variable here - we need to trust
530 // that is correct here for efficiency. We the new API it would be hard
531 // to have that happen. without a serious blunder
532 int i;
533 double theta, phi; /* corrected angles after dealing with signs */
534 double a, b, c, d;
535
536 //
537 // Undo any previous transformations
538 //
539 this->rotate_to_standard();
540 if (xsc.theta == M_PI) {
541 // This will be left handed
542 tmatrix[2][2] = -1.0;
543 dscal(nsamp, -1.0, u.get_address(2, 0), 3);
544 return;
545 }
546
547 if (xsc.theta < 0.0) {
548 theta = -(xsc.theta);
549 phi = xsc.phi + M_PI;
550 if (phi > M_PI)
551 phi -= (2.0 * M_PI);
552 } else if (xsc.theta > M_PI) {
553 theta = xsc.theta - M_PI;
554 phi = xsc.phi + M_PI;
555 if (phi > M_PI)
556 phi -= (2.0 * M_PI);
557 } else {
558 theta = xsc.theta;
559 phi = xsc.phi;
560 }
561 /* Am using a formula here for azimuth with is pi/2 - phi*/
562 double azimuth = M_PI_2 - phi;
563 a = cos(azimuth);
564 b = sin(azimuth);
565 c = cos(theta);
566 d = sin(theta);
567
568 tmatrix[0][0] = a;
569 tmatrix[1][0] = b * c;
570 tmatrix[2][0] = b * d;
571 tmatrix[0][1] = -b;
572 tmatrix[1][1] = a * c;
573 tmatrix[2][1] = a * d;
574 tmatrix[0][2] = 0.0;
575 tmatrix[1][2] = -d;
576 tmatrix[2][2] = c;
577
578 /* Now multiply the data by this transformation matrix. */
579 double *work[3];
580 for (i = 0; i < 3; ++i)
581 work[i] = new double[nsamp];
582 for (i = 0; i < 3; ++i) {
583 dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1);
584 dscal(nsamp, tmatrix[i][0], work[i], 1);
585 daxpy(nsamp, tmatrix[i][1], u.get_address(1, 0), 3, work[i], 1);
586 daxpy(nsamp, tmatrix[i][2], u.get_address(2, 0), 3, work[i], 1);
587 }
588 for (i = 0; i < 3; ++i)
589 dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3);
591 for (i = 0; i < 3; ++i)
592 delete[] work[i];
593}
void rotate_to_standard()
Definition CoreSeismogram.cc:381

References components_are_cardinal, mspass::seismic::BasicTimeSeries::dead(), mspass::utility::Metadata::get(), mspass::utility::dmatrix::get_address(), mspass::seismic::BasicTimeSeries::nsamp, rotate_to_standard(), mspass::utility::dmatrix::size(), mspass::utility::SphericalCoordinate::theta, tmatrix, and u.

◆ rotate_to_standard()

void mspass::seismic::CoreSeismogram::rotate_to_standard ( )

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.

Exceptions
SeisppErrorthrown 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.
381 {
382 if ((u.size()[1] <= 0) || this->dead())
383 return; // do nothing in these situations
384 double *work[3];
385 int i, j;
387 return;
388 /* We assume nsamp is the number of samples = number of columns in u - we
389 don't check here for efficiency */
390 for (j = 0; j < 3; ++j)
391 work[j] = new double[nsamp];
393 //
394 // Use a daxpy algorithm. tmatrix stores the
395 // forward transformation used to get current
396 // Use the transpose to get back
397 //
398 for (i = 0; i < 3; ++i) {
399 // x has a stride of 3 because we store in fortran order in x
400 dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1);
401 dscal(nsamp, tmatrix[0][i], work[i], 1);
402 daxpy(nsamp, tmatrix[1][i], u.get_address(1, 0), 3, work[i], 1);
403 daxpy(nsamp, tmatrix[2][i], u.get_address(2, 0), 3, work[i], 1);
404 }
405 for (i = 0; i < 3; ++i)
406 dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3);
407 } else {
408 //
409 // Enter here only when the transformation matrix is
410 // not orthogonal. We have to construct a fortran
411 // order matrix a to use LINPACK routine in sunperf/perf
412 // This could be done with the matrix template library
413 // but the overhead ain't worth it
414 //
415 double a[9];
416 int ipivot[3];
417 int info;
418 a[0] = tmatrix[0][0];
419 a[1] = tmatrix[1][0];
420 a[2] = tmatrix[2][0];
421 a[3] = tmatrix[0][1];
422 a[4] = tmatrix[1][1];
423 a[5] = tmatrix[2][1];
424 a[6] = tmatrix[0][2];
425 a[7] = tmatrix[1][2];
426 a[8] = tmatrix[2][2];
427 // LAPACK routine with FORTRAN interface using pass by reference and
428 // pointers
429 int three(3);
430 dgetrf(three, three, a, three, ipivot, info);
431 if (info != 0) {
432 for (i = 0; i < 3; ++i)
433 delete[] work[i];
434 throw(MsPASSError(string("rotate_to_standard: LU factorization of "
435 "transformation matrix failed"),
436 ErrorSeverity::Invalid));
437 }
438 // inversion routine after factorization from lapack FORT$RAN interface
439 double awork[10]; // Larger than required but safety value small cost
440 int ldwork(10);
441 dgetri(three, a, three, ipivot, awork, ldwork, info);
442 // This is the openblas version
443 // info=LAPACKE_dgetri(LAPACK_COL_MAJOR,3,a,3,ipivot);
444 if (info != 0) {
445 for (i = 0; i < 3; ++i)
446 delete[] work[i];
447 throw(MsPASSError(string("rotate_to_standard: LU factorization "
448 "inversion of transformation matrix failed"),
449 ErrorSeverity::Invalid));
450 }
451
452 tmatrix[0][0] = a[0];
453 tmatrix[1][0] = a[1];
454 tmatrix[2][0] = a[2];
455 tmatrix[0][1] = a[3];
456 tmatrix[1][1] = a[4];
457 tmatrix[2][1] = a[5];
458 tmatrix[0][2] = a[6];
459 tmatrix[1][2] = a[7];
460 tmatrix[2][2] = a[8];
461 /* The inverse is now in tmatrix so we reverse the
462 rows and columms from above loop */
463
464 for (i = 0; i < 3; ++i) {
465 dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1);
466 dscal(nsamp, tmatrix[i][0], work[i], 1);
467 daxpy(nsamp, tmatrix[i][1], u.get_address(1, 0), 3, work[i], 1);
468 daxpy(nsamp, tmatrix[i][2], u.get_address(2, 0), 3, work[i], 1);
469 }
470 for (i = 0; i < 3; ++i)
471 dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3);
473 }
474 //
475 // Have to set the transformation matrix to an identity now
476 //
477 for (i = 0; i < 3; ++i)
478 for (j = 0; j < 3; ++j)
479 if (i == j)
480 tmatrix[i][i] = 1.0;
481 else
482 tmatrix[i][j] = 0.0;
483
485 for (i = 0; i < 3; ++i)
486 delete[] work[i];
487}

References components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), mspass::utility::dmatrix::get_address(), mspass::seismic::BasicTimeSeries::nsamp, mspass::utility::dmatrix::size(), tmatrix, and u.

◆ set_dt()

void mspass::seismic::CoreSeismogram::set_dt ( const double  sample_interval)
virtual

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.

Parameters
sample_intervalis the new data sample interval to be used.

Reimplemented from mspass::seismic::BasicTimeSeries.

1007 {
1008 this->BasicTimeSeries::set_dt(sample_interval);
1009 /* This is the unique name defined in the mspass schema - we always set it. */
1010 this->put(SEISMICMD_dt, sample_interval);
1011 /* these are hard coded aliases for sample_interval */
1012 std::set<string> aliases;
1013 std::set<string>::iterator aptr;
1014 /* Note these aren't set in keywords - aliases are flexible and this
1015 can allow another way to make these attribute names more flexible. */
1016 aliases.insert("dt");
1017 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
1018 if (this->is_defined(*aptr)) {
1019 this->put(*aptr, sample_interval);
1020 }
1021 }
1022}
virtual void set_dt(const double sample_interval)
Set the sample interval.
Definition BasicTimeSeries.h:199
void put(const std::string key, T val) noexcept
Definition Metadata.h:277

References mspass::utility::Metadata::get(), mspass::utility::Metadata::is_defined(), mspass::utility::Metadata::put(), mspass::seismic::SEISMICMD_dt(), and mspass::seismic::BasicTimeSeries::set_dt().

◆ set_npts()

void mspass::seismic::CoreSeismogram::set_npts ( const size_t  npts)
virtual

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.

Parameters
nptsis the new number of points to set.

Reimplemented from mspass::seismic::BasicTimeSeries.

1040 {
1042 /* This is the unique name - we always set it. The weird
1043 cast is necessary to avoid type mismatch with unsigned.
1044 We use the name defined in keywords.h which we can always assume
1045 matches the schema for the unique name*/
1046 this->put(SEISMICMD_npts, (long int)npts);
1047 /* these are hard coded aliases for sample_interval */
1048 std::set<string> aliases;
1049 std::set<string>::iterator aptr;
1050 aliases.insert("nsamp");
1051 aliases.insert("wfdisc.nsamp");
1052 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
1053 if (this->is_defined(*aptr)) {
1054 this->put(*aptr, (long int)npts);
1055 }
1056 }
1057 /* this method has the further complication that npts sets the size of the
1058 data matrix. Here we resize the matrix and initialize it to 0s.*/
1059 if (npts == 0) {
1060 this->u = dmatrix();
1061 } else {
1062 this->u = dmatrix(3, npts);
1063 this->u.zero();
1064 }
1065}
virtual void set_npts(const size_t npts)
Set the number of samples attribute for data.
Definition BasicTimeSeries.h:211
void zero()
Definition dmatrix.cc:203

References mspass::utility::Metadata::get(), mspass::utility::Metadata::is_defined(), mspass::seismic::BasicTimeSeries::npts(), mspass::utility::Metadata::put(), mspass::seismic::SEISMICMD_npts(), mspass::seismic::BasicTimeSeries::set_npts(), u, and mspass::utility::dmatrix::zero().

◆ set_t0()

void mspass::seismic::CoreSeismogram::set_t0 ( const double  t0in)
virtual

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.

Parameters
t0inis the new data sample interval to be used.

Reimplemented from mspass::seismic::BasicTimeSeries.

1023 {
1025 /* This is the unique name - we always set it. Pulled from keywords.h
1026 which should match the schema. aliases are hard coded not defined as
1027 keywords */
1028 this->put(SEISMICMD_t0, t0in);
1029 /* these are hard coded aliases for sample_interval */
1030 std::set<string> aliases;
1031 std::set<string>::iterator aptr;
1032 aliases.insert("t0");
1033 aliases.insert("time");
1034 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
1035 if (this->is_defined(*aptr)) {
1036 this->put(*aptr, t0in);
1037 }
1038 }
1039}
virtual void set_t0(const double t0in)
Set the data start time.
Definition BasicTimeSeries.h:223

References mspass::utility::Metadata::get(), mspass::utility::Metadata::is_defined(), mspass::utility::Metadata::put(), mspass::seismic::SEISMICMD_t0(), and mspass::seismic::BasicTimeSeries::set_t0().

◆ set_transformation_matrix() [1/3]

bool mspass::seismic::CoreSeismogram::set_transformation_matrix ( const double  a[3][3])

Define the transformaton matrix with a C style 3x3 matrix.

Parameters
ais a C style 3x3 matrix.
Returns
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.
Exceptions
Willthrow a MsPASSError if the input matrix is not 3x3.
797 {
798 for (int i = 0; i < 3; ++i)
799 for (int j = 0; j < 3; ++j)
800 tmatrix[i][j] = a[i][j];
801 py::list tmatrix_l;
802 for (int i = 0; i < 3; ++i)
803 for (int j = 0; j < 3; ++j)
804 tmatrix_l.append(a[i][j]);
805 this->put_object(SEISMICMD_tmatrix, tmatrix_l);
806 bool cardinal;
807 cardinal = this->tmatrix_is_cardinal();
808 if (cardinal) {
811 } else {
813 /* Not necessarily true, but small overhead cost*/
815 }
817}
bool cardinal() const
Definition CoreSeismogram.h:489
void put_object(const std::string key, const pybind11::object val)
Definition Metadata.h:346
const std::string SEISMICMD_tmatrix("tmatrix")

References cardinal(), components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), mspass::utility::Metadata::put_object(), mspass::seismic::SEISMICMD_tmatrix(), and tmatrix.

◆ set_transformation_matrix() [2/3]

bool mspass::seismic::CoreSeismogram::set_transformation_matrix ( const mspass::utility::dmatrix A)

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.

Parameters
Ais the 3X3 matrix copied to the internal transformation matrix array.
Returns
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.
Exceptions
Willthrow a MsPASSError if the input matrix is not 3x3.
776 {
777 for (int i = 0; i < 3; ++i)
778 for (int j = 0; j < 3; ++j)
779 tmatrix[i][j] = A(i, j);
780 py::list tmatrix_l;
781 for (int i = 0; i < 3; ++i)
782 for (int j = 0; j < 3; ++j)
783 tmatrix_l.append(A(i, j));
784 this->put_object(SEISMICMD_tmatrix, tmatrix_l);
785 bool cardinal;
786 cardinal = this->tmatrix_is_cardinal();
787 if (cardinal) {
790 } else {
792 /* Not necessarily true, but small overhead cost*/
794 }
796}

References cardinal(), components_are_cardinal, components_are_orthogonal, mspass::utility::Metadata::get(), mspass::utility::Metadata::put_object(), mspass::seismic::SEISMICMD_tmatrix(), and tmatrix.

◆ set_transformation_matrix() [3/3]

bool mspass::seismic::CoreSeismogram::set_transformation_matrix ( pybind11::object  a)

Define the transformaton matrix with a python object.

Parameters
ais a python object of 9 elements with types of dmatrix, numpy array, or list.
Returns
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.
Exceptions
Willthrow a MsPASSError if the input type or dimension is not recognized.
818 {
819 if (py::isinstance<py::array>(a)) {
820 auto tmatrix_ary = a.cast<
821 py::array_t<double, py::array::c_style | py::array::forcecast>>();
822 py::buffer_info info = tmatrix_ary.request();
823 if ((info.ndim == 2 && info.shape[0] * info.shape[1] == 9) ||
824 (info.ndim == 1 && info.shape[0] == 9))
825 return this->set_transformation_matrix(
826 static_cast<double(*)[3]>(info.ptr));
827 else
828 throw(MsPASSError(
829 string("set_transformation_matrix: tmatrix should be a 3x3 matrix"),
830 ErrorSeverity::Invalid));
831 } else if (py::isinstance<dmatrix>(a)) {
832 auto tmatrix_ary = a.cast<dmatrix>();
833 if (tmatrix_ary.rows() != 3 || tmatrix_ary.columns() != 3)
834 throw(MsPASSError(
835 string("set_transformation_matrix: tmatrix should be a 3x3 matrix"),
836 ErrorSeverity::Invalid));
838 } else if (py::isinstance<py::list>(a)) {
839 dmatrix tmatrix_ary(3, 3);
840 double *ptr = tmatrix_ary.get_address(0, 0);
841 if (py::len(a) == 9) {
842 int i = 0;
843 for (auto item : a) {
844 try {
845 *(ptr + i) = item.cast<double>();
846 i++;
847 } catch (...) {
848 throw(MsPASSError(string("set_transformation_matrix: the elements of "
849 "tmatrix should be float"),
850 ErrorSeverity::Invalid));
851 }
852 }
853 } else if (py::len(a) == 3) {
854 int i = 0;
855 for (auto items : a) {
856 if (!py::isinstance<py::list>(items))
857 throw(MsPASSError(string("set_transformation_matrix: tmatrix should "
858 "be a 3x3 list of list"),
859 ErrorSeverity::Invalid));
860 else if (py::len(items) != 3)
861 throw(MsPASSError(string("set_transformation_matrix: tmatrix should "
862 "be a 3x3 list of list"),
863 ErrorSeverity::Invalid));
864 else {
865 for (auto item : items) {
866 try {
867 *(ptr + i) = item.cast<double>();
868 i++;
869 } catch (...) {
870 throw(MsPASSError(string("set_transformation_matrix: the "
871 "elements of tmatrix should be float"),
872 ErrorSeverity::Invalid));
873 }
874 }
875 }
876 }
877 } else {
878 throw(MsPASSError(string("set_transformation_matrix: tmatrix should be a "
879 "list of 9 floats or a 3x3 list of list"),
880 ErrorSeverity::Invalid));
881 }
882 return this->set_transformation_matrix(tr(tmatrix_ary));
883 } else {
884 throw(MsPASSError(
885 string("set_transformation_matrix: tmatrix's type is not recognized"),
886 ErrorSeverity::Invalid));
887 }
888}

References mspass::utility::Metadata::get(), and set_transformation_matrix().

◆ sync_npts()

void mspass::seismic::CoreSeismogram::sync_npts ( )

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.

1066 {
1067 if (nsamp != this->u.columns()) {
1068 this->BasicTimeSeries::set_npts(this->u.columns());
1069 /* This is the unique name - we always set it. The weird
1070 cast is necessary to avoid type mismatch with unsigned.
1071 As above converted to keywords.h const string to make
1072 this easier to maintain*/
1073 this->put(SEISMICMD_npts, (long int)nsamp);
1074 /* these are hard coded aliases for sample_interval */
1075 std::set<string> aliases;
1076 std::set<string>::iterator aptr;
1077 aliases.insert("nsamp");
1078 aliases.insert("wfdisc.nsamp");
1079 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
1080 if (this->is_defined(*aptr)) {
1081 this->put(*aptr, (long int)nsamp);
1082 }
1083 }
1084 }
1085}

References mspass::utility::dmatrix::columns(), mspass::utility::Metadata::get(), mspass::utility::Metadata::is_defined(), mspass::seismic::BasicTimeSeries::nsamp, mspass::utility::Metadata::put(), mspass::seismic::SEISMICMD_npts(), mspass::seismic::BasicTimeSeries::set_npts(), and u.

◆ transform()

void mspass::seismic::CoreSeismogram::transform ( const double  a[3][3])

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.

Parameters
ais a C style 3x3 matrix.
654 {
655 if ((u.size()[1] <= 0) || dead())
656 return; // do nothing in these situations
657 /* Older version had this - we need to trust ns is already u.columns(). */
658 // size_t ns = u.size()[1];
659 size_t i, j, k;
660 double *work[3];
661 for (i = 0; i < 3; ++i)
662 work[i] = new double[nsamp];
663 for (i = 0; i < 3; ++i) {
664 dcopy(nsamp, u.get_address(0, 0), 3, work[i], 1);
665 dscal(nsamp, a[i][0], work[i], 1);
666 daxpy(nsamp, a[i][1], u.get_address(1, 0), 3, work[i], 1);
667 daxpy(nsamp, a[i][2], u.get_address(2, 0), 3, work[i], 1);
668 }
669 for (i = 0; i < 3; ++i)
670 dcopy(nsamp, work[i], 1, u.get_address(i, 0), 3);
671 for (i = 0; i < 3; ++i)
672 delete[] work[i];
673 /* Hand code this rather than use dmatrix or other library.
674 Probably dumb, but this is just a 3x3 system. This
675 is simply a multiply of a*tmatrix with result replacing
676 the internal tmatrix */
677 double tmnew[3][3];
678 double prod;
679 for (i = 0; i < 3; ++i)
680 for (j = 0; j < 3; ++j) {
681 for (prod = 0.0, k = 0; k < 3; ++k)
682 prod += a[i][k] * tmatrix[k][j];
683 tmnew[i][j] = prod;
684 }
685 for (i = 0; i < 3; ++i)
686 for (j = 0; j < 3; ++j)
687 tmatrix[i][j] = tmnew[i][j];
688 components_are_cardinal = this->tmatrix_is_cardinal();
689 /* Assume this method does not yield cartesian coordinate directions.*/
692}

References components_are_cardinal, components_are_orthogonal, mspass::seismic::BasicTimeSeries::dead(), mspass::utility::Metadata::get(), mspass::utility::dmatrix::get_address(), mspass::seismic::BasicTimeSeries::nsamp, mspass::utility::dmatrix::size(), tmatrix, and u.

Member Data Documentation

◆ components_are_cardinal

bool mspass::seismic::CoreSeismogram::components_are_cardinal
protected

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.

◆ components_are_orthogonal

bool mspass::seismic::CoreSeismogram::components_are_orthogonal
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.

◆ tmatrix

double mspass::seismic::CoreSeismogram::tmatrix[3][3]
protected

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.

◆ u

mspass::utility::dmatrix mspass::seismic::CoreSeismogram::u

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.


The documentation for this class was generated from the following files: