gea-program 0.21.0__tar.gz → 0.22.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.21.0 → gea_program-0.22.0}/PKG-INFO +24 -2
- {gea_program-0.21.0 → gea_program-0.22.0}/README.md +23 -1
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/__init__.py +1 -1
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/__main__.py +50 -4
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/acceptance_tests.py +260 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/client_reports.py +33 -0
- gea_program-0.22.0/gea/elevation.py +60 -0
- gea_program-0.22.0/gea/help/interval.md +11 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/helplib.py +1 -0
- gea_program-0.22.0/gea/interval.py +250 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/service.py +32 -1
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/web/app.html +31 -3
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/workspace.py +79 -7
- {gea_program-0.21.0 → gea_program-0.22.0}/gea_program.egg-info/PKG-INFO +24 -2
- {gea_program-0.21.0 → gea_program-0.22.0}/gea_program.egg-info/SOURCES.txt +3 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/pyproject.toml +1 -1
- {gea_program-0.21.0 → gea_program-0.22.0}/LICENSE +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/backup.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/bench.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/blind_harness.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/certificates.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/cli.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/config_versioning.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/conformance.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/correlation.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/dashboard.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/deviation.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/doctor.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/earth_model.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/etp.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/example_register_map.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/fat_sat.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/files.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/follower.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/forward_model.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/gamma.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/geodesy.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/alarms.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/backup.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/conformance.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/data-in.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/doctor.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/drift.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/etp.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/files.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/instruments.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/month-end.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/notifications.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/osdu.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/patches.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/quality.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/seismic.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/site.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/sites.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/soak.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/sra.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/start.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/structure.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/transients.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/vibration.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/housekeeping.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/jobs.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/live_ports.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/loadtest.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/modbus.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/model_card.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/month_end.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/notify.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/opcua_port.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/operator_app.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/osdu.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/patches.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/permits.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/ports.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/ppdm.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/project.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/quakeml.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reconciler.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/sample_record.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/sbom.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seedlink.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/segy.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_array.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_film.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_response.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_track.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/service_life.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/shapefile.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/shell.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/shut_in.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/sla_report.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/soak.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/sra.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/store_forward.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/strata_join.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/structure.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/supervisor.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/survey_view.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/telemetry.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/tool_library.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/transient.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/vibration.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/well_assembler.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/wits0.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea/witsml.py +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.21.0 → gea_program-0.22.0}/setup.cfg +0 -0
- {gea_program-0.21.0 → gea_program-0.22.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.22.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 (353 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]")
|
|
@@ -648,6 +648,28 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
648
648
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
649
649
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
650
650
|
|
|
651
|
+
### The interval against the mapped tops
|
|
652
|
+
|
|
653
|
+
Where does the injection sit in the geology? The operator declares the
|
|
654
|
+
disposal interval - top and base, true vertical depth, and the reference
|
|
655
|
+
elevation the depths are measured from with what it is (kelly bushing, ground,
|
|
656
|
+
derrick floor) and where it came from: `gea workspace --action set-well --well
|
|
657
|
+
<id> --top-interval-ft 9850 --base-interval-ft 10400 --depth-ref TVD
|
|
658
|
+
--ref-elev-ft 2962 --ref-elev-datum KB --ref-elev-source "completion report"`
|
|
659
|
+
(`--elev-3dep` fetches the ground elevation from the USGS 3DEP point service
|
|
660
|
+
instead, stored with its raster and named as the ground). A contours layer is
|
|
661
|
+
told what it maps (`add-layer ... --formation-name Wolfcamp --role
|
|
662
|
+
top|isopach|base`). `--action interval --well <id>` then converts the interval
|
|
663
|
+
to subsea with the arithmetic printed and sets it beside each formation-top
|
|
664
|
+
bracket at the well: ABOVE the mapped top, BELOW it, or NOT RESOLVED where
|
|
665
|
+
the bracket overlaps the interval; a formation with a top and an isopach gets
|
|
666
|
+
a base bracket by arithmetic the report shows, and the declared depth tier
|
|
667
|
+
(shallow or deep by the base the SRA names) is CONSISTENT with the map,
|
|
668
|
+
INCONSISTENT, or NOT RESOLVED. The SRA packet's section 2c carries it and
|
|
669
|
+
section 2d sets the catalogue's events against the mapped faults - the
|
|
670
|
+
nearest trace to each epicentre, the counts within 1, 2 and 5 km, proximity
|
|
671
|
+
never called attribution. `gea help interval`.
|
|
672
|
+
|
|
651
673
|
### Mapped structure
|
|
652
674
|
|
|
653
675
|
The first geological statement the program makes about a well. A structure
|
|
@@ -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 (353 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]")
|
|
@@ -609,6 +609,28 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
609
609
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
610
610
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
611
611
|
|
|
612
|
+
### The interval against the mapped tops
|
|
613
|
+
|
|
614
|
+
Where does the injection sit in the geology? The operator declares the
|
|
615
|
+
disposal interval - top and base, true vertical depth, and the reference
|
|
616
|
+
elevation the depths are measured from with what it is (kelly bushing, ground,
|
|
617
|
+
derrick floor) and where it came from: `gea workspace --action set-well --well
|
|
618
|
+
<id> --top-interval-ft 9850 --base-interval-ft 10400 --depth-ref TVD
|
|
619
|
+
--ref-elev-ft 2962 --ref-elev-datum KB --ref-elev-source "completion report"`
|
|
620
|
+
(`--elev-3dep` fetches the ground elevation from the USGS 3DEP point service
|
|
621
|
+
instead, stored with its raster and named as the ground). A contours layer is
|
|
622
|
+
told what it maps (`add-layer ... --formation-name Wolfcamp --role
|
|
623
|
+
top|isopach|base`). `--action interval --well <id>` then converts the interval
|
|
624
|
+
to subsea with the arithmetic printed and sets it beside each formation-top
|
|
625
|
+
bracket at the well: ABOVE the mapped top, BELOW it, or NOT RESOLVED where
|
|
626
|
+
the bracket overlaps the interval; a formation with a top and an isopach gets
|
|
627
|
+
a base bracket by arithmetic the report shows, and the declared depth tier
|
|
628
|
+
(shallow or deep by the base the SRA names) is CONSISTENT with the map,
|
|
629
|
+
INCONSISTENT, or NOT RESOLVED. The SRA packet's section 2c carries it and
|
|
630
|
+
section 2d sets the catalogue's events against the mapped faults - the
|
|
631
|
+
nearest trace to each epicentre, the counts within 1, 2 and 5 km, proximity
|
|
632
|
+
never called attribution. `gea help interval`.
|
|
633
|
+
|
|
612
634
|
### Mapped structure
|
|
613
635
|
|
|
614
636
|
The first geological statement the program makes about a well. A structure
|
|
@@ -285,7 +285,7 @@ def main(argv=None) -> int:
|
|
|
285
285
|
"add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
|
|
286
286
|
"seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
|
|
287
287
|
"set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export", "well-identity", "vibration-report",
|
|
288
|
-
"add-live-station", "live-fold", "live-run", "month-end", "add-layer", "layers", "remove-layer", "structure"])
|
|
288
|
+
"add-live-station", "live-fold", "live-run", "month-end", "add-layer", "layers", "remove-layer", "structure", "interval"])
|
|
289
289
|
p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
|
|
290
290
|
p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
|
|
291
291
|
p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
|
|
@@ -362,6 +362,15 @@ def main(argv=None) -> int:
|
|
|
362
362
|
p_ws.add_argument("--layer", type=str, default=None, help="add-layer: the shapefile (zip, folder or .shp); remove-layer: the layer id")
|
|
363
363
|
p_ws.add_argument("--member", type=str, default=None, help="add-layer: which shapefile inside a zip or folder that holds several (gea structure --layer <zip> --list)")
|
|
364
364
|
p_ws.add_argument("--citation", type=str, default="", help="add-layer: where the map came from, in the site's words")
|
|
365
|
+
p_ws.add_argument("--formation-name", type=str, default=None, help="add-layer: the formation a contours layer maps (Wolfcamp)")
|
|
366
|
+
p_ws.add_argument("--role", type=str, choices=["top", "isopach", "base"], default=None, help="add-layer: what the contours are of that formation: its top elevation, its thickness, or its base")
|
|
367
|
+
p_ws.add_argument("--base-interval-ft", type=float, default=None, help="set-well: base of the disposal interval, ft")
|
|
368
|
+
p_ws.add_argument("--depth-ref", type=str, choices=["TVD", "MD"], default=None, help="set-well: the interval depths are true vertical (TVD) or measured (MD) depth")
|
|
369
|
+
p_ws.add_argument("--ref-elev-ft", type=float, default=None, help="set-well: the reference elevation the depths are measured from, ft above sea level")
|
|
370
|
+
p_ws.add_argument("--ref-elev-datum", type=str, choices=["KB", "GL", "DF"], default=None, help="set-well: what that elevation is: kelly bushing, ground level, derrick floor")
|
|
371
|
+
p_ws.add_argument("--ref-elev-source", type=str, default=None, help="set-well: where the elevation came from (completion report, survey plat)")
|
|
372
|
+
p_ws.add_argument("--elev-3dep", action="store_true", help="set-well: fetch the GROUND elevation at the surface position from the USGS 3DEP point service and store it as the reference (datum GL, source stated)")
|
|
373
|
+
p_ws.add_argument("--epqs-url", type=str, default=None, help="set-well --elev-3dep / gea elevation: the EPQS endpoint (default the USGS one)")
|
|
365
374
|
p_ws.add_argument("--period", type=str, default="previous", help="month-end: the month, YYYY-MM, or 'previous' (the last closed month)")
|
|
366
375
|
p_ws.add_argument("--no-refresh", action="store_true", help="month-end: collect the reports as they stand without refreshing first")
|
|
367
376
|
p_ws.add_argument("--name", type=str, default=None, help="init: site name; add-*: display name")
|
|
@@ -395,6 +404,10 @@ def main(argv=None) -> int:
|
|
|
395
404
|
p_hk.add_argument("--actor", type=str, default="housekeeping")
|
|
396
405
|
p_hk.add_argument("--json", action="store_true")
|
|
397
406
|
|
|
407
|
+
p_el = sub.add_parser("elevation", help="the ground elevation of a position from the USGS 3DEP point service: --lat --lon (the ground, NAVD88; not the kelly bushing)")
|
|
408
|
+
p_el.add_argument("--lat", type=float, required=True); p_el.add_argument("--lon", type=float, required=True)
|
|
409
|
+
p_el.add_argument("--url", type=str, default=None, help="the EPQS endpoint (default the USGS one)")
|
|
410
|
+
p_el.add_argument("--json", action="store_true")
|
|
398
411
|
p_st = sub.add_parser("structure", help="the nearest mapped fault and fold to a position, from a shapefile: --layer <zip|folder|.shp> --lat --lon; --info describes the layer")
|
|
399
412
|
p_st.add_argument("--layer", type=str, required=True)
|
|
400
413
|
p_st.add_argument("--lat", type=float, default=None); p_st.add_argument("--lon", type=float, default=None)
|
|
@@ -939,6 +952,16 @@ def main(argv=None) -> int:
|
|
|
939
952
|
for x in r['records_removed'][:20]:
|
|
940
953
|
print(f" record {x['well_id']}/{x['file']} {x['age_days']} d")
|
|
941
954
|
return 0
|
|
955
|
+
elif a.cmd == "elevation":
|
|
956
|
+
from .elevation import EPQS_URL, epqs
|
|
957
|
+
r = epqs(a.lat, a.lon, url=a.url or EPQS_URL)
|
|
958
|
+
if a.json:
|
|
959
|
+
print(json.dumps(r, indent=1)); return 0 if r["status"] == "OK" else 1
|
|
960
|
+
if r["status"] != "OK":
|
|
961
|
+
print(f"elevation: {r['status']} - {r.get('detail', '')}", file=sys.stderr); return 1
|
|
962
|
+
print(f"ground elevation at {a.lat}, {a.lon}: {r['elev_ft']:,.2f} ft ({r['vertical_datum']})")
|
|
963
|
+
print(f" source: {r['source']}")
|
|
964
|
+
return 0
|
|
942
965
|
elif a.cmd == "structure":
|
|
943
966
|
import json as _json
|
|
944
967
|
from . import shapefile as _SF
|
|
@@ -1953,13 +1976,28 @@ def main(argv=None) -> int:
|
|
|
1953
1976
|
elif a.action == "set-well":
|
|
1954
1977
|
if not a.well:
|
|
1955
1978
|
raise SystemExit("set-well needs --well <id>")
|
|
1956
|
-
surface = {"lat": a.lat, "lon": a.lon, "datum": a.datum} if (a.lat is not None or a.lon is not None) else
|
|
1979
|
+
surface = {"lat": a.lat, "lon": a.lon, "datum": a.datum} if (a.lat is not None or a.lon is not None) else {}
|
|
1980
|
+
if a.elev_3dep:
|
|
1981
|
+
from .elevation import EPQS_URL, as_reference, epqs
|
|
1982
|
+
w0 = ws.well(a.well)
|
|
1983
|
+
pos = {"lat": a.lat, "lon": a.lon} if a.lat is not None else (w0.get("surface") or {})
|
|
1984
|
+
if pos.get("lat") is None:
|
|
1985
|
+
raise SystemExit("--elev-3dep needs the surface position (give --lat --lon --datum, or declare it first)")
|
|
1986
|
+
r = epqs(float(pos["lat"]), float(pos["lon"]), url=a.epqs_url or EPQS_URL)
|
|
1987
|
+
if r["status"] != "OK":
|
|
1988
|
+
raise SystemExit(f"3DEP: {r['status']} - {r.get('detail', '')} ({r['url']})")
|
|
1989
|
+
surface["elevation"] = as_reference(r)
|
|
1990
|
+
print(f"3DEP ground elevation {r['elev_ft']:,.2f} ft ({r['source']})")
|
|
1991
|
+
elif a.ref_elev_ft is not None or a.ref_elev_datum or a.ref_elev_source:
|
|
1992
|
+
surface["elevation"] = {"ft": a.ref_elev_ft, "datum": a.ref_elev_datum, "source": a.ref_elev_source}
|
|
1957
1993
|
disposal = {k: v for k, v in {"api_number": a.api, "uic_number": a.uic, "depth_tier": a.depth_tier, "formation_basis": a.formation,
|
|
1958
1994
|
"channel_pressure": a.channel_pressure, "channel_rate": a.channel_rate, "rate_unit": a.rate_unit,
|
|
1959
1995
|
"bhp_method": a.bhp_method, "channel_bhp": a.channel_bhp, "top_interval_ft": a.top_interval_ft,
|
|
1996
|
+
"base_interval_ft": a.base_interval_ft, "depth_reference": a.depth_ref,
|
|
1960
1997
|
"gradient_psi_ft": a.gradient_psi_ft}.items() if v is not None}
|
|
1961
1998
|
if not surface and not disposal:
|
|
1962
|
-
raise SystemExit("set-well: nothing to declare (give --lat/--lon, or any of --api --uic --depth-tier --
|
|
1999
|
+
raise SystemExit("set-well: nothing to declare (give --lat/--lon, --ref-elev-ft, or any of --api --uic --depth-tier --top-interval-ft ...)")
|
|
2000
|
+
surface = surface or None
|
|
1963
2001
|
w = ws.set_well(a.well, surface=surface, disposal=disposal or None, actor=a.actor)
|
|
1964
2002
|
print(f"well {a.well}: surface {w.get('surface')}; disposal {w.get('disposal')}"); return 0
|
|
1965
2003
|
elif a.action == "sra-define":
|
|
@@ -2021,7 +2059,7 @@ def main(argv=None) -> int:
|
|
|
2021
2059
|
elif a.action == "add-layer":
|
|
2022
2060
|
if not a.layer or not a.name:
|
|
2023
2061
|
raise SystemExit("add-layer needs --name <display> and --layer <shapefile zip | folder | .shp>")
|
|
2024
|
-
L = ws.add_layer(a.layer, a.name, actor=a.actor, citation=a.citation, member=a.member)
|
|
2062
|
+
L = ws.add_layer(a.layer, a.name, actor=a.actor, citation=a.citation, member=a.member, formation=a.formation_name, role=a.role)
|
|
2025
2063
|
tc = ", ".join(f"{k} {v}" for k, v in list(L["type_counts"].items())[:6])
|
|
2026
2064
|
print(f"layer {L['id']} ({L['kind']}): {L['n_features']} {L['shape_type']} features, {L['crs']['kind']} {L['crs'].get('datum') or ''}, extent lon {L['bbox'][0]:.3f}..{L['bbox'][2]:.3f} lat {L['bbox'][1]:.3f}..{L['bbox'][3]:.3f}"
|
|
2027
2065
|
+ (f"; contour value {L['value_attr']}" if L.get('value_attr') else ''))
|
|
@@ -2050,6 +2088,14 @@ def main(argv=None) -> int:
|
|
|
2050
2088
|
for line in L["lines"]:
|
|
2051
2089
|
print(" " + line)
|
|
2052
2090
|
return 0 if r["status"] == "OK" else 1
|
|
2091
|
+
elif a.action == "interval":
|
|
2092
|
+
if not a.well:
|
|
2093
|
+
raise SystemExit("interval needs --well <id>")
|
|
2094
|
+
r = ws.well_interval(a.well, a.radius_km if a.radius_km is not None else 50.0, actor=a.actor)
|
|
2095
|
+
print(f"interval {a.well}: {r['status']}")
|
|
2096
|
+
for line in r["lines"]:
|
|
2097
|
+
print(" " + line)
|
|
2098
|
+
return 0 if r["status"] == "OK" else 1
|
|
2053
2099
|
elif a.action == "month-end":
|
|
2054
2100
|
from .month_end import report_text as _me_text
|
|
2055
2101
|
r = ws.month_end(a.period, actor=a.actor, refresh=not a.no_refresh, catalog_csv=a.catalog)
|
|
@@ -6336,6 +6336,265 @@ def section_bo_structure(tmp: str) -> None:
|
|
|
6336
6336
|
"all four; `gea structure --list` and `--member`"
|
|
6337
6337
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
6338
6338
|
|
|
6339
|
+
def section_bp_interval(tmp: str) -> None:
|
|
6340
|
+
"""Section BP - the declared interval against the mapped tops: subsea conversion through a stated reference, the
|
|
6341
|
+
interval ABOVE / BELOW / NOT RESOLVED against each formation-top bracket, a base from a top and an isopach by the
|
|
6342
|
+
arithmetic shown, the depth tier against the mapped base; the catalogue events against the mapped faults; the
|
|
6343
|
+
3DEP ground elevation client against a stand-in service; the workspace, the SRA packet's sections, the API, the
|
|
6344
|
+
page, the CLI, the help."""
|
|
6345
|
+
import http.cookiejar
|
|
6346
|
+
import http.server
|
|
6347
|
+
import math as _math
|
|
6348
|
+
import subprocess as _sp
|
|
6349
|
+
import urllib.request
|
|
6350
|
+
import urllib.error
|
|
6351
|
+
import zipfile
|
|
6352
|
+
from . import shapefile as SF
|
|
6353
|
+
from . import interval as IV
|
|
6354
|
+
from . import elevation as EL
|
|
6355
|
+
from . import helplib as H
|
|
6356
|
+
from .telemetry import TelemetryRecorder, TelemetryConfig
|
|
6357
|
+
from .downhole_engine import DownholeEngine, SimulatorConfig
|
|
6358
|
+
from .workspace import Workspace, WorkspaceError
|
|
6359
|
+
from .service import Service, Users
|
|
6360
|
+
pkg = Path(__file__).parent
|
|
6361
|
+
d = Path(tmp, "bp"); d.mkdir()
|
|
6362
|
+
|
|
6363
|
+
# -- BP1: the module on its own ----------------------------------------------------------------------------------
|
|
6364
|
+
ss = IV.subsea_ft(9850.0, 2962.0)
|
|
6365
|
+
c_above = IV.compare(-2050.0, -2650.0, (-8000.0, -7500.0))
|
|
6366
|
+
c_below = IV.compare(-8100.0, -8700.0, (-8000.0, -7500.0))
|
|
6367
|
+
c_nr = IV.compare(-7400.0, -7900.0, (-8000.0, -7500.0))
|
|
6368
|
+
c_swapped = IV.compare(-2650.0, -2050.0, (-8000.0, -7500.0)) # top and base given the wrong way round
|
|
6369
|
+
bb = IV.base_bracket((-8000.0, -7500.0), (1800.0, 2000.0))
|
|
6370
|
+
t_deep_ok = IV.tier_check("deep", -10050.0, -10650.0, bb)
|
|
6371
|
+
t_deep_bad = IV.tier_check("deep", -2050.0, -2650.0, bb)
|
|
6372
|
+
t_shallow_ok = IV.tier_check("shallow", -2050.0, -2650.0, bb)
|
|
6373
|
+
t_nr = IV.tier_check("shallow", -9500.0, -10100.0, bb)
|
|
6374
|
+
t_none = IV.tier_check(None, -2050.0, -2650.0, bb)
|
|
6375
|
+
br_on = IV.bracket_of({"status": "OK", "on": -7600.0, "between": None})
|
|
6376
|
+
br_none = IV.bracket_of({"status": "OUTSIDE LAYER EXTENT"})
|
|
6377
|
+
named = IV.formation_named("base of the Wolfcamp", ["Delaware", "Wolfcamp", "Bone Spring"])
|
|
6378
|
+
named_none = IV.formation_named("base of the Ellenburger", ["Wolfcamp"])
|
|
6379
|
+
well_md = {"id": "w", "surface": {"lat": 1.0, "lon": 1.0, "elevation": {"ft": 2950.0, "datum": "KB", "source": "report"}},
|
|
6380
|
+
"disposal": {"top_interval_ft": 5000.0, "base_interval_ft": 5600.0, "depth_reference": "MD"}}
|
|
6381
|
+
st_md = IV.statement(well_md, {"status": "OK", "layers": []}, [], None)
|
|
6382
|
+
st_undeclared = IV.statement({"id": "w"}, {"status": "OK", "layers": []}, [], None)
|
|
6383
|
+
well_gl = {"id": "w", "surface": {"lat": 1.0, "lon": 1.0, "elevation": {"ft": 2950.0, "datum": "GL", "source": "USGS 3DEP"}},
|
|
6384
|
+
"disposal": {"top_interval_ft": 5000.0, "base_interval_ft": 5600.0, "depth_reference": "TVD"}}
|
|
6385
|
+
st_gl = IV.statement(well_gl, {"status": "OK", "layers": [{"layer_id": "t", "layer": "t", "layer_kind": "contours", "status": "OK", "between": [-8000.0, -7500.0]}]},
|
|
6386
|
+
[{"id": "t", "kind": "contours", "formation": "Wolfcamp", "role": "top"}], None)
|
|
6387
|
+
c1 = {
|
|
6388
|
+
"subsea": ss == -6888.0,
|
|
6389
|
+
"relations": c_above["relation"] == "ABOVE" and c_below["relation"] == "BELOW" and c_nr["relation"] == "NOT RESOLVED" and c_swapped["relation"] == "ABOVE"
|
|
6390
|
+
and "cannot place the top" in c_nr["detail"],
|
|
6391
|
+
"base_arithmetic": bb == (-10000.0, -9300.0),
|
|
6392
|
+
"tier": t_deep_ok["status"] == "CONSISTENT" and t_deep_bad["status"] == "INCONSISTENT" and t_shallow_ok["status"] == "CONSISTENT"
|
|
6393
|
+
and t_nr["status"] == "NOT RESOLVED" and t_none["status"] == "NOT DECLARED",
|
|
6394
|
+
"brackets_and_names": br_on == (-7600.0, -7600.0) and br_none is None and named == "Wolfcamp" and named_none is None,
|
|
6395
|
+
"md_refused": st_md["status"] == "NOT RESOLVED" and "measured depth" in st_md["lines"][0] and "set-well --depth-ref TVD" in st_md["lines"][0],
|
|
6396
|
+
"undeclared_named": st_undeclared["status"] == "NOT DECLARED" and "--top-interval-ft" in st_undeclared["lines"][0] and "--ref-elev-ft" in st_undeclared["lines"][0]
|
|
6397
|
+
and "--depth-ref" in st_undeclared["lines"][0],
|
|
6398
|
+
"gl_note": st_gl["status"] == "OK" and st_gl["tops"][0]["relation"] == "ABOVE" and any("kelly bushing" in n and "not stated" in n for n in st_gl["notes"]),
|
|
6399
|
+
}
|
|
6400
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
6401
|
+
ok(not failed1,
|
|
6402
|
+
"BP1 the interval module: subsea = reference - depth; ABOVE, BELOW and NOT RESOLVED against a top bracket (the top and base taken either way round); "
|
|
6403
|
+
"a base from a top bracket and a thickness bracket by the arithmetic shown; the tier CONSISTENT, INCONSISTENT, NOT RESOLVED or NOT DECLARED against "
|
|
6404
|
+
"the base; a measured-depth interval refused naming the remedy; an undeclared interval named by what is missing; a ground-elevation reference "
|
|
6405
|
+
"carries the note that the kelly bushing is not stated"
|
|
6406
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
6407
|
+
|
|
6408
|
+
# -- BP2: the workspace, the SRA packet, the API, the page, the CLI, the help -----------------------------------
|
|
6409
|
+
lat1, lon1 = 31.42, -103.50
|
|
6410
|
+
kmlat = 1.0 / 111.195
|
|
6411
|
+
kmlon = 1.0 / (111.195 * _math.cos(_math.radians(lat1)))
|
|
6412
|
+
|
|
6413
|
+
def ns_contours(name, attr, values, spacing_km=4.0):
|
|
6414
|
+
ctr = [([[(lon1 + (i - (len(values) - 1) / 2) * spacing_km * kmlon, lat1 - 0.3), (lon1 + (i - (len(values) - 1) / 2) * spacing_km * kmlon, lat1 + 0.3)]],
|
|
6415
|
+
{attr: float(v)}) for i, v in enumerate(values)]
|
|
6416
|
+
SF.write(str(d / name), "PolyLine", ctr, [(attr, "F", 10, 1)], SF.NAD83_GEOGCS)
|
|
6417
|
+
with zipfile.ZipFile(str(d / (name + ".zip")), "w") as z:
|
|
6418
|
+
for ext in (".shp", ".shx", ".dbf", ".prj"):
|
|
6419
|
+
z.write(str(d / (name + ext)), name + ext)
|
|
6420
|
+
return str(d / (name + ".zip"))
|
|
6421
|
+
z_top = ns_contours("WC_Top", "ElevTop_ft", [-8500, -8000, -7500, -7000]) # the position between -8,000 and -7,500
|
|
6422
|
+
z_iso = ns_contours("WC_Isopach", "Thick_ft", [1600, 1800, 2000, 2200]) # between 1,800 and 2,000: base between -10,000 and -9,300
|
|
6423
|
+
z_del = ns_contours("DEL_Top", "ElevTop_ft", [-2400, -2000, -1600, -1200]) # between -2,000 and -1,600
|
|
6424
|
+
faults = [([[(lon1 - 0.3, lat1 + 3 * kmlat), (lon1 + 0.3, lat1 + 3 * kmlat)]], {"LONG_DESC": "fault", "NAME": "North Trace"}),
|
|
6425
|
+
([[(lon1 + 8 * kmlon, lat1 - 0.3), (lon1 + 8 * kmlon, lat1 + 0.3)]], {"LONG_DESC": "dotted fault", "NAME": "East Trace"})]
|
|
6426
|
+
SF.write(str(d / "Faults_Test"), "PolyLine", faults, [("LONG_DESC", "C", 30, 0), ("NAME", "C", 30, 0)], SF.NAD83_GEOGCS)
|
|
6427
|
+
with zipfile.ZipFile(str(d / "Faults_Test.zip"), "w") as z:
|
|
6428
|
+
for ext in (".shp", ".shx", ".dbf", ".prj"):
|
|
6429
|
+
z.write(str(d / ("Faults_Test" + ext)), "Faults_Test" + ext)
|
|
6430
|
+
hist = str(d / "hist.csv")
|
|
6431
|
+
TelemetryRecorder(engine=DownholeEngine(SimulatorConfig()), config=TelemetryConfig(duration_hours=1.0, seed=5)).run().export_csv(hist)
|
|
6432
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
6433
|
+
w = ws.add_well_file(hist, display="SWD 1", actor="tester")
|
|
6434
|
+
lay_t = ws.add_layer(z_top, "WC top", actor="tester", formation="Wolfcamp", role="top", citation="the gate's own layer")
|
|
6435
|
+
lay_i = ws.add_layer(z_iso, "WC isopach", actor="tester", formation="Wolfcamp", role="isopach")
|
|
6436
|
+
ws.add_layer(z_del, "Delaware top", actor="tester", formation="Delaware") # a formation named, the role defaulting to top
|
|
6437
|
+
ws.add_layer(str(d / "Faults_Test.zip"), "Faults", actor="tester")
|
|
6438
|
+
role_err = None
|
|
6439
|
+
try:
|
|
6440
|
+
ws.add_layer(str(d / "Faults_Test.zip"), "Faults as top", actor="tester", formation="X", role="top")
|
|
6441
|
+
except WorkspaceError as e:
|
|
6442
|
+
role_err = str(e)
|
|
6443
|
+
before = ws.well_interval(w["id"], actor="tester")
|
|
6444
|
+
elev_err = None
|
|
6445
|
+
try:
|
|
6446
|
+
ws.set_well(w["id"], surface={"lat": lat1, "lon": lon1, "datum": "NAD83", "elevation": {"ft": 2950.0, "datum": "KB"}}, actor="tester")
|
|
6447
|
+
except WorkspaceError as e:
|
|
6448
|
+
elev_err = str(e)
|
|
6449
|
+
ws.set_well(w["id"], surface={"lat": lat1, "lon": lon1, "datum": "NAD83", "elevation": {"ft": 2950.0, "datum": "KB", "source": "completion report"}}, actor="tester")
|
|
6450
|
+
order_err = None
|
|
6451
|
+
try:
|
|
6452
|
+
ws.set_well(w["id"], disposal={"top_interval_ft": 5600.0, "base_interval_ft": 5000.0}, actor="tester")
|
|
6453
|
+
except WorkspaceError as e:
|
|
6454
|
+
order_err = str(e)
|
|
6455
|
+
ws.set_well(w["id"], disposal={"top_interval_ft": 5000.0, "base_interval_ft": 5600.0, "depth_reference": "TVD", "depth_tier": "shallow",
|
|
6456
|
+
"formation_basis": "completed above the base of the Wolfcamp", "channel_pressure": "P_surf_psi", "channel_rate": "Q_bpm",
|
|
6457
|
+
"rate_unit": "bbl/min", "bhp_method": "probe", "channel_bhp": "P_bh_psi"}, actor="tester")
|
|
6458
|
+
site = ws.add_site("Pad 3 site", wells=[w["id"]], actor="tester")
|
|
6459
|
+
ws.sra_define(site["id"], actor="tester", name="Pad 3 SRA", lat=lat1, lon=lon1, radius_km=9.08, plan_date="2026-01-15")
|
|
6460
|
+
st1 = ws.well_interval(w["id"], actor="tester")
|
|
6461
|
+
rel1 = {t["layer_id"]: t["relation"] for t in st1["tops"]}
|
|
6462
|
+
ws.set_well(w["id"], disposal={"top_interval_ft": 13000.0, "base_interval_ft": 13600.0, "depth_tier": "deep"}, actor="tester")
|
|
6463
|
+
st2 = ws.well_interval(w["id"], actor="tester")
|
|
6464
|
+
ws.set_well(w["id"], disposal={"top_interval_ft": 10300.0, "base_interval_ft": 10900.0, "depth_tier": "deep"}, actor="tester") # -7,350 to -7,950: across the top, above the base
|
|
6465
|
+
st3 = ws.well_interval(w["id"], actor="tester")
|
|
6466
|
+
ws.set_well(w["id"], disposal={"top_interval_ft": 12400.0, "base_interval_ft": 13000.0, "depth_tier": "deep"}, actor="tester") # -9,450 to -10,050: across the base bracket
|
|
6467
|
+
st3b = ws.well_interval(w["id"], actor="tester")
|
|
6468
|
+
ws.set_well(w["id"], disposal={"top_interval_ft": 5000.0, "base_interval_ft": 5600.0, "depth_tier": "shallow"}, actor="tester")
|
|
6469
|
+
# the catalogue: three events, two beside a trace
|
|
6470
|
+
cat = str(d / "cat.csv")
|
|
6471
|
+
with open(cat, "w", encoding="utf-8") as f:
|
|
6472
|
+
f.write("EventID,Origin Date,Origin Time,Local Magnitude,Latitude (WGS84),Longitude (WGS84),Depth of Hypocenter (Km. Rel to MSL)\n")
|
|
6473
|
+
f.write(f"e1,2026-03-01,00:00:00,3.6,{lat1 + 2.5 * kmlat:.6f},{lon1:.6f},6.0\n")
|
|
6474
|
+
f.write(f"e2,2026-03-02,00:00:00,2.0,{lat1:.6f},{lon1 + 7.5 * kmlon:.6f},5.0\n")
|
|
6475
|
+
f.write(f"e3,2026-03-03,00:00:00,2.2,{lat1 - 0.05:.6f},{lon1 - 0.05:.6f},7.0\n")
|
|
6476
|
+
pk = ws.write_sra_packet(site["id"], catalog_csv=cat, actor="tester")
|
|
6477
|
+
sra_md = open(pk["paths"]["markdown"], encoding="utf-8").read()
|
|
6478
|
+
audit = [a["action"] for a in ws.audit_log()]
|
|
6479
|
+
users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator")
|
|
6480
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
6481
|
+
base_url = svc.url.rstrip("/")
|
|
6482
|
+
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
6483
|
+
|
|
6484
|
+
def call(method, path, body=None):
|
|
6485
|
+
req = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
6486
|
+
req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
|
|
6487
|
+
try:
|
|
6488
|
+
with opener.open(req, timeout=120) as rr_:
|
|
6489
|
+
return rr_.status, json.loads(rr_.read())
|
|
6490
|
+
except urllib.error.HTTPError as e:
|
|
6491
|
+
return e.code, json.loads(e.read())
|
|
6492
|
+
try:
|
|
6493
|
+
call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
|
|
6494
|
+
st_g, got = call("GET", f"/api/wells/{w['id']}/interval")
|
|
6495
|
+
st_d, dec = call("POST", f"/api/wells/{w['id']}/declare", {"disposal": {"top_interval_ft": 13000.0, "base_interval_ft": 13600.0, "depth_tier": "deep"}})
|
|
6496
|
+
st_bad = call("POST", f"/api/wells/{w['id']}/declare", {"disposal": {"depth_reference": "feet"}})[0]
|
|
6497
|
+
st_p, wrote = call("POST", f"/api/wells/{w['id']}/interval", {})
|
|
6498
|
+
st_l, lst = call("GET", "/api/layers")
|
|
6499
|
+
finally:
|
|
6500
|
+
svc.stop()
|
|
6501
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
6502
|
+
help_md = (pkg / "help" / "interval.md").read_text(encoding="utf-8")
|
|
6503
|
+
r_cli = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "interval", "--well", w["id"]], capture_output=True, text=True)
|
|
6504
|
+
r_set = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "set-well", "--well", w["id"], "--top-interval-ft", "5000", "--base-interval-ft", "5600",
|
|
6505
|
+
"--depth-ref", "TVD", "--depth-tier", "shallow", "--ref-elev-ft", "2950", "--ref-elev-datum", "KB", "--ref-elev-source", "completion report"], capture_output=True, text=True)
|
|
6506
|
+
r_add = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "add-layer", "--name", "WC top again", "--layer", z_top, "--formation-name", "Wolfcamp", "--role", "top"],
|
|
6507
|
+
capture_output=True, text=True)
|
|
6508
|
+
sec2c = sra_md.split("The declared interval against the mapped tops")[1].split("## 2d")[0] if "The declared interval against the mapped tops" in sra_md else ""
|
|
6509
|
+
sec2d = sra_md.split("Catalogue events against the mapped faults")[1].split("## 3")[0] if "Catalogue events against the mapped faults" in sra_md else ""
|
|
6510
|
+
c2 = {
|
|
6511
|
+
"layers_declared": lay_t["formation"] == "Wolfcamp" and lay_t["role"] == "top" and lay_i["role"] == "isopach" and role_err and "contours layer" in role_err
|
|
6512
|
+
and next(L_["role"] for L_ in ws.layers() if L_["id"] == "Delaware-top") == "top",
|
|
6513
|
+
"declarations_refused": before["status"] == "NOT DECLARED" and elev_err and "source" in elev_err and order_err and "deeper than its top" in order_err,
|
|
6514
|
+
"shallow_statement": st1["status"] == "OK" and st1["declared"]["top_subsea_ft"] == -2050.0 and st1["declared"]["base_subsea_ft"] == -2650.0
|
|
6515
|
+
and rel1 == {lay_t["id"]: "ABOVE", lay_i["id"]: "THICKNESS", "Delaware-top": "BELOW"}
|
|
6516
|
+
and st1["formations"][0]["base"] == [-10000.0, -9300.0] and st1["tier"]["status"] == "CONSISTENT" and st1["tier"]["formation"] == "Wolfcamp"
|
|
6517
|
+
and os.path.isfile(Path(wsp, "reports", "wells", w["id"], "interval.json")),
|
|
6518
|
+
"deep_and_unresolved": st2["tier"]["status"] == "CONSISTENT" and st2["formations"][0]["interval_vs_base"]["relation"] == "BELOW"
|
|
6519
|
+
and st3["tier"]["status"] == "INCONSISTENT" and {t["layer_id"]: t["relation"] for t in st3["tops"]}[lay_t["id"]] == "NOT RESOLVED"
|
|
6520
|
+
and st3b["tier"]["status"] == "NOT RESOLVED" and st3b["formations"][0]["interval_vs_base"]["relation"] == "NOT RESOLVED",
|
|
6521
|
+
"sra_sections": "## 2c" in sra_md and "| SWD-1 | OK |" in sec2c and "ABOVE WC top (-8,000/-7,500)" in sec2c and "CONSISTENT" in sec2c
|
|
6522
|
+
and "## 2d" in sra_md and "| Faults | e1 |" in sec2d and "North Trace | 0.50 |" in sec2d and "East Trace | 0.50 |" in sec2d
|
|
6523
|
+
and "2 within 1 km, 2 within 2 km, 2 within 5 km" in sec2d and "proximity to a mapped trace is not an attribution" in sec2d,
|
|
6524
|
+
"audited": "well.interval" in audit and audit.count("well.interval") >= 3,
|
|
6525
|
+
"api": st_g == 200 and got["status"] == "OK" and st_d == 200 and dec["disposal"]["depth_tier"] == "deep" and st_bad == 400 and st_p == 200
|
|
6526
|
+
and wrote["tier"]["status"] == "CONSISTENT" and st_l == 200 and any(L_.get("role") == "isopach" for L_ in lst["layers"]),
|
|
6527
|
+
"page": "intervalCardHtml" in page_src and "/api/wells/' + id + '/interval'" in page_src and "/api/elevation" in page_src and "lyRole" in page_src
|
|
6528
|
+
and "kelly bushing is deeper by the KB height" in page_src,
|
|
6529
|
+
"cli": r_cli.returncode == 0 and "BELOW the mapped Wolfcamp top" in r_cli.stdout and "Depth tier against the map (base of the Wolfcamp): CONSISTENT" in r_cli.stdout
|
|
6530
|
+
and r_set.returncode == 0 and "'depth_reference': 'TVD'" in r_set.stdout and r_add.returncode == 0,
|
|
6531
|
+
"help": any(t["topic"] == "interval" for t in H.topics()) and not H.check("interval") and "never a value between them" in help_md.split("will not call a measurement")[1],
|
|
6532
|
+
}
|
|
6533
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
6534
|
+
ok(not failed2,
|
|
6535
|
+
"BP2 the workspace: a contours layer declared with its formation and role (a role on a faults layer refused); the interval and its reference declared "
|
|
6536
|
+
"(an elevation without its source refused, a base above its top refused), NOT DECLARED before; the shallow interval ABOVE the Wolfcamp top and "
|
|
6537
|
+
"BELOW the Delaware top, the Wolfcamp base from top and isopach, the tier CONSISTENT; a deep interval CONSISTENT; an interval across the top NOT "
|
|
6538
|
+
"RESOLVED against it and INCONSISTENT with a deep declaration; an interval across the base bracket NOT RESOLVED; the SRA packet's 2c and 2d; audited; the API declares, answers and writes; the page card with the 3DEP button; the CLI; the help"
|
|
6539
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
6540
|
+
|
|
6541
|
+
# -- BP3: the 3DEP client against a stand-in service, and set-well --elev-3dep through it ----------------------
|
|
6542
|
+
seen = []
|
|
6543
|
+
|
|
6544
|
+
class _EPQS(http.server.BaseHTTPRequestHandler):
|
|
6545
|
+
def log_message(self, *a): # quiet
|
|
6546
|
+
pass
|
|
6547
|
+
|
|
6548
|
+
def do_GET(self):
|
|
6549
|
+
import urllib.parse as up
|
|
6550
|
+
q = up.parse_qs(up.urlsplit(self.path).query)
|
|
6551
|
+
seen.append(q)
|
|
6552
|
+
y = float(q.get("y", ["0"])[0]); x = float(q.get("x", ["0"])[0])
|
|
6553
|
+
if y > 80:
|
|
6554
|
+
body = json.dumps({"location": {"x": x, "y": y}, "value": "-1000000", "rasterSource": "", "resolution": 0})
|
|
6555
|
+
elif y < -80:
|
|
6556
|
+
body = b"<html>not json</html>"
|
|
6557
|
+
else:
|
|
6558
|
+
body = json.dumps({"location": {"x": x, "y": y, "spatialReference": {"wkid": 4326}}, "locationId": 0, "value": "2951.37",
|
|
6559
|
+
"rasterSource": "https://prd-tnm.s3.amazonaws.com/USGS_13_n32w104.tif", "resolution": 10})
|
|
6560
|
+
data = body.encode() if isinstance(body, str) else body
|
|
6561
|
+
self.send_response(200); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", str(len(data))); self.end_headers()
|
|
6562
|
+
self.wfile.write(data)
|
|
6563
|
+
srv = http.server.ThreadingHTTPServer(("127.0.0.1", 0), _EPQS)
|
|
6564
|
+
th = threading.Thread(target=srv.serve_forever, daemon=True); th.start()
|
|
6565
|
+
url = f"http://127.0.0.1:{srv.server_address[1]}/v1/json"
|
|
6566
|
+
try:
|
|
6567
|
+
r_ok = EL.epqs(lat1, lon1, url=url)
|
|
6568
|
+
r_nodata = EL.epqs(85.0, lon1, url=url)
|
|
6569
|
+
r_notjson = EL.epqs(-85.0, lon1, url=url)
|
|
6570
|
+
r_unreach = EL.epqs(lat1, lon1, url="http://127.0.0.1:9/v1/json", timeout=3)
|
|
6571
|
+
ref = EL.as_reference(r_ok)
|
|
6572
|
+
r_cli_el = _sp.run([sys.executable, "-m", "gea", "elevation", "--lat", str(lat1), "--lon", str(lon1), "--url", url], capture_output=True, text=True)
|
|
6573
|
+
r_set3 = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "set-well", "--well", w["id"], "--elev-3dep", "--epqs-url", url], capture_output=True, text=True)
|
|
6574
|
+
w3 = ws.well(w["id"])
|
|
6575
|
+
st4 = ws.well_interval(w["id"], actor="tester")
|
|
6576
|
+
finally:
|
|
6577
|
+
srv.shutdown(); srv.server_close()
|
|
6578
|
+
c3 = {
|
|
6579
|
+
"answer": r_ok["status"] == "OK" and r_ok["elev_ft"] == 2951.37 and r_ok["resolution_m"] == 10.0 and "USGS_13_n32w104" in r_ok["raster"]
|
|
6580
|
+
and r_ok["source"].startswith("USGS 3DEP EPQS, ") and "10 m raster" in r_ok["source"] and "NAVD88" in r_ok["vertical_datum"],
|
|
6581
|
+
"query_as_specified": seen and seen[0].get("wkid") == ["4326"] and seen[0].get("units") == ["Feet"] and abs(float(seen[0]["x"][0]) - lon1) < 1e-6 and abs(float(seen[0]["y"][0]) - lat1) < 1e-6,
|
|
6582
|
+
"failures_named": r_nodata["status"] == "NO DATA" and r_notjson["status"] == "NOT JSON" and r_unreach["status"] == "UNREACHABLE" and EL.as_reference(r_nodata) is None,
|
|
6583
|
+
"reference": ref == {"ft": 2951.37, "datum": "GL", "source": r_ok["source"], "vertical_datum": "NAVD88", "raster": r_ok["raster"], "queried_utc": r_ok["queried_utc"]},
|
|
6584
|
+
"cli": r_cli_el.returncode == 0 and "2,951.37 ft" in r_cli_el.stdout and "source: USGS 3DEP EPQS" in r_cli_el.stdout,
|
|
6585
|
+
"set_well": r_set3.returncode == 0 and "3DEP ground elevation 2,951.37 ft" in r_set3.stdout and w3["surface"]["elevation"]["datum"] == "GL"
|
|
6586
|
+
and w3["surface"]["elevation"]["ft"] == 2951.37 and "USGS 3DEP EPQS" in w3["surface"]["elevation"]["source"],
|
|
6587
|
+
"gl_note_in_statement": st4["status"] == "OK" and st4["declared"]["ref_datum"] == "GL" and any("kelly bushing" in n for n in st4["notes"])
|
|
6588
|
+
and st4["declared"]["top_subsea_ft"] == round(2951.37 - 5000.0, 1),
|
|
6589
|
+
}
|
|
6590
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
6591
|
+
ok(not failed3,
|
|
6592
|
+
"BP3 the 3DEP point service: the query as the service specifies it (x, y, wkid 4326, feet), the answer with its raster, resolution and query "
|
|
6593
|
+
"time as the source, the ground and NAVD88 named; no data, not JSON and unreachable each named; the answer as a reference elevation (datum GL); "
|
|
6594
|
+
"`gea elevation` and `set-well --elev-3dep` through the stand-in service; the statement then carries the kelly-bushing note"
|
|
6595
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
6596
|
+
|
|
6597
|
+
|
|
6339
6598
|
def main() -> int:
|
|
6340
6599
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
6341
6600
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -6406,6 +6665,7 @@ def main() -> int:
|
|
|
6406
6665
|
section_bm_month_end(tmp)
|
|
6407
6666
|
section_bn_soak(tmp)
|
|
6408
6667
|
section_bo_structure(tmp)
|
|
6668
|
+
section_bp_interval(tmp)
|
|
6409
6669
|
if _FAILS:
|
|
6410
6670
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
6411
6671
|
for f in _FAILS:
|
|
@@ -1664,6 +1664,39 @@ def sra_packet_report(pk: dict, evaluated_at: Optional[datetime] = None, program
|
|
|
1664
1664
|
'outside a layer\'s extent is said to be outside it. A distance to a mapped trace is a fact of the map at that map\'s scale, not a hazard '
|
|
1665
1665
|
'judgement; the layer\'s own metadata and the site\'s citation say where the map came from.'],
|
|
1666
1666
|
[Table(['Well', 'Layer', 'Kind', 'Status', 'What the layer says'], srows, 'Mapped structure')]))
|
|
1667
|
+
irows = []
|
|
1668
|
+
for wid, iv in (pk.get('interval') or {}).items():
|
|
1669
|
+
d = iv.get('declared') or {}
|
|
1670
|
+
decl = (f"{d['top_ft']:,.0f}-{d['base_ft']:,.0f} ft {d['depth_reference']} below {d['ref_datum']} {d['ref_elev_ft']:,.0f} ft = "
|
|
1671
|
+
f"{d['top_subsea_ft']:,.0f} to {d['base_subsea_ft']:,.0f} ft subsea") if d else '-'
|
|
1672
|
+
tops = '; '.join(f"{t['relation']} {t['layer']}" + (f" ({t['bracket'][0]:,.0f}/{t['bracket'][1]:,.0f})" if t.get('bracket') else '') for t in iv.get('tops', [])) or '-'
|
|
1673
|
+
tier = iv.get('tier') or {}
|
|
1674
|
+
irows.append([wid, iv.get('status', '-'), decl, tops[:260], (f"{tier.get('status')}: {tier.get('detail', '')}"[:220] if tier else '-')])
|
|
1675
|
+
if irows:
|
|
1676
|
+
secs.append(Section('2c', 'The declared interval against the mapped tops', [
|
|
1677
|
+
'For each well inside the area, the declared disposal interval - top and base in true vertical depth below the reference elevation the '
|
|
1678
|
+
'operator stated - converted to subsea elevation and set beside each formation-top bracket the layers give at the well: ABOVE the mapped top, '
|
|
1679
|
+
'BELOW it, or NOT RESOLVED where the bracket overlaps the interval. Where a formation has a top layer and an isopach layer, its base is the '
|
|
1680
|
+
'arithmetic shown, and the declared depth tier (shallow or deep by the base the plan names) is CONSISTENT with the map, INCONSISTENT, or '
|
|
1681
|
+
'NOT RESOLVED. A contour map is read, not interpolated; the completion report is the record of the interval.'],
|
|
1682
|
+
[Table(['Well', 'Status', 'Declared interval', 'Against the mapped tops', 'Depth tier against the map'], irows, 'Interval against the tops')]))
|
|
1683
|
+
ef = pk.get('events_faults') or []
|
|
1684
|
+
if ef:
|
|
1685
|
+
paras = ['The catalogue events inside the area, each against the mapped fault traces of every structure layer: the nearest trace to the epicentre by '
|
|
1686
|
+
'distance and bearing, and the counts within 1, 2 and 5 km of any trace. An epicentre against a surface trace at the map\'s scale: the '
|
|
1687
|
+
'hypocentral depth is not used, and proximity to a mapped trace is not an attribution of the event to it.']
|
|
1688
|
+
erows = []
|
|
1689
|
+
for lay in ef:
|
|
1690
|
+
for ln in lay.get('lines', [])[:2]:
|
|
1691
|
+
paras.append(f"{lay.get('layer_display') or lay.get('layer')}: {ln}")
|
|
1692
|
+
for e in lay.get('events', []):
|
|
1693
|
+
f_ = e.get('fault') or {}
|
|
1694
|
+
erows.append([lay.get('layer_display') or lay.get('layer'), e.get('event_id'), (e.get('time') or '')[:16].replace('T', ' '),
|
|
1695
|
+
(f"{e['magnitude']:.1f}" if e.get('magnitude') is not None else '-'), e.get('status', '-'),
|
|
1696
|
+
f_.get('name') or f_.get('type') or '-', (f"{f_['distance_km']:.2f}" if f_.get('distance_km') is not None else '-'),
|
|
1697
|
+
(f"{f_['bearing_deg']:.0f}" if f_.get('bearing_deg') is not None else '-')])
|
|
1698
|
+
secs.append(Section('2d', 'Catalogue events against the mapped faults', paras,
|
|
1699
|
+
[Table(['Layer', 'Event', 'Time (UTC)', 'M', 'Status', 'Nearest mapped fault', 'Distance (km)', 'Bearing'], erows[:200], 'Events against the faults')]))
|
|
1667
1700
|
n = 3
|
|
1668
1701
|
inside_wells = [w for w in pk['wells'] if w['inside'] and w.get('months')]
|
|
1669
1702
|
for w in inside_wells:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
"""elevation - the ground elevation of a position from the USGS 3D Elevation Program (3DEP).
|
|
5
|
+
|
|
6
|
+
The Elevation Point Query Service (EPQS) answers one position with the ground elevation from the best 3DEP
|
|
7
|
+
raster it holds, the raster it came from and that raster's resolution: a public, documented, no-key service at
|
|
8
|
+
https://epqs.nationalmap.gov/v1/json?x=<lon>&y=<lat>&wkid=4326&units=Feet . The elevation is orthometric
|
|
9
|
+
(NAVD88 in the conterminous United States) - sea level for the purposes of a subsea depth - and it is the
|
|
10
|
+
GROUND, not the kelly bushing: a depth measured from the KB is deeper by the KB height, which the completion
|
|
11
|
+
report states and this service does not. The program stores what the service answered, with its raster and
|
|
12
|
+
the time of the query, as the reference elevation's source; nothing is fetched unless asked.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import urllib.parse
|
|
18
|
+
import urllib.request
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
EPQS_URL = 'https://epqs.nationalmap.gov/v1/json'
|
|
23
|
+
NO_DATA = -1000000.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def epqs(lat: float, lon: float, url: str = EPQS_URL, timeout: float = 20.0, units: str = 'Feet') -> dict:
|
|
27
|
+
"""One query to the EPQS: the ground elevation at (lat, lon), the raster, its resolution, the query time."""
|
|
28
|
+
q = urllib.parse.urlencode({'x': f'{float(lon):.6f}', 'y': f'{float(lat):.6f}', 'wkid': '4326', 'units': units, 'includeDate': 'false'})
|
|
29
|
+
full = url + ('&' if '?' in url else '?') + q
|
|
30
|
+
req = urllib.request.Request(full, headers={'Accept': 'application/json', 'User-Agent': 'gea-program'})
|
|
31
|
+
t0 = datetime.now(timezone.utc)
|
|
32
|
+
try:
|
|
33
|
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
34
|
+
raw = r.read()
|
|
35
|
+
except Exception as e: # noqa: BLE001 - every failure is reported the same way
|
|
36
|
+
return {'status': 'UNREACHABLE', 'detail': f'{type(e).__name__}: {e}', 'url': full, 'queried_utc': t0.isoformat()}
|
|
37
|
+
try:
|
|
38
|
+
d = json.loads(raw.decode('utf-8'))
|
|
39
|
+
except Exception: # noqa: BLE001
|
|
40
|
+
return {'status': 'NOT JSON', 'detail': raw[:120].decode('utf-8', 'replace'), 'url': full, 'queried_utc': t0.isoformat()}
|
|
41
|
+
v = d.get('value')
|
|
42
|
+
try:
|
|
43
|
+
val = float(v)
|
|
44
|
+
except (TypeError, ValueError):
|
|
45
|
+
return {'status': 'NO VALUE', 'detail': f'the service answered without an elevation: {str(d)[:200]}', 'url': full, 'queried_utc': t0.isoformat()}
|
|
46
|
+
if val <= NO_DATA + 1 or val < -1500 or val > 30000:
|
|
47
|
+
return {'status': 'NO DATA', 'detail': f'the service holds no elevation at this position (value {val:g})', 'url': full, 'queried_utc': t0.isoformat()}
|
|
48
|
+
res = d.get('resolution')
|
|
49
|
+
out = {'status': 'OK', 'elev_ft': round(val, 2) if units == 'Feet' else round(val * 3.280839895, 2), 'units_answered': units,
|
|
50
|
+
'raster': d.get('rasterSource') or d.get('raster') or '', 'resolution_m': (float(res) if isinstance(res, (int, float, str)) and str(res).replace('.', '', 1).isdigit() else None),
|
|
51
|
+
'vertical_datum': 'NAVD88 (orthometric; the ground, not the kelly bushing)', 'url': full, 'queried_utc': t0.isoformat()}
|
|
52
|
+
out['source'] = f"USGS 3DEP EPQS, {out['queried_utc'][:10]}" + (f", {out['resolution_m']:g} m raster" if out['resolution_m'] else '') + (f" ({out['raster']})" if out['raster'] else '')
|
|
53
|
+
return out
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def as_reference(res: dict) -> Optional[dict]:
|
|
57
|
+
"""The EPQS answer as a well's reference elevation record (datum GL, the source stated), or None."""
|
|
58
|
+
if res.get('status') != 'OK':
|
|
59
|
+
return None
|
|
60
|
+
return {'ft': res['elev_ft'], 'datum': 'GL', 'source': res['source'], 'vertical_datum': 'NAVD88', 'raster': res.get('raster', ''), 'queried_utc': res['queried_utc']}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The interval against the mapped tops
|
|
2
|
+
|
|
3
|
+
> The second geological statement this program makes about a well, and the first question a reviewer of a disposal well in a seismic response area asks: where does the injection interval sit against the mapped geology? The operator declares the interval and the elevation it is measured from; the formation-top layers the site brought give a bracket at the well; the program puts the two side by side and says ABOVE, BELOW, or NOT RESOLVED - and whether the declared depth tier agrees with the mapped base of the formation the plan names.
|
|
4
|
+
|
|
5
|
+
**The command**: `gea workspace --path C:\site --action set-well --well <id> --top-interval-ft 9850 --base-interval-ft 10400 --depth-ref TVD --ref-elev-ft 2962 --ref-elev-datum KB --ref-elev-source "completion report W-2"` declares the interval in true vertical depth below the kelly bushing at the elevation the completion report states. `--elev-3dep` instead fetches the GROUND elevation at the declared surface position from the USGS 3D Elevation Program point service (`gea elevation --lat --lon` does the same for any position) and stores it as the reference with datum GL and the service's raster as the source. Layers are told what they map: `--action add-layer --name "EIA Wolfcamp top" --layer Wolfcamp_Elevation_Isopachs.zip --member Wolfcamp_Formation_Elevation_201712 --formation-name Wolfcamp --role top`, and the isopach the same way with `--role isopach`. Then `--action interval --well <id>` writes the statement; the well page's "Interval against the mapped tops" card and the SRA packet's section 2c carry it, and section 2d sets the catalogue's events against the mapped faults.
|
|
6
|
+
|
|
7
|
+
**What it writes**: the declared interval converted to subsea elevation - reference elevation minus depth, so 9,850 ft below a KB at 2,962 ft is -6,888 ft subsea - and, for each contours layer with a bracket at the well, whether the interval is ABOVE the mapped top (its base shallower than the shallowest the top can be), BELOW it (its top deeper than the deepest the top can be), or NOT RESOLVED (the bracket overlaps the interval). For a formation with a top layer and an isopach layer, the base bracket by the arithmetic shown - top minus thickness, both as brackets - and the declared depth tier against it: CONSISTENT, INCONSISTENT, or NOT RESOLVED. For the catalogue events inside the SRA, the nearest mapped fault trace to each epicentre with its distance and bearing, and the counts within 1, 2 and 5 km. Written to `reports/wells/<id>/interval.json` and audited as `well.interval`.
|
|
8
|
+
|
|
9
|
+
**The number to check**: the subsea top and base of the interval, and the status beside each top - and the tier line: `CONSISTENT`, `INCONSISTENT`, or `NOT RESOLVED` against the mapped base. `NOT DECLARED` names what is missing.
|
|
10
|
+
|
|
11
|
+
**What this page will not call a measurement**: a depth between two contours - a bracket is two contours, never a value between them, and an interval the bracket overlaps is NOT RESOLVED, not placed. A kelly-bushing depth from a ground elevation - 3DEP answers the ground; the KB is higher by a height only the completion report states, and the page says so whenever the reference is GL. A measured depth - this program converts true vertical depth only; a deviated well's MD needs its survey first. The tier being right - the statement is whether the declaration and the map agree; the completion report is the record. An event's cause - an epicentre near a mapped trace at the map's scale is a distance, not an attribution, and the hypocentral depth is not used.
|