DATRW++ library: seismic data I/O with multiple formats
sucomanager.cc
Go to the documentation of this file.
1 /*! \file sucomanager.cc
2  * \brief manage coordinate scaling (implementation)
3  *
4  * ----------------------------------------------------------------------------
5  *
6  * \author Thomas Forbriger
7  * \date 06/12/2010
8  *
9  * manage coordinate scaling (implementation)
10  *
11  * Copyright (c) 2010 by Thomas Forbriger (BFO Schiltach)
12  *
13  * ----
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27  * ----
28  *
29  *
30  * REVISIONS and CHANGES
31  * - 06/12/2010 V1.0 Thomas Forbriger
32  * - 14/01/2011 V1.1 extended modifications:
33  * - resolved problem with coordinate values close to
34  * zero which lead to a logarithm approaching
35  * infinity upon testing the number of significant
36  * digits
37  * - resolved a problem with unreasonable large scale
38  * values in case of small round off errors in
39  * floating point coordinate values
40  * - apply proper rounding to coordinate values, such
41  * that 0.0999999 will be stored as 0.1
42  * - check actual round-off error rather than scale
43  * and issue a warning in cases where round-off error
44  * exceed 0.1%
45  * - 29/03/2011 V1.2 - search for the smallest possible power larger or
46  * equal a desired value
47  * - 22/01/2012 V1.3
48  * - handle control parameters
49  * - pass control parameters to Coordinates and ScalCoo
50  * - add conversion functions
51  * - 24/01/2012 V1.4
52  * - static cast of value to type float in function
53  * ScalCoo::set(const double&) caused terrible
54  * roundoff resulting in awfully many trailing
55  * digits. I see no reason for this cast and remove
56  * it.
57  * - reworked ScalCoo::smallestpower(const short&)
58  * - 26/01/2012 V1.5 bug fix: ScalCoo::set(const double&):
59  * initial scale value must equal one
60  * see ticket:168
61  * - 08/07/2016 V1.6 make correct use of new DATRW_report_assert
62  * - 01/12/2016 V1.7 provide improved error message and warning to user
63  *
64  * ============================================================================
65  */
66 #define DATRW_SUCOMANAGER_CC_VERSION \
67  "DATRW_SUCOMANAGER_CC V1.7"
68 
69 #include <cmath>
70 #include <climits>
71 #include <datrwxx/suformat.h>
72 #include <datrwxx/sucomanager.h>
73 #include <datrwxx/error.h>
74 #include <datrwxx/debug.h>
75 #include <datrwxx/util.h>
76 
77 namespace datrw {
78 
79  namespace su {
80 
81  /*======================================================================*/
82 
83  /*! some local helpers
84  * \ingroup group_su
85  */
86  namespace helper {
87 
88  /*! \brief output format modifier for debug output
89  * \ingroup group_su
90  */
92  public:
93  void set_output_format(std::ostream& os) const
94  {
95  os.width(14);
96  os.precision(12);
97  }
98  }; // class MyOutputFormat {
99 
100  /*----------------------------------------------------------------------*/
101 
102  /*! \brief output operator to apply output format modifier
103  */
104  std::ostream& operator<<(std::ostream& os, const MyOutputFormat& f)
105  {
106  f.set_output_format(os);
107  return(os);
108  } // std::ostream& operator<<(std::ostream& os, const MyOutputFormat& f)
109 
110 
111  } // namespace helper
112 
113  /*======================================================================*/
114  // constants to support coordinate scaling
115 
117 
118  /*----------------------------------------------------------------------*/
119 
120  /*! set from header fields
121  *
122  * checked 29.3.2011
123  */
124  void ScalCoo::set(const short& sin, const int& c)
125  {
126  // make a copy to allow modification
127  short s=sin;
129  this->scale=s;
130  this->coo=c;
131  } // void ScalCoo::set(const short& s, const int& c)
132 
133  /*----------------------------------------------------------------------*/
134 
135  /*! set from coordinate value in meters
136  *
137  * this functions takes the coordinate value as a floating point value in
138  * meters; it has to find the appropriate scaling to represent this value
139  * with appropriate precision in an integer variable
140  */
141  void ScalCoo::set(const double& value)
142  {
143  const bool debug=false;
144  this->scale=1;
145  this->coo=static_cast<int>(nearbyint(value));
146  double v=(value>=0 ? value : -value);
148  {
149  this->scale=1;
150  this->coo=0;
151  }
152  else
153  {
154  DATRW_debug(debug, "ScalCoo::set(const double& v)",
155  "v: " << helper::MyOutputFormat() << v << "\n"
156  "log10(v): " << std::log10(v) << "\n"
157  "floor(log10(v)): " << std::floor(std::log10(v)));
158  int nlead=1+static_cast<int>(std::floor(std::log10(v)));
159  int ntrail=::datrw::util::ntrailingdigits(v);
160  int nsig=::datrw::util::nsignificantdigits(v, debug);
161  int nzero=ntrail-nsig;
162  DATRW_debug(debug, "ScalCoo::set(const double& v)",
163  "nlead: " << nlead <<
164  " ntrail: " << ntrail <<
165  " nsig: " << nsig);
166  nsig = nsig>static_cast<int>(Mcontrol.coodigits)
167  ? Mcontrol.coodigits : nsig;
168  ntrail=nsig+nzero;
169  DATRW_debug(debug, "ScalCoo::set(const double& v)",
170  "nlead: " << nlead <<
171  " ntrail: " << ntrail <<
172  " nsig: " << nsig);
173  if (ntrail>0)
174  {
175  DATRW_debug(debug, "ScalCoo::set(const double& value)",
176  "ntrail>0");
177  int s=std::pow(10,ntrail);
178  s = s > 10000 ? 10000 : s;
179  DATRW_assert(s<100000,
180  "ERROR (ScalCoo::set): scale too large");
181  this->scale=-s;
182  this->coo=static_cast<int>(nearbyint(value*s));
183  // check truncation error
184  double truevalue=value*s;
185  double roundedvalue=nearbyint(truevalue);
186  double truncationerror=fabs(1.-(truevalue/roundedvalue));
187  DATRW_debug(debug, "ScalCoo::set(const double& value)",
188  "truncation error:"
189  << " truevalue: " << truevalue
190  << " roundedvalue: " << roundedvalue
191  << " truncationerror: " << truncationerror);
192  DATRW_report_assert(truncationerror<0.001,
193  "WARNING (ScalCoo::set) "
194  "truncation error > 0.1%\n"
195  "coordinate value "
197  << " will be truncated!"
198  << "\n*"
199  << " scaled value: " << truevalue
200  << " rounded scaled value: "
201  << roundedvalue);
202  }
203  else if (nlead>nsig)
204  {
205  DATRW_debug(debug, "ScalCoo::set(const double& value)",
206  "nlead>nsig");
207  int s=std::pow(10,nlead-nsig);
208  s = s > 10000 ? 10000 : s;
209  DATRW_assert(s<100000,
210  "ERROR (ScalCoo::set): scale too large");
211  this->scale=s;
212  this->coo=static_cast<int>(nearbyint(value/s));
213  }
214  } // if (v<ScalCoo::effectivezero), else clause
215  // adjust scale
217  DATRW_debug(debug, "ScalCoo::set(const double& value)",
218  "value on exit:"
219  << " value: " << value
220  << " this->coo: " << this->coo
221  << " this->scale: " << this->scale);
222  } // void ScalCoo::set(const double& v)
223 
224  /*----------------------------------------------------------------------*/
225 
226  /*! return decimal power of scaling factor
227  *
228  * tested 29.3.2011
229  */
230  int ScalCoo::power() const
231  {
232  return(datrw::su::scaletopower(this->scale));
233  } // int ScalCoo::power() const
234 
235  /*----------------------------------------------------------------------*/
236 
237  //! scale to given scaling factor as defined by decimal power
238  void ScalCoo::scaletopower(const int& p)
239  {
240  int pd=this->power()-p;
241  int vnew, vcmp;
242  if (pd<0)
243  {
244  int fac=std::pow(10,-pd);
245  vnew=static_cast<int>(std::floor(this->coo/fac));
246  vcmp=static_cast<int>(std::floor(vnew*fac));
247  }
248  else
249  {
250  int fac=std::pow(10,pd);
251  vnew=std::floor(this->coo*fac);
252  vcmp=static_cast<int>(std::floor(vnew/fac));
253  }
254  DATRW_report_assert(vcmp==this->coo,
255  "WARNING ScalCoo::scaletopower will truncate "
256  "coordinate value\n"
257  "value was "
258  << helper::MyOutputFormat() << this->coo <<
259  " value will be " << vcmp);
260  this->coo = vnew;
261  this->scale=powertoscale(p);
262  DATRW_assert(this-scale != 0,
263  "ERROR (ScalCoo::scaletopower): illegal scale value!");
264  } // void ScalCoo::scaletopower(const int& p)
265 
266  /*----------------------------------------------------------------------*/
267 
268  /*! \brief adjust scale to optimal representation of significant digits
269  *
270  * This function removes trailing zeroes from the ScalCoo::coo value, by
271  * chosing an appropriate scale value.
272  * Upon return from this function the scale power is as large as possible.
273  * Any increase in the power of the scale value will result in truncation
274  * of the coordinate value.
275  */
277  {
278  int p=this->power();
279  int v1, v2;
280  do {
281  v1=this->coo;
282  v2=std::floor(v1/10)*10;
283  if ((v1==v2) && (p<4))
284  {
285  ++p;
286  this->coo /= 10;
287  }
288  } while ((v1==v2) && (p<4));
289  this->scale=powertoscale(p);
290  DATRW_assert(this-scale != 0,
291  "ERROR (ScalCoo::scaletopower): illegal scale value!");
292  } // void ScalCoo::adjustscale()
293 
294  /*----------------------------------------------------------------------*/
295 
296  /*! \brief return smallest power possible without truncation
297  */
298  int ScalCoo::smallestpower(const short& desiredscale) const
299  {
300  // limiting power values
301  int desired=datrw::su::scaletopower(desiredscale);
302  int lp=desired >= -4 ? desired : -4;
303  lp = lp <= 4 ? lp : 4;
304  // initial power
305  int p=this->power();
306  /*
307  * code modification; 24.1.2012
308  * tests were previously done detecting overflow
309  *
310  // value and compare value
311  ScalCoo v1(Mcontrol), v2(Mcontrol);
312  v1.coo=this->coo;
313  v2.coo=std::floor(v1.coo*10)/10;
314  do {
315  if ((v1.coo==v2.coo) && (p>lp))
316  {
317  --p;
318  v1.coo *= 10;
319  v2.coo=std::floor(v1.coo*10)/10;
320  }
321  } while ((v1.coo==v2.coo) && (p>lp));
322  *
323  * I prefer to use numerical comparison
324  * increasing the scale power will decrease the coo value and vice versa;
325  * start with the optimal power as being found in this->scale
326  * decrease the power until
327  * either a further decrease would result in coo not fitting into a
328  * variable of type short
329  * or the desired scale (scale power) value is obtained
330  */
331  int c1=this->coo;
332  int c2;
333  bool hot=true;
334  do {
335  if (hot && (p>lp))
336  {
337  --p;
338  c1 *= 10;
339  }
340  c2=c1*10;
341  hot = ((c2 <= SHRT_MAX) && (c2 >= SHRT_MIN));
342  } while (hot && (p>lp));
343  return(p);
344  } // int ScalCoo::smallestpower()
345 
346  /*----------------------------------------------------------------------*/
347 
348  //! return coordinate value
349  double ScalCoo::value() const
350  {
351  double c=static_cast<double>(this->coo);
352  double retval = c*scalefactor(this->scale,
354  return(retval);
355  } // double ScalCoo::value() const
356 
357  /*======================================================================*/
358 
359  //! read values from SU header
361  {
362  bool debug=false;
363  DATRW_debug(debug, "Coordinates::getvaluesfrom:",
364  "values in header on entry:"
365  << " scalco: " << h.scalco
366  << " scalel: " << h.scalel
367  << " sx: " << h.sx
368  << " sy: " << h.sy
369  << " gx: " << h.gx
370  << " gy: " << h.gy
371  << " sdepth: " << h.sdepth
372  << " gelev: " << h.gelev);
373  this->sx.set(h.scalco, h.sx);
374  this->sy.set(h.scalco, h.sy);
375  this->gx.set(h.scalco, h.gx);
376  this->gy.set(h.scalco, h.gy);
377  this->sdepth.set(h.scalel, h.sdepth);
378  this->gelev.set(h.scalel, h.gelev);
379  } // Coordinates::getvaluesfrom(const TraceHeaderStruct& h)
380 
381  /*----------------------------------------------------------------------*/
382 
383  //! set values in SU header
385  {
386  bool debug=false;
387  this->equalizescaling();
388  h.scalco=this->sx.scale;
389  h.sx=this->sx.coo;
390  h.sy=this->sy.coo;
391  h.gx=this->gx.coo;
392  h.gy=this->gy.coo;
393  h.scalel=this->sdepth.scale;
394  h.sdepth=this->sdepth.coo;
395  h.gelev=this->gelev.coo;
396  DATRW_debug(debug, "Coordinates::setvaluesin:",
397  "values in header on finish:"
398  << " scalco: " << h.scalco
399  << " scalel: " << h.scalel
400  << " sx: " << h.sx
401  << " sy: " << h.sy
402  << " gx: " << h.gx
403  << " gy: " << h.gy
404  << " sdepth: " << h.sdepth
405  << " gelev: " << h.gelev);
406  } // Coordinates::setvaluesin(TraceHeaderStruct& h) const
407 
408  /*----------------------------------------------------------------------*/
409 
410  //! equalize scaling
412  {
413  bool debug=false;
414  // adjust horizontal coordinates
415  DATRW_debug(debug, "Coordinates::equalizescaling",
416  "scales on entry"
417  << " gelev.scale " << this->gelev.scale
418  << " sdepth.scale " << this->sdepth.scale);
419  /* is not required when search for the smallest possible power
420  this->sx.adjustscale();
421  this->sy.adjustscale();
422  this->gx.adjustscale();
423  this->gy.adjustscale();
424  */
425  // search for smallest possible power
426  int psx=this->sx.smallestpower(Mcontrol.scalco);
427  int psy=this->sy.smallestpower(Mcontrol.scalco);
428  int pgx=this->gx.smallestpower(Mcontrol.scalco);
429  int pgy=this->gy.smallestpower(Mcontrol.scalco);
430  int pnew = psx > psy ? psx : psy;
431  pnew = pnew > pgx ? pnew : pgx;
432  pnew = pnew > pgy ? pnew : pgy;
433  // check against largest possible power
434  /* is not necessary
435  pnew = pnew < sx.power() ? pnew : sx.power();
436  pnew = pnew < sy.power() ? pnew : sy.power();
437  pnew = pnew < gx.power() ? pnew : gx.power();
438  pnew = pnew < gy.power() ? pnew : gy.power();
439  */
440  this->sx.scaletopower(pnew);
441  this->sy.scaletopower(pnew);
442  this->gx.scaletopower(pnew);
443  this->gy.scaletopower(pnew);
444  DATRW_assert(((this->sx.scale==this->sy.scale) &&
445  (this->sy.scale==this->gx.scale) &&
446  (this->gx.scale==this->gy.scale)),
447  "ERROR: inconsistent coordinate scaling");
448  // adjust vertical coordinates
449  /* is not required when scaling with the smallest possible power
450  this->sdepth.adjustscale();
451  this->gelev.adjustscale();
452  DATRW_debug(debug, "Coordinates::equalizescaling",
453  "scales after adjust"
454  << " gelev.scale " << this->gelev.scale
455  << " sdepth.scale " << this->sdepth.scale);
456  */
457  int psdepth=this->sdepth.smallestpower(Mcontrol.scalco);
458  int pgelev=this->gelev.smallestpower(Mcontrol.scalco);
459  pnew = psdepth > pgelev ? psdepth : pgelev;
460  /* is not necessary
461  pnew = pnew < sdepth.power() ? pnew : sdepth.power();
462  pnew = pnew < gelev.power() ? pnew : gelev.power();
463  */
464  this->sdepth.scaletopower(pnew);
465  this->gelev.scaletopower(pnew);
466  DATRW_assert((this->sdepth.scale==this->gelev.scale),
467  "ERROR: inconsistent coordinate scaling");
468  } // void Coordinates::equalizescaling()
469 
470  /*======================================================================*/
471 
472  /*! \brief convert a decimal power to a SeismicUn*x scale value
473  */
474  short powertoscale(const int& p)
475  {
476  int pp = p < 0 ? -p : p;
477  short s=static_cast<short>(std::pow(10,pp));
478  return(p<0 ? -s: s);
479  } // short powertoscale(const int& p)
480 
481  /*----------------------------------------------------------------------*/
482 
483  /*! \brief convert a SeismicUn*x scale value to a decimal power
484  */
485  int scaletopower(short s, const bool& strict)
486  {
487  fixscalevalue(s, strict);
488  int sabs=s > 0 ? s : -s;
489  int retval=std::log10(sabs);
490  if (s < 0) { retval *= -1; }
491  return(retval);
492  } // int scaletopower(short s, const bool& strict=true)
493 
494  /*----------------------------------------------------------------------*/
495 
496  /*! \brief fix a SeismicUn*x scale value
497  */
498  void fixscalevalue(short& s, const bool& strict)
499  {
500  /*
501  * a scale value of 0 is not defined for the trace header in the SEGY
502  * standard; since Geode data acquisitions produce SEGY data with scalel
503  * set to 0, we have to deal with this value
504  */
505  DATRW_report_assert(s != 0,
506  "WARNING (ScalCoo::set): incorrect scale\n"
507  "The value for the scalco and scalel field\n"
508  "violates the SEG-Y trace header definition.\n"
509  "value: " << s << "; will be set to 1");
510  DATRW_assert(!((strict) && (s==0)),
511  "Violation of SeismicUn*x format definition!");
512  // make a copy to allow modification
513  if (s==0) { s=1; }
514  int sabs=s > 0 ? s : -s;
515  if ((sabs < 10) && (s != 1))
516  {
517  /*
518  * some code out there produces SU data with scale values understood
519  * as exponents of ten; SEGY defines the scale value to be taken as
520  * numerator or denominator of the scaling factor with values -10000,
521  * -1000, -100, -10, 1, 10, 100, 1000, 10000 only; here we allow for
522  * the non-standard setting indicated by the absolute value of scale
523  * being larger than 1 and smaller than 10
524  */
525  DATRW_report_assert(((sabs < 10) && (s != 1)),
526  "WARNING (ScalCoo::set): non-standard scale\n"
527  "The value for the scalco and scalel field\n"
528  "violates the SEG-Y trace header definition.\n"
529  "value: " << s);
531  "Violation of SeismicUn*x format definition!");
532  /*
533  * 10 to a power larger than 4 cannot be represented by a short int
534  */
535  DATRW_assert((sabs<5),
536  "ERROR (ScalCoo::set): "
537  "scale value does not fit in short int field");
538  s= s > 0 ? std::pow(10,sabs) : -std::pow(10,sabs);
539  }
540  } // void fixscalevalue(short& s, const bool& strict=true)
541 
542  /*----------------------------------------------------------------------*/
543 
544  /*! \brief convert scale value to a factor to be applied
545  */
546  double scalefactor(short s, const bool& strict)
547  {
548  fixscalevalue(s, strict);
549  double sv=static_cast<double>(s);
550  return(s>0 ? sv : -1./sv);
551  } // double scalefactor(short s, const bool& strict=true)
552 
553  } // namespace su
554 
555 } // namespace datrw
556 
557 /* ----- END OF sucomanager.cc ----- */
int gy
Group coordinate - Y.
int sy
Source coordinate - Y.
short scalel
Scalar to be applied to the previous 7 entries to give the real value (see details).
datrw::su::options::SpatialSampling Mcontrol
control parameters
Definition: sucomanager.h:223
#define DATRW_assert(C, M)
Check an assertion and report by throwing an exception.
Definition: error.h:92
int sx
Source coordinate - X.
int scaletopower(short s, const bool &strict)
convert a SeismicUn*x scale value to a decimal power
Definition: sucomanager.cc:485
void adjustscale()
adjust scale to optimal representation of significant digits
Definition: sucomanager.cc:276
macro function for debugging output (prototypes)
ScalCoo gy
receiver y coordinate
Definition: sucomanager.h:217
SEG-Y and SU trace header as taken from segy.h coming with SeismicUnixsegy - trace identification hea...
unsigned int coodigits
maximum number of significant digits to be used
Definition: suformat.h:129
bool bestrict
if true: strictly use header definition by SeismicUnix source
Definition: suformat.h:133
ScalCoo sx
source x coordinate
Definition: sucomanager.h:211
int coo
coordinate
Definition: sucomanager.h:171
const double thresholdzero
Definition: suformat.cc:75
short scale
scale like scalco
Definition: sucomanager.h:169
short scalco
preferred scalco value
Definition: suformat.h:131
int gelev
Receiver group elevation from sea level (all elevations above the Vertical datum are positive and bel...
exception class declaration for libdatrwxx (prototypes)
const char *const strict
strictly interpret header as defined in SeismicUnix source
Definition: suformat.cc:65
ScalCoo gx
receiver x coordinate
Definition: sucomanager.h:215
int sdepth
Source depth below surface (a positive number).
Root namespace of library.
Definition: aalibdatrwxx.cc:16
ScalCoo gelev
source y coordinate
Definition: sucomanager.h:221
short scalco
Scalar to be applied to the next 4 entries to give the real value (see details).
static double effectivezero
lower limit of values
Definition: sucomanager.h:142
utilities used by more than one type of data reader (prototypes)
ScalCoo sdepth
source z coordinate
Definition: sucomanager.h:219
datrw::su::options::SpatialSampling Mcontrol
control parameters
Definition: sucomanager.h:173
int ntrailingdigits(double v, const bool &debug)
return number of trailing digits (after decimal point)
Definition: util.cc:97
output format modifier for debug output
Definition: sucomanager.cc:91
int nsignificantdigits(double v, const bool &debug)
return number of significant digits
Definition: util.cc:49
int smallestpower(const short &desiredscale=datrw::su::subformat::def::scalco) const
smallest possible power larger or equal desired
Definition: sucomanager.cc:298
int gx
Group coordinate - X.
void equalizescaling()
equalize scaling
Definition: sucomanager.cc:411
#define DATRW_debug(C, N, M)
produce debug output
Definition: debug.h:50
short powertoscale(const int &p)
convert a decimal power to a SeismicUn*x scale value
Definition: sucomanager.cc:474
void scaletopower(const int &p)
scale to given scaling factor as defined by decimal power
Definition: sucomanager.cc:238
ScalCoo sy
source y coordinate
Definition: sucomanager.h:213
std::ostream & operator<<(std::ostream &os, const MyOutputFormat &f)
output operator to apply output format modifier
Definition: sucomanager.cc:104
void set(const short &s, const int &c)
set from header fields
Definition: sucomanager.cc:124
double value() const
return coordinate value
Definition: sucomanager.cc:349
int power() const
return decimal power of scaling factor
Definition: sucomanager.cc:230
#define DATRW_report_assert(C, M)
Check an assertion and report only.
Definition: error.h:120
void set_output_format(std::ostream &os) const
Definition: sucomanager.cc:93
void fixscalevalue(short &s, const bool &strict)
fix a SeismicUn*x scale value
Definition: sucomanager.cc:498
void setvaluesin(TraceHeaderStruct &h)
set values in SU header
Definition: sucomanager.cc:384
void getvaluesfrom(const TraceHeaderStruct &h)
read values from SU header
Definition: sucomanager.cc:360
double scalefactor(short s, const bool &strict)
convert scale value to a factor to be applied
Definition: sucomanager.cc:546