Files
rustytorch/demos/rtx-seismic-demo/src/sample_data.rs
T
2026-03-04 00:08:42 +00:00

635 lines
18 KiB
Rust

//! Sample data and configurations for the SeismicAI demo.
//!
//! Provides realistic earthquake scenarios, station networks, and velocity models
//! for demonstration and testing purposes.
use seismic_shared::{
EarthquakeSource, FocalMechanism, GeoLocation, InstrumentType, SimulationConfig, SiteClass,
StationConfig, VelocityLayer, VelocityModel, WarningConfig,
};
// ============================================================================
// Sample Earthquake Scenarios
// ============================================================================
/// Create a local shallow earthquake (M4.5, 8km depth).
/// Representative of typical California small/moderate events.
pub fn local_earthquake() -> EarthquakeSource {
let mut source = EarthquakeSource::new(
37.8044, // Near Berkeley, CA (Hayward Fault)
-122.2712,
8.0, // Shallow depth
4.5, // Moderate magnitude
FocalMechanism::strike_slip(),
);
source.event_id = "NC72345678".to_string();
source.stress_drop_mpa = Some(5.0);
source
}
/// Create a regional moderate earthquake (M6.0, 15km depth).
/// Representative of damaging regional events.
pub fn regional_earthquake() -> EarthquakeSource {
let mut source = EarthquakeSource::new(
36.5, // Central California (San Andreas Fault)
-121.0,
15.0, // Mid-crustal depth
6.0, // Significant magnitude
FocalMechanism::strike_slip(),
);
source.event_id = "NC72456789".to_string();
source.stress_drop_mpa = Some(10.0);
source
}
/// Create a major earthquake scenario (M7.0, 10km depth).
/// Representative of potentially catastrophic events.
pub fn major_earthquake() -> EarthquakeSource {
let mut source = EarthquakeSource::new(
37.4, // Near San Jose (Hayward Fault)
-122.1,
10.0,
7.0, // Major magnitude
FocalMechanism {
strike: 150.0,
dip: 80.0,
rake: 175.0, // Right-lateral strike-slip
},
);
source.event_id = "NC72567890".to_string();
source.stress_drop_mpa = Some(8.0);
source.rupture_duration_s = Some(15.0);
source
}
/// Create a subduction zone earthquake scenario (M8.0, 20km depth).
/// Representative of Cascadia-style megathrust events.
pub fn subduction_earthquake() -> EarthquakeSource {
let mut source = EarthquakeSource::new(
44.0, // Offshore Oregon (Cascadia Subduction Zone)
-125.0,
20.0,
8.0, // Great earthquake
FocalMechanism::reverse(),
);
source.event_id = "PW72678901".to_string();
source.stress_drop_mpa = Some(3.0); // Lower stress drop for subduction
source.rupture_area_km2 = Some(10000.0);
source.rupture_duration_s = Some(60.0);
source
}
/// Create a deep earthquake scenario (M5.5, 100km depth).
/// Representative of deep intraslab events.
pub fn deep_earthquake() -> EarthquakeSource {
let mut source = EarthquakeSource::new(
47.5, // Puget Sound region
-122.5,
100.0, // Deep intraslab
5.5,
FocalMechanism::normal(),
);
source.event_id = "UW72789012".to_string();
source.stress_drop_mpa = Some(50.0); // Higher stress drop for deep events
source
}
// ============================================================================
// Sample Station Networks
// ============================================================================
/// Create a dense urban seismic network (100 stations in 10x10 grid).
/// Representative of ShakeAlert-style networks in urban areas.
pub fn dense_network() -> Vec<StationConfig> {
let center = GeoLocation::new(37.8, -122.4);
let grid_spacing = 0.05; // ~5km at this latitude
let mut stations = Vec::with_capacity(100);
for i in 0..10 {
for j in 0..10 {
let lat = center.latitude - 0.25 + i as f64 * grid_spacing;
let lon = center.longitude - 0.25 + j as f64 * grid_spacing;
let mut station = StationConfig::new(&format!("BK{:02}{:02}", i, j), lat, lon);
station.name = format!("Bay Area Station {:02}-{:02}", i, j);
station.network = "BK".to_string();
station.instrument = InstrumentType::Accelerometer;
// Vary site classes
station.site_class = match (i + j) % 5 {
0 => SiteClass::A,
1 => SiteClass::B,
2 => SiteClass::C,
3 => SiteClass::D,
_ => SiteClass::E,
};
stations.push(station);
}
}
stations
}
/// Create a sparse regional seismic network (5 stations).
/// Representative of USGS/university backbone networks.
pub fn sparse_network() -> Vec<StationConfig> {
vec![
{
let mut s = StationConfig::new("BK.BKS", 37.8764, -122.2356);
s.name = "Berkeley Seismic Station".to_string();
s.network = "BK".to_string();
s.instrument = InstrumentType::Broadband;
s.site_class = SiteClass::B;
s.vs30 = Some(950.0);
s
},
{
let mut s = StationConfig::new("BK.CMB", 38.0346, -120.3865);
s.name = "Columbia College".to_string();
s.network = "BK".to_string();
s.instrument = InstrumentType::Broadband;
s.site_class = SiteClass::B;
s.vs30 = Some(800.0);
s
},
{
let mut s = StationConfig::new("BK.SAO", 36.7640, -121.4472);
s.name = "San Andreas Observatory".to_string();
s.network = "BK".to_string();
s.instrument = InstrumentType::Broadband;
s.site_class = SiteClass::C;
s.vs30 = Some(550.0);
s
},
{
let mut s = StationConfig::new("NC.FARB", 37.6977, -123.0016);
s.name = "Farallon Islands".to_string();
s.network = "NC".to_string();
s.instrument = InstrumentType::Accelerometer;
s.site_class = SiteClass::A;
s.vs30 = Some(1500.0);
s
},
{
let mut s = StationConfig::new("NC.SF", 37.7749, -122.4194);
s.name = "San Francisco".to_string();
s.network = "NC".to_string();
s.instrument = InstrumentType::Accelerometer;
s.site_class = SiteClass::D;
s.vs30 = Some(280.0);
s.z1_0 = Some(0.5);
s
},
]
}
/// Create a linear array for source characterization.
pub fn linear_array() -> Vec<StationConfig> {
let start = GeoLocation::new(37.0, -122.0);
let end = GeoLocation::new(38.0, -122.0);
let num_stations = 20;
(0..num_stations)
.map(|i| {
let t = i as f64 / (num_stations - 1) as f64;
let lat = start.latitude + t * (end.latitude - start.latitude);
let lon = start.longitude + t * (end.longitude - start.longitude);
let mut station = StationConfig::new(&format!("LA{:02}", i), lat, lon);
station.name = format!("Linear Array Station {:02}", i);
station.network = "XX".to_string();
station.instrument = InstrumentType::ShortPeriod;
station.site_class = SiteClass::C;
station
})
.collect()
}
/// Create a MEMS network for community early warning.
pub fn mems_network() -> Vec<StationConfig> {
let center = GeoLocation::new(34.0522, -118.2437); // Los Angeles
let num_stations = 50;
(0..num_stations)
.map(|i| {
// Random-ish distribution
let angle = i as f64 * 2.4; // Golden angle
let radius = 0.1 + 0.05 * (i as f64).sqrt();
let lat = center.latitude + radius * angle.cos();
let lon = center.longitude + radius * angle.sin();
let mut station = StationConfig::new(&format!("ME{:02}", i), lat, lon);
station.name = format!("MEMS Station {:02}", i);
station.network = "CE".to_string();
station.instrument = InstrumentType::Mems;
station.site_class = if i % 3 == 0 {
SiteClass::D
} else {
SiteClass::C
};
station
})
.collect()
}
// ============================================================================
// Sample Velocity Models
// ============================================================================
/// Create California 1D velocity model (SCEC CVM-H based).
pub fn california_velocity_model() -> VelocityModel {
let mut model = VelocityModel::new("California CVM-H Simplified");
model.reference_lat = 35.0;
model.reference_lon = -118.0;
// Layered model from surface to Moho
model.add_layer(VelocityLayer {
depth_km: 0.0,
thickness_km: 1.0,
vp: 2.5,
vs: 1.2,
density: 2.1,
qp: 100.0,
qs: 50.0,
});
model.add_layer(VelocityLayer {
depth_km: 1.0,
thickness_km: 2.0,
vp: 4.0,
vs: 2.3,
density: 2.4,
qp: 200.0,
qs: 100.0,
});
model.add_layer(VelocityLayer {
depth_km: 3.0,
thickness_km: 5.0,
vp: 5.5,
vs: 3.2,
density: 2.6,
qp: 400.0,
qs: 200.0,
});
model.add_layer(VelocityLayer {
depth_km: 8.0,
thickness_km: 7.0,
vp: 6.3,
vs: 3.6,
density: 2.8,
qp: 600.0,
qs: 300.0,
});
model.add_layer(VelocityLayer {
depth_km: 15.0,
thickness_km: 10.0,
vp: 6.7,
vs: 3.9,
density: 2.9,
qp: 800.0,
qs: 400.0,
});
model.add_layer(VelocityLayer {
depth_km: 25.0,
thickness_km: 10.0,
vp: 7.2,
vs: 4.1,
density: 3.0,
qp: 1000.0,
qs: 500.0,
});
// Moho
model.add_layer(VelocityLayer {
depth_km: 35.0,
thickness_km: 15.0,
vp: 7.8,
vs: 4.4,
density: 3.2,
qp: 1500.0,
qs: 750.0,
});
model
}
/// Create Pacific Northwest velocity model (Cascadia).
pub fn cascadia_velocity_model() -> VelocityModel {
let mut model = VelocityModel::new("Cascadia Subduction Zone");
model.reference_lat = 45.0;
model.reference_lon = -123.0;
// Sedimentary basin
model.add_layer(VelocityLayer::new(0.0, 3.0, 3.5, 2.0, 2.2));
// Upper crust
model.add_layer(VelocityLayer::new(3.0, 12.0, 6.0, 3.5, 2.7));
// Lower crust
model.add_layer(VelocityLayer::new(15.0, 15.0, 6.8, 3.9, 2.9));
// Subducting oceanic crust
model.add_layer(VelocityLayer::new(30.0, 10.0, 7.2, 4.1, 3.1));
// Mantle
model.add_layer(VelocityLayer::new(40.0, 60.0, 8.1, 4.6, 3.3));
model
}
/// Create a simple generic velocity model.
pub fn generic_velocity_model() -> VelocityModel {
let mut model = VelocityModel::new("Generic");
model.add_layer(VelocityLayer::new(0.0, 5.0, 5.0, 2.9, 2.5));
model.add_layer(VelocityLayer::new(5.0, 15.0, 6.5, 3.7, 2.8));
model.add_layer(VelocityLayer::new(20.0, 15.0, 7.5, 4.3, 3.1));
model.add_layer(VelocityLayer::new(35.0, 65.0, 8.0, 4.6, 3.3));
model
}
// ============================================================================
// Sample Configurations
// ============================================================================
/// High-resolution simulation configuration.
pub fn high_resolution_config() -> SimulationConfig {
SimulationConfig {
dt: 0.005,
duration: 120.0,
dx: 0.25,
domain_size: (200.0, 200.0, 60.0),
max_frequency: 20.0,
include_attenuation: true,
include_site_effects: true,
use_neural_operator: true,
fno_modes: 32,
hidden_dim: 128,
}
}
/// Standard simulation configuration.
pub fn standard_config() -> SimulationConfig {
SimulationConfig::default()
}
/// Fast/coarse simulation configuration.
pub fn fast_config() -> SimulationConfig {
SimulationConfig {
dt: 0.02,
duration: 60.0,
dx: 1.0,
domain_size: (100.0, 100.0, 40.0),
max_frequency: 5.0,
include_attenuation: true,
include_site_effects: true,
use_neural_operator: true,
fno_modes: 8,
hidden_dim: 32,
}
}
/// Early warning configuration for urban areas.
pub fn urban_warning_config() -> WarningConfig {
WarningConfig {
min_magnitude: 3.5,
min_stations: 4,
alert_threshold_seconds: 5.0,
enable_sound: true,
enable_notifications: true,
alert_radius_km: 150.0,
}
}
/// Early warning configuration for regional networks.
pub fn regional_warning_config() -> WarningConfig {
WarningConfig {
min_magnitude: 4.5,
min_stations: 3,
alert_threshold_seconds: 10.0,
enable_sound: true,
enable_notifications: true,
alert_radius_km: 300.0,
}
}
// ============================================================================
// Complete Scenarios
// ============================================================================
/// Complete scenario: Hayward Fault rupture affecting Bay Area.
pub fn hayward_scenario() -> (
EarthquakeSource,
Vec<StationConfig>,
VelocityModel,
SimulationConfig,
) {
(
major_earthquake(),
dense_network(),
california_velocity_model(),
standard_config(),
)
}
/// Complete scenario: Small local event for testing.
pub fn local_test_scenario() -> (
EarthquakeSource,
Vec<StationConfig>,
VelocityModel,
SimulationConfig,
) {
(
local_earthquake(),
sparse_network(),
generic_velocity_model(),
fast_config(),
)
}
/// Complete scenario: Cascadia megathrust.
pub fn cascadia_scenario() -> (
EarthquakeSource,
Vec<StationConfig>,
VelocityModel,
SimulationConfig,
) {
(
subduction_earthquake(),
sparse_network(),
cascadia_velocity_model(),
high_resolution_config(),
)
}
// ============================================================================
// Tests
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_local_earthquake() {
let eq = local_earthquake();
assert!(eq.magnitude >= 4.0 && eq.magnitude <= 5.0);
assert!(eq.hypocenter.depth_km < 15.0);
}
#[test]
fn test_regional_earthquake() {
let eq = regional_earthquake();
assert!(eq.magnitude >= 5.5 && eq.magnitude <= 6.5);
}
#[test]
fn test_major_earthquake() {
let eq = major_earthquake();
assert!(eq.magnitude >= 6.5);
}
#[test]
fn test_subduction_earthquake() {
let eq = subduction_earthquake();
assert!(eq.magnitude >= 7.5);
assert!(eq.hypocenter.depth_km >= 15.0);
}
#[test]
fn test_deep_earthquake() {
let eq = deep_earthquake();
assert!(eq.hypocenter.depth_km >= 50.0);
}
#[test]
fn test_dense_network() {
let network = dense_network();
assert_eq!(network.len(), 100);
// Check diversity of site classes
let site_classes: Vec<_> = network.iter().map(|s| s.site_class).collect();
assert!(site_classes.contains(&SiteClass::A));
assert!(site_classes.contains(&SiteClass::E));
}
#[test]
fn test_sparse_network() {
let network = sparse_network();
assert!(network.len() >= 4 && network.len() <= 10);
// All should have valid Vs30
for station in &network {
assert!(station.get_vs30() > 0.0);
}
}
#[test]
fn test_linear_array() {
let array = linear_array();
assert_eq!(array.len(), 20);
// Check stations are in a line
let first_lon = array[0].location.longitude;
for station in &array {
assert!((station.location.longitude - first_lon).abs() < 0.01);
}
}
#[test]
fn test_mems_network() {
let network = mems_network();
assert_eq!(network.len(), 50);
for station in &network {
assert_eq!(station.instrument, InstrumentType::Mems);
}
}
#[test]
fn test_california_velocity_model() {
let model = california_velocity_model();
assert!(!model.layers.is_empty());
// Velocity should increase with depth
for i in 1..model.layers.len() {
assert!(model.layers[i].vp >= model.layers[i - 1].vp * 0.9);
}
}
#[test]
fn test_cascadia_velocity_model() {
let model = cascadia_velocity_model();
assert!(!model.layers.is_empty());
// Should have deep layers for subduction
let max_depth: f64 = model
.layers
.iter()
.map(|l| l.depth_km + l.thickness_km)
.fold(0.0, f64::max);
assert!(max_depth >= 50.0);
}
#[test]
fn test_simulation_configs() {
let fast = fast_config();
let standard = standard_config();
let high = high_resolution_config();
// Higher resolution means smaller dx
assert!(high.dx < standard.dx);
assert!(standard.dx < fast.dx);
// Higher resolution means smaller dt
assert!(high.dt < standard.dt);
assert!(standard.dt < fast.dt);
}
#[test]
fn test_warning_configs() {
let urban = urban_warning_config();
let regional = regional_warning_config();
// Urban should have lower magnitude threshold
assert!(urban.min_magnitude <= regional.min_magnitude);
// Both should require multiple stations
assert!(urban.min_stations >= 2);
assert!(regional.min_stations >= 2);
}
#[test]
fn test_hayward_scenario() {
let (source, stations, model, config) = hayward_scenario();
assert!(source.magnitude >= 6.5);
assert!(stations.len() >= 50);
assert!(!model.layers.is_empty());
assert!(config.duration > 0.0);
}
#[test]
fn test_local_test_scenario() {
let (source, stations, model, config) = local_test_scenario();
assert!(source.magnitude < 6.0);
assert!(stations.len() < 20);
assert!(config.dt >= 0.01); // Not too fine for testing
}
#[test]
fn test_cascadia_scenario() {
let (source, _stations, model, config) = cascadia_scenario();
assert!(source.magnitude >= 7.5);
assert_eq!(model.name, "Cascadia Subduction Zone");
assert!(config.max_frequency >= 10.0);
}
}