gea-program 0.11.1__tar.gz → 0.13.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.11.1/gea_program.egg-info → gea_program-0.13.0}/PKG-INFO +38 -2
- {gea_program-0.11.1 → gea_program-0.13.0}/README.md +37 -1
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/__init__.py +1 -1
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/__main__.py +51 -2
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/acceptance_tests.py +243 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/client_reports.py +17 -0
- gea_program-0.13.0/gea/help/ppdm.md +11 -0
- gea_program-0.13.0/gea/help/quakeml.md +11 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/helplib.py +2 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/osdu.py +18 -3
- gea_program-0.13.0/gea/ppdm.py +291 -0
- gea_program-0.13.0/gea/quakeml.py +525 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/sra.py +19 -2
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/workspace.py +53 -2
- {gea_program-0.11.1 → gea_program-0.13.0/gea_program.egg-info}/PKG-INFO +38 -2
- {gea_program-0.11.1 → gea_program-0.13.0}/gea_program.egg-info/SOURCES.txt +4 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/pyproject.toml +1 -1
- {gea_program-0.11.1 → gea_program-0.13.0}/LICENSE +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/bench.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/blind_harness.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/certificates.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/cli.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/config_versioning.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/correlation.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/dashboard.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/deviation.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/doctor.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/earth_model.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/example_register_map.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/fat_sat.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/files.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/follower.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/forward_model.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/gamma.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/geodesy.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/alarms.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/data-in.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/doctor.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/drift.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/files.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/instruments.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/notifications.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/osdu.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/patches.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/quality.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/seismic.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/site.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/sites.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/sra.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/start.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/transients.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/housekeeping.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/jobs.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/live_ports.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/loadtest.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/modbus.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/model_card.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/notify.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/opcua_port.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/operator_app.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/patches.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/permits.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/ports.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/project.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reconciler.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/sample_record.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/sbom.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/segy.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_array.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_film.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_response.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_track.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/service.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/service_life.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/shell.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/shut_in.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/sla_report.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/store_forward.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/strata_join.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/supervisor.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/survey_view.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/telemetry.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/tool_library.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/transient.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/web/app.html +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/well_assembler.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/wits0.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea/witsml.py +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.11.1 → gea_program-0.13.0}/setup.cfg +0 -0
- {gea_program-0.11.1 → gea_program-0.13.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.13.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 (327 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]")
|
|
@@ -566,6 +566,42 @@ operation between them written out; a point on an unknown datum gets no WGS
|
|
|
566
566
|
84 coordinates and is named. Without the operator's partition, ACL and legal
|
|
567
567
|
tag the manifest is NOT LOADABLE and says so. `gea help osdu`.
|
|
568
568
|
|
|
569
|
+
### The QuakeML catalogue export
|
|
570
|
+
|
|
571
|
+
The site's own events in the format the regulator's catalogue tools read.
|
|
572
|
+
After `associate`, `gea workspace --action quakeml-export --site <id>
|
|
573
|
+
[--agency "<name>"]` writes `reports/sites/<site>/quakeml/<site>_events.xml`
|
|
574
|
+
as QuakeML 1.2 to the Basic Event Description schema: one event per
|
|
575
|
+
associated event with its origin (the misfit region as the uncertainties,
|
|
576
|
+
depth in metres and "operator assigned" because it was declared, the method
|
|
577
|
+
and the earth model named, the quality block with the counts, the RMS, the
|
|
578
|
+
azimuthal gap and the distances), one pick per station with the record's own
|
|
579
|
+
FDSN codes, one arrival per pick with its residual. Every origin and pick is
|
|
580
|
+
automatic and preliminary in the schema's own words; the catalogue's AGREES
|
|
581
|
+
or DIFFERS is a comment with the catalogue's event id; no magnitude is
|
|
582
|
+
written and a comment says why. The shape is checked the way a reader checks
|
|
583
|
+
it first - identifiers, allowed children, required children, enumerations,
|
|
584
|
+
references - and the file validates against the published XSD. `gea help
|
|
585
|
+
quakeml`; `gea quakeml` runs the labelled scene.
|
|
586
|
+
|
|
587
|
+
### The well as master data names it
|
|
588
|
+
|
|
589
|
+
`gea workspace --action well-identity --well <id>` names a well the way the
|
|
590
|
+
operator's master data and a regulator's well file name it: the US Well
|
|
591
|
+
Number (the API number, whose standard PPDM has held since 2010) taken apart
|
|
592
|
+
- state and offshore code, county code, unique well and its range, sidetrack
|
|
593
|
+
and event when given, and which component the number identifies (ten digits
|
|
594
|
+
the Well Origin, twelve a Wellbore, fourteen an event) - and the components
|
|
595
|
+
of PPDM's "What is a Well" the site can name: the Well and its one Origin at
|
|
596
|
+
the declared position on its datum, the Wellbore only when the sidetrack
|
|
597
|
+
code was given, the gauge station as a measured depth along the wellbore,
|
|
598
|
+
the Wellhead Stream as the channels recorded at the wellhead, the Well Set
|
|
599
|
+
as the site, every alias typed. The identity rides on every well row of the
|
|
600
|
+
SRA packet (section 2a) and in the OSDU export's Well and Wellbore records.
|
|
601
|
+
It will not assume a sidetrack, invent an injection interval or a
|
|
602
|
+
completion, or name a county. `gea ppdm --action parse --number ...` takes a
|
|
603
|
+
number apart from the command line; `gea help ppdm`.
|
|
604
|
+
|
|
569
605
|
### Audit / Update
|
|
570
606
|
|
|
571
607
|
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 (327 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]")
|
|
@@ -527,6 +527,42 @@ operation between them written out; a point on an unknown datum gets no WGS
|
|
|
527
527
|
84 coordinates and is named. Without the operator's partition, ACL and legal
|
|
528
528
|
tag the manifest is NOT LOADABLE and says so. `gea help osdu`.
|
|
529
529
|
|
|
530
|
+
### The QuakeML catalogue export
|
|
531
|
+
|
|
532
|
+
The site's own events in the format the regulator's catalogue tools read.
|
|
533
|
+
After `associate`, `gea workspace --action quakeml-export --site <id>
|
|
534
|
+
[--agency "<name>"]` writes `reports/sites/<site>/quakeml/<site>_events.xml`
|
|
535
|
+
as QuakeML 1.2 to the Basic Event Description schema: one event per
|
|
536
|
+
associated event with its origin (the misfit region as the uncertainties,
|
|
537
|
+
depth in metres and "operator assigned" because it was declared, the method
|
|
538
|
+
and the earth model named, the quality block with the counts, the RMS, the
|
|
539
|
+
azimuthal gap and the distances), one pick per station with the record's own
|
|
540
|
+
FDSN codes, one arrival per pick with its residual. Every origin and pick is
|
|
541
|
+
automatic and preliminary in the schema's own words; the catalogue's AGREES
|
|
542
|
+
or DIFFERS is a comment with the catalogue's event id; no magnitude is
|
|
543
|
+
written and a comment says why. The shape is checked the way a reader checks
|
|
544
|
+
it first - identifiers, allowed children, required children, enumerations,
|
|
545
|
+
references - and the file validates against the published XSD. `gea help
|
|
546
|
+
quakeml`; `gea quakeml` runs the labelled scene.
|
|
547
|
+
|
|
548
|
+
### The well as master data names it
|
|
549
|
+
|
|
550
|
+
`gea workspace --action well-identity --well <id>` names a well the way the
|
|
551
|
+
operator's master data and a regulator's well file name it: the US Well
|
|
552
|
+
Number (the API number, whose standard PPDM has held since 2010) taken apart
|
|
553
|
+
- state and offshore code, county code, unique well and its range, sidetrack
|
|
554
|
+
and event when given, and which component the number identifies (ten digits
|
|
555
|
+
the Well Origin, twelve a Wellbore, fourteen an event) - and the components
|
|
556
|
+
of PPDM's "What is a Well" the site can name: the Well and its one Origin at
|
|
557
|
+
the declared position on its datum, the Wellbore only when the sidetrack
|
|
558
|
+
code was given, the gauge station as a measured depth along the wellbore,
|
|
559
|
+
the Wellhead Stream as the channels recorded at the wellhead, the Well Set
|
|
560
|
+
as the site, every alias typed. The identity rides on every well row of the
|
|
561
|
+
SRA packet (section 2a) and in the OSDU export's Well and Wellbore records.
|
|
562
|
+
It will not assume a sidetrack, invent an injection interval or a
|
|
563
|
+
completion, or name a county. `gea ppdm --action parse --number ...` takes a
|
|
564
|
+
number apart from the command line; `gea help ppdm`.
|
|
565
|
+
|
|
530
566
|
### Audit / Update
|
|
531
567
|
|
|
532
568
|
The Audit / Update tab holds three things. The whole audit log
|
|
@@ -282,7 +282,7 @@ def main(argv=None) -> int:
|
|
|
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
284
|
"seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
|
|
285
|
-
"set-well", "sra-define", "sra-report", "associate", "osdu-export"])
|
|
285
|
+
"set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export", "well-identity"])
|
|
286
286
|
p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
|
|
287
287
|
p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
|
|
288
288
|
p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
|
|
@@ -315,7 +315,8 @@ def main(argv=None) -> int:
|
|
|
315
315
|
p_ws.add_argument("--legal-tag", type=str, nargs="*", default=None, help="osdu-export: legal tag(s)")
|
|
316
316
|
p_ws.add_argument("--country", type=str, nargs="*", default=None, help="osdu-export: otherRelevantDataCountries (default US)")
|
|
317
317
|
p_ws.add_argument("--operator-org", type=str, default="", help="osdu-export: the operator's Organisation record id on the platform")
|
|
318
|
-
p_ws.add_argument("--out", type=str, default=None, help="osdu-export: the folder to write the manifest and files into")
|
|
318
|
+
p_ws.add_argument("--out", type=str, default=None, help="osdu-export: the folder to write the manifest and files into; quakeml-export: the file to write")
|
|
319
|
+
p_ws.add_argument("--agency", type=str, default="", help="quakeml-export: the agencyID written in creationInfo (default: the site's name)")
|
|
319
320
|
p_ws.add_argument("--depth-km", type=float, default=6.0, help="associate: the hypocentral depth the location is made under, km (declared)")
|
|
320
321
|
p_ws.add_argument("--rms-tol", type=float, default=0.15, help="associate: the RMS residual an event must fit within, s")
|
|
321
322
|
p_ws.add_argument("--wells", type=str, nargs="+", default=None, help="add-site: well ids of this workspace")
|
|
@@ -399,6 +400,12 @@ def main(argv=None) -> int:
|
|
|
399
400
|
|
|
400
401
|
p_osdu = sub.add_parser("osdu", help="the OSDU-shaped export: the self-test on a labelled site")
|
|
401
402
|
p_osdu.add_argument("--action", choices=["selftest"], default="selftest")
|
|
403
|
+
p_ppdm = sub.add_parser("ppdm", help="the well named as the US Well Number and PPDM name it: take a number apart, or run the self-test")
|
|
404
|
+
p_ppdm.add_argument("--action", choices=["selftest", "parse"], default="selftest")
|
|
405
|
+
p_ppdm.add_argument("--number", type=str, default=None, help="parse: a US Well Number (10, 12 or 14 digits, dashes optional)")
|
|
406
|
+
p_qml = sub.add_parser("quakeml", help="the QuakeML 1.2 catalogue export: the self-test on the association leg's labelled scene")
|
|
407
|
+
p_qml.add_argument("--action", choices=["selftest"], default="selftest")
|
|
408
|
+
p_qml.add_argument("--out", type=str, default=None, help="where to leave the self-test's file (default: a temporary folder)")
|
|
402
409
|
p_sra = sub.add_parser("sra", help="the Seismicity Response Area packet: the self-test on a labelled scene")
|
|
403
410
|
p_sra.add_argument("--action", choices=["selftest"], default="selftest")
|
|
404
411
|
p_sra.add_argument("--json", action="store_true")
|
|
@@ -1496,6 +1503,36 @@ def main(argv=None) -> int:
|
|
|
1496
1503
|
return 0
|
|
1497
1504
|
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)"))
|
|
1498
1505
|
return 0
|
|
1506
|
+
elif a.cmd == "ppdm":
|
|
1507
|
+
from . import ppdm as _P
|
|
1508
|
+
if a.action == "parse":
|
|
1509
|
+
if not a.number:
|
|
1510
|
+
raise SystemExit("ppdm --action parse needs --number")
|
|
1511
|
+
r = _P.parse_well_number(a.number)
|
|
1512
|
+
print(f"{r['input']}: {r['status']}" + (f" - {r['form']}; identifies the {r['identifies'].replace('_', ' ')}" if r.get('form') and r['status'] == 'VALID' else ''))
|
|
1513
|
+
if r.get('state'):
|
|
1514
|
+
print(f" state {r['state']['code']} ({r['state']['name'] or 'not a state code'}), county {r['county']['code']}, unique well {r['unique_well']['code']}"
|
|
1515
|
+
f" ({r['unique_well']['range'] or '-'})" + (f"; sidetrack {r['sidetrack']['code']}: {r['sidetrack']['meaning']}" if r.get('sidetrack') else '')
|
|
1516
|
+
+ (f"; event {r['event']['code']}: {r['event']['meaning']}" if r.get('event') else ''))
|
|
1517
|
+
for x in r["problems"]:
|
|
1518
|
+
print(f" problem: {x}")
|
|
1519
|
+
for x in r.get("notes") or []:
|
|
1520
|
+
print(f" note: {x}")
|
|
1521
|
+
return 0 if r["status"] == "VALID" else 1
|
|
1522
|
+
r = _P.selftest()
|
|
1523
|
+
print(f"ppdm selftest [{r['label']}]: {r['status']}")
|
|
1524
|
+
for k, v in r["checks"].items():
|
|
1525
|
+
print(f" {'ok ' if v else 'BAD'} {k}")
|
|
1526
|
+
print(_P.report_text(r["identity"]))
|
|
1527
|
+
return 0 if r["status"] == "OK" else 1
|
|
1528
|
+
elif a.cmd == "quakeml":
|
|
1529
|
+
from . import quakeml as _Q
|
|
1530
|
+
r = _Q.selftest(a.out)
|
|
1531
|
+
print(f"quakeml selftest [{r['label']}]: {r['status']}")
|
|
1532
|
+
for k, v in r["checks"].items():
|
|
1533
|
+
print(f" {'ok ' if v else 'BAD'} {k}")
|
|
1534
|
+
print(_Q.report_text({**r, 'status': 'WRITTEN' if r['status'] == 'OK' else 'FAILED', 'agency': 'SIMULATION_SELF_TEST site', 'refused': 2}))
|
|
1535
|
+
return 0 if r["status"] == "OK" else 1
|
|
1499
1536
|
elif a.cmd == "osdu":
|
|
1500
1537
|
from . import osdu as _O
|
|
1501
1538
|
r = _O.selftest()
|
|
@@ -1753,6 +1790,18 @@ def main(argv=None) -> int:
|
|
|
1753
1790
|
countries=a.country, operator_org_id=a.operator_org, out_dir=a.out, actor=a.actor)
|
|
1754
1791
|
print(_osdu_text(r)); v = r["validate"]
|
|
1755
1792
|
print(f" structure: {'ok' if v['ok'] else 'PROBLEMS'} ({v['records']} record(s))" + ('' if v['ok'] else '; ' + '; '.join(v['problems'][:5]))); return 0
|
|
1793
|
+
elif a.action == "well-identity":
|
|
1794
|
+
from .ppdm import report_text as _ppdm_text
|
|
1795
|
+
if not a.well:
|
|
1796
|
+
raise SystemExit("well-identity needs --well <id>")
|
|
1797
|
+
r = ws.well_identity(a.well)
|
|
1798
|
+
print(_ppdm_text(r)); return 0
|
|
1799
|
+
elif a.action == "quakeml-export":
|
|
1800
|
+
from .quakeml import report_text as _qml_text
|
|
1801
|
+
if not a.site:
|
|
1802
|
+
raise SystemExit("quakeml-export needs --site <id>")
|
|
1803
|
+
r = ws.quakeml_export(a.site, agency=a.agency, out=a.out, actor=a.actor)
|
|
1804
|
+
print(_qml_text(r)); return 0 if r["status"] == "WRITTEN" else 1
|
|
1756
1805
|
elif a.action == "seismic-dataset":
|
|
1757
1806
|
r = ws.write_seismic_dataset_report(actor=a.actor)
|
|
1758
1807
|
inv = r["inventory"]
|
|
@@ -4967,6 +4967,247 @@ def section_be_osdu(tmp: str) -> None:
|
|
|
4967
4967
|
"file is a named gap; the structure validates; every export is audited; `gea osdu` runs the self-test from the command line")
|
|
4968
4968
|
|
|
4969
4969
|
|
|
4970
|
+
|
|
4971
|
+
def section_bf_quakeml(tmp: str) -> None:
|
|
4972
|
+
"""Section BF - the QuakeML catalogue export: the site's own events in the exchange format a regulator's
|
|
4973
|
+
catalogue tool reads, built to the Basic Event Description schema, automatic and preliminary in the
|
|
4974
|
+
schema's own words, with no magnitude and with the catalogue's standing as a comment."""
|
|
4975
|
+
import csv as _csv
|
|
4976
|
+
import subprocess as _sp
|
|
4977
|
+
import xml.etree.ElementTree as _ET
|
|
4978
|
+
from . import quakeml as Q
|
|
4979
|
+
from . import seismic as S
|
|
4980
|
+
from . import seismic_assoc as SA
|
|
4981
|
+
from .workspace import Workspace, WorkspaceError
|
|
4982
|
+
from . import helplib as H
|
|
4983
|
+
pkg = Path(__file__).parent
|
|
4984
|
+
d = Path(tmp, "bf"); d.mkdir()
|
|
4985
|
+
B = lambda t: "{%s}%s" % (Q.NS_BED, t)
|
|
4986
|
+
|
|
4987
|
+
# -- the self-test on the labelled scene, read back ------------------------------------------
|
|
4988
|
+
r = Q.selftest(str(d / "self"))
|
|
4989
|
+
root = _ET.parse(r["path"]).getroot()
|
|
4990
|
+
origins = root.findall(".//" + B("origin"))
|
|
4991
|
+
o = origins[0]
|
|
4992
|
+
lat_u = float(o.find(B("latitude")).find(B("uncertainty")).text); lon_u = float(o.find(B("longitude")).find(B("uncertainty")).text)
|
|
4993
|
+
hu = float(o.find(B("originUncertainty")).find(B("horizontalUncertainty")).text)
|
|
4994
|
+
q = o.find(B("quality"))
|
|
4995
|
+
ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
|
|
4996
|
+
and root.tag == "{%s}quakeml" % Q.NS_Q and len(origins) == 2
|
|
4997
|
+
and 0 < lat_u < 0.1 and 0 < lon_u < 0.1 and 0 < hu < 10000 and abs(hu - max(lat_u * Q.KM_PER_DEG, 0) * 1000) < 1.0 + hu * 0.5
|
|
4998
|
+
and q.find(B("usedStationCount")).text == "5" and q.find(B("usedPhaseCount")).text == "5"
|
|
4999
|
+
and float(q.find(B("standardError")).text) < 0.05 and 0 < float(q.find(B("azimuthalGap")).text) < 180
|
|
5000
|
+
and 0 < float(q.find(B("minimumDistance")).text) < float(q.find(B("maximumDistance")).text) < 0.2
|
|
5001
|
+
and o.find(B("methodID")).text.startswith("smi:local/gea/method/") and "vp-" in o.find(B("earthModelID")).text
|
|
5002
|
+
and all(float(a.find(B("timeWeight")).text) == 1.0 and abs(float(a.find(B("timeResidual")).text)) < 0.15 and 0 < float(a.find(B("distance")).text) < 0.2
|
|
5003
|
+
for a in o.findall(B("arrival"))),
|
|
5004
|
+
"BF1 the self-test on the association leg's labelled scene: two events written and read back under the QuakeML 1.2 namespaces, five picks "
|
|
5005
|
+
"and five arrivals each, depth in metres with depthType 'operator assigned' because it was declared, the misfit region as the latitude and "
|
|
5006
|
+
"longitude uncertainties in degrees and the horizontal uncertainty in metres, the quality block with the counts, the RMS, the azimuthal gap "
|
|
5007
|
+
"and the epicentral distances in degrees, every arrival with its residual and unit weight, the method and the earth model named, every "
|
|
5008
|
+
"origin and pick automatic and preliminary, the catalogue's AGREES and DIFFERS as comments, the record's own FDSN codes in every waveformID, "
|
|
5009
|
+
"and no magnitude anywhere")
|
|
5010
|
+
|
|
5011
|
+
# -- the shape check catches what a reader would refuse ------------------------------------------
|
|
5012
|
+
text = Path(r["path"]).read_text(encoding="utf-8")
|
|
5013
|
+
broken = []
|
|
5014
|
+
bad1 = text.replace('publicID="smi:local/gea/SYNTHETIC/event/', 'publicID="bad id ', 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad1))))
|
|
5015
|
+
bad2 = text.replace("<pickID>smi:local/gea/SYNTHETIC/pick/", "<pickID>smi:local/gea/SYNTHETIC/nopick/", 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad2))))
|
|
5016
|
+
bad3 = text.replace("<evaluationStatus>preliminary</evaluationStatus>", "<evaluationStatus>reported</evaluationStatus>", 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad3))))
|
|
5017
|
+
bad4 = text.replace("<depthType>", "<depthKind>").replace("</depthType>", "</depthKind>"); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad4))))
|
|
5018
|
+
first_origin_end = text.index("</origin>")
|
|
5019
|
+
bad5 = text[:first_origin_end] + '<magnitude publicID="smi:local/gea/x/mag/1"><mag><value>2.0</value></mag></magnitude>' + text[first_origin_end:]
|
|
5020
|
+
broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad5))))
|
|
5021
|
+
bad6 = text.replace("<value>2026-03-05T11:03:", "<value>2026-03-05 11:03:", 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad6))))
|
|
5022
|
+
ok(Q.validate(r["path"])["status"] == "VALID_SHAPE" and all(b["status"] == "INVALID" for b in broken)
|
|
5023
|
+
and any("ResourceIdentifier" in p for p in broken[0]["problems"]) and any("pickID" in p for p in broken[1]["problems"])
|
|
5024
|
+
and any("enumeration" in p for p in broken[2]["problems"]) and any("not in the schema" in p for p in broken[3]["problems"])
|
|
5025
|
+
and any("magnitude" in p for p in broken[4]["problems"]) and any("dateTime" in p for p in broken[5]["problems"])
|
|
5026
|
+
and all(Q.RID_PATTERN.match(x) for x in (Q.rid("Pad 3", "event", "assoc 1"), Q.rid("a", "x", authority="ab"), Q.rid("s", "comment", "a/b")))
|
|
5027
|
+
and not Q.RID_PATTERN.match("smi:ab/x") and not Q.RID_PATTERN.match("urn:local/x"),
|
|
5028
|
+
"BF2 the shape check is the first thing a QuakeML reader does, done here: an identifier off the schema's pattern, an arrival whose pick is "
|
|
5029
|
+
"not in its event, an evaluation status outside the enumeration, a child element the schema does not know, a magnitude this program did "
|
|
5030
|
+
"not estimate, and a time that is not an xs:dateTime are each named as a problem; and the identifier builder cleans a site name with a "
|
|
5031
|
+
"space and a short authority to the pattern")
|
|
5032
|
+
|
|
5033
|
+
# -- through the workspace and the command line ---------------------------------------------------
|
|
5034
|
+
sc = SA.synthetic_scene()
|
|
5035
|
+
for sid, tr in sc["traces"].items():
|
|
5036
|
+
S.write_mseed([tr], str(d / f"{sid}.mseed"))
|
|
5037
|
+
cat = d / "cat.csv"
|
|
5038
|
+
with open(cat, "w", newline="") as f:
|
|
5039
|
+
w = _csv.writer(f); w.writerow(["EventID", "Origin Time", "Local Magnitude", "Latitude", "Longitude", "Depth"])
|
|
5040
|
+
for c in sc["catalog"]:
|
|
5041
|
+
w.writerow([c["event_id"], c["time"], c["magnitude"], c["lat"], c["lon"], 6.0])
|
|
5042
|
+
ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
|
|
5043
|
+
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"]
|
|
5044
|
+
for sid, st in sc["stations"].items()]
|
|
5045
|
+
ws.add_site("QML block", seismic=ids, client="Acme", actor="tester")
|
|
5046
|
+
try:
|
|
5047
|
+
ws.quakeml_export("QML-block"); early = False
|
|
5048
|
+
except WorkspaceError as e:
|
|
5049
|
+
early = "associate" in str(e)
|
|
5050
|
+
run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
|
|
5051
|
+
r1 = run("--action", "associate", "--site", "QML-block", "--catalog", str(cat))
|
|
5052
|
+
aj = json.loads(Path(ws.path, "reports", "sites", "QML-block", "association.json").read_text(encoding="utf-8"))
|
|
5053
|
+
r2 = run("--action", "quakeml-export", "--site", "QML-block", "--agency", "Acme Operating")
|
|
5054
|
+
xml_path = Path(ws.path, "reports", "sites", "QML-block", "quakeml", "QML-block_events.xml")
|
|
5055
|
+
summ = json.loads(Path(ws.path, "reports", "sites", "QML-block", "quakeml", "export_summary.json").read_text(encoding="utf-8"))
|
|
5056
|
+
root2 = _ET.parse(str(xml_path)).getroot()
|
|
5057
|
+
agencies = {c.find(B("agencyID")).text for c in root2.iter(B("creationInfo"))}
|
|
5058
|
+
wids = root2.findall(".//" + B("waveformID"))
|
|
5059
|
+
audit = Path(ws.path, "records", "audit.jsonl").read_text(encoding="utf-8")
|
|
5060
|
+
r3 = _sp.run([sys.executable, "-m", "gea", "quakeml", "--out", str(d / "cli")], capture_output=True, text=True)
|
|
5061
|
+
help_md = (pkg / "help" / "quakeml.md").read_text(encoding="utf-8")
|
|
5062
|
+
ok(early and r1.returncode == 0 and all((v.get("stream") or {}).get("station") == sid for sid, v in aj["stations"].items())
|
|
5063
|
+
and r2.returncode == 0 and "WRITTEN - 2 event(s), 10 pick(s), 10 arrival(s)" in r2.stdout and "VALID_SHAPE" in r2.stdout and "gap:" not in r2.stdout
|
|
5064
|
+
and xml_path.is_file() and summ["status"] == "WRITTEN" and summ["schema"] == "QuakeML-BED-1.2" and summ["n_events"] == 2 and not summ["gaps"]
|
|
5065
|
+
and summ["validate"]["status"] == "VALID_SHAPE" and summ["association_generated_utc"] == aj["generated_utc"]
|
|
5066
|
+
and agencies == {"Acme Operating"} and len(wids) == 10 and all(w.get("networkCode") == "XX" and w.get("channelCode") == "HHZ" for w in wids)
|
|
5067
|
+
and "site.quakeml_export" in audit and r3.returncode == 0 and "SIMULATION_SELF_TEST" in r3.stdout and (d / "cli" / "SYNTHETIC_events.xml").is_file()
|
|
5068
|
+
and H.VIEW_TOPIC.get("quakeml", "seismic") == "seismic" and any(t["topic"] == "quakeml" for t in H.topics()) and not H.check("quakeml")
|
|
5069
|
+
and "operator assigned" in help_md and "preliminary" in help_md and "magnitude" in help_md.split("will not call a measurement")[1],
|
|
5070
|
+
"BF3 `--action quakeml-export` refuses a site that has not been associated, and after `--action associate` (which now keeps each record's "
|
|
5071
|
+
"FDSN codes) writes reports/sites/<site>/quakeml/<site>_events.xml with the agency the operator named in every creationInfo, the record's "
|
|
5072
|
+
"codes in every waveformID, a summary beside it that carries the association's own timestamp, and audits it; `gea quakeml` runs the "
|
|
5073
|
+
"labelled scene from the command line; the help page names the declared depth, the preliminary status and the magnitude it does not write")
|
|
5074
|
+
|
|
5075
|
+
# -- a station without codes or position is named as a gap, never invented ------------------------
|
|
5076
|
+
doc = {"association": {"events": [{"id": "e1", "origin_utc": "2026-03-05T11:03:00.000Z", "lat": 31.95, "lon": -102.25, "depth_km": 6.0,
|
|
5077
|
+
"depth_basis": "declared", "rms_s": 0.02, "n_stations": 3, "stations": ["A", "B", "C"], "dropped": [],
|
|
5078
|
+
"picks": {"A": {"time": "2026-03-05T11:03:01.000Z", "snr": 10.0, "residual_s": 0.01},
|
|
5079
|
+
"B": {"time": "2026-03-05T11:03:01.200Z", "snr": 9.0, "residual_s": -0.01},
|
|
5080
|
+
"C": {"time": "2026-03-05T11:03:01.400Z", "snr": 8.0, "residual_s": 0.0}},
|
|
5081
|
+
"region_km": {"extent_east_km": 1.0, "extent_north_km": 2.0, "nodes": 8, "reaches_grid_edge": False},
|
|
5082
|
+
"magnitude": None, "magnitude_basis": "not estimated",
|
|
5083
|
+
"model": {"vp_km_s": 5.8, "depth_km": 6.0, "pick_sigma_s": 0.05, "basis": "flat earth"}}],
|
|
5084
|
+
"refused": [], "n_unused_picks": 0, "model": {}, "basis": "b", "not_a_measurement": []}, "catalogue": None}
|
|
5085
|
+
stations = {"A": {"id": "A", "lat": 31.99, "lon": -102.30, "stream": {"network": "TX", "station": "PB01", "location": "00", "channel": "HHZ"}},
|
|
5086
|
+
"B": {"id": "B", "lat": 31.90, "lon": -102.20}, # no codes
|
|
5087
|
+
"C": {"id": "C"}} # no codes, no position
|
|
5088
|
+
b = Q.build(doc, {"id": "s", "name": "Gap site"}, stations, version="0.0")
|
|
5089
|
+
out = Path(d, "gaps.xml"); Q.write(b["tree"], str(out))
|
|
5090
|
+
v = Q.validate(str(out))
|
|
5091
|
+
rootg = _ET.parse(str(out)).getroot()
|
|
5092
|
+
ws_ids = {w.get("stationCode"): w for w in rootg.findall(".//" + B("waveformID"))}
|
|
5093
|
+
arrs = {a.find(B("pickID")).text.rsplit("/", 1)[1]: a for a in rootg.findall(".//" + B("arrival"))}
|
|
5094
|
+
og = rootg.find(".//" + B("origin"))
|
|
5095
|
+
ok(v["status"] == "VALID_SHAPE" and len(b["gaps"]) == 3 and sum("no network and station code" in g for g in b["gaps"]) == 2
|
|
5096
|
+
and sum("no position" in g for g in b["gaps"]) == 1
|
|
5097
|
+
and ws_ids["PB01"].get("networkCode") == "TX" and ws_ids["PB01"].get("locationCode") == "00"
|
|
5098
|
+
and ws_ids["B"].get("networkCode") == "XX" and ws_ids["C"].get("networkCode") == "XX"
|
|
5099
|
+
and arrs["A"].find(B("azimuth")) is not None and arrs["C"].find(B("azimuth")) is None and arrs["C"].find(B("distance")) is None
|
|
5100
|
+
and abs(float(og.find(B("originUncertainty")).find(B("horizontalUncertainty")).text) - 1000.0) < 1e-6
|
|
5101
|
+
and abs(float(og.find(B("latitude")).find(B("uncertainty")).text) - 1.0 / Q.KM_PER_DEG) < 1e-6
|
|
5102
|
+
and og.find(B("quality")).find(B("azimuthalGap")) is not None,
|
|
5103
|
+
"BF4 a station whose record carried no network and station code goes out under the test network XX with its own id and is named as a gap; "
|
|
5104
|
+
"a station with no position gets a pick but an arrival without azimuth or distance and is named as a gap; neither is invented; the misfit "
|
|
5105
|
+
"region of 1 km by 2 km becomes a 1000 m horizontal uncertainty and a latitude uncertainty of one kilometre in degrees")
|
|
5106
|
+
|
|
5107
|
+
|
|
5108
|
+
def section_bg_ppdm(tmp: str) -> None:
|
|
5109
|
+
"""Section BG - the well named the way the operator's master data names it: the US Well Number taken
|
|
5110
|
+
apart and checked, the PPDM "What is a Well" components the site can name, and the identity carried into
|
|
5111
|
+
the SRA packet and the OSDU export without a sidetrack, an interval or a county name being invented."""
|
|
5112
|
+
import csv as _csv
|
|
5113
|
+
import subprocess as _sp
|
|
5114
|
+
from datetime import datetime as _dt, timedelta as _td, timezone as _tz
|
|
5115
|
+
from . import ppdm as P
|
|
5116
|
+
from . import helplib as H
|
|
5117
|
+
from .client_reports import forbidden_terms
|
|
5118
|
+
from .workspace import Workspace
|
|
5119
|
+
pkg = Path(__file__).parent
|
|
5120
|
+
d = Path(tmp, "bg"); d.mkdir()
|
|
5121
|
+
|
|
5122
|
+
# -- the number ----------------------------------------------------------------------------------
|
|
5123
|
+
r = P.selftest()
|
|
5124
|
+
a = P.parse_well_number("42 329 39123 01 00")
|
|
5125
|
+
b = P.parse_well_number("4232939123")
|
|
5126
|
+
ok(r["status"] == "OK" and all(r["checks"].values())
|
|
5127
|
+
and a["status"] == "VALID" and a["api14"] == "42-329-39123-01-00" and a["identifies"] == "event" and a["event"]["meaning"].startswith("the original")
|
|
5128
|
+
and b["status"] == "VALID" and b["api10"] == "42-329-39123" and b["api12"] is None and b["identifies"] == "well_origin"
|
|
5129
|
+
and len(P.STATE_CODES) == 55 and P.STATE_CODES["42"] == "Texas" and P.STATE_CODES["60"] == "Northern Gulf of Mexico" and "52" not in P.STATE_CODES
|
|
5130
|
+
and P.parse_well_number("42-329-61000")["unique_well"]["range"] == "reserved" and P.parse_well_number("42-329-00500")["unique_well"]["range"] == "historical"
|
|
5131
|
+
and P.parse_well_number("42-329-96000")["unique_well"]["range"] == "exempt"
|
|
5132
|
+
and "booklet" in P.parse_well_number("42-329-39123")["county"]["basis"],
|
|
5133
|
+
"BG1 a US Well Number in any written form is taken apart to the standard - state and offshore codes named (51 states and territories, four "
|
|
5134
|
+
"offshore areas, 52-54 reserved), the county code kept as a code because the county booklet is not held, the unique well placed in the "
|
|
5135
|
+
"standard's historical, current, reserved or exempt range - and ten, twelve and fourteen digits name the Well Origin, a Wellbore and an "
|
|
5136
|
+
"event; a reserved or unknown state, a wrong length, a stray letter and a zero unique well are each named as a problem, never repaired")
|
|
5137
|
+
|
|
5138
|
+
# -- the identity, and what it will not name ---------------------------------------------------
|
|
5139
|
+
ident = r["identity"]; c = ident["components"]
|
|
5140
|
+
ok(c["well"]["identifier"] == "42-329-39123" and c["wellbore"]["identifier"] is None and "sidetrack code" in c["wellbore"]["detail"]
|
|
5141
|
+
and c["well_origin"]["position"]["datum"] == "NAD27" and c["wellhead_stream"]["direction"] == "into the ground"
|
|
5142
|
+
and c["wellbore_contact_interval"]["identifier"] is None and c["wellbore_completion"]["identifier"] is None
|
|
5143
|
+
and all(k in P.COMPONENTS for k in ("well", "well_origin", "wellbore", "wellbore_segment", "wellbore_contact_interval", "wellbore_completion", "wellhead_stream", "well_set"))
|
|
5144
|
+
and "one and only one" in P.COMPONENTS["wellbore_segment"] and "not an activity" in P.COMPONENTS["wellbore_completion"]
|
|
5145
|
+
and sum("not given" in g for g in ident["gaps"]) == 1 and sum("not held here" in g for g in ident["gaps"]) == 1,
|
|
5146
|
+
"BG2 the identity names the Well and its Origin at the declared position on its datum, the Wellhead Stream with its direction, the Well Set "
|
|
5147
|
+
"as the site and every alias typed, and names as not named - with the reason - the Wellbore when the sidetrack code was not given, and the "
|
|
5148
|
+
"Contact Interval and the Completion that are the permit's and the completion report's facts; the component definitions are PPDM's")
|
|
5149
|
+
|
|
5150
|
+
# -- through the workspace: the packet and the OSDU export carry it ----------------------------
|
|
5151
|
+
t0 = _dt(2026, 3, 1, tzinfo=_tz.utc)
|
|
5152
|
+
hist = d / "swd.csv"
|
|
5153
|
+
with open(hist, "w", newline="") as f:
|
|
5154
|
+
w = _csv.writer(f); w.writerow(["timestamp", "P_surf_psi", "Q_bpm"])
|
|
5155
|
+
for i in range(2 * 144):
|
|
5156
|
+
w.writerow([(t0 + _td(seconds=600 * i)).strftime("%Y-%m-%dT%H:%M:%SZ"), "1500.0", "5.0"])
|
|
5157
|
+
ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
|
|
5158
|
+
w1 = ws.add_well_file(str(hist), display="SWD 1", actor="tester", station_md_ft=9800.0)
|
|
5159
|
+
run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
|
|
5160
|
+
r0 = run("--action", "well-identity", "--well", w1["id"]) # nothing declared yet
|
|
5161
|
+
r1 = run("--action", "set-well", "--well", w1["id"], "--lat", "31.96", "--lon", "-102.24", "--datum", "NAD27", "--api", "42-329-39123", "--uic", "123456",
|
|
5162
|
+
"--depth-tier", "deep", "--formation", "completed below the base of the Wolfcamp", "--channel-pressure", "P_surf_psi", "--channel-rate", "Q_bpm",
|
|
5163
|
+
"--rate-unit", "bbl/min", "--bhp-method", "calculated")
|
|
5164
|
+
r2 = run("--action", "add-site", "--name", "PPDM block", "--wells", w1["id"], "--client", "Acme Operating")
|
|
5165
|
+
r3 = run("--action", "well-identity", "--well", w1["id"])
|
|
5166
|
+
r4 = run("--action", "sra-define", "--site", "PPDM-block", "--name", "Test SRA", "--lat", "31.95", "--lon", "-102.25", "--datum", "WGS84",
|
|
5167
|
+
"--radius-km", "9.08", "--plan-date", "2026-02-25")
|
|
5168
|
+
r5 = run("--action", "sra-report", "--site", "PPDM-block")
|
|
5169
|
+
out = Path(ws.path, "reports", "sites", "PPDM-block")
|
|
5170
|
+
md = (out / "sra_packet.md").read_text(encoding="utf-8"); html = (out / "sra_packet.html").read_text(encoding="utf-8")
|
|
5171
|
+
pkj = json.loads((out / "sra_packet.json").read_text(encoding="utf-8"))["packet"]
|
|
5172
|
+
ws2 = Workspace(ws.path)
|
|
5173
|
+
o1 = ws2.osdu_export("PPDM-block", partition="opendes", acl_owners=["o"], acl_viewers=["v"], legal_tags=["t"], actor="tester")
|
|
5174
|
+
man1 = json.loads(Path(o1["manifest"]).read_text(encoding="utf-8"))
|
|
5175
|
+
wells1 = [x for x in man1["MasterData"] if x["kind"].endswith(":master-data--Well:1.0.0")]
|
|
5176
|
+
bores1 = [x for x in man1["MasterData"] if x["kind"].endswith(":master-data--Wellbore:1.0.0")]
|
|
5177
|
+
ws2.set_well(w1["id"], disposal={"api_number": "42-329-39123-01"}, actor="tester")
|
|
5178
|
+
o2 = ws2.osdu_export("PPDM-block", partition="opendes", acl_owners=["o"], acl_viewers=["v"], legal_tags=["t"], actor="tester")
|
|
5179
|
+
man2 = json.loads(Path(o2["manifest"]).read_text(encoding="utf-8"))
|
|
5180
|
+
bores2 = [x for x in man2["MasterData"] if x["kind"].endswith(":master-data--Wellbore:1.0.0")]
|
|
5181
|
+
r6 = _sp.run([sys.executable, "-m", "gea", "ppdm", "--action", "parse", "--number", "42-329-39123-01"], capture_output=True, text=True)
|
|
5182
|
+
r7 = _sp.run([sys.executable, "-m", "gea", "ppdm", "--action", "parse", "--number", "53-329-39123"], capture_output=True, text=True)
|
|
5183
|
+
r8 = _sp.run([sys.executable, "-m", "gea", "ppdm"], capture_output=True, text=True)
|
|
5184
|
+
ident_w = pkj["wells"][0]["identity"]
|
|
5185
|
+
ok(r0.returncode == 0 and "no US Well Number declared" in r0.stdout and r1.returncode == 0 and r2.returncode == 0
|
|
5186
|
+
and r3.returncode == 0 and "US Well Number 42-329-39123" in r3.stdout and "state 42 (Texas)" in r3.stdout and "Wellbore: not identified" in r3.stdout
|
|
5187
|
+
and "Well Set: PPDM block" in r3.stdout and "Wellhead Stream: 2 channel(s)" in r3.stdout and "into the ground" in r3.stdout
|
|
5188
|
+
and "9800 ft MD" in r3.stdout and "123456 [UIC permit number]" in r3.stdout
|
|
5189
|
+
and r4.returncode == 0 and r5.returncode == 0 and ident_w["us_well_number"] == "42-329-39123" and ident_w["state"] == "Texas"
|
|
5190
|
+
and ident_w["wellbore"] is None and ident_w["origin_datum"] == "NAD27" and not any("sidetrack" in g or "Completion" in g for g in pkj["gaps"])
|
|
5191
|
+
and "Well identity" in md and "42-329-39123" in md and "not identified" in md and "US Well Number and PPDM" in html
|
|
5192
|
+
and not forbidden_terms(md) and not forbidden_terms(html)
|
|
5193
|
+
and len(wells1) == 1 and wells1[0]["data"]["FacilityID"] == "42-329-39123"
|
|
5194
|
+
and any(al["AliasName"] == "42-329-39123" and al["AliasNameTypeID"].endswith("AliasNameType:USWellNumber:") for al in wells1[0]["data"]["NameAliases"])
|
|
5195
|
+
and wells1[0]["data"]["ExtensionProperties"]["gea"]["us_well_number"]["state"]["name"] == "Texas"
|
|
5196
|
+
and len(bores1) == 1 and bores1[0]["data"]["FacilityID"] == w1["id"] and "not identified" in bores1[0]["data"]["ExtensionProperties"]["gea"]["wellbore_identifier"]
|
|
5197
|
+
and bores2[0]["data"]["FacilityID"] == "42-329-39123-01" and bores2[0]["data"]["ExtensionProperties"]["gea"]["wellbore_identifier"] == "42-329-39123-01"
|
|
5198
|
+
and o2["validate"]["ok"]
|
|
5199
|
+
and r6.returncode == 0 and "identifies the wellbore" in r6.stdout and r7.returncode == 1 and "reserved" in r7.stdout
|
|
5200
|
+
and r8.returncode == 0 and "ppdm selftest" in r8.stdout
|
|
5201
|
+
and any(t["topic"] == "ppdm" for t in H.topics()) and not H.check("ppdm")
|
|
5202
|
+
and "sidetrack code" in (pkg / "help" / "ppdm.md").read_text(encoding="utf-8").split("will not call a measurement")[1],
|
|
5203
|
+
"BG3 `--action well-identity` names a bare well as unidentified and a declared one by its US Well Number, state, origin datum, wellbore "
|
|
5204
|
+
"standing, gauge depth, wellhead stream, well set and typed aliases; the SRA packet carries the identity on every well row and in its own "
|
|
5205
|
+
"table (section 2a) without counting its gaps against the packet, and the packet's prose passes the vocabulary gate; the OSDU export's Well "
|
|
5206
|
+
"record carries the ten-digit number as FacilityID and a typed alias with the state in the extension, and its Wellbore record keeps the "
|
|
5207
|
+
"program's id and says the sidetrack code was not given until a twelve-digit number is declared, when it carries that; `gea ppdm` parses "
|
|
5208
|
+
"a number from the command line with the exit code saying whether it is valid, and runs the self-test; the help page names the sidetrack "
|
|
5209
|
+
"it will not assume")
|
|
5210
|
+
|
|
4970
5211
|
def main() -> int:
|
|
4971
5212
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
4972
5213
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -5027,6 +5268,8 @@ def main() -> int:
|
|
|
5027
5268
|
section_bc_sra(tmp)
|
|
5028
5269
|
section_bd_association(tmp)
|
|
5029
5270
|
section_be_osdu(tmp)
|
|
5271
|
+
section_bf_quakeml(tmp)
|
|
5272
|
+
section_bg_ppdm(tmp)
|
|
5030
5273
|
if _FAILS:
|
|
5031
5274
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
5032
5275
|
for f in _FAILS:
|
|
@@ -1613,6 +1613,23 @@ def sra_packet_report(pk: dict, evaluated_at: Optional[datetime] = None, program
|
|
|
1613
1613
|
f"from the centre, with every position carried to WGS84 first; {m['n_undecided']} could not be placed."],
|
|
1614
1614
|
[Table(['Well', 'Name', 'API', 'UIC', 'Membership', 'Distance (km)', 'Depth tier', 'BHP method', 'Daily record'], wrows, 'Wells'),
|
|
1615
1615
|
Table(['Station', 'Name', 'Kind', 'Membership', 'Distance (km)'], srows, 'Seismic stations')]))
|
|
1616
|
+
# the wells named the way the operator's master data names them (v0.13.0)
|
|
1617
|
+
irows = []
|
|
1618
|
+
for w in pk['wells']:
|
|
1619
|
+
i = w.get('identity')
|
|
1620
|
+
if not i:
|
|
1621
|
+
continue
|
|
1622
|
+
irows.append([w['id'], i.get('us_well_number') or '-', (i.get('identifies') or '-').replace('_', ' '), i.get('state') or '-',
|
|
1623
|
+
i.get('wellbore') or 'not identified', i.get('origin_datum') or '-', '; '.join(i.get('aliases') or []),
|
|
1624
|
+
'; '.join(g.split(': ', 1)[1] if ': ' in g else g for g in (i.get('gaps') or [])) or '-'])
|
|
1625
|
+
if irows:
|
|
1626
|
+
secs.append(Section('2a', 'The wells as the US Well Number and PPDM name them', [
|
|
1627
|
+
'Each well by its US Well Number taken apart - the state, the county, the unique well - and by the components of PPDM\'s "What is a '
|
|
1628
|
+
'Well" the site can name: the Well and its one Origin at the declared surface position, the Wellbore when the sidetrack code was '
|
|
1629
|
+
'given, the Well Set as the site, and every alias typed. Ten digits name the Well Origin and nothing below it; a Wellbore is named '
|
|
1630
|
+
'only when the regulator\'s sidetrack code was declared. The injection interval and the completion are the permit\'s and the '
|
|
1631
|
+
'completion report\'s facts and are named here as not held.'],
|
|
1632
|
+
[Table(['Well', 'US Well Number', 'Identifies', 'State', 'Wellbore', 'Origin datum', 'Aliases', 'Not named'], irows, 'Well identity')]))
|
|
1616
1633
|
n = 3
|
|
1617
1634
|
inside_wells = [w for w in pk['wells'] if w['inside'] and w.get('months')]
|
|
1618
1635
|
for w in inside_wells:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The well as master data names it
|
|
2
|
+
|
|
3
|
+
> The US Well Number taken apart - state, county, unique well, sidetrack, event - and the components of PPDM's "What is a Well" the site can name for each well: the Well and its one Origin, the Wellbore, the Wellhead Stream, the Well Set, and every alias typed.
|
|
4
|
+
|
|
5
|
+
**The command**: `gea workspace --path C:\site --action well-identity --well <id>` prints a well's identity from its declarations (`set-well --api --uic --lat --lon --datum`) and the site it belongs to. `gea ppdm --action parse --number 42-329-39123-01` takes a number apart and names every way it is not valid. `gea ppdm` runs the self-test. The identity also rides on every well row of the SRA packet (section 2a) and in the OSDU export's Well record (the ten-digit number as a typed alias and `FacilityID`, the state and the parts in the extension; the Wellbore record's `FacilityID` is the twelve-digit number when it was given).
|
|
6
|
+
|
|
7
|
+
**What it writes**: the US Well Number in the standard's form `SS-CCC-UUUUU-SS-EE` (the standard the API transferred to the PPDM Association in 2010; PPDM's 2013 US Well Number is its successor): the state or offshore code and its name, the county code, the unique well number and which of the standard's ranges it falls in (historical, current, reserved, exempt), the sidetrack code and the event sequence when given, and which component the number identifies - ten digits the Well Origin, twelve a Wellbore, fourteen an event. Then the components: the Well; the Well Origin at the declared surface position on its datum; the Wellbore, identified only when the sidetrack code was given; the gauge station as a measured depth along the wellbore; the Wellhead Stream as the channels recorded at the wellhead with the declared pressure, rate and bottomhole channels named and the direction (into the ground for a disposal well); the Well Set as the site; the aliases - the operator's name, the program's id, the US Well Number, the UIC permit - each typed.
|
|
8
|
+
|
|
9
|
+
**The number to check**: `IDENTIFIED` or `UNIDENTIFIED` on the identity, `VALID` or `INVALID` on the number, and the `gap:` lines - every component the site cannot name is named as not named, with the reason.
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: a sidetrack code that was not given - ten digits name the Well Origin and nothing below it, and "00" is the original hole only when the regulator assigned it, so a Wellbore is not identified from a ten-digit number. A Wellbore Contact Interval (the injection interval) or a Wellbore Completion - those are the permit's and the completion report's facts, and the identity names them as not held. A county name - the standard's state and county booklet carries it, this program does not, and a county code is checked for its shape only. A state from a reserved code (52-54) or a code outside the standard.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The QuakeML catalogue export
|
|
2
|
+
|
|
3
|
+
> The site's own events - what its stations located together - in QuakeML 1.2, the exchange format a regulator's catalogue tool, a university network's review desk and every seismological package read.
|
|
4
|
+
|
|
5
|
+
**The command**: `gea workspace --path C:\site --action quakeml-export --site <id> [--agency "<name>"] [--out <file.xml>]`, after `--action associate` has run on the site (the export is built from `reports/sites/<site>/association.json`). `--agency` is the agencyID written in every creationInfo; the site's name by default. `gea quakeml` runs the self-test on the association leg's labelled scene and leaves the file where `--out` says.
|
|
6
|
+
|
|
7
|
+
**What it writes**: `reports/sites/<site>/quakeml/<site>_events.xml` - a `q:quakeml` document in the QuakeML 1.2 namespaces, built to the Basic Event Description schema (QuakeML-BED-1.2.xsd): one `event` per associated event with its `origin` (time; latitude and longitude with the misfit region's half-extent as their uncertainty in degrees; depth in metres with `depthType` "operator assigned", because the depth was declared; `methodID` and `earthModelID` naming the grid search and the one-velocity flat-earth model; `quality` with the station and phase counts, the RMS as standardError, the azimuthal gap and the epicentral distances in degrees; `originUncertainty` with the misfit region in metres), one `pick` per station that made the event (the onset time with the pick uncertainty, the record's own FDSN codes in `waveformID`, phaseHint P), and one `arrival` per pick on the origin (azimuth, distance, the residual, weight 1). Every origin and pick is `evaluationMode` automatic and `evaluationStatus` preliminary. The catalogue's standing - AGREES, DIFFERS with both positions, NOT IN CATALOGUE - is a comment on the event with the catalogue's own event id. `export_summary.json` beside it carries the counts, the gaps and the shape check. Every export is in `records/audit.jsonl`.
|
|
8
|
+
|
|
9
|
+
**The number to check**: `shape VALID_SHAPE` on the second line - the first checks a QuakeML reader makes, made here first: the namespaces; every publicID to the schema's ResourceIdentifier pattern and used once; every child element one the schema allows for its parent; time, latitude and longitude on every origin; time and waveformID on every pick; every arrival's pickID a pick of the same event; every event's preferredOriginID one of its origins; every enumerated value in its enumeration. A `gap:` line names a station whose record carried no network and station code (written under the test network XX with the station id) or no position (its arrival carries no azimuth or distance).
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: a magnitude - the association does not estimate one, the file carries no `magnitude` element, and a comment on every event says why. The catalogue's events as this site's - they are the catalogue's to publish; the file names them in comments and carries only what the site's own stations located. A reviewed or final event - a person has not looked at these, and the file says automatic and preliminary in the schema's own words. A located depth - it was declared, and `depthType` says so. A full schema validation - the shape check is what a reader reads first, not a validator; the file was validated against the published XSD when this export was written, and the check here holds it to that shape.
|
|
@@ -40,6 +40,8 @@ INDEX: List[tuple] = [
|
|
|
40
40
|
('notifications', 'Notifications', 'rules, channels, the delivery log'),
|
|
41
41
|
('sra', 'The Seismicity Response Area', 'the packet an operator inside an SRA puts in front of the Commission'),
|
|
42
42
|
('osdu', 'The OSDU-shaped export', 'the site as the manifest an operator\'s data platform loads'),
|
|
43
|
+
('quakeml', 'The QuakeML catalogue export', 'the site\'s own events in the format the regulator\'s catalogue tools read'),
|
|
44
|
+
('ppdm', 'The well as master data names it', 'the US Well Number taken apart and the PPDM "What is a Well" components the site can name'),
|
|
43
45
|
('doctor', 'Which code is running', 'gea doctor and the serve start-up gate'),
|
|
44
46
|
('audit-update', 'Audit / Update', 'the whole audit log with filters, the program against PyPI, every report against its source'),
|
|
45
47
|
('upkeep', 'Upkeep', 'housekeeping, sessions, the TLS proxy, the load test'),
|
|
@@ -140,14 +140,25 @@ def spatial_location(lat: float, lon: float, datum: Optional[str], ctx: Context,
|
|
|
140
140
|
def well_record(well: dict, ctx: Context) -> dict:
|
|
141
141
|
disp = well.get('disposal') or {}
|
|
142
142
|
surf = well.get('surface') or {}
|
|
143
|
+
from . import ppdm as P
|
|
144
|
+
ident = P.well_identity(well)
|
|
145
|
+
wn = ident.get('us_well_number') or {}
|
|
143
146
|
data: Dict[str, Any] = {**_common(ctx), 'FacilityName': well.get('display') or well['id'],
|
|
144
|
-
'FacilityID': disp.get('api_number') or well['id'],
|
|
147
|
+
'FacilityID': wn.get('api10') if wn.get('status') == 'VALID' else (disp.get('api_number') or well['id']),
|
|
145
148
|
'FacilityTypeID': ctx.ref('reference-data--FacilityType', 'Well'),
|
|
146
149
|
'OperatingEnvironmentID': ctx.ref('reference-data--OperatingEnvironment', 'Onshore'),
|
|
147
150
|
'NameAliases': [{'AliasName': well['id'], 'AliasNameTypeID': ctx.ref('reference-data--AliasNameType', 'UniqueIdentifier')}],
|
|
148
151
|
'ExtensionProperties': {'gea': {'well_id': well['id'], 'kind': well.get('kind'), 'role': disp.get('role')}}}
|
|
149
152
|
if disp.get('api_number'):
|
|
150
153
|
data['NameAliases'].append({'AliasName': disp['api_number'], 'AliasNameTypeID': ctx.ref('reference-data--AliasNameType', 'RegulatoryIdentifier')})
|
|
154
|
+
if wn.get('status') == 'VALID':
|
|
155
|
+
# the US Well Number as the standard writes it: the ten-digit well origin, typed; the state named in the extension
|
|
156
|
+
data['NameAliases'].append({'AliasName': wn['api10'], 'AliasNameTypeID': ctx.ref('reference-data--AliasNameType', 'USWellNumber')})
|
|
157
|
+
data['ExtensionProperties']['gea']['us_well_number'] = {'api10': wn['api10'], 'api12': wn.get('api12'), 'api14': wn.get('api14'),
|
|
158
|
+
'state': wn['state'], 'county_code': wn['county']['code'],
|
|
159
|
+
'unique_well': wn['unique_well'], 'identifies': wn['identifies']}
|
|
160
|
+
elif disp.get('api_number'):
|
|
161
|
+
data['ExtensionProperties']['gea']['us_well_number'] = {'declared': disp['api_number'], 'status': 'INVALID', 'problems': wn.get('problems')}
|
|
151
162
|
if disp.get('uic_number'):
|
|
152
163
|
data['NameAliases'].append({'AliasName': disp['uic_number'], 'AliasNameTypeID': ctx.ref('reference-data--AliasNameType', 'PermitNumber')})
|
|
153
164
|
if ctx.operator_org_id:
|
|
@@ -171,12 +182,16 @@ def well_record(well: dict, ctx: Context) -> dict:
|
|
|
171
182
|
|
|
172
183
|
|
|
173
184
|
def wellbore_record(well: dict, ctx: Context) -> dict:
|
|
185
|
+
from . import ppdm as P
|
|
186
|
+
wn = P.well_identity(well).get('us_well_number') or {}
|
|
174
187
|
data: Dict[str, Any] = {**_common(ctx), 'FacilityName': (well.get('display') or well['id']) + ' wellbore',
|
|
175
|
-
|
|
188
|
+
# twelve digits name a wellbore; ten name the origin only, so the wellbore keeps the program's id and says so
|
|
189
|
+
'FacilityID': wn.get('api12') or well['id'],
|
|
176
190
|
'FacilityTypeID': ctx.ref('reference-data--FacilityType', 'Wellbore'),
|
|
177
191
|
'WellID': _sk('well', well['id']),
|
|
178
192
|
'TrajectoryTypeID': ctx.ref('reference-data--WellboreTrajectoryType', 'Unknown'),
|
|
179
|
-
'ExtensionProperties': {'gea': {'well_id': well['id']
|
|
193
|
+
'ExtensionProperties': {'gea': {'well_id': well['id'],
|
|
194
|
+
'wellbore_identifier': wn.get('api12') or 'not identified: the sidetrack code was not given'}}}
|
|
180
195
|
surf = well.get('surface') or {}
|
|
181
196
|
if surf.get('lat') is not None and surf.get('lon') is not None:
|
|
182
197
|
sl = spatial_location(surf['lat'], surf['lon'], surf.get('datum'), ctx, well.get('added_utc')); sl.pop('_gap', None)
|