//! 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 { 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 { 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 { 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 { 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, 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, VelocityModel, SimulationConfig, ) { ( local_earthquake(), sparse_network(), generic_velocity_model(), fast_config(), ) } /// Complete scenario: Cascadia megathrust. pub fn cascadia_scenario() -> ( EarthquakeSource, Vec, 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); } }