GeoTessCPPExamples  2.0
Tomography2D.cc
Go to the documentation of this file.
1 //- ****************************************************************************
2 //-
3 //- Copyright 2009 Sandia Corporation. Under the terms of Contract
4 //- DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
5 //- retains certain rights in this software.
6 //-
7 //- BSD Open Source License.
8 //- All rights reserved.
9 //-
10 //- Redistribution and use in source and binary forms, with or without
11 //- modification, are permitted provided that the following conditions are met:
12 //-
13 //- * Redistributions of source code must retain the above copyright notice,
14 //- this list of conditions and the following disclaimer.
15 //- * Redistributions in binary form must reproduce the above copyright
16 //- notice, this list of conditions and the following disclaimer in the
17 //- documentation and/or other materials provided with the distribution.
18 //- * Neither the name of Sandia National Laboratories nor the names of its
19 //- contributors may be used to endorse or promote products derived from
20 //- this software without specific prior written permission.
21 //-
22 //- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 //- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 //- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 //- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 //- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 //- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 //- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 //- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 //- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 //- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 //- POSSIBILITY OF SUCH DAMAGE.
33 //-
34 //- ****************************************************************************
35 
36 #include "CPPUtils.h"
37 #include "CPPGlobals.h"
38 #include "CpuTimer.h"
39 #include "GeoTessGrid.h"
40 #include "GeoTessModel.h"
41 #include "GeoTessPosition.h"
42 #include "GeoTessModelUtils.h"
43 
44 #include <vector>
45 
46 using namespace geotess;
47 
48 // Earth centered unit vector representing the geographic location
49 // of seismic station ANMO near Albuquerque, New Mexico, USA.
50 static double ANMO[3];
51 
52 /**
53  * This application illustrates how to use features available in GeoTessJava to
54  * execute tomography on a 2D model. This application does not implement
55  * tomography but merely illustrates how to call methods in GeoTessJava that one
56  * would likely need to perform tomography.
57  *
58  * <p>In order to run properly, this program must be run from directory
59  * GeoTessBuilderExamples/tomo2dTest.
60  *
61  * <p>
62  * This application illustrates the following tasks:
63  * <ol>
64  * <li>Generate 11 great circle ray paths along the surface of the WGS84
65  * ellipsoid.
66  * <li>Generate a 2D, global starting model consisting of values of attenuation
67  * as a function of geographic position on the globe.
68  * <li>Limit the application of tomography to a region of the Earth in North
69  * America. This limitation is optional.
70  * <li>Trace rays through the starting model, calculating the path integral of
71  * the attenuation along the ray path and the weights (data kernels) of all the
72  * grid nodes attributable to interpolation of points on ray paths.
73  * <li>Call methods in GeoTessJava to identify the neighbors of a specified
74  * node. These methods are needed to apply regularization of the tomography
75  * matrix.
76  * <li>Apply changes in model attribute values computed by tomographic
77  * inversion.
78  * <li>Compute a new GeoTessModel whose attribute values are the number of times
79  * each grid node was 'touched' by one of the ray paths (hit count).
80  * <li>Execute application GeoTessBuilder to generate a new grid that is more
81  * refined in areas of high hit count.
82  * <li>Generate a new model based on the new, refined grid generated with
83  * GeoTessBuilder but containing attenuation values copied or interpolated from
84  * the original model.
85  * </ol>
86  *
87  * @author sballar
88  *
89  */
90 int main(int argc, char** argv)
91 {
92  // function declarations.
93  GeoTessModel* startingModel(const string& gridFile);
94  void generateRayPaths(vector<double**>& rayPaths);
95  void integrateRayPaths(GeoTessModel* model, const vector<double**>& rayPaths);
96  void regularization(GeoTessModel* model);
97  void applyAttributeChanges(GeoTessModel* model,
98  const int& attributeIndex, const vector<float>& attributeChanges);
99  GeoTessModel* hitCount(GeoTessModel* inputModel, const vector<double**>& rayPaths);
100  GeoTessModel* refineModel(GeoTessModel* oldModel, GeoTessModel* hitCountModelRefined);
101 
102  try
103  {
104  // specify the ellipsoid that is to be used when converting between geographic
105  // and geocentric coordinates and between depth and radius.
106  EarthShape ellipsoid("WGS84");
107 
108  // seismic station ANMO near Albuquerque, New Mexico, USA.
109  // Latitude and longitude of the station are converted to an
110  // earth centered unit vector.
111  ellipsoid.getVectorDegrees(34.9462, -106.4567, ANMO);
112 
113  // Generate a starting model. In this example, the starting model is
114  // very simple, consisting of constant values of attenuation at each
115  // node of the grid. Real applications could start from some other
116  // starting model loaded from a file.
117  string gridFile = "geotess_grid_04000.geotess";
118 
119  if (!CPPUtils::fileExists(gridFile))
120  {
121  cout << "ERROR: grid file " << gridFile << " does not exist." << endl
122  << "This program must be run from directory GeoTessBuilderExamples/tomo2dTest" << endl;
123  exit(1);
124  }
125 
126  GeoTessModel* model = startingModel(gridFile);
127 
128  // Generate 11 great circle ray paths for use in this example. In
129  // a real application, these ray paths would surely be loaded from
130  // a file or some other source.
131  vector<double**> rayPaths;
132  generateRayPaths(rayPaths);
133 
134  // We will limit the active nodes in the model using a Polygon
135  // object. Nodes that reside inside the polygon will be the only
136  // ones modified by tomography. The others are simply carried along
137  // but do not participate in tomography. The polygon that we will
138  // define is a small circle with radius 38 degrees centered on
139  // seismic station ANMO. For information on other ways to define
140  // Polygons, see the User's Manual or GeoTess code documentation.
141  // If we wanted to execute a global tomography model, we would
142  // not apply this step.
143  GeoTessPolygon* polygon = new GeoTessPolygon(ANMO, CPPUtils::toRadians(38), 100);
144  model->setActiveRegion(polygon);
145 
146  // Trace rays though the model and extract integrated attribute
147  // values along the ray path and interpolation coefficient 'weights'
148  // associated with each ray path. In a real tomography application,
149  // these values would be used to populate the vector of residuals
150  // and the tomography A matrix of data kernels. In this example,
151  // they are simply extracted from the model and some are printed
152  // to the screen.
153  integrateRayPaths(model, rayPaths);
154 
155  // Call a method that illustrates how to find the indices of model
156  // points that are neighbors of a specified model point. These are
157  // often used in tomography to perform regularization or smoothing.
158  regularization(model);
159 
160  // At this point, a real tomography application would actually
161  // perform tomographic inversion, resulting in a vector containing
162  // changes in attenuation that should be applied at each model
163  // point. For this example, we will simply specify these changes
164  // and apply them.
165  vector<float> attributeChanges;
166  for (int i=0; i<model->getNPoints(); ++i)
167  attributeChanges.push_back(0.01F);
168 
169  // apply the attenuation changes.
170  applyAttributeChanges(model, 0, attributeChanges);
171 
172  // Now we will assume that the user wishes to refine the model
173  // in regions of high hit count before executing the next
174  // iteration of tomography. In a real tomography application
175  // this involves several steps which need to be implemented in
176  // separate applications.
177  // First, we build a new GeoTessModel where the attribute
178  // is HIT_COUNT, write that model to a file and
179  // terminate execution. Then we use the GeoTessBuilder
180  // application to generate a refined grid. Then, in
181  // a new application, we build a new GeoTessModel based
182  // on the refined grid, which contains attenuation values
183  // that are either copied or interpolated from the original
184  // model.
185 
186  // Given a current model and a collection of ray paths, build
187  // a new model where the attribute is HIT_COUNT.
188  GeoTessModel* hitCountModel = hitCount(model, rayPaths);
189 
190  // save the hitCountModel to a file
191  hitCountModel->writeModel("hitcount_model_original.geotess");
192 
193  // At this point, this application should be terminated and
194  // application GeoTessBuilder should be executed to build
195  // a new model that has a grid that has been refined in areas of
196  // high hit count. There is a sample GeoTessBuilder properties file in
197  // GeoTessBuilderExamples/tomo2dTest/gridbuilder_refine_model.properties
198  // that illustrates how to build model
199  // hitcount_model_refined.geotess.
200  // See the GeoTess User's Manual for more information about the
201  // GeoTessBuilder application. After GeoTessBulder has been
202  // executed, a new application should be executed that calls
203  // the remaining methods. This example includes everything in
204  // one application, i.e., the method calls below assumes that
205  // GeoTessBuilder has already been executed.
206 
207  // Load the refined hit count model from file. This model
208  // has a grid that is refined in areas of high hit count.
209  // The model attribute is HIT_COUNT. Hit count attribute
210  // values at points that did not exist in the original hit
211  // count model are interpolated values.
212  GeoTessModel* hitCountModelRefined = new GeoTessModel("hitcount_model_refined.geotess");
213 
214  // At this point, we have an original model and a new, refined grid
215  // that has all the grid nodes of the original model but extra grid
216  // nodes that are not in the original model. The next method
217  // generates a new model based on the refined grid, where values for
218  // missing grid nodes are generated by either copying or
219  // interpolating attenuation values from the original model.
220  GeoTessModel* refinedModel = refineModel(model, hitCountModelRefined);
221 
222  refinedModel->writeModel("attenuation_model_refined.geotess");
223 
224  delete model;
225 
226  delete hitCountModel;
227 
228  delete hitCountModelRefined;
229 
230  delete refinedModel;
231 
232  for (int i=0; i<(int)rayPaths.size(); ++i)
233  CPPUtils::delete2DArray<double>(rayPaths[i]);
234  rayPaths.clear();
235 
236  printf("Done.");
237 
238  }
239  catch (const GeoTessException& ex)
240  {
241  cout << endl << ex.emessage << endl;
242  return 1;
243  }
244  catch (...)
245  {
246  cout << endl << "Unidentified error detected " << endl
247  << __FILE__ << " " << __LINE__ << endl;
248  return 2;
249  }
250  return 0;
251 }
252 
253 /**
254  * Generate a starting model for the Tomography2D example program. The model
255  * will have a single attribute (attenuation), and will be a 2D model, i.e.,
256  * there will be no radius associated with the nodes of the model. For this
257  * simple example, the model is populated with a single, constant value of
258  * attenuation, 0.1
259  *
260  * @param gridFile the name of the file containing the GeoTessGrid upon
261  * which the starting model will be based.
262  * @return a pointer to a GeoTessModel
263  */
264 GeoTessModel* startingModel(const string& gridFile)
265 {
266  printf("************************************************************\n");
267  printf("*\n");
268  printf("* startingModel()\n");
269  printf("*\n");
270  printf("************************************************************\n");
271  printf("\n");
272 
273  // Load an existing GeoTessGrid file. Several grid files were
274  // delivered with the GeoTess package and can be found in the
275  // GeoTessModels directory. For this example, a grid consisting of
276  // uniform 4 degree triangles, was specified.
277 
278  // Create a MetaData object in which we can specify information
279  // needed for model contruction.
280  GeoTessMetaData* metaData = new GeoTessMetaData();
281 
282  // specify the ellipsoid that is to be used when interacting with this model.
283  // This call is really unnecessary here because WGS84 is the default,
284  // but other options are possible.
285  metaData->setEarthShape("WGS84");
286 
287  // Specify a description of the model. This information is not
288  // processed in any way by GeoTess. It is carried around for
289  // information purposes.
290  metaData->setDescription("GeoTessModel for example program Tomography2D");
291 
292  // This model will have only one layer, named 'surface'.
293  metaData->setLayerNames("surface");
294 
295  // Specify one attribute: attenuation, with units of 1/km
296  metaData->setAttributes("attenuation", "1/km");
297 
298  // specify the DataType for the data. All attributes, in all
299  // profiles, will have the same data type.
300  metaData->setDataType(GeoTessDataType::FLOAT);
301 
302  // specify the name of the software that is going to generate
303  // the model. This gets stored in the model for future reference.
304  metaData->setModelSoftwareVersion("Tomography2D");
305 
306  // specify the date when the model was generated. This gets
307  // stored in the model for future reference.
308  metaData->setModelGenerationDate(CpuTimer::now());
309 
310  // call a GeoTessModel constructor to build the model. This will
311  // load the grid, and initialize all the data structures to null.
312  // To be useful, we will have to populate the data structures.
313  GeoTessModel* model = new GeoTessModel(gridFile, metaData);
314 
315  float attenuation = 0.1F;
316 
317  // generate some data and store it in the model.
318  for (int vtx = 0; vtx < model->getNVertices(); ++vtx)
319  // create ProfileSurface objects with a constant value of type float.
320  model->setProfile(vtx, &attenuation, 1);
321 
322  printf("%s\n", model->toString().c_str());
323 
324  return model;
325 }
326 
327 /**
328  * Generate 11 ray paths on the surface of the WGS84 ellipsoid. Each ray
329  * path is defined by two unit vector locations, one representing an event,
330  * and the other a station. All of the ray paths generated here have the
331  * same station, ANMO, located near Albuquerque, New Mexico, USA. The first
332  * ray path has zero length (the event is colocated with the station). The
333  * remaining events range in distance from 5 to 50 degrees in distance and 0
334  * to 360 in azimuth from the station.
335  * <p>
336  * There is no requirement in GeoTess that the ray paths be represented this
337  * way, this parameterization was designed for this example program. In
338  * fact, GeoTess has no concept of a ray path at all.
339  *
340  * @return an ArrayList of raypaths. Each ray path consists of two unit
341  * vectors, one for the event and one for the station.
342  */
343 void generateRayPaths(vector<double**>& rayPaths)
344 {
345  printf("************************************************************\n");
346  printf("*\n");
347  printf("* generateRayPaths()\n");
348  printf("*\n");
349  printf("************************************************************\n\n");
350 
351  // specify the ellipsoid that is to be used when converting between geographic
352  // and geocentric coordinates and between depth and radius.
353  EarthShape ellipsoid("WGS84");
354 
355  rayPaths.clear();
356  int nRays = 11;
357 
358  for (int i = 0; i < nRays; ++i)
359  {
360  double** path = CPPUtils::new2DArray<double>(2,3);
361  rayPaths.push_back(path);
362  double* event = path[0];
363  double* station = path[1];
364 
365  station[0] = ANMO[0];
366  station[1] = ANMO[1];
367  station[2] = ANMO[2];
368 
369  // populate event with a unit vector obtained by moving station ANMO
370  // some distance and azimuth specified in radians
371  GeoTessUtils::moveDistAz(station, CPPUtils::toRadians(i*5), CPPUtils::toRadians(i * 36), event);
372  }
373 
374  // the remainder of this method prints out information about the ray
375  // paths.
376 
377  printf(" id event station distance azimuth\n");
378  for (int i = 0; i < nRays; ++i)
379  {
380  double** rayPath = rayPaths[i];
381  double* event = rayPath[0];
382  double* station = rayPath[1];
383 
384  printf("%3d %s %s %10.4f %10.4f\n", i,
385  ellipsoid.getLatLonString(event).c_str(),
386  ellipsoid.getLatLonString(station).c_str(),
387  GeoTessUtils::angleDegrees(station, event),
388  GeoTessUtils::azimuthDegrees(station, event, NaN_DOUBLE)
389  );
390  }
391  printf("\n");
392 }
393 
394 /**
395  * For every ray path, trace the ray through the model. Compute the integral
396  * of the model attribute along the ray path. Also accumulate the 'weight'
397  * associated with each grid node during interpolation of the attribute
398  * values along the ray path.
399  *
400  * <p>
401  * The GeoTess method used to compute the required information assume that
402  * each ray path is a great circle path from event to station. The radii of
403  * the points along the ray path are assumed to coincide with the surface of
404  * the WGS84 ellipsoid.
405  *
406  * <p>
407  * This method doesn't do anything with the results (the integrated value
408  * and the weights). This method merely serves as an example of how to
409  * extract the relevant information from a GeoTessModel. In a real
410  * tomography application, additional code would be required to transfer the
411  * information to tomographic matrices for inversion.
412  *
413  * @param model
414  * @param rayPaths
415  */
416 void integrateRayPaths(GeoTessModel* model, const vector<double**>& rayPaths)
417 {
418  printf("************************************************************\n");
419  printf("*\n");
420  printf("* rayTrace()\n");
421  printf("*\n");
422  printf("************************************************************\n\n");
423 
424  // the index of the attribute that we want to integrate along the ray
425  // paths.
426  int attribute = 0;
427 
428  // approximate point spacing to use for numerical integration.
429  // one tenth of a degree, converted to radians.
430  double pointSpacing = CPPUtils::toRadians(0.1);
431 
432  // the radius of the earth in km. If user wishes to assume a spherical
433  // earth, the radius can be specified here. By specifying a value
434  // <= 0 km, GeoTess will compute local values of earth radius assuming
435  // the WGS84 ellipsoid.
436  double earthRadius = -1;
437 
438  // horizontal interpolation type; either LINEAR or NATURAL_NEIGHBOR
439  const GeoTessInterpolatorType& interpType = GeoTessInterpolatorType::NATURAL_NEIGHBOR;
440 
441  // A note about vertices, nodes and points: Vertices refer to 2D geographic
442  // location of vertices in a 2D grid. In a 3D model there are generally many
443  // nodes associated with each vertex, but 2D models are comprised of a single,
444  // infinitely thin layer, and hence there is only one node per vertex.
445  // So in a 2D model, nodes and vertices are synonymous (not so in 3D models!).
446  // Points refer to nodes that reside inside the currently defined 'active region'.
447  // In a global 2D model, the active region includes all nodes and hence vertices,
448  // nodes and points are all synonymous. However, if the active region is limited
449  // to some subset of the entire globe by specifying a Polygon object, then points
450  // include only those nodes that reside inside the active region.
451 
452  // weights will be a map from a model point index to the weight
453  // ascribed to that point index by the integration points along the ray.
454  // The sum of all the weights will equal the length of the ray path in km.
455  // The keys in the map are point indices, not node indices. When the ray
456  // touches nodes that are outside the current active region, the point
457  // index will be -1. So the weights will always add up to the length of
458  // the ray in km, but some of the weight may be ascribed to a point
459  // with index -1 which will include the weight attributable to all
460  // nodes that are outside the active region.
461  map<int, double> weights;
462 
463  // loop over the ray paths
464  for (int i = 0; i < (int)rayPaths.size(); ++i)
465  {
466  // each ray path is comprised of two unit vectors, one for the event
467  // and one for the station.
468  double** rayPath = rayPaths[i];
469  double* event = rayPath[0];
470  double* station = rayPath[1];
471 
472  // we want a set of weights for each ray path, so we need to clear
473  // the map in between calls to getPathIntegral().
474  weights.clear();
475 
476  // integrate the attribute of interest along the ray path.
477  // Also accumulate the weights ascribed to all the points
478  // 'touched' by the integration points that define the ray path. The
479  // sum of all the weights will equal the length of the ray path in km.
480  double attenuation = model->getPathIntegral2D(attribute,
481  event, station, pointSpacing, earthRadius, interpType, &weights);
482 
483  // the rest of the code in this method prints information about the
484  // ray path and its weights to the screen. In a real tomography
485  // application, we would transfer the information into other data
486  // structures for use in tomography.
487 
488  // print out a bunch of information about the ray, including the
489  // value of attenuation
490  printf("----------------------------------------------------------------------------\n");
491  printf("ray station event distance azimuth attenuation\n");
492  printf("%3d %s %s %10.4f %10.4f %12.5f\n\n", i,
493  model->getEarthShape().getLatLonString(station).c_str(),
494  model->getEarthShape().getLatLonString(event).c_str(),
495  GeoTessUtils::angleDegrees(station, event),
496  GeoTessUtils::azimuthDegrees(station, event, NaN_DOUBLE),
497  attenuation);
498 
499  // print out information about the grid nodes and weights.
500  printf("pointId weight | point lat, lon, distance and azimuth from station\n");
501 
502  double sumWeights = 0;
503 
504  if (weights.size() == 0)
505  printf("No weights because event-station distance = 0\n");
506 
507  map<int, double>::iterator it;
508  for (it=weights.begin(); it != weights.end(); ++it)
509  {
510  int pointIndex = it->first;
511  double weight = it->second;
512 
513  sumWeights += weight;
514 
515  if (pointIndex < 0)
516  {
517  printf("%7d %9.2f | outside polygon\n", pointIndex, weight);
518  }
519  else
520  {
521  const double* gridNode = model->getPointMap()->getPointUnitVector(pointIndex);
522 
523  printf("%7d %9.2f | %s %10.4f %10.4f\n",
524  pointIndex, weight,
525  model->getEarthShape().getLatLonString(gridNode).c_str(),
526  GeoTessUtils::angleDegrees(station, gridNode),
527  GeoTessUtils::azimuthDegrees(station, gridNode, NaN_DOUBLE));
528  }
529  }
530  printf("\n");
531 
532  printf("Sum of weights (length of ray path) = %10.4f km \n\n", sumWeights);
533  }
534 }
535 
536 /**
537  * Find the indices of the model 'points' that are the neighbors of each
538  * model point. In a real tomography application, this information would be
539  * used to apply regularization. Here, the GeoTessGrid is interrogated for
540  * the required information, but nothing is done with it.
541  *
542  * @param model
543  */
544 void regularization(GeoTessModel* model)
545 {
546  printf("************************************************************\n");
547  printf("*\n");
548  printf("* regularization()\n");
549  printf("*\n");
550  printf("************************************************************\n\n");
551 
552  // tessellaltion index is zero because 2D models use grids that consist
553  // of only one multi-level tessellation.
554  int tessId = 0;
555 
556  // find the index of the last level in the multi-level tessellation.
557  int level = model->getGrid().getLastLevel(tessId);
558 
559  // order specifies the maximum number of triangle edges that are to be
560  // traversed when searching for neighbors. Users are invited to try
561  // higher values to see what happens.
562  int order = 1;
563 
564  for (int pointIndex = 0; pointIndex < model->getNPoints(); ++pointIndex)
565  {
566  // for 2D models, pointIndex and vertexId will be equal, except if
567  // a polygon is used to down sample the set of active nodes.
568  // For a discussion of the difference between points and vertices,
569  // see the User's Manual.
570  int vertexId = model->getPointMap()->getVertexIndex(pointIndex);
571 
572  // get the unit vector of the current vertex
573  const double* vertex = model->getGrid().getVertex(vertexId);
574 
575  // find the indices of the vertexes that are connected to the
576  // current vertex by a single triangle edge
577  set<int> neighbors;
578  model->getGrid().getVertexNeighbors(tessId, level, vertexId, order, neighbors);
579 
580  // only print information about a subset of the vertices
581  if (pointIndex % 100 == 0)
582  {
583  printf("--------------------------------------------------------\n");
584  printf("point %d, vertex %d, lat,lon %s:\n\n",
585  pointIndex, vertexId,
586  model->getEarthShape().getLatLonString(vertex).c_str());
587 
588  printf("neighbor neighbor distance azimuth\n");
589  printf("vertexid pointid (deg) (deg)\n");
590  set<int>::iterator it;
591  for (it=neighbors.begin(); it != neighbors.end(); ++it)
592  {
593  int neighborVertexId = *it;
594 
595  // neighbor is the vertexId of a model vertex that is
596  // a neighbor of the current vertex.
597  const double* neighborVertex = model->getGrid().getVertex(neighborVertexId);
598 
599  // find the pointIndex that corresponds to neighborVertexId
600  int neighborPointId = model->getPointMap()->getPointIndex(
601  neighborVertexId, 0, 0);
602 
603  printf("%8d %8d %8.2f %8.2f\n", neighborVertexId,
604  neighborPointId,
605  GeoTessUtils::angleDegrees(vertex, neighborVertex),
606  GeoTessUtils::azimuthDegrees(vertex, neighborVertex, NaN_DOUBLE));
607  }
608  printf("\n");
609  }
610  }
611 }
612 
613 /**
614  * Given a model and an array of attribute changes, apply the changes to the
615  * model.
616  *
617  * @param model
618  * @param attributeIndex
619  * @param attributeChanges
620  */
621 void applyAttributeChanges(GeoTessModel* model,
622  const int& attributeIndex, const vector<float>& attributeChanges)
623 {
624  for (int pointIndex = 0; pointIndex < model->getNPoints(); ++pointIndex)
625  model->setValue(pointIndex, attributeIndex,
626  model->getValueFloat(pointIndex, attributeIndex)
627  + attributeChanges[pointIndex]);
628 }
629 
630 /**
631  * Build a new GeoTessModel with the same grid nodes as the input model.
632  * There will a single attribute value of type int assigned to each grid
633  * node. The name of the attribute is HIT_COUNT and it is unitless.
634  *
635  * @param inputModel
636  * @param rayPaths
637  * @return
638  */
639 GeoTessModel* hitCount(GeoTessModel* inputModel, const vector<double**>& rayPaths)
640 {
641  printf("************************************************************\n");
642  printf("*\n");
643  printf("* hitCount()\n");
644  printf("*\n");
645  printf("************************************************************\n");
646 
647  // Create a MetaData object in which we can specify information
648  // needed for model construction.
649  GeoTessMetaData* metaData = new GeoTessMetaData();
650 
651  // specify the ellipsoid that is to be used when interacting with this model.
652  // This call is really unnecessary here because WGS84 is the default,
653  // but other options are possible.
654  metaData->setEarthShape("WGS84");
655 
656  // Specify a description of the model.
657  metaData->setDescription("GeoTessModel of hit count for example program Tomography2D");
658 
659  // This model will have only one layer, named 'surface'.
660  metaData->setLayerNames("surface");
661 
662  // Specify one unitless attribute
663  metaData->setAttributes("HIT_COUNT", "count");
664 
665  // specify the DataType for the data.
666  metaData->setDataType(GeoTessDataType::INT);
667 
668  // specify the name of the software that is going to generate
669  // the model. This gets stored in the model for future reference.
670  metaData->setModelSoftwareVersion("Tomography2D hitCount()");
671 
672  // specify the date when the model was generated. This gets
673  // stored in the model for future reference.
674  metaData->setModelGenerationDate(CpuTimer::now());
675 
676  // call a GeoTessModel constructor to build the model. Use the same grid
677  // as the one used by the input model.
678  GeoTessModel* hitCountModel = new GeoTessModel(&inputModel->getGrid(), metaData);
679 
680  int count = 0;
681 
682  // initialize the data value (hit count) of every node with int value zero.
683  for (int vertexId = 0; vertexId < hitCountModel->getNVertices(); ++vertexId)
684  hitCountModel->setProfile(vertexId, &count, 1);
685 
686  // if the inputModel had its active nodes specified with a Polygon,
687  // then apply the same polygon to the hit count model.
688  hitCountModel->setActiveRegion(inputModel->getPointMap()->getPolygon());
689 
690  // approximate point spacing to use to define the ray path
691  double pointSpacing = CPPUtils::toRadians(0.1);
692 
693  // horizontal interpolation type; either LINEAR or NATURAL_NEIGHBOR
694  const GeoTessInterpolatorType* interpType = &GeoTessInterpolatorType::LINEAR;
695 
696  // model has only one attribute with index 0.
697  int attributeIndex = 0;
698 
699  // weights will be a map from a model point index to the weight
700  // ascribed to that point index by the integration points along the ray.
701  // The sum of all the weights will equal the length of the ray path in km.
702  map<int, double> weights;
703 
704  // loop over the ray paths
705  for (int i = 0; i < (int)rayPaths.size(); ++i)
706  {
707  // each ray path is comprised of two unit vectors, one for the event
708  // and one for the station.
709  double** rayPath = rayPaths[i];
710  double* event = rayPath[0];
711  double* station = rayPath[1];
712 
713  // we want a set of weights for each ray path, so we need to clear
714  // the map in between calls to getPathIntegral().
715  weights.clear();
716 
717  // integrate points along the ray path. We don't care about the
718  // integral, we only want the weights assigned to each model point.
719  inputModel->getPathIntegral2D(-1, event, station, pointSpacing, -1., *interpType, &weights);
720 
721  map<int, double>::iterator it;
722  for (it=weights.begin(); it != weights.end(); ++it)
723  {
724  int pointIndex = it->first;
725  if (pointIndex >= 0)
726  {
727  int hitcount = hitCountModel->getValueInt(pointIndex, attributeIndex);
728  ++hitcount;
729  hitCountModel->setValue(pointIndex, attributeIndex, hitcount);
730  }
731  }
732  }
733 
734  // hitCountModel has been populated with the hit count of every vertex.
735  // print information about the points that have hit count > 0
736  printf(" point vertex lat lon distance hitcount\n");
737  for (int pointIndex = 0; pointIndex < hitCountModel->getNPoints(); ++pointIndex)
738  if (hitCountModel->getValueInt(pointIndex, 0) > 0)
739  {
740  const double* u = hitCountModel->getPointMap()->getPointUnitVector( pointIndex);
741 
742  printf("%8d %8d %s %9.2f %6d\n",
743  pointIndex,
744  hitCountModel->getPointMap()->getVertexIndex(pointIndex),
745  hitCountModel->getPointMap()->getPointLatLonString(pointIndex).c_str(),
746  GeoTessUtils::angleDegrees(ANMO, u),
747  hitCountModel->getValueInt(pointIndex, 0));
748  }
749  printf("\n");
750 
751  return hitCountModel;
752 }
753 
754 /**
755  * At this point, we have a new GeoTessModel that has been refined to have
756  * higher resolution (more vertices) than the old model. But the new model has
757  * attribute value HIT_COUNT, not ATTENUATION. We need to make a
758  * new model using the refined grid from hitCountModelRefined but using data
759  * obtained from the old model. Where the old model has a vertex that is
760  * colocated with the vertex in the new model, the data from the old model is
761  * copied to the new model. For vertices in the new model that do not have
762  * colocated vertices in the old model, data will be interpolated from the
763  * data in the old model.
764  *
765  * @param oldModel
766  * @param hitCountModelRefined
767  * @return
768  */
769 GeoTessModel* refineModel(GeoTessModel* oldModel, GeoTessModel* hitCountModelRefined)
770 {
771  printf("************************************************************\n");
772  printf("*\n");
773  printf("* refineModel()\n");
774  printf("*\n");
775  printf("************************************************************\n");
776 
777  // get a reference to the refined grid in hitCountModelRefined
778  GeoTessGrid refinedGrid = hitCountModelRefined->getGrid();
779 
780  // make a new model with the refined grid and a reference to the meta
781  // data from the old model.
782  GeoTessModel* newModel = new GeoTessModel(
783  &hitCountModelRefined->getGrid(), &oldModel->getMetaData());
784 
785  // we will need to interpolate data from the old model at vertices in
786  // the new model that do not exist in the old model. For that purpose,
787  // we will need a GeoTessPosition object obtained from the old model.
788  GeoTessPosition* pos = oldModel->getPosition(GeoTessInterpolatorType::LINEAR);
789 
790  // both old and new models are 2D models and hence have only a single layer
791  int layerIndex = 0;
792  // in 2D models, the one-and-only layer has only a single node.
793  int nodeIndex = 0;
794  // both old and new models have only one attribute, which has index 0.
795  int attributeIndex = 0;
796  float attenuation;
797 
798  // loop over every vertex in the new model and populate it with data.
799  for (int vtx = 0; vtx < newModel->getNVertices(); ++vtx)
800  {
801  // find the unit vector of the vertex from the new model.
802  // There may or may not be a vertex in the old model that is
803  // colocated with this unit vector.
804  const double* u = refinedGrid.getVertex(vtx);
805 
806  // request the index of the vertex in the old model that is
807  // colocated with the new vertex. If the old model has no colocated
808  // vertex, then oldVtx will be -1.
809  int oldVtx = oldModel->getGrid().getVertexIndex(u);
810 
811  if (oldVtx >= 0)
812  // retrieve attenuation value from the old model directly
813  attenuation = oldModel->getValueFloat(oldVtx, layerIndex, nodeIndex, attributeIndex);
814  else
815  {
816  // interpolate a new attenuation value from values in the old model.
817  pos->set(layerIndex, u, 6371.);
818  attenuation = (float)pos->getValue(0);
819  }
820 
821  // set the data in the new model.
822  newModel->setProfile(vtx, &attenuation, 1);
823  }
824 
825  delete pos;
826 
827  return newModel;
828 }
geotess
Definition: AK135Model.h:55
regularization
void regularization(GeoTessModel *model)
Definition: Tomography2D.cc:544
main
int main(int argc, char **argv)
Definition: Tomography2D.cc:90
applyAttributeChanges
void applyAttributeChanges(GeoTessModel *model, const int &attributeIndex, const vector< float > &attributeChanges)
Definition: Tomography2D.cc:621
refineModel
GeoTessModel * refineModel(GeoTessModel *oldModel, GeoTessModel *hitCountModelRefined)
Definition: Tomography2D.cc:769
hitCount
GeoTessModel * hitCount(GeoTessModel *inputModel, const vector< double ** > &rayPaths)
Definition: Tomography2D.cc:639
integrateRayPaths
void integrateRayPaths(GeoTessModel *model, const vector< double ** > &rayPaths)
Definition: Tomography2D.cc:416
startingModel
GeoTessModel * startingModel(const string &gridFile)
Definition: Tomography2D.cc:264
generateRayPaths
void generateRayPaths(vector< double ** > &rayPaths)
Definition: Tomography2D.cc:343
ANMO
static double ANMO[3]
Definition: Tomography2D.cc:50