gea-program 0.8.1__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gea_program-0.8.1 → gea_program-0.10.0}/PKG-INFO +25 -2
- {gea_program-0.8.1 → gea_program-0.10.0}/README.md +24 -1
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/__init__.py +1 -1
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/__main__.py +95 -2
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/acceptance_tests.py +244 -2
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/client_reports.py +102 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/seismic.md +1 -1
- gea_program-0.10.0/gea/help/sra.md +15 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/helplib.py +1 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/jobs.py +15 -6
- gea_program-0.10.0/gea/seismic_assoc.py +421 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/service.py +13 -1
- gea_program-0.10.0/gea/sra.py +623 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/web/app.html +3 -3
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/workspace.py +179 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/PKG-INFO +25 -2
- {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/SOURCES.txt +3 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/pyproject.toml +1 -1
- {gea_program-0.8.1 → gea_program-0.10.0}/LICENSE +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/bench.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/blind_harness.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/certificates.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/cli.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/config_versioning.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/correlation.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/dashboard.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/deviation.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/doctor.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/earth_model.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/example_register_map.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/fat_sat.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/files.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/follower.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/forward_model.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/gamma.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/geodesy.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/alarms.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/data-in.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/doctor.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/drift.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/files.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/instruments.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/notifications.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/patches.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/quality.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/site.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/sites.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/start.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/transients.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/housekeeping.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/live_ports.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/loadtest.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/modbus.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/model_card.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/notify.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/opcua_port.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/operator_app.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/patches.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/permits.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/ports.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/project.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reconciler.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/sample_record.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/sbom.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/segy.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_array.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_film.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_response.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_track.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/service_life.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/shell.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/shut_in.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/sla_report.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/store_forward.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/strata_join.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/supervisor.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/survey_view.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/telemetry.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/tool_library.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/transient.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/well_assembler.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/wits0.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea/witsml.py +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/setup.cfg +0 -0
- {gea_program-0.8.1 → gea_program-0.10.0}/tests/test_acceptance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gea-program
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
|
|
5
5
|
Author: Daniel T. Murphy
|
|
6
6
|
License: MPL-2.0
|
|
@@ -125,7 +125,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
|
|
|
125
125
|
gea client-report --report accuracy --out client_report
|
|
126
126
|
gea model-cards --out model_cards
|
|
127
127
|
gea sbom --out sbom
|
|
128
|
-
gea accept # the product gate (
|
|
128
|
+
gea accept # the product gate (314 checks)
|
|
129
129
|
gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
|
|
130
130
|
gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
|
|
131
131
|
gea gui # the desktop window (pip install "gea-program[desktop]")
|
|
@@ -528,6 +528,29 @@ so one geophone can still say when each rig was working - and the Seismic
|
|
|
528
528
|
Station Report and the station page carry the section, with the tracks drawn
|
|
529
529
|
against time.
|
|
530
530
|
|
|
531
|
+
### The Seismicity Response Area packet
|
|
532
|
+
|
|
533
|
+
The Railroad Commission of Texas declares a Seismicity Response Area around a
|
|
534
|
+
cluster of felt earthquakes and expects the operators inside it to run a
|
|
535
|
+
response plan. The plans on file have one shape - a circle of about 9 km, a
|
|
536
|
+
plan written against M 3.5, an 18-month goal, a response group meeting
|
|
537
|
+
within 48 hours of a threshold event, quarterly checkpoints with Commission
|
|
538
|
+
staff, disposal wells in two tiers by the base of the Wolfcamp - and the
|
|
539
|
+
Commission's December 2023 Notice to Operators names the data: four daily
|
|
540
|
+
parameters (maximum and average surface injection pressure, injection
|
|
541
|
+
volume, maximum injection rate) and three bottomhole-pressure methods, one
|
|
542
|
+
of which is a permanent downhole probe. `gea/sra.py` builds the packet to
|
|
543
|
+
that shape from what the site holds. The operator declares what the program
|
|
544
|
+
cannot measure (`set-well`: surface position and datum, API and UIC, depth
|
|
545
|
+
tier, which channels are the pressure, the rate and the downhole gauge, the
|
|
546
|
+
BHP method); `sra-define` puts the area on the site; `sra-report --catalog
|
|
547
|
+
texnet.csv` writes the packet - membership by geodesic distance, the daily
|
|
548
|
+
record per well rolled up by month, the catalogue's events against the plan
|
|
549
|
+
with the trigger and the goal clock, the schedule, and every gap by name -
|
|
550
|
+
and a daily export under the Notice's own parameter names. It will not
|
|
551
|
+
invent a volume for a well with no rate channel, a tier from depth alone, or
|
|
552
|
+
an aftershock: those are declarations, and the packet says so.
|
|
553
|
+
|
|
531
554
|
### Audit / Update
|
|
532
555
|
|
|
533
556
|
The Audit / Update tab holds three things. The whole audit log
|
|
@@ -86,7 +86,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
|
|
|
86
86
|
gea client-report --report accuracy --out client_report
|
|
87
87
|
gea model-cards --out model_cards
|
|
88
88
|
gea sbom --out sbom
|
|
89
|
-
gea accept # the product gate (
|
|
89
|
+
gea accept # the product gate (314 checks)
|
|
90
90
|
gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
|
|
91
91
|
gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
|
|
92
92
|
gea gui # the desktop window (pip install "gea-program[desktop]")
|
|
@@ -489,6 +489,29 @@ so one geophone can still say when each rig was working - and the Seismic
|
|
|
489
489
|
Station Report and the station page carry the section, with the tracks drawn
|
|
490
490
|
against time.
|
|
491
491
|
|
|
492
|
+
### The Seismicity Response Area packet
|
|
493
|
+
|
|
494
|
+
The Railroad Commission of Texas declares a Seismicity Response Area around a
|
|
495
|
+
cluster of felt earthquakes and expects the operators inside it to run a
|
|
496
|
+
response plan. The plans on file have one shape - a circle of about 9 km, a
|
|
497
|
+
plan written against M 3.5, an 18-month goal, a response group meeting
|
|
498
|
+
within 48 hours of a threshold event, quarterly checkpoints with Commission
|
|
499
|
+
staff, disposal wells in two tiers by the base of the Wolfcamp - and the
|
|
500
|
+
Commission's December 2023 Notice to Operators names the data: four daily
|
|
501
|
+
parameters (maximum and average surface injection pressure, injection
|
|
502
|
+
volume, maximum injection rate) and three bottomhole-pressure methods, one
|
|
503
|
+
of which is a permanent downhole probe. `gea/sra.py` builds the packet to
|
|
504
|
+
that shape from what the site holds. The operator declares what the program
|
|
505
|
+
cannot measure (`set-well`: surface position and datum, API and UIC, depth
|
|
506
|
+
tier, which channels are the pressure, the rate and the downhole gauge, the
|
|
507
|
+
BHP method); `sra-define` puts the area on the site; `sra-report --catalog
|
|
508
|
+
texnet.csv` writes the packet - membership by geodesic distance, the daily
|
|
509
|
+
record per well rolled up by month, the catalogue's events against the plan
|
|
510
|
+
with the trigger and the goal clock, the schedule, and every gap by name -
|
|
511
|
+
and a daily export under the Notice's own parameter names. It will not
|
|
512
|
+
invent a volume for a well with no rate channel, a tier from depth alone, or
|
|
513
|
+
an aftershock: those are declarations, and the packet says so.
|
|
514
|
+
|
|
492
515
|
### Audit / Update
|
|
493
516
|
|
|
494
517
|
The Audit / Update tab holds three things. The whole audit log
|
|
@@ -281,9 +281,36 @@ def main(argv=None) -> int:
|
|
|
281
281
|
p_ws.add_argument("--action", type=str, default="list",
|
|
282
282
|
choices=["init", "add-file", "add-catalog", "add-live", "remove", "list", "migrate", "refresh", "audit",
|
|
283
283
|
"add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
|
|
284
|
-
"seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename"
|
|
284
|
+
"seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
|
|
285
|
+
"set-well", "sra-define", "sra-report", "associate"])
|
|
285
286
|
p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
|
|
286
287
|
p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
|
|
288
|
+
p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
|
|
289
|
+
p_ws.add_argument("--uic", type=str, default=None, help="set-well: the UIC permit number")
|
|
290
|
+
p_ws.add_argument("--depth-tier", type=str, choices=["shallow", "deep"], default=None, help="set-well: above or below the named formation base")
|
|
291
|
+
p_ws.add_argument("--formation", type=str, default=None, help="set-well: the basis of the tier, e.g. 'completed below the base of the Wolfcamp'")
|
|
292
|
+
p_ws.add_argument("--channel-pressure", type=str, default=None, help="set-well: the channel that is the surface injection pressure (psi)")
|
|
293
|
+
p_ws.add_argument("--channel-rate", type=str, default=None, help="set-well: the channel that is the injection rate")
|
|
294
|
+
p_ws.add_argument("--rate-unit", type=str, default=None, help="set-well: the rate channel's unit: bbl/min or bbl/d")
|
|
295
|
+
p_ws.add_argument("--bhp-method", type=str, choices=["calculated", "dip_in", "probe"], default=None, help="set-well: how bottomhole pressure is known")
|
|
296
|
+
p_ws.add_argument("--channel-bhp", type=str, default=None, help="set-well: the downhole gauge channel (probe method)")
|
|
297
|
+
p_ws.add_argument("--top-interval-ft", type=float, default=None, help="set-well: top of the disposal interval, ft (calculated method)")
|
|
298
|
+
p_ws.add_argument("--gradient-psi-ft", type=float, default=None, help="set-well: fluid gradient, psi/ft (calculated method)")
|
|
299
|
+
p_ws.add_argument("--radius-km", type=float, default=None, help="sra-define: the area's radius in km (Gardendale: 9.08)")
|
|
300
|
+
p_ws.add_argument("--plan-date", type=str, default=None, help="sra-define: the plan date, YYYY-MM-DD")
|
|
301
|
+
p_ws.add_argument("--threshold-m", type=float, default=3.5, help="sra-define: the magnitude the plan is written against (3.5)")
|
|
302
|
+
p_ws.add_argument("--goal-months", type=int, default=18, help="sra-define: months without a threshold event that meet the goal (18)")
|
|
303
|
+
p_ws.add_argument("--response-hours", type=int, default=48, help="sra-define: hours within which the response group meets (48)")
|
|
304
|
+
p_ws.add_argument("--checkpoint-months", type=int, default=3, help="sra-define: months between checkpoints with Commission staff (3)")
|
|
305
|
+
p_ws.add_argument("--formation-boundary", type=str, default="base of the Wolfcamp", help="sra-define: the stratigraphic boundary between the two tiers")
|
|
306
|
+
p_ws.add_argument("--triggering-event", type=str, default="", help="sra-define: the catalogue id of the event the plan answers")
|
|
307
|
+
p_ws.add_argument("--catalog", type=str, default=None, help="sra-report: a TexNet earthquake-catalogue export (CSV)")
|
|
308
|
+
p_ws.add_argument("--aftershocks", type=str, nargs="*", default=None, help="sra-report: catalogue ids the operator declares as aftershocks (exempt)")
|
|
309
|
+
p_ws.add_argument("--start", type=str, default=None, help="sra-report: first day of the record, YYYY-MM-DD")
|
|
310
|
+
p_ws.add_argument("--end", type=str, default=None, help="sra-report: last day of the record, YYYY-MM-DD")
|
|
311
|
+
p_ws.add_argument("--vp", type=float, default=5.8, help="associate: the P velocity of the model, km/s (declared)")
|
|
312
|
+
p_ws.add_argument("--depth-km", type=float, default=6.0, help="associate: the hypocentral depth the location is made under, km (declared)")
|
|
313
|
+
p_ws.add_argument("--rms-tol", type=float, default=0.15, help="associate: the RMS residual an event must fit within, s")
|
|
287
314
|
p_ws.add_argument("--wells", type=str, nargs="+", default=None, help="add-site: well ids of this workspace")
|
|
288
315
|
p_ws.add_argument("--seismic", type=str, nargs="+", default=None, help="add-site: seismic station ids of this workspace")
|
|
289
316
|
p_ws.add_argument("--tracks", type=str, nargs="+", default=None, help="add-site: track ids of this workspace")
|
|
@@ -362,6 +389,9 @@ def main(argv=None) -> int:
|
|
|
362
389
|
p_w0.add_argument("--seed", type=int, default=1)
|
|
363
390
|
p_w0.add_argument("--connect", type=str, default=None, help="host:port - instead of listening, connect to a listening tap and push frames")
|
|
364
391
|
|
|
392
|
+
p_sra = sub.add_parser("sra", help="the Seismicity Response Area packet: the self-test on a labelled scene")
|
|
393
|
+
p_sra.add_argument("--action", choices=["selftest"], default="selftest")
|
|
394
|
+
p_sra.add_argument("--json", action="store_true")
|
|
365
395
|
p_dr = sub.add_parser("doctor", help="which code is running and can it serve: Python, the package, duplicates, the page, PyPI; with --workspace also the site folder and the port")
|
|
366
396
|
p_dr.add_argument("--workspace", type=str, default=None)
|
|
367
397
|
p_dr.add_argument("--host", type=str, default="127.0.0.1")
|
|
@@ -395,7 +425,8 @@ def main(argv=None) -> int:
|
|
|
395
425
|
p_se.add_argument("--action", choices=["info", "spectrum", "lines", "fetch", "stations", "detect", "selftest", "convert", "response", "remove-response",
|
|
396
426
|
"beam", "array-detect", "locate", "array-selftest", "sar-film", "bearings", "track", "track-selftest",
|
|
397
427
|
"signatures", "multi-beam", "multi-bearings", "multi-track", "signature-selftest", "field-selftest",
|
|
398
|
-
"harmonics", "harmonic-selftest", "datum", "geodesy-selftest", "array-qc", "qc-selftest", "unlisted", "unlisted-selftest", "bearing-sigma", "coverage", "uncertainty-selftest"
|
|
428
|
+
"harmonics", "harmonic-selftest", "datum", "geodesy-selftest", "array-qc", "qc-selftest", "unlisted", "unlisted-selftest", "bearing-sigma", "coverage", "uncertainty-selftest",
|
|
429
|
+
"assoc-selftest"], default="info")
|
|
399
430
|
p_se.add_argument("--signatures", type=str, default=None, help="multi-beam/multi-bearings: the signatures JSON from --action signatures")
|
|
400
431
|
p_se.add_argument("--histories", type=str, nargs="+", default=None, help="track: two or more bearing-history JSONs (from --action bearings), one per array")
|
|
401
432
|
p_se.add_argument("--truth", type=str, default=None, help="track: ground truth CSV (point_id, lat, lon, utc[, note]) - the lateral's points in time")
|
|
@@ -851,6 +882,18 @@ def main(argv=None) -> int:
|
|
|
851
882
|
+ (f"; verdict {v['verdict']} ({v['hit']} of {v['of']} inside their ellipse)" if v else "")
|
|
852
883
|
+ (f" - {row.get('detail', '')}" if row.get("detail") else ""))
|
|
853
884
|
return 0 if r["ok"] else 1
|
|
885
|
+
if a.action == "assoc-selftest":
|
|
886
|
+
from . import seismic_assoc as _SA
|
|
887
|
+
r = _SA.selftest()
|
|
888
|
+
print(f"association selftest [{r['label']}]: {r['status']} - {r['n_picks']} picks, {len(r['events'])} events, errors {r['errors_km']} km")
|
|
889
|
+
for k, v in r["checks"].items():
|
|
890
|
+
print(f" {'ok ' if v else 'BAD'} {k}")
|
|
891
|
+
for e in r["events"]:
|
|
892
|
+
print(f" {e['id']}: {e['origin_utc']} {e['lat']:.4f}, {e['lon']:.4f} {e['n_stations']} stations RMS {e['rms_s']} s "
|
|
893
|
+
f"misfit region {e['region_km']['extent_east_km']} x {e['region_km']['extent_north_km']} km magnitude: not estimated")
|
|
894
|
+
for x in r["catalogue"]["rows"]:
|
|
895
|
+
print(f" vs catalogue: {x['event']} {x['standing']}")
|
|
896
|
+
return 0 if r["status"] == "OK" else 1
|
|
854
897
|
if a.action == "uncertainty-selftest":
|
|
855
898
|
from . import seismic_uncertainty as UQ
|
|
856
899
|
r = UQ.selftest()
|
|
@@ -1443,6 +1486,18 @@ def main(argv=None) -> int:
|
|
|
1443
1486
|
return 0
|
|
1444
1487
|
print(f"notifications: {'configured' if n.cfg else 'not configured'}" + (f" - {len(n.cfg['channels'])} channel(s), {len(n.cfg['rules'])} rule(s), quiet {n.cfg['quiet_s']} s" if n.cfg else " (gea notify --example)"))
|
|
1445
1488
|
return 0
|
|
1489
|
+
elif a.cmd == "sra":
|
|
1490
|
+
from . import sra as _SRA
|
|
1491
|
+
r = _SRA.selftest()
|
|
1492
|
+
if a.json:
|
|
1493
|
+
print(json.dumps({k: v for k, v in r.items() if k != "packet"}, indent=1))
|
|
1494
|
+
else:
|
|
1495
|
+
print(f"sra selftest [{r['label']}]: {r['status']}")
|
|
1496
|
+
for k, v in r["checks"].items():
|
|
1497
|
+
print(f" {'ok ' if v else 'BAD'} {k}")
|
|
1498
|
+
print(f" deep well volume {r['deep_mean_volume_bbl_day']} bbl/day; probe offset {r['probe_offset_psi']} psi")
|
|
1499
|
+
print(_SRA.report_text(r["packet"]))
|
|
1500
|
+
return 0 if r["status"] == "OK" else 1
|
|
1446
1501
|
elif a.cmd == "doctor":
|
|
1447
1502
|
from .doctor import run as _doctor
|
|
1448
1503
|
return _doctor(a.workspace, a.host, a.port, a.json)
|
|
@@ -1630,6 +1685,44 @@ def main(argv=None) -> int:
|
|
|
1630
1685
|
if not a.site:
|
|
1631
1686
|
raise SystemExit("remove-site needs --site <id>")
|
|
1632
1687
|
ws.remove_site(a.site, actor=a.actor); print("removed (folder kept):", a.site); return 0
|
|
1688
|
+
elif a.action == "set-well":
|
|
1689
|
+
if not a.well:
|
|
1690
|
+
raise SystemExit("set-well needs --well <id>")
|
|
1691
|
+
surface = {"lat": a.lat, "lon": a.lon, "datum": a.datum} if (a.lat is not None or a.lon is not None) else None
|
|
1692
|
+
disposal = {k: v for k, v in {"api_number": a.api, "uic_number": a.uic, "depth_tier": a.depth_tier, "formation_basis": a.formation,
|
|
1693
|
+
"channel_pressure": a.channel_pressure, "channel_rate": a.channel_rate, "rate_unit": a.rate_unit,
|
|
1694
|
+
"bhp_method": a.bhp_method, "channel_bhp": a.channel_bhp, "top_interval_ft": a.top_interval_ft,
|
|
1695
|
+
"gradient_psi_ft": a.gradient_psi_ft}.items() if v is not None}
|
|
1696
|
+
if not surface and not disposal:
|
|
1697
|
+
raise SystemExit("set-well: nothing to declare (give --lat/--lon, or any of --api --uic --depth-tier --channel-pressure ...)")
|
|
1698
|
+
w = ws.set_well(a.well, surface=surface, disposal=disposal or None, actor=a.actor)
|
|
1699
|
+
print(f"well {a.well}: surface {w.get('surface')}; disposal {w.get('disposal')}"); return 0
|
|
1700
|
+
elif a.action == "sra-define":
|
|
1701
|
+
if not a.site or not a.name or a.lat is None or a.lon is None or a.radius_km is None or not a.plan_date:
|
|
1702
|
+
raise SystemExit("sra-define needs --site, --name, --lat, --lon, --radius-km and --plan-date")
|
|
1703
|
+
sra = ws.sra_define(a.site, actor=a.actor, name=a.name, lat=a.lat, lon=a.lon, radius_km=a.radius_km, plan_date=a.plan_date,
|
|
1704
|
+
datum=a.datum or "WGS84", threshold_m=a.threshold_m, goal_months=a.goal_months, response_hours=a.response_hours,
|
|
1705
|
+
checkpoint_months=a.checkpoint_months, formation_boundary=a.formation_boundary,
|
|
1706
|
+
triggering_event=a.triggering_event, note=a.note or "")
|
|
1707
|
+
print(f"sra: {sra['name']} on site {a.site} - {sra['radius_km']} km around {sra['centre']['lat']}, {sra['centre']['lon']} "
|
|
1708
|
+
f"({sra['centre']['datum']}); plan {sra['plan_date']}; M {sra['threshold_m']}; goal {sra['goal_months']} months"); return 0
|
|
1709
|
+
elif a.action == "sra-report":
|
|
1710
|
+
from .sra import report_text as _sra_text
|
|
1711
|
+
if not a.site:
|
|
1712
|
+
raise SystemExit("sra-report needs --site <id>")
|
|
1713
|
+
r = ws.write_sra_packet(a.site, catalog_csv=a.catalog, aftershocks=a.aftershocks, start=a.start, end=a.end, actor=a.actor)
|
|
1714
|
+
print(_sra_text(r["packet"]))
|
|
1715
|
+
print(" report:", r["paths"]["html"]); print(" daily export:", r["paths"]["daily_csv"], f"({r['export_rows']} rows)"); return 0
|
|
1716
|
+
elif a.action == "associate":
|
|
1717
|
+
from .seismic_assoc import report_text as _assoc_text
|
|
1718
|
+
if not a.site:
|
|
1719
|
+
raise SystemExit("associate needs --site <id>")
|
|
1720
|
+
r = ws.associate_site(a.site, vp_km_s=a.vp, depth_km=a.depth_km, band=a.band, start=a.start, end=a.end, catalog_csv=a.catalog,
|
|
1721
|
+
rms_tol_s=a.rms_tol, actor=a.actor)
|
|
1722
|
+
for sid, x in r["stations"].items():
|
|
1723
|
+
print(f" {sid}: {x['status']}, {x['picks']} pick(s)")
|
|
1724
|
+
print(_assoc_text(r["association"], r["catalogue"]))
|
|
1725
|
+
print(" written:", os.path.join(ws.path, "reports", "sites", a.site, "association.json")); return 0
|
|
1633
1726
|
elif a.action == "seismic-dataset":
|
|
1634
1727
|
r = ws.write_seismic_dataset_report(actor=a.actor)
|
|
1635
1728
|
inv = r["inventory"]
|
|
@@ -2914,7 +2914,8 @@ def section_am_report_samples(tmp: str) -> None:
|
|
|
2914
2914
|
"model_card_well_baseline.html", "model_card_well_test_detector.html", "model_card_rock_density_inventory.html",
|
|
2915
2915
|
"model_card_strata_property_estimator.html", "gauge_drift_report_monitored_SYNTHETIC.html", "alarm_event_report_SYNTHETIC.html",
|
|
2916
2916
|
"data_resilience_report_SYNTHETIC.html", "sla_report_SYNTHETIC.html", "sat_protocol.html", "seismic_station_report_SYNTHETIC.html", "seismic_track_report_SYNTHETIC.html",
|
|
2917
|
-
"seismic_dataset_report_SYNTHETIC.html", "seismic_field_report_SYNTHETIC.html", "site_report_SYNTHETIC.html"
|
|
2917
|
+
"seismic_dataset_report_SYNTHETIC.html", "seismic_field_report_SYNTHETIC.html", "site_report_SYNTHETIC.html",
|
|
2918
|
+
"sra_packet_SYNTHETIC.html"}
|
|
2918
2919
|
have = {p.name for p in d.glob("*.html")}
|
|
2919
2920
|
md = (d / "SAMPLES.md").read_text(encoding="utf-8") if (d / "SAMPLES.md").exists() else ""
|
|
2920
2921
|
texts = {n: (d / n).read_text(encoding="utf-8", errors="replace") for n in sorted(have & want)}
|
|
@@ -4593,9 +4594,248 @@ def section_bb_supervisor(tmp: str) -> None:
|
|
|
4593
4594
|
"panel's own page carries the Stop and Restart controls, the authorisation step behind a popup, and the auto-restart switch")
|
|
4594
4595
|
|
|
4595
4596
|
|
|
4597
|
+
def section_bc_sra(tmp: str) -> None:
|
|
4598
|
+
"""Section BC - the Seismicity Response Area packet. The Railroad Commission's operator-led response plans
|
|
4599
|
+
and its Notice to Operators on disposal-well monitoring name a shape; the packet is built to it from what
|
|
4600
|
+
the site holds, and names what it is missing rather than leaving a column blank."""
|
|
4601
|
+
import csv as _csv
|
|
4602
|
+
import subprocess as _sp
|
|
4603
|
+
from datetime import datetime as _dt, timedelta as _td, timezone as _tz
|
|
4604
|
+
from . import sra as SR
|
|
4605
|
+
from .client_reports import forbidden_terms, sra_packet_report, write
|
|
4606
|
+
from .workspace import Workspace, WorkspaceError
|
|
4607
|
+
pkg = Path(__file__).parent
|
|
4608
|
+
d = Path(tmp, "bc"); d.mkdir()
|
|
4609
|
+
|
|
4610
|
+
# -- the self-test: the labelled scene ----------------------------------------------
|
|
4611
|
+
r = SR.selftest()
|
|
4612
|
+
ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
|
|
4613
|
+
and abs(r["deep_mean_volume_bbl_day"] - 8640.0) / 8640.0 < 0.03 and abs(r["probe_offset_psi"] - 0.465 * 9800) < 15,
|
|
4614
|
+
"BC1 the self-test on a labelled scene: membership by geodesic distance with a NAD27 well's shift named, the four daily parameters from "
|
|
4615
|
+
"the well's own channels with the volume integrated from the rate to within 3 %, two shut-in days at zero, the probe's offset from "
|
|
4616
|
+
"surface pressure recovered, the shallow well refusing a volume it has no rate channel for, the catalogue's trigger and exempt "
|
|
4617
|
+
"aftershock and goal clock, the gaps named, the checkpoints from the plan date")
|
|
4618
|
+
|
|
4619
|
+
# -- the area: declarations, refused when not an area --------------------------------
|
|
4620
|
+
sra = SR.define("Test SRA", 31.95, -102.25, 9.08, "2026-02-25", "NAD27", triggering_event="x")
|
|
4621
|
+
bad = 0
|
|
4622
|
+
for kw in ({"datum": "UNKNOWN"}, {"radius_km": 0}, {"plan_date": "not a date"}, {"name": " "}):
|
|
4623
|
+
try:
|
|
4624
|
+
SR.define(**{**{"name": "T", "lat": 31.95, "lon": -102.25, "radius_km": 9.08, "plan_date": "2026-02-25", "datum": "WGS84"}, **kw});
|
|
4625
|
+
except ValueError:
|
|
4626
|
+
bad += 1
|
|
4627
|
+
cps = SR.checkpoints(sra)
|
|
4628
|
+
ok(sra["centre"]["datum"] == "NAD27" and sra["centre_wgs84"]["shift_m"] > 10 and sra["area_km2"] == 259.0 and sra["threshold_m"] == 3.5
|
|
4629
|
+
and sra["goal_months"] == 18 and sra["response_hours"] == 48 and sra["checkpoint_months"] == 3 and bad == 4
|
|
4630
|
+
and [c["date"] for c in cps][:2] == ["2026-05-25", "2026-08-25"] and cps[-1]["n"] == "goal" and cps[-1]["date"] == "2027-08-25"
|
|
4631
|
+
and "not a measurement" in " ".join(sra["not_a_measurement"]).lower().replace("never derived here", "not a measurement"),
|
|
4632
|
+
"BC2 an area is a centre on a stated datum (carried to WGS84 with its shift named), a radius, a plan date and the plan's numbers - "
|
|
4633
|
+
"M 3.5, 18 months, 48 hours, quarterly - recorded as declarations; an unknown datum, a zero radius, a non-date or a blank name is refused; "
|
|
4634
|
+
"the checkpoints fall every three months from the plan date and the goal date closes the list")
|
|
4635
|
+
|
|
4636
|
+
# -- the catalogue reader: a TexNet-shaped export ------------------------------------
|
|
4637
|
+
cat = d / "texnet.csv"
|
|
4638
|
+
with open(cat, "w", newline="") as f:
|
|
4639
|
+
w = _csv.writer(f); w.writerow(["EventID", "Origin Date", "Origin Time", "Local Magnitude", "Latitude", "Longitude", "Depth of Hypocenter (Km)"])
|
|
4640
|
+
w.writerow(["texnet2026aaaa", "2026-02-20", "04:15:10", "4.4", "31.950", "-102.250", "7.2"]) # before the plan date
|
|
4641
|
+
w.writerow(["texnet2026aaab", "2026-03-05", "11:02:33", "3.7", "31.960", "-102.240", "6.8"]) # counts
|
|
4642
|
+
w.writerow(["texnet2026aaac", "2026-03-06", "01:00:00", "3.6", "31.955", "-102.245", "6.5"]) # declared aftershock
|
|
4643
|
+
w.writerow(["texnet2026aaad", "2026-03-09", "21:40:00", "2.0", "31.940", "-102.260", "6.1"])
|
|
4644
|
+
w.writerow(["texnet2026aaae", "2026-03-12", "03:00:00", "3.9", "32.500", "-102.700", "8.0"]) # outside
|
|
4645
|
+
w.writerow(["", "", "", "", "", "", ""]) # a blank line is skipped, not an event
|
|
4646
|
+
c = SR.read_catalog(str(cat))
|
|
4647
|
+
se = SR.seismicity(sra, c, as_of=_dt(2026, 4, 1, tzinfo=_tz.utc), aftershocks=["texnet2026aaac"])
|
|
4648
|
+
ok(c["n_events"] == 5 and c["n_skipped"] == 1 and c["columns"]["origin_date"] == "Origin Date" and c["columns"]["origin_time"] == "Origin Time"
|
|
4649
|
+
and c["events"][0]["time"] == "2026-02-20T04:15:10Z" and c["events"][0]["depth_km"] == 7.2
|
|
4650
|
+
and se["n_inside"] == 4 and se["n_at_or_above"] == 3 and se["n_counted"] == 1 and se["n_exempt"] == 1
|
|
4651
|
+
and se["largest"]["event_id"] == "texnet2026aaaa" and se["trigger"]["status"] == "TRIGGERED" and se["trigger"]["event_id"] == "texnet2026aaab"
|
|
4652
|
+
and se["trigger"]["respond_by"] == "2026-03-07T11:02:33Z" and se["goal"]["clock_started"] == "2026-03-05" and se["goal"]["status"] == "RUNNING"
|
|
4653
|
+
and se["goal"]["goal_date"] == "2027-09-05",
|
|
4654
|
+
"BC3 a TexNet-shaped export is read by its column names (date and time in separate columns, the depth under its long name), a blank line "
|
|
4655
|
+
"is skipped and counted; against the plan: the M 4.4 before the plan date is the largest but does not count, the declared aftershock is "
|
|
4656
|
+
"exempt, the M 3.9 outside the circle is outside, the M 3.7 triggers the 48-hour response with its deadline, and the 18-month clock "
|
|
4657
|
+
"starts from it")
|
|
4658
|
+
|
|
4659
|
+
# -- the daily record from a stream: refused without a calendar --------------------------------
|
|
4660
|
+
from .ports import LiveStream, StreamChannel
|
|
4661
|
+
import numpy as _np
|
|
4662
|
+
st = LiveStream(name="x", source_format="synthetic", index_kind="time_s", index=_np.arange(100) * 600.0,
|
|
4663
|
+
channels={"P": StreamChannel("P", "psi", _np.full(100, 1500.0))}, meta={})
|
|
4664
|
+
rr = SR.daily_records(st, {"channel_pressure": "P"})
|
|
4665
|
+
sc = SR.synthetic_scene(days=3)
|
|
4666
|
+
dr = SR.daily_records(sc["streams"]["deep-1"], {**sc["wells"][0]["disposal"], "rate_unit": "furlongs"})
|
|
4667
|
+
ok(rr["status"] == "REFUSED" and "calendar" in rr["detail"]
|
|
4668
|
+
and dr["parameters"]["injection_volume_bbl"]["status"] == "NOT RECORDED" and "furlongs" in dr["parameters"]["injection_volume_bbl"]["why"]
|
|
4669
|
+
and dr["parameters"]["max_surface_injection_pressure_psi"]["status"] == "RECORDED",
|
|
4670
|
+
"BC4 a daily record needs a calendar: a stream with no start time is refused, not dated from zero; a rate in a unit the program does not "
|
|
4671
|
+
"convert leaves the volume NOT RECORDED with the unit named, while the pressures are still recorded")
|
|
4672
|
+
|
|
4673
|
+
# -- through the workspace and the command line ----------------------------------------
|
|
4674
|
+
t0 = _dt(2026, 3, 1, tzinfo=_tz.utc); n = 6 * 144
|
|
4675
|
+
hist = d / "swd.csv"
|
|
4676
|
+
with open(hist, "w", newline="") as f:
|
|
4677
|
+
w = _csv.writer(f); w.writerow(["timestamp", "P_surf_psi", "Q_bpm", "P_bh_psi"])
|
|
4678
|
+
for i in range(n):
|
|
4679
|
+
w.writerow([(t0 + _td(seconds=600 * i)).strftime("%Y-%m-%dT%H:%M:%SZ"), "1500.0", "5.0", f"{1500.0 + 0.465 * 9000:.1f}"])
|
|
4680
|
+
ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
|
|
4681
|
+
w1 = ws.add_well_file(str(hist), display="SWD 1", actor="tester")
|
|
4682
|
+
try:
|
|
4683
|
+
ws.set_well(w1["id"], disposal={"depth_tier": "middle"}, actor="tester"); tier_bad = False
|
|
4684
|
+
except WorkspaceError:
|
|
4685
|
+
tier_bad = True
|
|
4686
|
+
run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
|
|
4687
|
+
r1 = run("--action", "set-well", "--well", w1["id"], "--lat", "31.96", "--lon", "-102.24", "--datum", "NAD27", "--api", "42-329-12345", "--uic", "123456",
|
|
4688
|
+
"--depth-tier", "deep", "--formation", "completed below the base of the Wolfcamp", "--channel-pressure", "P_surf_psi", "--channel-rate", "Q_bpm",
|
|
4689
|
+
"--rate-unit", "bbl/min", "--bhp-method", "probe", "--channel-bhp", "P_bh_psi")
|
|
4690
|
+
r2 = run("--action", "add-site", "--name", "SRA block", "--wells", w1["id"], "--client", "Acme Operating")
|
|
4691
|
+
r3 = run("--action", "sra-report", "--site", "SRA-block") # no area yet: refused
|
|
4692
|
+
r4 = run("--action", "sra-define", "--site", "SRA-block", "--name", "Test SRA", "--lat", "31.95", "--lon", "-102.25", "--datum", "WGS84",
|
|
4693
|
+
"--radius-km", "9.08", "--plan-date", "2026-02-25")
|
|
4694
|
+
r5 = run("--action", "sra-report", "--site", "SRA-block", "--catalog", str(cat), "--aftershocks", "texnet2026aaac")
|
|
4695
|
+
ws2 = Workspace(ws.path)
|
|
4696
|
+
out = Path(ws.path, "reports", "sites", "SRA-block")
|
|
4697
|
+
rows = list(_csv.DictReader(open(out / "sra_daily_export.csv", encoding="utf-8")))
|
|
4698
|
+
md = (out / "sra_packet.md").read_text(encoding="utf-8")
|
|
4699
|
+
audit = Path(ws.path, "records", "audit.jsonl").read_text(encoding="utf-8")
|
|
4700
|
+
pkj = json.loads((out / "sra_packet.json").read_text(encoding="utf-8"))["packet"]
|
|
4701
|
+
ok(tier_bad and r1.returncode == 0 and r2.returncode == 0 and r3.returncode != 0 and "sra-define" in (r3.stderr + r3.stdout)
|
|
4702
|
+
and r4.returncode == 0 and r5.returncode == 0 and "COMPLETE" in r5.stdout and "TRIGGERED" in r5.stdout
|
|
4703
|
+
and ws2.site("SRA-block")["sra"]["name"] == "Test SRA" and pkj["status"] == "COMPLETE" and pkj["wells"][0]["inside"] and pkj["wells"][0]["depth_tier"] == "deep"
|
|
4704
|
+
and len(rows) == 6 and rows[0]["API number"] == "42-329-12345" and rows[0]["UIC permit number"] == "123456"
|
|
4705
|
+
and abs(float(rows[1]["Injection volume (barrels per day)"]) - 7200.0) < 10 and rows[1]["Maximum surface injection pressure (pounds per square inch)"] == "1500.0"
|
|
4706
|
+
and abs(float(rows[1]["Bottomhole pressure (pounds per square inch)"]) - (1500 + 0.465 * 9000)) < 1 and rows[1]["BHP method"] == "probe"
|
|
4707
|
+
and "Maximum surface injection pressure (pounds per square inch)" in rows[0]
|
|
4708
|
+
and not forbidden_terms(md) and "well.declare" in audit and "site.sra_define" in audit and "site.sra_packet" in audit,
|
|
4709
|
+
"BC5 `set-well` records what the operator declares - surface position and datum, API and UIC, depth tier by the named formation, which "
|
|
4710
|
+
"channels are the surface pressure, the rate and the downhole gauge, the BHP method - and refuses a tier that is not shallow or deep; "
|
|
4711
|
+
"`sra-define` puts the area on the site; `sra-report` is refused until it does; then the packet is COMPLETE for a well with everything "
|
|
4712
|
+
"declared, the daily export carries the Notice's parameter names with the API and UIC on every line, the volume integrates to 7,200 bbl "
|
|
4713
|
+
"a day from 5 bbl/min, the probe is the bottomhole pressure, the report passes the vocabulary gate, and every step is audited")
|
|
4714
|
+
|
|
4715
|
+
# -- the service offers it and the page carries it ------------------------------------------
|
|
4716
|
+
svc_src = (pkg / "service.py").read_text(encoding="utf-8"); page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
4717
|
+
r6 = _sp.run([sys.executable, "-m", "gea", "sra"], capture_output=True, text=True)
|
|
4718
|
+
ok("'sra_report'" in svc_src and "sra_packet.html" in svc_src and "Seismicity Response Area</th>" in page_src and "sra-define" in page_src
|
|
4719
|
+
and r6.returncode == 0 and "SIMULATION_SELF_TEST" in r6.stdout and "OK" in r6.stdout
|
|
4720
|
+
and (pkg / "help" / "sra.md").is_file(),
|
|
4721
|
+
"BC6 the Sites page carries the area and the packet's status beside each site; `gea sra` runs the self-test from the command line and "
|
|
4722
|
+
"labels it SIMULATION_SELF_TEST; the help page exists")
|
|
4723
|
+
|
|
4724
|
+
|
|
4725
|
+
def section_bd_association(tmp: str) -> None:
|
|
4726
|
+
"""Section BD - association: several stations heard the same thing, or they did not. The onset picker,
|
|
4727
|
+
the associator with its grid search and its refusals, the location with its misfit region, the array as
|
|
4728
|
+
one station, the catalogue comparison, and the workspace run that feeds the SRA packet."""
|
|
4729
|
+
import csv as _csv
|
|
4730
|
+
import subprocess as _sp
|
|
4731
|
+
import numpy as _np
|
|
4732
|
+
from . import seismic as S
|
|
4733
|
+
from . import seismic_assoc as SA
|
|
4734
|
+
from .workspace import Workspace, WorkspaceError
|
|
4735
|
+
d = Path(tmp, "bd"); d.mkdir()
|
|
4736
|
+
|
|
4737
|
+
# -- the self-test -----------------------------------------------------------------------
|
|
4738
|
+
r = SA.selftest()
|
|
4739
|
+
ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
|
|
4740
|
+
and r["errors_km"]["ev-A"] < 1.0 and r["errors_km"]["ev-B"] < 1.0,
|
|
4741
|
+
"BD1 the self-test on a labelled scene: picks at every station, two events located to within a kilometre and a third of a second with all "
|
|
4742
|
+
"five stations and RMS under 0.05 s, a two-station event refused rather than located, a spurious burst left unused, finite misfit "
|
|
4743
|
+
"regions, no magnitude, and against the catalogue one AGREES, one DIFFERS by its separation and one catalogue event the stations did "
|
|
4744
|
+
"not associate")
|
|
4745
|
+
|
|
4746
|
+
# -- the picker: the onset is the first energy, not half a window early ------------------
|
|
4747
|
+
sc = SA.synthetic_scene()
|
|
4748
|
+
fr = sc["frame"]
|
|
4749
|
+
from .seismic_array import local_xy
|
|
4750
|
+
devs = []
|
|
4751
|
+
for sid, s in sc["stations"].items():
|
|
4752
|
+
ex, ey = local_xy(sc["truth"][0]["lat"], sc["truth"][0]["lon"], fr["lat0"], fr["lon0"])
|
|
4753
|
+
tt = SA.travel_time(ex, ey, s, sc["vp"], sc["depth_km"])
|
|
4754
|
+
pk = [p for p in SA.picks(sc["traces"][sid], band=(2.0, 20.0)) if abs(p.time - (sc["truth"][0]["t0"] + tt)) < 2.0]
|
|
4755
|
+
devs.append(pk[0].time - sc["truth"][0]["t0"] - tt if pk else 9.0)
|
|
4756
|
+
quiet = S.Trace("XX", "Q", "", "HHZ", 0.0, 100.0, _np.random.default_rng(1).normal(0, 1, 60000).astype(_np.float32), "synthetic", "FLOAT32")
|
|
4757
|
+
ok(all(abs(x) < 0.08 for x in devs) and not SA.picks(quiet, band=(2.0, 20.0)) and all(p.snr > 10 for p in SA.picks(sc["traces"]["STA5"], band=(2.0, 20.0))),
|
|
4758
|
+
"BD2 the picker puts every onset within 80 ms of the true arrival (the first refinement walked back into the noise and every pick came "
|
|
4759
|
+
"out 150 ms early, which the location absorbed into the origin time and nobody would have seen), picks nothing on a minute of plain "
|
|
4760
|
+
"noise, and carries a signal-to-noise ratio on every pick")
|
|
4761
|
+
|
|
4762
|
+
# -- the locator: refuses two stations, refines about the coarse best, names the model ----
|
|
4763
|
+
two = SA.locate({"STA1": 10.0, "STA2": 10.5}, sc["stations"], sc["vp"], sc["depth_km"])
|
|
4764
|
+
ex, ey = local_xy(sc["truth"][1]["lat"], sc["truth"][1]["lon"], fr["lat0"], fr["lon0"])
|
|
4765
|
+
pset = {sid: sc["truth"][1]["t0"] + SA.travel_time(ex, ey, s, sc["vp"], sc["depth_km"]) for sid, s in sc["stations"].items()}
|
|
4766
|
+
exact = SA.locate(pset, sc["stations"], sc["vp"], sc["depth_km"])
|
|
4767
|
+
off = SA.locate({**pset, "STA2": pset["STA2"] + 0.6}, sc["stations"], sc["vp"], sc["depth_km"])
|
|
4768
|
+
ok(two["status"] == "NOT ASSOCIATED" and "three" in two["detail"]
|
|
4769
|
+
and exact["status"] == "LOCATED" and abs(exact["best"]["x_km"] - ex) < 0.1 and abs(exact["best"]["y_km"] - ey) < 0.1 and exact["best"]["rms_s"] < 0.01
|
|
4770
|
+
and abs(exact["best"]["t0"] - sc["truth"][1]["t0"]) < 0.02 and not exact["region_km"]["reaches_grid_edge"]
|
|
4771
|
+
and "flat earth" in exact["model"]["basis"] and exact["model"]["depth_km"] == sc["depth_km"]
|
|
4772
|
+
and off["best"]["rms_s"] > 0.15 and abs(off["residuals_s"]["STA2"]) == max(abs(v) for v in off["residuals_s"].values()),
|
|
4773
|
+
"BD3 the locator refuses two stations, finds exact picks to within 100 m of the true epicentre and 20 ms of the origin time (the first "
|
|
4774
|
+
"version refined about the stations' centroid instead of the coarse best node and lost every event more than a kilometre from the "
|
|
4775
|
+
"centre), names the straight-ray flat-earth model and the declared depth on the result, and a pick 0.6 s off shows as the largest residual")
|
|
4776
|
+
|
|
4777
|
+
# -- the associator: the outlier is dropped, the event stands on the rest ----------------
|
|
4778
|
+
picks_all = []
|
|
4779
|
+
for sid, tr in sc["traces"].items():
|
|
4780
|
+
picks_all += SA.picks(tr, band=(2.0, 20.0))
|
|
4781
|
+
bad = [SA.Pick("STA4", sc["truth"][0]["t0"] + 3.2, 50.0, 1.0)] # a wrong pick at STA4 near event A
|
|
4782
|
+
picks_mix = [p for p in picks_all if not (p.station == "STA4" and abs(p.time - sc["truth"][0]["t0"]) < 3.0)] + bad
|
|
4783
|
+
res = SA.associate(picks_mix, sc["stations"], sc["vp"], sc["depth_km"], frame_info=fr)
|
|
4784
|
+
evA = sorted(res["events"], key=lambda e: e["origin_utc"])[0]
|
|
4785
|
+
ok(res["n_events"] == 2 and evA["n_stations"] == 4 and evA["dropped"] == ["STA4"] and evA["rms_s"] < 0.05
|
|
4786
|
+
and evA["magnitude"] is None and "not estimated" in evA["magnitude_basis"],
|
|
4787
|
+
"BD4 a wrong pick at one station is dropped as the largest residual and the event stands on the other four; an event carries no "
|
|
4788
|
+
"magnitude and says why")
|
|
4789
|
+
|
|
4790
|
+
# -- an array is one station ----------------------------------------------------------------
|
|
4791
|
+
raw = [SA.Pick("ARR", 100.0 + k * 0.03, 20.0 + k, 1.0, channel=f"S{k}") for k in range(6)] + [SA.Pick("ARR", 130.0, 9.0, 0.5, channel="S3")]
|
|
4792
|
+
col = SA.collapse_array_picks(raw, "ARR")
|
|
4793
|
+
ok(len(col) == 1 and abs(col[0].time - 100.075) < 0.02 and "6 sensor" in col[0].channel,
|
|
4794
|
+
"BD5 an array's sensors' picks within half a second become one pick at the median time at the array's position, and a burst one sensor "
|
|
4795
|
+
"alone saw is not an arrival at the array")
|
|
4796
|
+
|
|
4797
|
+
# -- through the workspace and the command line, into the packet ---------------------------
|
|
4798
|
+
for sid, tr in sc["traces"].items():
|
|
4799
|
+
S.write_mseed([tr], str(d / f"{sid}.mseed"))
|
|
4800
|
+
cat = d / "cat.csv"
|
|
4801
|
+
with open(cat, "w", newline="") as f:
|
|
4802
|
+
w = _csv.writer(f); w.writerow(["EventID", "Origin Time", "Local Magnitude", "Latitude", "Longitude", "Depth"])
|
|
4803
|
+
for c in sc["catalog"]:
|
|
4804
|
+
w.writerow([c["event_id"], c["time"], c["magnitude"], c["lat"], c["lon"], 6.0])
|
|
4805
|
+
ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
|
|
4806
|
+
ids = [ws.add_seismic_station(sid, [str(d / f"{sid}.mseed")], st.lat, st.lon, "tester", datum=st.datum, band=[2.0, 20.0])["id"]
|
|
4807
|
+
for sid, st in sc["stations"].items()]
|
|
4808
|
+
ws.add_site("Assoc block", seismic=ids[:2], client="Acme", actor="tester")
|
|
4809
|
+
try:
|
|
4810
|
+
ws.associate_site("Assoc-block"); few = False
|
|
4811
|
+
except WorkspaceError as e:
|
|
4812
|
+
few = "three" in str(e)
|
|
4813
|
+
ws.add_site("Assoc block 5", seismic=ids, client="Acme", actor="tester")
|
|
4814
|
+
run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
|
|
4815
|
+
r1 = run("--action", "associate", "--site", "Assoc-block-5", "--catalog", str(cat))
|
|
4816
|
+
r2 = run("--action", "sra-define", "--site", "Assoc-block-5", "--name", "Test SRA", "--lat", "31.95", "--lon", "-102.25", "--radius-km", "9.08", "--plan-date", "2026-02-25")
|
|
4817
|
+
r3 = run("--action", "sra-report", "--site", "Assoc-block-5", "--catalog", str(cat))
|
|
4818
|
+
aj = json.loads(Path(ws.path, "reports", "sites", "Assoc-block-5", "association.json").read_text(encoding="utf-8"))
|
|
4819
|
+
md = Path(ws.path, "reports", "sites", "Assoc-block-5", "sra_packet.md").read_text(encoding="utf-8")
|
|
4820
|
+
pk = json.loads(Path(ws.path, "reports", "sites", "Assoc-block-5", "sra_packet.json").read_text(encoding="utf-8"))["packet"]
|
|
4821
|
+
audit = Path(ws.path, "records", "audit.jsonl").read_text(encoding="utf-8")
|
|
4822
|
+
r4 = _sp.run([sys.executable, "-m", "gea", "seismic", "--action", "assoc-selftest"], capture_output=True, text=True)
|
|
4823
|
+
ok(few and r1.returncode == 0 and "2 event(s)" in r1.stdout and "AGREES" in r1.stdout and "DIFFERS" in r1.stdout and "NOT ASSOCIATED BY THE STATIONS" in r1.stdout
|
|
4824
|
+
and aj["association"]["n_events"] == 2 and all(v["status"] == "PICKED" for v in aj["stations"].values())
|
|
4825
|
+
and r2.returncode == 0 and r3.returncode == 0 and pk["association"]["n_inside"] == 2
|
|
4826
|
+
and "Events located by the site's own stations" in md and "AGREES" in md and "no magnitude is estimated here" in md
|
|
4827
|
+
and "site.associate" in audit and r4.returncode == 0 and "SIMULATION_SELF_TEST" in r4.stdout,
|
|
4828
|
+
"BD6 `--action associate` refuses a site with fewer than three stations, picks five miniSEED stations, associates and locates two events, "
|
|
4829
|
+
"sets them against the catalogue, writes association.json and audits it; the SRA packet then carries the events the site's own stations "
|
|
4830
|
+
"located beside the catalogue's, inside or outside the area, with the model named and no magnitude; `gea seismic --action assoc-selftest` "
|
|
4831
|
+
"runs the labelled scene from the command line")
|
|
4832
|
+
|
|
4833
|
+
|
|
4596
4834
|
def main() -> int:
|
|
4597
4835
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
4598
|
-
|
|
4836
|
+
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
4837
|
+
# the checks have already passed or failed on their own, and the folder is the temp folder
|
|
4838
|
+
with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp:
|
|
4599
4839
|
section_a_cli(tmp)
|
|
4600
4840
|
section_b_las(tmp)
|
|
4601
4841
|
section_c_reconciler(tmp)
|
|
@@ -4648,6 +4888,8 @@ def main() -> int:
|
|
|
4648
4888
|
section_az_uncertainty(tmp)
|
|
4649
4889
|
section_ba_sites(tmp)
|
|
4650
4890
|
section_bb_supervisor(tmp)
|
|
4891
|
+
section_bc_sra(tmp)
|
|
4892
|
+
section_bd_association(tmp)
|
|
4651
4893
|
if _FAILS:
|
|
4652
4894
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
4653
4895
|
for f in _FAILS:
|