GeoTessCExamples  2.0
Tomography2D.c
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 "Tomography2D.h"
37 
38 /**
39  * This application illustrates how to use features available in GeoTessJava to
40  * execute tomography on a 2D model. This application does not implement
41  * tomography but merely illustrates how to call methods in GeoTessJava that one
42  * would likely need to perform tomography.
43  *
44  * <p>In order to run properly, this program must be run from directory
45  * GeoTessBuilderExamples/tomo2dTest.
46  *
47  * <p>
48  * This application illustrates the following tasks:
49  * <ol>
50  * <li>Generate 11 great circle ray paths along the surface of the Earth.
51  * <li>Generate a 2D, global starting model consisting of values of attenuation
52  * as a function of geographic position on the globe.
53  * <li>Limit the application of tomography to a region of the Earth in North
54  * America. This limitation is optional.
55  * <li>Trace rays through the starting model, calculating the path integral of
56  * the attenuation along the ray path and the weights (data kernels) of all the
57  * grid nodes attributable to interpolation of points on ray paths.
58  * <li>Call methods in GeoTessJava to identify the neighbors of a specified
59  * node. These methods are needed to apply regularization of the tomography
60  * matrix.
61  * <li>Apply changes in model attribute values computed by tomographic
62  * inversion.
63  * <li>Compute a new GeoTessModel whose attribute values are the number of times
64  * each grid node was 'touched' by one of the ray paths (hit count).
65  * <li>Execute application GeoTessBuilder to generate a new grid that is more
66  * refined in areas of high hit count.
67  * <li>Generate a new model based on the new, refined grid generated with
68  * GeoTessBuilder but containing attenuation values copied or interpolated from
69  * the original model.
70  * </ol>
71  *
72  * @author Sandy Ballard
73  *
74  */
75 int main(int argc, char** argv)
76 {
77  int i;
78 
79  GeoTessModelC* model;
80  GeoTessModelC* hitCountModel;
81  GeoTessModelC* hitCountModelRefined;
82  GeoTessModelC* refinedModel;
83  EarthShapeC* ellipsoid;
84  PolygonC* polygon;
85 
86  double*** rayPaths=NULL;
87  int nRayPaths=0;
88 
89  int nChanges=0;
90  float* attributeChanges=NULL;
91 
92  // specify the ellipsoid to use for converting betweenn geographic and gecentric
93  // coordinates and between radius and depth.
94  ellipsoid = earthshape_create("WGS84");
95 
96  // seismic station ANMO near Albuquerque, New Mexico, USA.
97  // Latitude and longitude of the station are converted to an
98  // earth centered unit vector and stored in variable ANMO
99  earthshape_getVectorDegrees(ellipsoid, 34.9462, -106.4567, ANMO);
100 
101  // Generate a starting model. In this example, the starting model is
102  // very simple, consisting of constant values of attenuation at each
103  // node of the grid. Real applications could start from some other
104  // starting model loaded from a file.
105  //
106  // If an error message appears indicating that the file could not be
107  // found, it is likely because the program was not executed from
108  // directory GeoTessBuilderExamples/tomo2dTest
109  model = startingModel("geotess_grid_04000.geotess");
110 
111  // generate 11 ray paths that emanate from station ANMO. They
112  // range in length from 0 to 50 degrees.
113  generateRayPaths(&rayPaths, &nRayPaths);
114 
115  // We will limit the active nodes in the model using a Polygon
116  // object. All nodes will be used for ray tracing but only nodes
117  // that reside inside the polygon will be modified by tomography.
118  // Nodes inside the polygon are referred to as 'points'.
119  //
120  // The polygon that we will define is a small circle with radius
121  // 38 degrees centered on seismic station ANMO. For information
122  // on other ways to define Polygons, see the User's Manual or
123  // GeoTess code documentation. If we wanted to execute a global
124  // tomography model, we would not apply this step (i.e., skip
125  // the next 3 statements).
126  polygon = geopoly_create2(ANMO, 38*dtr, 100);
127 
128  geomodel_setActiveRegionPoly(model, polygon);
129 
130  // free the polygon wrapper. A reference to the c++ Polygon
131  // is still retained by the model and will be deleted by the
132  // model in its destructor.
133  geopoly_destroy(polygon);
134 
135  // Trace rays though the model and extract integrated attribute
136  // values along the ray path and interpolation coefficient 'weights'
137  // associated with each ray path. In a real tomography application,
138  // these values would be used to populate the vector of residuals
139  // and the tomography A matrix of data kernels. In this example,
140  // they are simply extracted from the model and some are printed
141  // to the screen.
142  integrateRayPaths(model, rayPaths, nRayPaths);
143 
144  // Call a method that illustrates how to find the indices of model
145  // points that are neighbors of a specified model point. These are
146  // often used in tomography to perform regularization or smoothing.
147  regularization(model);
148 
149  // At this point, a real tomography application would actually
150  // perform tomographic inversion, resulting in a vector containing
151  // changes in attenuation that should be applied at each model
152  // point. For this example, we will simply specify these changes
153  // and apply them. There will an entry for every point in the
154  // model (points include only those nodes that are inside the
155  // polygon previously defined).
156  nChanges = geomodel_getNPoints(model);
157  attributeChanges = (float*)malloc(nChanges * sizeof(float));
158  for (i=0; i<nChanges; ++i)
159  attributeChanges[i] = 0.01F;
160 
161  // apply the attenuation changes.
162  applyAttributeChanges(model, 0, attributeChanges, nChanges);
163 
164  free(attributeChanges);
165 
166  geomodel_writeModel(model, "attenuation_model.geotess");
167 
168  // Now we will refine the model
169  // in regions of high hit count before executing the next
170  // iteration of tomography. In a real tomography application
171  // this involves several steps which need to be implemented in
172  // separate applications.
173  // First, we build a new GeoTessModel where the attribute
174  // is HIT_COUNT, write that model to a file and
175  // terminate execution. Then we use the GeoTessBuilder
176  // application to generate a refined grid. Then, in
177  // a new application, we build a new GeoTessModel based
178  // on the refined grid, which contains attenuation values
179  // that are either copied or interpolated from the original
180  // model.
181 
182  // Given a current model and a collection of ray paths, build
183  // a new model where the attribute is HIT_COUNT.
184  hitCountModel = hitCount(model, rayPaths, nRayPaths);
185 
186  // save the hitCountModel to a file
187  geomodel_writeModel(hitCountModel, "hitcount_model_original.geotess");
188 
189  // free all the models we created.
190  geomodel_destroy(model);
191  geomodel_destroy(hitCountModel);
192  earthshape_destroy(ellipsoid);
193 
194  // At this point, this application should be terminated and
195  // application GeoTessBuilder should be executed to build
196  // a new model that has a grid that has been refined in areas of
197  // high hit count. There is a sample GeoTessBuilder properties file in
198  // GeoTessBuilderExamples/tomo2dTest/gridbuilder_refine_model.properties
199  // that illustrates how to build model 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  // reload the last attenuation model.
208  model = geomodel_create("attenuation_model.geotess");
209 
210  // Load the refined hit count model from file. This model
211  // has a grid that is refined in areas of high hit count.
212  // The model attribute is HIT_COUNT. Hit count attribute
213  // values at points that did not exist in the original hit
214  // count model are interpolated values.
215  hitCountModelRefined = geomodel_create("hitcount_model_refined.geotess");
216 
217  // At this point, we have an original model and a new, refined grid
218  // that has all the grid nodes of the original model but extra grid
219  // nodes that are not in the original model. The next method
220  // generates a new model based on the refined grid, where values for
221  // missing grid nodes are generated by either copying or
222  // interpolating attenuation values from the original model.
223  refinedModel = refineModel(model, hitCountModelRefined);
224 
225  // write the refined attenuation model out to a file.
226  geomodel_writeModel(refinedModel, "attenuation_model_refined.geotess");
227 
228  // free all the models we created.
229  geomodel_destroy(model);
230  geomodel_destroy(hitCountModelRefined);
231  geomodel_destroy(refinedModel);
232 
233  // free the ray paths
234  for (i=0; i<nRayPaths; ++i)
235  {
236  free(rayPaths[i][0]);
237  free(rayPaths[i][1]);
238  free(rayPaths[i]);
239  }
240  free(rayPaths);
241 
242  // check for errors and abort if any have occurred.
243  errorCheck();
244 
245  printf("Tomography2D completed successfully.\n");
246  return 0;
247 }
248 
249 /**
250  * Generate a starting model for the Tomography2D example program. The model
251  * will have a single attribute (attenuation), and will be a 2D model, i.e.,
252  * there will be no radius associated with the nodes of the model. For this
253  * simple example, the model is populated with a single, constant value of
254  * attenuation, 0.1
255  *
256  * @param gridFile the name of the file containing the GeoTessGrid upon
257  * which the starting model will be based.
258  * @return a GeoTessModelC
259  */
260 GeoTessModelC* startingModel(char* gridFile)
261 {
262  GeoTessMetaDataC* metaData;
263  GeoTessModelC* model;
264  float attenuation;
265  int i;
266  char* str;
267 
268  printf("************************************************************\n");
269  printf("*\n");
270  printf("* startingModel()\n");
271  printf("*\n");
272  printf("************************************************************\n");
273  printf("\n");
274 
275  // If an error message appears indicating that file geotess_grid_04000.geotes
276  // could not be found, it is likely because the program was not executed from
277  // directory GeoTessBuilderExamples/tomo2dTest
278 
279  // construct a GeoTessMetaDataC object, which is basically a wrapper
280  // around a C++ GeoTessMetaData object.
281  metaData = geometadata_create();
282 
283  // specify the ellipsoid to use for converting between geographic and geocentric
284  // coordinates and between radius and depth. This is really not necessary here since
285  // WGS84 is the default, but other options are available.
286  geometadata_setEarthShape(metaData, "WGS84");
287 
288  geometadata_setDescription(metaData, "GeoTessModel for example program Tomography2D\n");
289 
290  // Specify a list of layer names. A model could have many layers,
291  // e.g., ("core", "mantle", "crust"), specified in order of increasing
292  // radius. This simple example has only one layer.
293  geometadata_setLayerNames1(metaData, "surface");
294 
295  // specify the names of the attributes and the units of the attributes in
296  // two String arrays. This model only includes one attribute.
297  geometadata_setAttributes1(metaData, "Distance", "degrees");
298 
299  // specify the DataType for the data. All attributes, in all profiles, will
300  // have the same data type.
301  geometadata_setDataType1(metaData, FLOAT);
302 
303  // specify the name of the software that is going to generate
304  // the model. This gets stored in the model for future reference.
305 
306  geometadata_setModelSoftwareVersion(metaData, "GeoTessCExamples.Tomography2D 1.0.0");
307 
308  // specify the date when the model was generated. This gets
309  // stored in the model for future reference.
310  str = cpu_now();
311  geometadata_setModelGenerationDate(metaData, str);
312  free(str);
313 
314  // call a GeoTessModel constructor to build the model. This will build the
315  // grid, and initialize all the data structures to null. To be useful, we
316  // will have to populate the data structures.
317  model = geomodel_create3(gridFile, metaData);
318 
319  // check for errors and abort if any have occurred.
320  errorCheck();
321 
322  // Delete the GeoTessMetaDataC object, which is a wrapper around a C++ GeoTessMetaData
323  // object. Ownership of the underlying C++ GeoTessMetaData object has been transfered
324  // to the newly created GeoTessModel object. The model will delete the C++ GeoTessMetaData
325  // object when it is done with it. We need to delete the memory allocated to the
326  // GeoTessMetaDataC wrapper, but not the wrapped C++ GeoTessMetaData object.
327  // Hence the second parameter is false.
328  geometadata_destroy(metaData);
329 
330  attenuation = 0.1F;
331 
332  // generate some data and store it in the model.
333  for (i = 0; i < geomodel_getNVertices(model); ++i)
334  geomodel_setProfSurfFloats(model, i, &attenuation, 1);
335 
336  //printf("%s", geomodel_toString(model));
337 
338  // check for errors and abort if any have occurred.
339  errorCheck();
340 
341  return model;
342 }
343 
344 /**
345  * Generate 11 ray paths on the surface of the WGS84 ellipsoid. Each ray
346  * path is defined by two unit vector locations, one representing an event,
347  * and the other a station. All of the ray paths generated here have the
348  * same station, ANMO, located near Albuquerque, New Mexico, USA. The first
349  * ray path has zero length (the event is colocated with the station). The
350  * remaining events range in distance from 5 to 50 degrees in distance and 0
351  * to 360 in azimuth from the station.
352  * <p>
353  * There is no requirement in GeoTess that the ray paths be represented this
354  * way, this parameterization was designed for this example program. In
355  * fact, GeoTess has no concept of a ray path at all.
356  *
357  * @param rayPaths a pointer to a 3D array containing the rayPaths.
358  * Each ray path consists of two unit vectors, one for the event and one for
359  * the station. On input, this pointer should be null.
360  * @param nRays the number of rays created.
361  */
362 void generateRayPaths(double**** rayPaths, int* nRays)
363 {
364  int i;
365  double** rayPath;
366  double* event;
367  double* station;
368  char* str1;
369  char* str2;
370  EarthShapeC* wgs84;
371 
372  printf("************************************************************\n");
373  printf("*\n");
374  printf("* generateRayPaths()\n");
375  printf("*\n");
376  printf("************************************************************\n\n");
377 
378  wgs84 = earthshape_create("WGS84");
379 
380  *nRays = 11;
381 
382  // Generate 11 great circle ray paths for use in this example. In
383  // a real application, these ray paths would surely be loaded from
384  // a file or some other source.
385  *rayPaths = (double***)malloc((*nRays) * sizeof(double**));
386  for (i=0; i<*nRays; ++i)
387  {
388  rayPath = (*rayPaths)[i] = (double**)malloc(2 * sizeof(double*));
389  event = rayPath[0] = (double*)malloc(3*sizeof(double));
390  station = rayPath[1] = (double*)malloc(3*sizeof(double));
391 
392  station[0] = ANMO[0];
393  station[1] = ANMO[1];
394  station[2] = ANMO[2];
395 
396  // generate the event location my moving the station a specified
397  // distance and azimuth.
398  geoutils_moveDistAz(station, i*5*dtr, i*36*dtr, event);
399  }
400 
401  // the remainder of this method prints out information about the ray paths.
402  printf(" id event station distance azimuth\n");
403  for (i = 0; i < *nRays; ++i)
404  {
405  rayPath = (*rayPaths)[i];
406  event = rayPath[0];
407  station = rayPath[1];
408 
409  str1 = earthshape_getLatLonString(wgs84, event);
410  str2 = earthshape_getLatLonString(wgs84, station);
411  printf("%3d %s %s %10.4f %10.4f\n", i,
412  str1, str2, geoutils_angleDegrees(station, event),
413  geoutils_azimuthDegrees(station, event, -999999.0));
414  free(str1);
415  free(str2);
416  }
417  printf("\n");
418 
419  earthshape_destroy(wgs84);
420 }
421 
422 /**
423  * For every ray path, trace the ray through the model. Compute the integral
424  * of the model attribute along the ray path. Also accumulate the 'weight'
425  * associated with each grid node during interpolation of the attribute
426  * values along the ray path.
427  *
428  * <p>
429  * The GeoTess method used to compute the required information assume that
430  * each ray path is a great circle path from event to station. The radii of
431  * the points along the ray path are assumed to coincide with the surface of
432  * the WGS84 ellipsoid.
433  *
434  * <p>
435  * This method doesn't do anything with the results (the integrated value
436  * and the weights). This method merely serves as an example of how to
437  * extract the relevant information from a GeoTessModel. In a real
438  * tomography application, additional code would be required to transfer the
439  * information to tomographic matrices for inversion.
440  *
441  * @param model
442  * @param rayPaths
443  * @param nRays
444  */
445 void integrateRayPaths(GeoTessModelC* model, double*** rayPaths, int nRays)
446 {
447  int i,j;
448 
449  // the index of the attribute that we want to integrate along the ray
450  // paths.
451  int attribute = 0;
452 
453  // approximate point spacing to use for numerical integration.
454  // one tenth of a degree, converted to radians. The actual point
455  // spacing will be slightly less than this so that there will be
456  // an integral number of equally spaced points along the the path.
457  double pointSpacing = 0.1*rtd;
458 
459  // the radius of the earth in km. If user wishes to assume a spherical
460  // earth, the radius can be specified here. By specifying a value
461  // <= 0 km, GeoTess will compute local values of earth radius assuming
462  // the WGS84 ellipsoid.
463  double earthRadius = -1;
464 
465  // horizontal interpolation type; either LINEAR or NATURAL_NEIGHBOR
466  InterpolatorTypeC interpType = NATURAL_NEIGHBOR;
467 
468  // Initialize these arrays to NULL. Memory will be allocated for them
469  // in function geomodel_getPathIntegral2DW() as needed.
470  int allocatedSize=0;
471  int* pointIndices=NULL;
472  double* weights=NULL;
473 
474  int nWeights=0;
475 
476  PointMapC* pointMap = geomodel_getPointMap(model);
477 
478  EarthShapeC* ellipsoid = geomodel_getEarthShape(model);
479 
480  double** rayPath;
481  double* event;
482  double* station;
483  double* vertex;
484  char* str1;
485  char* str2;
486 
487  double attenuation, sumWeights;
488 
489  printf("************************************************************\n");
490  printf("*\n");
491  printf("* rayTrace()\n");
492  printf("*\n");
493  printf("************************************************************\n\n");
494 
495  // A note about vertices, nodes and points: Vertices refer to 2D geographic
496  // location of vertices in a 2D grid. In a 3D model there are generally many
497  // nodes associated with each vertex, but 2D models are comprised of a single,
498  // infinitely thin layer, and hence there is only one node per vertex.
499  // So in a 2D model, nodes and vertices are synonymous (not so in 3D models!).
500  // Points refer to nodes that reside inside the currently defined 'active region'.
501  // In a global 2D model, the active region includes all nodes and hence vertices,
502  // nodes and points are all synonymous. However, if the active region is limited
503  // to some subset of the entire globe by specifying a Polygon object, then points
504  // include only those nodes that reside inside the active region.
505 
506  // weights will be a map from a model point index to the weight
507  // ascribed to that point index by the integration points along the ray.
508  // The sum of all the weights will equal the length of the ray path in km.
509  // The keys in the map are point indices, not node indices. When the ray
510  // touches nodes that are outside the current active region, the point
511  // index will be -1. So the weights will always add up to the length of
512  // the ray in km, but some of the weight may be ascribed to a point
513  // with index -1 which will include the weight attributable to all
514  // nodes that are outside the active region.
515  for (i = 0; i < nRays; ++i)
516  {
517  // each ray path is comprised of two unit vectors, one for the event
518  // and one for the station.
519  rayPath = rayPaths[i];
520  event = rayPath[0];
521  station = rayPath[1];
522 
523  // integrate the attribute of interest along the ray path.
524  // Also accumulate the weights ascribed to all the grid nodes
525  // 'touched' by the integration points that define the ray path.
526  attenuation = geomodel_getPathIntegral2DW(model, attribute,
527  event, station, pointSpacing, earthRadius, LINEAR,
528  &pointIndices, &weights, &allocatedSize, &nWeights);
529 
530  // check for errors and abort if any have occurred. Omit in production code.
531  errorCheck();
532 
533  // the rest of the code in this method prints information about the
534  // ray path and its weights to the screen. In a real tomography
535  // application, we would transfer the information into other data
536  // structures for use in tomography.
537 
538  // print out a bunch of information about the ray, including the value of attenuation.
539  // Since attenuation in the model is a constant value of 0.1, the path integrated
540  // value of attenuation should equal 0.1 * the length of the ray in km.
541  str1 = earthshape_getLatLonString(ellipsoid, station);
542  str2 = earthshape_getLatLonString(ellipsoid, event);
543  printf("----------------------------------------------------------------------------\n");
544  printf("ray station event distance azimuth attenuation\n");
545  printf("%3d %s %s %10.4f %10.4f %12.5f\n\n", i, str1, str2,
546  geoutils_angleDegrees(station, event),
547  geoutils_azimuthDegrees(station, event, -999.0),
548  attenuation);
549  free(str1);
550  free(str2);
551 
552  // print out information about the grid nodes and weights.
553  printf("pointId weight | point lat, lon, distance and azimuth from station\n");
554 
555  sumWeights = 0;
556 
557  if (nWeights == 0)
558  printf("No weights because event-station distance = 0\n");
559 
560  for (j=0; j<nWeights; ++j)
561  {
562  sumWeights += weights[j];
563 
564  if (pointIndices[j] < 0)
565  {
566  printf("%7d %9.2f | outside polygon\n", pointIndices[j], weights[j]);
567  }
568  else
569  {
570  vertex = geopoint_getPointUnitVector(pointMap, pointIndices[j]);
571 
572  str1 = earthshape_getLatLonString(ellipsoid, vertex);
573  printf("%7d %9.2f | %s %10.4f %10.4f\n",
574  pointIndices[j], weights[j], str1,
575  geoutils_angleDegrees(station, vertex),
576  geoutils_azimuthDegrees(station, vertex, -999.));
577  free(str1);
578  }
579  }
580  printf("\n");
581 
582  // the sum of the weights should equal the length of the ray path in km.
583  printf("Sum of weights (length of ray path) = %10.4f km \n\n", sumWeights);
584 
585  // check for errors and abort if any have occurred.
586  errorCheck();
587 }
588 
589  // free allocated memory
590  if (pointIndices) free(pointIndices);
591  if (weights) free(weights);
592 
593  // free the wrapper around the point map
594  geopoint_destroy(pointMap);
595  earthshape_destroy(ellipsoid);
596 }
597 
598 /**
599  * Find the indices of the model 'points' that are the neighbors of each
600  * model point. In a real tomography application, this information would be
601  * used to apply regularization. Here, the GeoTessGrid is interrogated for
602  * the required information, but nothing is done with it.
603  *
604  * @param model
605  */
606 void regularization(GeoTessModelC* model)
607 {
608  int i;
609 
610  GeoTessGridC* grid = geomodel_getGrid(model);
611  PointMapC* pointMap = geomodel_getPointMap(model);
612  EarthShapeC* ellipsoid = geomodel_getEarthShape(model);
613 
614  // tessellaltion index is zero because 2D models use grids that consist
615  // of only one multi-level tessellation.
616  int tessId = 0;
617 
618  // find the index of the last level in the multi-level tessellation.
619  int level = geogrid_getNLevelsTess(grid, tessId)-1;
620 
621  // order specifies the maximum number of triangle edges that are to be
622  // traversed when searching for neighbors. Users are invited to try
623  // higher values to see what happens.
624  int order = 1;
625 
626  int pointIndex, vertexIndex;
627  double* vertex;
628  double* neighborVertex;
629  int neighborPointId;
630 
631  int* neighbors=NULL;
632  int nNeighbors;
633  int allocatedSize=0;
634  char* tmp;
635 
636  printf("************************************************************\n");
637  printf("*\n");
638  printf("* regularization()\n");
639  printf("*\n");
640  printf("************************************************************\n\n");
641 
642  for (pointIndex = 0; pointIndex < geopoint_size(pointMap); ++pointIndex)
643  {
644  // for 2D models, pointIndex and vertexId will be equal, except if
645  // a polygon is used to down sample the set of active nodes.
646  // For a discussion of the difference between points and vertices,
647  // see the User's Manual. We are looping over points but need
648  // to know the vertexIndex for this pointIndex.
649  vertexIndex = geopoint_getVertexIndex(pointMap, pointIndex);
650 
651  // get the unit vector of the current vertex
652  vertex = geogrid_getVertex(grid, vertexIndex);
653 
654  // call function to retrieve the neighbors of the current vertex
655  geogrid_getVertexNeighborsWithOrder(grid, tessId, level, vertexIndex, order,
656  &neighbors, &nNeighbors, &allocatedSize);
657 
658  // check for errors and abort if any have occurred.
659  errorCheck();
660 
661  // only print information about a subset of the vertices
662  if (pointIndex % 100 == 0)
663  {
664  printf("--------------------------------------------------------\n");
665  tmp = earthshape_getLatLonString(ellipsoid, vertex);
666  printf("point %d, vertex %d, lat,lon %s:\n\n",
667  pointIndex, vertexIndex, tmp);
668  free(tmp);
669 
670  printf("neighbor neighbor distance azimuth\n");
671  printf("vertexid pointid (deg) (deg)\n");
672  for (i=0; i<nNeighbors; ++i)
673  {
674  // neighbor is the vertexIndex of a model vertex that is
675  // a neighbor of the current vertex. Find the corresponding
676  // unit vector.
677  neighborVertex = geogrid_getVertex(grid, neighbors[i]);
678 
679  // find the pointIndex that corresponds to neighbors[i]
680  neighborPointId = geopoint_getPointIndex(pointMap, neighbors[i], 0, 0);
681 
682  printf("%8d %8d %8.2f %8.2f\n", neighbors[i], neighborPointId,
683  geoutils_angleDegrees(vertex, neighborVertex),
684  geoutils_azimuthDegrees(vertex, neighborVertex, -999.));
685  }
686  printf("\n");
687  }
688  // check for errors and abort if any have occurred.
689  errorCheck();
690 
691  }
692 
693  if (neighbors) free(neighbors);
694 
695  geogrid_destroy(grid);
696  geopoint_destroy(pointMap);
697  earthshape_destroy(ellipsoid);
698 
699  // check for errors and abort if any have occurred.
700  errorCheck();
701 
702 }
703 
704 /**
705  * Given a model and an array of attribute changes, apply the changes to the
706  * model.
707  *
708  * @param model
709  * @param attributeIndex
710  * @param attributeChanges
711  * @param nChanges
712  */
713 void applyAttributeChanges(GeoTessModelC* model, int attributeIndex, float* attributeChanges, int nChanges)
714 {
715  PointMapC* pointMap = geomodel_getPointMap(model);
716  int pointIndex;
717  float attenuation;
718  for (pointIndex = 0; pointIndex < nChanges; ++pointIndex)
719  {
720  attenuation = geopoint_getPointValue(pointMap, pointIndex, attributeIndex);
721  attenuation += attributeChanges[pointIndex];
722  geopoint_setPointValue(pointMap, pointIndex, attributeIndex, attenuation);
723  }
724  geopoint_destroy(pointMap);
725 
726  // check for errors and abort if any have occurred.
727  errorCheck();
728 }
729 
730 /**
731  * Build a new GeoTessModel with the same grid nodes as the input model.
732  * There will a single attribute value of type int assigned to each grid
733  * node. The name of the attribute is HIT_COUNT and it is unitless.
734  *
735  * @param model
736  * @param rayPaths
737  * @param nRays
738  * @return a model containing hit_count values
739  */
740 GeoTessModelC* hitCount(GeoTessModelC* model, double*** rayPaths, int nRays)
741 {
742  GeoTessMetaDataC* metadata;
743  GeoTessGridC* grid;
744  GeoTessModelC* hitCountModel;
745  PolygonC* polygon;
746  InterpolatorTypeC interpType;
747 
748  int i,j;
749  int vertexId;
750  int count;
751  double pointSpacing;
752  int attributeIndex;
753  double earthRadius;
754 
755  double** rayPath;
756  double* event;
757  double* station;
758  double* u;
759 
760  // initialize these arrays to NULL. Memory will be allocated for them
761  // in function geomodel_getPathIntegral2DW() as needed.
762  int allocatedSize=0;
763  int* pointIndices=NULL;
764  double* weights=NULL;
765 
766  int nPoints;
767  int hitcount;
768  PointMapC* pointMap;
769  int pointIndex;
770  char* str1;
771 
772 
773  printf("************************************************************\n");
774  printf("*\n");
775  printf("* hitCount()\n");
776  printf("*\n");
777  printf("************************************************************\n");
778 
779  // Create a MetaData object in which we can specify information
780  // needed for model construction.
781  metadata = geometadata_create();
782 
783  // specify the ellipsoid to use for converting between geographic and geocentric
784  // coordinates and between radius and depth. This is really not necessary here since
785  // WGS84 is the default, but other options are available.
786  geometadata_setEarthShape(metadata, "WGS84");
787 
788  // Specify a description of the model.
789  geometadata_setDescription(metadata, "GeoTessModel of hit count for example program Tomography2D");
790 
791  // This model will have only one layer, named 'surface'.
792  geometadata_setLayerNames1(metadata, "surface");
793 
794  // Specify one unitless attribute
795  geometadata_setAttributes1(metadata, "HIT_COUNT", "count");
796 
797  // specify the DataType for the data.
798  geometadata_setDataType1(metadata, INT);
799 
800  // specify the name of the software that is going to generate
801  // the model. This gets stored in the model for future reference.
802  geometadata_setModelSoftwareVersion(metadata, "Tomography2D hitCount()");
803 
804  // specify the date when the model was generated. This gets
805  // stored in the model for future reference.
806  str1 = cpu_now();
807  geometadata_setModelGenerationDate(metadata, str1);
808  free(str1);
809 
810  // check for errors and abort if any have occurred.
811  errorCheck();
812 
813  // retrieve the grid from current model. Old model and new model
814  // will share a reference to the same grid.
815  grid = geomodel_getGrid(model);
816 
817  // call a GeoTessModel constructor to build the model. Use the same grid
818  // as the one used by the input model.
819  hitCountModel = geomodel_create4(grid, metadata);
820 
821  // check for errors and abort if any have occurred.
822  errorCheck();
823 
824  geometadata_destroy(metadata);
825 
826  // initialize the data value (hit count) of every node with int value zero.
827  count = 0;
828  for (vertexId = 0; vertexId < geogrid_getNVertices(grid); ++vertexId)
829  geomodel_setProfSurfInts(hitCountModel, vertexId, &count, 1);
830 
831  // check for errors and abort if any have occurred.
832  errorCheck();
833 
834  geogrid_destroy(grid);
835 
836  polygon = geomodel_getPolygon(model);
837  if (polygon != NULL)
838  {
839  geomodel_setActiveRegionPoly(hitCountModel, polygon);
840  geopoly_destroy(polygon);
841  }
842 
843  // check for errors and abort if any have occurred.
844  errorCheck();
845 
846  pointMap = geomodel_getPointMap(hitCountModel);
847 
848  // the index of the attribute that we want to integrate along the ray paths.
849  attributeIndex = 0;
850 
851  // approximate point spacing to use for numerical integration.
852  // one tenth of a degree, converted to radians. The actual point
853  // spacing will be slightly less than this so that there will be
854  // an integral number of equally spaced points along the the path.
855  pointSpacing = 0.1*rtd;
856 
857  // the radius of the earth in km. If user wishes to assume a spherical
858  // earth, the radius can be specified here. By specifying a value
859  // <= 0 km, GeoTess will compute local values of earth radius assuming
860  // the WGS84 ellipsoid.
861  earthRadius = -1;
862 
863  // horizontal interpolation type; either LINEAR or NATURAL_NEIGHBOR
864  interpType = NATURAL_NEIGHBOR;
865 
866  // weights will be a map from a model point index to the weight
867  // ascribed to that point index by the integration points along the ray.
868  // The sum of all the weights will equal the length of the ray path in km.
869 
870  // loop over the ray paths
871  for (i = 0; i < nRays; ++i)
872  {
873  // each ray path is comprised of two unit vectors, one for the event and one for the station.
874  rayPath = rayPaths[i];
875  event = rayPath[0];
876  station = rayPath[1];
877 
878  // integrate attenuation values along the path. We don't really care about the
879  // attenuation or the weights. All we really want are the pointIndices of the
880  // points 'touched' by the ray.
881  geomodel_getPathIntegral2DW(model, attributeIndex, event, station,
882  pointSpacing, earthRadius, interpType,
883  &pointIndices, &weights, &allocatedSize, &nPoints);
884 
885  for (j=0; j<nPoints; ++j)
886  {
887  if (pointIndices[j] >= 0)
888  {
889  hitcount = geopoint_getPointValueInt(pointMap, pointIndices[j], attributeIndex);
890  ++hitcount;
891  geopoint_setPointValue(pointMap, pointIndices[j], attributeIndex, hitcount);
892  }
893  }
894  }
895 
896  // hitCountModel has been populated with the hit count of every vertex.
897  // print information about the points that have hit count > 0
898  printf(" point vertex lat lon distance hitcount\n");
899  for (pointIndex = 0; pointIndex < geopoint_size(pointMap); ++pointIndex)
900  if (geopoint_getPointValueInt(pointMap, pointIndex, attributeIndex) > 0)
901  {
902  // find the location of the current point
903  u = geopoint_getPointUnitVector(pointMap, pointIndex);
904 
905  // retrieve string representation of the lat,lon of current point
906  str1 = geopoint_getPointLatLonString(pointMap, pointIndex);
907  printf("%8d %8d %s %9.2f %6d\n",
908  pointIndex,
909  geopoint_getVertexIndex(pointMap, pointIndex), str1,
910  geoutils_angleDegrees(ANMO, u),
911  geopoint_getPointValueInt(pointMap, pointIndex, attributeIndex));
912  free(str1);
913  }
914  printf("\n");
915 
916  // free the pointmap wrapper
917  geopoint_destroy(pointMap);
918 
919  // free allocated memory
920  if (pointIndices) free(pointIndices);
921  if (weights) free(weights);
922 
923  return hitCountModel;
924 }
925 
926 /**
927  * At this point, we have a new GeoTessModel that has been refined to have
928  * higher resolution (more vertices) than the old model. But the new model has
929  * attribute value HIT_COUNT, not ATTENUATION. We need to make a
930  * new model using the refined grid from hitCountModelRefined but using data
931  * obtained from the old model. Where the old model has a vertex that is
932  * colocated with the vertex in the new model, the data from the old model is
933  * copied to the new model. For vertices in the new model that do not have
934  * colocated vertices in the old model, data will be interpolated from the
935  * data in the old model.
936  *
937  * @param oldModel
938  * @param hitCountModelRefined
939  * @return new model with grid from refinedGrid and attribute values from oldModel.
940  */
941 GeoTessModelC* refineModel(GeoTessModelC* oldModel, GeoTessModelC* hitCountModelRefined)
942 {
943  // get a reference to the grid in the oldModel
944  GeoTessGridC* oldGrid = geomodel_getGrid(oldModel);
945 
946  // get a reference to the metadata from the old model. It is all still
947  // appropriate for the new, refined model.
948  GeoTessMetaDataC* metaData = geomodel_getMetaData(oldModel);
949 
950  // get a reference to the refined grid in hitCountModelRefined
951  GeoTessGridC* refinedGrid = geomodel_getGrid(hitCountModelRefined);
952 
953  // make a new model with the refined grid and a reference to the meta
954  // data from the old model.
955  GeoTessModelC* newModel = geomodel_create4(refinedGrid, metaData);
956 
957  // we will need to interpolate data from the old model at vertices in
958  // the new model that do not exist in the old model. For that purpose,
959  // we will need a GeoTessPosition object obtained from the old model.
960  GeoTessPositionC* pos = geomodel_getPosition2(oldModel, LINEAR);
961 
962  // both old and new models are 2D models and hence have only a single layer
963  int layerIndex = 0;
964  // in 2D models, the one-and-only layer has only a single node.
965  int nodeIndex = 0;
966  // both old and new models have only one attribute, which has index 0.
967  int attributeIndex = 0;
968 
969  int vtx, oldVtx;
970  float attenuation;
971  double* u;
972 
973  printf("************************************************************\n");
974  printf("*\n");
975  printf("* refineModel()\n");
976  printf("*\n");
977  printf("************************************************************\n\n");
978 
979  // loop over every node in the new model and populate it with data.
980  // Note that we must loop over all nodes not just the points.
981  // If a Polygon was applied to limit the active region to something
982  // less than the entire globe, then points would not include all nodes
983  // in the model. We need to populate all nodes.
984  for (vtx = 0; vtx < geomodel_getNVertices(newModel); ++vtx)
985  {
986  // find the unit vector of the vertex from the new model.
987  // There may or may not be a vertex in the old model that is
988  // colocated with this unit vector.
989  u = geogrid_getVertex(refinedGrid, vtx);
990 
991  // request the index of the vertex in the old model that is
992  // colocated with the new vertex. If the old model has no colocated
993  // vertex, then oldVtx will be -1.
994  oldVtx = geogrid_getVertexIndex1(oldGrid, u);
995 
996  if (oldVtx >= 0)
997  // retrieve attenuation value from the old model directly
998  attenuation = geomodel_getValueFloat(oldModel, oldVtx, layerIndex, nodeIndex, attributeIndex);
999  else
1000  {
1001  // interpolate a new attenuation value from values in the old model.
1002  geoposition_set4(pos, layerIndex, u, 6371.);
1003  attenuation = (float)geoposition_getValue(pos, attributeIndex);
1004  }
1005 
1006  // set the data in the new model.
1007  geomodel_setProfSurfFloats(newModel, vtx, &attenuation, 1);
1008  }
1009 
1010  geogrid_destroy(oldGrid);
1011  geogrid_destroy(refinedGrid);
1012  geometadata_destroy(metaData);
1013  geoposition_destroy(pos);
1014 
1015  return newModel;
1016 }
1017 
1018 /**
1019  * Check to see if any errors have occurred. If any, print out the error
1020  * messages and abort.
1021  */
1023 {
1024  // print out error messages from the message stack,
1025  // most recent error first.
1026  while (error_exists())
1027  {
1028  fprintf(stderr, "%s\n", error_getMessage());
1029 
1030  // if there were any error messages to start with,
1031  // and the last error message has been printed,
1032  // then exit.
1033  if (!error_exists())
1034  exit(-1);
1035  }
1036 
1037 }
1038 
startingModel
GeoTessModelC * startingModel(char *gridFile)
Definition: Tomography2D.c:260
hitCount
GeoTessModelC * hitCount(GeoTessModelC *model, double ***rayPaths, int nRays)
Definition: Tomography2D.c:740
dtr
static const double dtr
Definition: Integrate2D.c:55
errorCheck
void errorCheck()
Definition: Tomography2D.c:1022
refineModel
GeoTessModelC * refineModel(GeoTessModelC *oldModel, GeoTessModelC *hitCountModelRefined)
Definition: Tomography2D.c:941
regularization
void regularization(GeoTessModelC *model)
Definition: Tomography2D.c:606
applyAttributeChanges
void applyAttributeChanges(GeoTessModelC *model, int attributeIndex, float *attributeChanges, int nChanges)
Definition: Tomography2D.c:713
integrateRayPaths
void integrateRayPaths(GeoTessModelC *model, double ***rayPaths, int nRays)
Definition: Tomography2D.c:445
main
int main(int argc, char **argv)
Definition: Tomography2D.c:75
rtd
static const double rtd
Definition: Integrate2D.c:54
generateRayPaths
void generateRayPaths(double ****rayPaths, int *nRays)
Definition: Tomography2D.c:362