Skip to content

Commit e7f58c3

Browse files
committed
ENH: multi-event relocation is now performed per cluster (vs all events in one)
Instead of relocating all events in one single double-difference system each cluster is relocated independently. This results in improved solutions since the parameters of the solver are cluster dependent (e.g. downweighting by residuals) Also each cluster covers a smaller area, which helps in avoiding breaking the solver assumption of an euclidean geometry (the lat/lon/depth coordinates are converted internally to an euclidean system by the solver)
1 parent 048791d commit e7f58c3

File tree

3 files changed

+152
-51
lines changed

3 files changed

+152
-51
lines changed

apps/scrtdd/hdd/clustering.cpp

+91-23
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,7 @@ selectNeighbouringEvents(const CatalogCPtr& catalog,
346346
}
347347

348348

349-
350-
std::list<NeighboursPtr>
349+
deque< list<NeighboursPtr> >
351350
selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
352351
double minPhaseWeight,
353352
double minESdist,
@@ -364,7 +363,7 @@ selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
364363
SEISCOMP_INFO("Selecting Catalog Neighbouring Events ");
365364

366365
// neighbours for each event
367-
list<NeighboursPtr> neighboursByEvent;
366+
list<NeighboursPtr> neighboursList;
368367

369368
// for each event find the neighbours
370369
CatalogPtr validCatalog = new Catalog(*catalog);
@@ -408,7 +407,7 @@ selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
408407
// add newly computed neighbors catalogs to previous ones
409408
for ( NeighboursPtr& neighbours : newNeighbourCats )
410409
{
411-
neighboursByEvent.push_back( neighbours );
410+
neighboursList.push_back( neighbours );
412411
// make sure we won't recompute what has been already done
413412
todoEvents.remove( neighbours->refEvId );
414413
}
@@ -420,7 +419,7 @@ selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
420419
redo = false;
421420
list<NeighboursPtr> validNeighbourCats;
422421

423-
for ( NeighboursPtr& neighbours : neighboursByEvent )
422+
for ( NeighboursPtr& neighbours : neighboursList )
424423
{
425424
bool currCatInvalid = false;
426425
for (unsigned removedEventId : removedEvents)
@@ -442,38 +441,107 @@ selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
442441
validNeighbourCats.push_back( neighbours );
443442
}
444443

445-
neighboursByEvent.clear();
446-
neighboursByEvent = validNeighbourCats;
444+
neighboursList.clear();
445+
neighboursList = validNeighbourCats;
447446

448447
} while( redo );
449448
}
450449

451-
// We don't want to report the same pairs multiple times
452-
// when creating double-difference observations. So we'll
453-
// remove the pairs that appeared in previous catalogs from
454-
// the following catalogs
455-
std::unordered_multimap<unsigned,unsigned> existingPairs;
450+
return clusterizeNeighbouringEvents(neighboursList);
451+
}
452+
453+
454+
/*
455+
* Organize the neighbours by not connected clusters
456+
* Also, we don't want to report the same pair multiple times
457+
* (e.g. ev1-ev2 and ev2-ev1) since we only want one observation
458+
* for pair when creating the double-difference observations
459+
* system
460+
*/
461+
deque< list<NeighboursPtr> >
462+
clusterizeNeighbouringEvents(const list<NeighboursPtr>& neighboursList)
463+
{
464+
map<unsigned, list<NeighboursPtr> > clusters;
465+
466+
unordered_map<unsigned, unsigned> clusterIdByEvent; // event id, cluster id
467+
468+
unordered_map<unsigned, NeighboursPtr> neighboursByEvent; // key event id
469+
for ( const NeighboursPtr& neighbours : neighboursList )
470+
neighboursByEvent[ neighbours->refEvId ] = neighbours;
456471

457-
for ( NeighboursPtr& neighbours : neighboursByEvent )
472+
while ( ! neighboursByEvent.empty() )
458473
{
459-
unsigned currEventId = neighbours->refEvId;
474+
// keep track of event pairs found (for dropping identical pairs)
475+
unordered_multimap<unsigned,unsigned> discoveredPairs;
460476

461-
// remove from currrent catalog the existing pairs
462-
auto eqlrng = existingPairs.equal_range(currEventId );
463-
for (auto existingPair = eqlrng.first; existingPair != eqlrng.second; existingPair++)
477+
// start traversal with first unseen event neighbours
478+
unordered_set<unsigned> clusterEvs( { neighboursByEvent.begin()->first } );
479+
480+
list<NeighboursPtr> currentCluster;
481+
unordered_set<unsigned> connectedClusters;
482+
483+
while ( ! clusterEvs.empty() ) // when empty the cluster is fully built
464484
{
465-
neighbours->ids.erase( existingPair->second );
466-
neighbours->phases.erase( existingPair->second );
485+
unsigned currentEv = *clusterEvs.begin();
486+
clusterEvs.erase(currentEv);
487+
488+
// keep track of clusters connected to the current one
489+
if ( clusterIdByEvent.find(currentEv) != clusterIdByEvent.end() )
490+
connectedClusters.insert( clusterIdByEvent.at(currentEv) );
491+
492+
const auto& neighboursByEventIt = neighboursByEvent.find( currentEv );
493+
494+
// skip already processed events
495+
if ( neighboursByEventIt == neighboursByEvent.end() )
496+
continue;
497+
498+
NeighboursPtr neighbours = neighboursByEventIt->second;
499+
neighboursByEvent.erase( neighbours->refEvId );
500+
501+
// update the set for the traversal of this cluster
502+
for ( unsigned neighEvId : neighbours->ids )
503+
clusterEvs.insert( neighEvId );
504+
505+
// remove from current neighbours the pairs that appeared previously
506+
// in this cluster
507+
auto eqlrng = discoveredPairs.equal_range( neighbours->refEvId );
508+
for (auto existingPair = eqlrng.first; existingPair != eqlrng.second; existingPair++)
509+
{
510+
neighbours->ids.erase( existingPair->second );
511+
neighbours->phases.erase( existingPair->second );
512+
}
513+
514+
// keep track of new event pairs for following iterations and
515+
// update the queue for the breadth-first traversal
516+
for ( unsigned neighEvId : neighbours->ids )
517+
discoveredPairs.emplace(neighEvId, neighbours->refEvId);
518+
519+
// populate current cluster
520+
currentCluster.push_back( neighbours );
467521
}
468522

469-
// remove current pairs from following catalogs
470-
for ( unsigned neighEvId : neighbours->ids )
523+
if ( ! connectedClusters.empty() )
471524
{
472-
existingPairs.emplace(neighEvId, currEventId);
525+
// merge all connected clusters to the current one
526+
for ( unsigned clusterId : connectedClusters )
527+
{
528+
currentCluster.splice(currentCluster.end(), clusters[clusterId] );
529+
clusters.erase(clusterId);
530+
}
473531
}
532+
533+
unsigned maxKey = clusters.empty() ? 0 : clusters.rbegin()->first;
534+
unsigned newClusterId = maxKey + 1;
535+
536+
clusters[newClusterId] = currentCluster;
537+
538+
for ( const NeighboursPtr& n : currentCluster )
539+
clusterIdByEvent[ n->refEvId ] = newClusterId;
474540
}
475541

476-
return neighboursByEvent;
542+
deque< list<NeighboursPtr> > returnClusters;
543+
for (auto& kv : clusters ) returnClusters.push_back(kv.second);
544+
return returnClusters;
477545
}
478546

479547

apps/scrtdd/hdd/clustering.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <unordered_map>
2424
#include <unordered_set>
2525
#include <set>
26+
#include <list>
27+
#include <deque>
2628

2729

2830
namespace Seiscomp {
@@ -104,7 +106,7 @@ selectNeighbouringEvents(const CatalogCPtr& catalog,
104106
double maxEllipsoidSize=10,
105107
bool keepUnmatched=false);
106108

107-
std::list<NeighboursPtr>
109+
std::deque< std::list<NeighboursPtr> >
108110
selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
109111
double minPhaseWeight,
110112
double minESdis,
@@ -118,6 +120,9 @@ selectNeighbouringEventsCatalog(const CatalogCPtr& catalog,
118120
double maxEllipsoidSize,
119121
bool keepUnmatched);
120122

123+
std::deque< std::list<NeighboursPtr> >
124+
clusterizeNeighbouringEvents(const std::list<NeighboursPtr>& neighboursList);
125+
121126
}
122127
}
123128

apps/scrtdd/hdd/hypodd.cpp

+55-27
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ CatalogPtr HypoDD::relocateCatalog()
228228
}
229229

230230
// Find Neighbouring Events in the catalog
231-
list<NeighboursPtr> neighbourCats = selectNeighbouringEventsCatalog(
231+
deque< list<NeighboursPtr> > clusters = selectNeighbouringEventsCatalog(
232232
catToReloc, _cfg.ddObservations2.minWeight,
233233
_cfg.ddObservations2.minESdist, _cfg.ddObservations2.maxESdist,
234234
_cfg.ddObservations2.minEStoIEratio, _cfg.ddObservations2.minDTperEvt,
@@ -237,28 +237,58 @@ CatalogPtr HypoDD::relocateCatalog()
237237
_cfg.ddObservations2.maxEllipsoidSize, true
238238
);
239239

240+
SEISCOMP_INFO("Found %lu event clusters", clusters.size() );
241+
240242
// write catalog for debugging purpose
241243
if ( ! _workingDirCleanup )
242244
{
243245
catToReloc->writeToFile(
244246
(boost::filesystem::path(catalogWorkingDir)/"starting-event.csv").string(),
245247
(boost::filesystem::path(catalogWorkingDir)/"starting-phase.csv").string(),
246248
(boost::filesystem::path(catalogWorkingDir)/"starting-station.csv").string() );
247-
CatalogPtr catToDump( new Catalog() );
248-
for (const auto& n : neighbourCats)
249-
catToDump->add(n->refEvId, *catToReloc, true);
250-
catToDump->writeToFile(
251-
(boost::filesystem::path(catalogWorkingDir)/"selected-event.csv").string(),
252-
(boost::filesystem::path(catalogWorkingDir)/"selected-phase.csv").string(),
253-
(boost::filesystem::path(catalogWorkingDir)/"selected-station.csv").string() );
254-
}
249+
}
250+
251+
//
252+
// Relocate one cluster a time
253+
//
254+
CatalogPtr relocatedCatalog( new Catalog() );
255+
256+
for (unsigned clusterId = 0; clusterId < clusters.size(); clusterId++ )
257+
{
258+
const list<NeighboursPtr>& neighCluster = clusters[clusterId];
259+
260+
SEISCOMP_INFO("Relocating cluster %u (%lu events)", clusterId+1, neighCluster.size() );
261+
262+
if ( ! _workingDirCleanup )
263+
{
264+
CatalogPtr catToDump( new Catalog() );
265+
for (const NeighboursPtr& n : neighCluster )
266+
catToDump->add(n->refEvId, *catToReloc, true);
267+
string prefix = "cluster-" + to_string(clusterId + 1);
268+
catToDump->writeToFile(
269+
(boost::filesystem::path(catalogWorkingDir)/(prefix+"-event.csv")).string(),
270+
(boost::filesystem::path(catalogWorkingDir)/(prefix+"-phase.csv")).string(),
271+
(boost::filesystem::path(catalogWorkingDir)/(prefix+"-station.csv")).string() );
272+
}
255273

256-
// Perform cross correlation, which also detects picks around theoretical
257-
// arrival times. The catalog will be updated with those theoretical phases
258-
const XCorrCache xcorr = buildXCorrCache(catToReloc, neighbourCats, _useArtificialPhases);
274+
// Perform cross correlation, which also detects picks around theoretical
275+
// arrival times. The catalog will be updated with those theoretical phases
276+
const XCorrCache xcorr = buildXCorrCache(catToReloc, neighCluster, _useArtificialPhases);
259277

260-
// The actual relocation
261-
CatalogPtr relocatedCatalog = relocate(catToReloc, neighbourCats, false, xcorr);
278+
// The actual relocation
279+
CatalogPtr relocatedCluster = relocate(catToReloc, neighCluster, false, xcorr);
280+
281+
relocatedCatalog->add(*relocatedCluster, true);
282+
283+
if ( ! _workingDirCleanup )
284+
{
285+
string prefix = "relocated-cluster-" + to_string(clusterId + 1);
286+
relocatedCluster->writeToFile(
287+
(boost::filesystem::path(catalogWorkingDir)/(prefix+"-event.csv")).string(),
288+
(boost::filesystem::path(catalogWorkingDir)/(prefix+"-phase.csv")).string(),
289+
(boost::filesystem::path(catalogWorkingDir)/(prefix+"-station.csv")).string() );
290+
}
291+
}
262292

263293
// write catalog for debugging purpose
264294
if ( ! _workingDirCleanup )
@@ -448,12 +478,10 @@ HypoDD::relocateEventSingleStep(const CatalogCPtr& evToRelocateCat,
448478

449479
CatalogPtr
450480
HypoDD::relocate(CatalogPtr& catalog,
451-
const std::list<NeighboursPtr>& neighbourCats,
481+
const std::list<NeighboursPtr>& neighCluster,
452482
bool keepNeighboursFixed,
453483
const XCorrCache& xcorr) const
454484
{
455-
// Create a solver and then add observations
456-
Solver solver(_cfg.solver.type);
457485
ObservationParams obsparams;
458486

459487
for ( unsigned iteration=0; iteration < _cfg.solver.algoIterations; iteration++ )
@@ -485,18 +513,18 @@ HypoDD::relocate(CatalogPtr& catalog,
485513
SEISCOMP_INFO("Solving iteration %u num events %lu (observ. weight TTdiff %.2f xcorr %.2f "
486514
"dampingFactor %.2f downWeightingByResidual %.2f "
487515
"meanShiftConstrainWeight %.2f,%.2f,%.2f,%.2f)",
488-
iteration, neighbourCats.size(), absTTDiffObsWeight, xcorrObsWeight,
516+
iteration, neighCluster.size(), absTTDiffObsWeight, xcorrObsWeight,
489517
dampingFactor, downWeightingByResidual,
490518
meanLonShiftConstraint, meanLatShiftConstraint,
491519
meanDepthShiftConstraint, meanTTShiftConstraint);
492520

493-
494-
solver.reset();
521+
// Create a solver and then add observations
522+
Solver solver(_cfg.solver.type);
495523

496524
//
497525
// Add absolute travel time/xcorr differences to the solver (the observations)
498526
//
499-
for (const NeighboursPtr& neighbours : neighbourCats)
527+
for (const NeighboursPtr& neighbours : neighCluster)
500528
{
501529
addObservations(solver, absTTDiffObsWeight, xcorrObsWeight, catalog,
502530
neighbours, keepNeighboursFixed, xcorr, obsparams);
@@ -521,12 +549,12 @@ HypoDD::relocate(CatalogPtr& catalog,
521549
obsparams = ObservationParams();
522550

523551
// update event parameters
524-
catalog = updateRelocatedEvents(solver, catalog, neighbourCats, obsparams);
552+
catalog = updateRelocatedEvents(solver, catalog, neighCluster, obsparams);
525553
}
526554

527555
// build the relocated catalog from the results of relocations
528556
CatalogPtr relocatedCatalog( new Catalog() );
529-
for (const NeighboursPtr& neighbours : neighbourCats)
557+
for (const NeighboursPtr& neighbours : neighCluster)
530558
{
531559
const Event& event = catalog->getEvents().at(neighbours->refEvId);
532560
if ( event.relocInfo.isRelocated )
@@ -695,15 +723,15 @@ HypoDD::ObservationParams::addToSolver(Solver& solver) const
695723
CatalogPtr
696724
HypoDD::updateRelocatedEvents(const Solver& solver,
697725
const CatalogCPtr& catalog,
698-
const std::list<NeighboursPtr>& neighbourCats,
726+
const std::list<NeighboursPtr>& neighCluster,
699727
ObservationParams& obsparams ) const
700728
{
701729
unordered_map<string,Station> stations = catalog->getStations();
702730
map<unsigned,Event> events = catalog->getEvents();
703731
unordered_multimap<unsigned,Phase> phases = catalog->getPhases();
704732
unsigned relocatedEvs = 0;
705733

706-
for (const NeighboursPtr& neighbours : neighbourCats)
734+
for (const NeighboursPtr& neighbours : neighCluster)
707735
{
708736
Event& event = events.at(neighbours->refEvId);
709737
event.relocInfo.isRelocated = false;
@@ -1014,14 +1042,14 @@ HypoDD::createThoreticalPhase(const Station& station,
10141042

10151043
XCorrCache
10161044
HypoDD::buildXCorrCache(CatalogPtr& catalog,
1017-
const std::list<NeighboursPtr>& neighbourCats,
1045+
const std::list<NeighboursPtr>& neighCluster,
10181046
bool computeTheoreticalPhases)
10191047
{
10201048
XCorrCache xcorr;
10211049
_counters = {0};
10221050
_wf->resetCounters();
10231051

1024-
for (const NeighboursPtr& neighbours : neighbourCats)
1052+
for (const NeighboursPtr& neighbours : neighCluster)
10251053
{
10261054
const Event& refEv = catalog->getEvents().at(neighbours->refEvId);
10271055

0 commit comments

Comments
 (0)