MsPASS C++ API  2.4.4.dev113+gc53d735d5
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.

28 /* mlive and tref are set in BasicTimeSeries so we don't use putters for
29 them here. These three initialize Metadata properly for these attributes*/
30 this->set_dt(1.0);
31 this->set_t0(0.0);
32 this->set_npts(0);
35 for (int i = 0; i < 3; ++i)
36 for (int j = 0; j < 3; ++j)
37 if (i == j)
38 tmatrix[i][i] = 1.0;
39 else
40 tmatrix[i][j] = 0.0;
41}
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:964
void set_t0(const double t0in)
Set the data start time.
Definition CoreSeismogram.cc:980
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:997
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.
44 /* IMPORTANT: this constructor assumes BasicTimeSeries initializes the
45 equivalent of:
46 set_dt(1.0)
47 set_t0(0.0)
48 set_tref(TimeReferenceType::Relative)
49 this->kill() - i.e. marked dead
50 */
51 this->set_npts(
52 nsamples); // Assume this is an allocator of the 3xnsamples matrix
55 for (int i = 0; i < 3; ++i)
56 for (int j = 0; j < 3; ++j)
57 if (i == j)
58 tmatrix[i][i] = 1.0;
59 else
60 tmatrix[i][j] = 0.0;
61}

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.
212 dynamic_cast<const BasicTimeSeries &>(ts[component_to_clone])),
213 Metadata(dynamic_cast<const Metadata &>(ts[component_to_clone])), u() {
214 const string base_error("CoreSeismogram constructor from 3 Time Series: ");
215 int i, j;
216 /* This is needed in case nsamp does not match s.size(0) */
217 int nstest = ts[component_to_clone].s.size();
218 if (nsamp != nstest)
219 this->nsamp = nstest;
220 /* this method allocates u and sets the proper metadata for npts*/
221 this->CoreSeismogram::set_npts(this->nsamp);
222 /* beware irregular sample rates, but don' be too machevelian.
223 Abort only if the mismatch is large defined as accumulated time
224 over data range of this constructor is less than half a sample */
225 if ((ts[0].dt() != ts[1].dt()) || (ts[1].dt() != ts[2].dt())) {
226 double ddtmag1 = fabs(ts[0].dt() - ts[1].dt());
227 double ddtmag2 = fabs(ts[1].dt() - ts[2].dt());
228 double ddtmag;
229 if (ddtmag1 > ddtmag1)
230 ddtmag = ddtmag1;
231 else
232 ddtmag = ddtmag2;
233 ddtmag1 = fabs(ts[0].dt() - ts[2].dt());
234 if (ddtmag1 > ddtmag)
235 ddtmag = ddtmag1;
236 double ddtcum = ddtmag * ((double)ts[0].s.size());
237 if (ddtcum > (ts[0].dt()) / 2.0) {
238 stringstream ss;
239 ss << base_error << "Sample intervals of components are not consistent"
240 << endl;
241 for (int ie = 0; ie < 3; ++ie)
242 ss << "Component " << ie << " dt=" << ts[ie].dt() << " ";
243 ss << endl;
244 throw MsPASSError(ss.str(), ErrorSeverity::Invalid);
245 }
246 }
247 // temporaries to hold component values
248 double t0_component[3];
249 double hang[3];
250 double vang[3];
251 // Load up these temporary arrays inside this try block and arrange to
252 // throw an exception if required metadata are missing
253 try {
254 /* WARNING hang and vang attributes in Metadata
255 are always assumed to have been read from a database where they
256 were stored in degrees. We convert these to radians below to
257 compute the transformation matrix.
258
259 Feb 2021: converted to use keywords.h definitions assuming these
260 came from the channel collection in MongoDB - Can be changed in
261 kewords.h for application outside mspass */
262 hang[0] = ts[0].get_double(SEISMICMD_hang);
263 hang[1] = ts[1].get_double(SEISMICMD_hang);
264 hang[2] = ts[2].get_double(SEISMICMD_hang);
265 vang[0] = ts[0].get_double(SEISMICMD_vang);
266 vang[1] = ts[1].get_double(SEISMICMD_vang);
267 vang[2] = ts[2].get_double(SEISMICMD_vang);
268 } catch (MetadataGetError &mde) {
269 stringstream ss;
270 ss << base_error
271 << "missing hang or vang variable in component TimeSeries objects "
272 "received"
273 << endl;
274 ss << "Message posted by Metadata::get_double: " << mde.what() << endl;
275 throw MsPASSError(ss.str(), ErrorSeverity::Invalid);
276 }
277 /* We couldn't get here if hang and vang were not set on comp 0 so
278 we don't test for that condition. We do need to clear hang and vang
279 from result here, however, as both attributes are meaningless
280 for a 3C seismogram */
281 this->erase(SEISMICMD_hang);
282 this->erase(SEISMICMD_vang);
283 // These are loaded just for convenience
284 t0_component[0] = ts[0].t0();
285 t0_component[1] = ts[1].t0();
286 t0_component[2] = ts[2].t0();
287
288 // Treat the normal case specially and avoid a bunch of work unless
289 // it is required
290 if ((ts[0].s.size() == ts[1].s.size()) &&
291 (ts[1].s.size() == ts[2].s.size()) &&
292 (fabs((t0_component[0] - t0_component[1]) / dt()) < 1.0) &&
293 (fabs((t0_component[1] - t0_component[2]) / dt()) < 1.0)) {
294 /* Older code had this. No longer needed with logic above that
295 calls set_npts. that method creates and initialized the u dmatrix*/
296 // this->u=dmatrix(3,nsamp);
297 // Load data by a simple copy operation
298 /* This is a simple loop version
299 for(j=0;j<nsamp;++nsamp)
300 {
301 this->u(0,j)=ts[0].s[j];
302 this->u(1,j)=ts[1].s[j];
303 this->u(2,j)=ts[2].s[j];
304 }
305 */
306 // This is a vector version that I'll use because it will
307 // be faster albeit infinitely more obscure and
308 // intrinsically more dangerous
309 dcopy(nsamp, &(ts[0].s[0]), 1, u.get_address(0, 0), 3);
310 dcopy(nsamp, &(ts[1].s[0]), 1, u.get_address(1, 0), 3);
311 dcopy(nsamp, &(ts[2].s[0]), 1, u.get_address(2, 0), 3);
312 } else {
313 /*Land here if the start time or number of samples
314 is irregular. We cut the output to latest start time to earliest end time*/
315 /* WARNING - debugging may be needed for this block. SEISPP versio of this
316 used gaps. Here we cut the output to match an irregularities. */
317 double tsmax, temin;
320 temin = min(ts[0].endtime(), ts[1].endtime());
321 temin = min(temin, ts[2].endtime());
322 nstest = (int)round((temin - tsmax) / mdt);
323 if (nstest <= 0)
324 throw MsPASSError(
325 base_error + "Irregular time windows of components have no overlap",
326 ErrorSeverity::Invalid);
327 else
328 this->CoreSeismogram::set_npts(nstest);
329 // Now load the data. Use the time and sample number methods
330 // to simplify process
331 double t;
332 t = tsmax;
333 this->set_t0(t);
334 double delta = this->dt();
335 for (int ic = 0; ic < 3; ++ic) {
336 t = this->t0();
337 for (j = 0; j < ts[ic].s.size(); ++j) {
338 i = ts[ic].sample_number(t);
339 // silently do nothing if outside bounds. This
340 // perhaps should be an error as it shouldn't really
341 // happen with the above algorithm, but safety is good
342 if ((i >= 0) && (i < nsamp))
343 this->u(ic, j) = ts[ic].s[i];
344 t += delta;
345 }
346 }
347 }
348 /* Finally we need to set the transformation matrix.
349 This is a direct application of conversion of routines
350 in spherical coordinate procedures. They are procedural
351 routines, not objects so the code is procedural.
352 */
353 SphericalCoordinate scor;
354 double *nu;
355 // convert all the hang values to spherical coordinate phi
356 // (angle from postive east) from input assumed in degrees
357 // azimuth from north. At the same time convert vang to radians.
358 for (i = 0; i < 3; ++i) {
359 hang[i] = mspass::utility::rad(90.0 - hang[i]);
360 vang[i] = mspass::utility::rad(vang[i]);
361 }
362 for (i = 0; i < 3; ++i) {
363 scor.phi = hang[i];
364 scor.theta = vang[i];
365 nu = SphericalToUnitVector(scor);
366 for (j = 0; j < 3; ++j)
367 tmatrix[i][j] = nu[j];
368 delete[] nu;
369 }
370 components_are_cardinal = this->tmatrix_is_cardinal();
373 else
375 /* Last but not least set the datum live before returning */
376 this->set_live();
377}
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.
94 : Metadata(md) {
95 string dfile, dir;
96 long foff;
97 FILE *fp;
98 double *inbuffer;
99
101 mlive = false;
102 try {
103 /* Names used are from mspass defintions as of Jan 2020.
104 We don't need to call the set methods for these attributes as they
105 would add the overhead of setting delta, startime, and npts to the
106 same value passed. */
107 this->mdt = this->get_double(SEISMICMD_dt);
108 this->mt0 = this->get_double(SEISMICMD_t0);
110 if (this->get_string(SEISMICMD_time_standard) == "UTC")
112 else {
114 /* For now we can't post an error because this is CoreSeismogram
115 so elog is not defined. For now let this error be silent as it
116 is harmless */
117 /*
118 this->elog.log_error("CoreSeismogram Metadata constructor",
119 SEISMICMD_time_standard+" attribute is not defined - set to Relative",
120 ErrorSeverity::Complaint);
121 */
122 }
123 }
124 if (this->time_is_relative()) {
125 /* It is not an error if a t0 shift is not defined and we are
126 in relative time. That is the norm for active source data. */
127 if (this->is_defined(SEISMICMD_t0_shift)) {
128 double t0shift = this->get_double(SEISMICMD_t0_shift);
129 this->force_t0_shift(t0shift);
130 }
131 }
132 /* This section is done specially to handle interaction with MongoDB.
133 We store tmatrix there as a python object so we use a get_any to fetch
134 it. Oct 22, 2021 added a bug fix to handle tmatrix not defined.
135 We will take a null (undefined) tmatrix stored in the database to
136 imply the data are cardinal and orthogonal (i.e. stardard geographic
137 coordinates in e,n,z order.)*/
138 if (this->is_defined("tmatrix")) {
140 boost::any_cast<py::object>(this->get_any("tmatrix")));
141 components_are_cardinal = this->tmatrix_is_cardinal();
144 else
145 components_are_orthogonal = false; // May be wrong but cost is tiny
146 } else {
147 /* this might not be needed but best be explicit*/
148 for (auto i = 0; i < 3; ++i)
149 for (auto j = 0; j < 3; ++j)
150 this->tmatrix[i][j] = 0.0;
151 for (auto i = 0; i < 3; ++i)
152 this->tmatrix[i][i] = 1.0;
155 }
156 /* We have to handle nsamp specially in the case when load_data
157 is false. To be consistent with TimeSeries we use a feature that
158 if the Metadata container does not define npts we default it.
159 In this case that means the default constructor for u and set
160 nsamp to 0 (via set_npts). */
161 if (md.is_defined(SEISMICMD_npts)) {
162 long int ns = md.get_long(SEISMICMD_npts);
163 this->set_npts(ns); /* note this is assumed to initialize u*/
164 } else {
165 this->set_npts(0);
166 }
167 /* Note previous code had an else clause to to with the
168 following conditional. It used to zero the u matrix.
169 The call to set_npts above will always do that so that would
170 have been redundant and was removed June 2022*/
171 if (load_data) {
172 dir = this->get_string(SEISMICMD_dir);
173 dfile = this->get_string(SEISMICMD_dfile);
174 foff = this->get_long(SEISMICMD_foff);
175 string fname = dir + "/" + dfile;
176 if ((fp = fopen(fname.c_str(), "r")) == NULL)
177 throw(MsPASSError(string("Open failure for file ") + fname,
178 ErrorSeverity::Invalid));
179 if (foff > 0)
180 fseek(fp, foff, SEEK_SET);
181 /* The older seispp code allowed byte swapping here. For
182 efficiency we don't support that here and assume can do a
183 raw fread from the file and get valid data. If support for
184 other types is needed this will need to be extended. Here
185 we just point fread at the internal u array. */
186 inbuffer = this->u.get_address(0, 0);
187 unsigned int nt = 3 * this->nsamp;
188 if (fread((void *)(inbuffer), sizeof(double), nt, fp) != nt) {
189 fclose(fp);
190 throw(MsPASSError(
191 string("CoreSeismogram constructor: fread error on file ") + fname,
192 ErrorSeverity::Invalid));
193 }
194 fclose(fp);
195 mlive = true;
196 }
197 } catch (MsPASSError &mpe) {
198 throw(mpe);
199 } catch (boost::bad_any_cast &be) {
200 throw(MsPASSError(
201 string("CoreSeismogram constructor: tmatrix type is not recognized"),
202 ErrorSeverity::Invalid));
203 } catch (...) {
204 throw;
205 };
206}
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:779
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.

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

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

References tmatrix.

◆ operator*=()

Multiply data by a scalar.

907 {
908 /* do nothing to empty data or data marked dead*/
909 if ((this->npts() == 0) || (this->dead()))
910 return (*this);
911 /* We can use this dscal blas function because dmatrix puts all the data
912 in a continguous block. Beware if there is am implementation change for
913 the matrix data*/
914 double *ptr = this->u.get_address(0, 0);
915 dscal(3 * this->npts(), scale, ptr, 1);
916 return (*this);
917}
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.

953 {
954 CoreSeismogram result(*this);
955 result += other;
956 return result;
957}
CoreSeismogram()
Definition CoreSeismogram.cc:27

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
MsPASSErrorif lhs and rhs have incompatible time standards, sample intervals, or start-time grids.
918 {
919 // Silently do nothing if d or lhs is marked dead
920 if (d.dead() || (this->dead()))
921 return (*this);
922 const auto overlap =
923 detail::arithmetic_overlap(*this, d, "CoreSeismogram::operator+=");
924 for (std::size_t i = 0; i < overlap.count; ++i) {
925 const std::size_t lhs_index = overlap.lhs_begin + i;
926 const std::size_t rhs_index = overlap.rhs_begin + i;
927 this->u(0, lhs_index) += d.u(0, rhs_index);
928 this->u(1, lhs_index) += d.u(1, rhs_index);
929 this->u(2, lhs_index) += d.u(2, rhs_index);
930 }
931 return (*this);
932}

References mspass::seismic::BasicTimeSeries::dead(), mspass::utility::Metadata::get(), 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).

959 {
960 CoreSeismogram result(*this);
961 result -= other;
962 return result;
963}

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
MsPASSErrorif lhs and rhs have incompatible time standards, sample intervals, or start-time grids.
937 {
938 // Silently do nothing if d is marked dead
939 if (d.dead())
940 return (*this);
941 const auto overlap =
942 detail::arithmetic_overlap(*this, d, "CoreSeismogram::operator-=");
943 for (std::size_t i = 0; i < overlap.count; ++i) {
944 const std::size_t lhs_index = overlap.lhs_begin + i;
945 const std::size_t rhs_index = overlap.rhs_begin + i;
946 this->u(0, lhs_index) -= d.u(0, rhs_index);
947 this->u(1, lhs_index) -= d.u(1, rhs_index);
948 this->u(2, lhs_index) -= d.u(2, rhs_index);
949 }
950 return (*this);
951}

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

◆ operator=()

Standard assignment operator.

892 {
893 if (this != &seisin) {
896 components_are_orthogonal = seisin.components_are_orthogonal;
897 components_are_cardinal = seisin.components_are_cardinal;
898 for (int i = 0; i < 3; ++i) {
899 for (int j = 0; j < 3; ++j) {
900 tmatrix[i][j] = seisin.tmatrix[i][j];
901 }
902 }
903 u = seisin.u;
904 }
905 return (*this);
906}
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.
1055 {
1056 try {
1057 vector<double> result;
1058 int i = this->sample_number(t);
1059 /* could test for a negative i and i too large but we assume
1060 the dmatrix container will throw an exception if it resolves that way*/
1061 for (int k = 0; k < 3; ++k)
1062 result.push_back(this->u(k, i));
1063 return result;
1064 } catch (...) {
1065 throw;
1066 };
1067}
int sample_number(double t) const
Definition BasicTimeSeries.h:72

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
1044 {
1045 try {
1046 vector<double> result;
1047 result.reserve(3);
1048 for (int k = 0; k < 3; ++k)
1049 result.push_back(this->u(k, i));
1050 return result;
1051 } catch (...) {
1052 throw;
1053 };
1054}

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.
597 {
598 if ((u.size()[1] <= 0) || this->dead())
599 return; // do nothing in these situations
600 SphericalCoordinate xsc = UnitVectorToSpherical(nu);
601 this->rotate(xsc);
602}

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

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

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

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.

964 {
965 this->BasicTimeSeries::set_dt(sample_interval);
966 /* This is the unique name defined in the mspass schema - we always set it. */
967 this->put(SEISMICMD_dt, sample_interval);
968 /* these are hard coded aliases for sample_interval */
969 std::set<string> aliases;
970 std::set<string>::iterator aptr;
971 /* Note these aren't set in keywords - aliases are flexible and this
972 can allow another way to make these attribute names more flexible. */
973 aliases.insert("dt");
974 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
975 if (this->is_defined(*aptr)) {
976 this->put(*aptr, sample_interval);
977 }
978 }
979}
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.

997 {
999 /* This is the unique name - we always set it. The weird
1000 cast is necessary to avoid type mismatch with unsigned.
1001 We use the name defined in keywords.h which we can always assume
1002 matches the schema for the unique name*/
1003 this->put(SEISMICMD_npts, (long int)npts);
1004 /* these are hard coded aliases for sample_interval */
1005 std::set<string> aliases;
1006 std::set<string>::iterator aptr;
1007 aliases.insert("nsamp");
1008 aliases.insert("wfdisc.nsamp");
1009 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
1010 if (this->is_defined(*aptr)) {
1011 this->put(*aptr, (long int)npts);
1012 }
1013 }
1014 /* this method has the further complication that npts sets the size of the
1015 data matrix. Here we resize the matrix and initialize it to 0s.*/
1016 if (npts == 0) {
1017 this->u = dmatrix();
1018 } else {
1019 this->u = dmatrix(3, npts);
1020 this->u.zero();
1021 }
1022}
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.

980 {
982 /* This is the unique name - we always set it. Pulled from keywords.h
983 which should match the schema. aliases are hard coded not defined as
984 keywords */
985 this->put(SEISMICMD_t0, t0in);
986 /* these are hard coded aliases for sample_interval */
987 std::set<string> aliases;
988 std::set<string>::iterator aptr;
989 aliases.insert("t0");
990 aliases.insert("time");
991 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
992 if (this->is_defined(*aptr)) {
993 this->put(*aptr, t0in);
994 }
995 }
996}
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.
800 {
801 for (int i = 0; i < 3; ++i)
802 for (int j = 0; j < 3; ++j)
803 tmatrix[i][j] = a[i][j];
804 py::list tmatrix_l;
805 for (int i = 0; i < 3; ++i)
806 for (int j = 0; j < 3; ++j)
807 tmatrix_l.append(a[i][j]);
808 this->put_object(SEISMICMD_tmatrix, tmatrix_l);
809 bool cardinal;
810 cardinal = this->tmatrix_is_cardinal();
811 if (cardinal) {
814 } else {
816 /* Not necessarily true, but small overhead cost*/
818 }
820}
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.
779 {
780 for (int i = 0; i < 3; ++i)
781 for (int j = 0; j < 3; ++j)
782 tmatrix[i][j] = A(i, j);
783 py::list tmatrix_l;
784 for (int i = 0; i < 3; ++i)
785 for (int j = 0; j < 3; ++j)
786 tmatrix_l.append(A(i, j));
787 this->put_object(SEISMICMD_tmatrix, tmatrix_l);
788 bool cardinal;
789 cardinal = this->tmatrix_is_cardinal();
790 if (cardinal) {
793 } else {
795 /* Not necessarily true, but small overhead cost*/
797 }
799}

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

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.

1023 {
1024 if (nsamp != this->u.columns()) {
1025 this->BasicTimeSeries::set_npts(this->u.columns());
1026 /* This is the unique name - we always set it. The weird
1027 cast is necessary to avoid type mismatch with unsigned.
1028 As above converted to keywords.h const string to make
1029 this easier to maintain*/
1030 this->put(SEISMICMD_npts, (long int)nsamp);
1031 /* these are hard coded aliases for sample_interval */
1032 std::set<string> aliases;
1033 std::set<string>::iterator aptr;
1034 aliases.insert("nsamp");
1035 aliases.insert("wfdisc.nsamp");
1036 for (aptr = aliases.begin(); aptr != aliases.end(); ++aptr) {
1037 if (this->is_defined(*aptr)) {
1038 this->put(*aptr, (long int)nsamp);
1039 }
1040 }
1041 }
1042}

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

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: