gea-program 0.79.0__tar.gz → 0.81.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.79.0/gea_program.egg-info → gea_program-0.81.0}/PKG-INFO +15 -5
- {gea_program-0.79.0 → gea_program-0.81.0}/README.md +14 -4
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/__init__.py +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/acceptance_tests.py +347 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/driller_link.py +28 -8
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/edr.py +3 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/IT_SECURITY.md +4 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/driller.md +2 -2
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/install.md +2 -2
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/reader.md +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/recorder.md +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/01-install.md +2 -2
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/19-dossier-reader.md +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/22-updates.md +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/25-install-recorder.md +2 -2
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/28-driller-office.md +3 -3
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/29-driller-rig.md +4 -4
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial.md +2 -2
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/web/app.html +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0/gea_program.egg-info}/PKG-INFO +15 -5
- {gea_program-0.79.0 → gea_program-0.81.0}/pyproject.toml +1 -1
- {gea_program-0.79.0 → gea_program-0.81.0}/LICENSE +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/__main__.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/alerts.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/backup.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/bench.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/bit_seismic.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/blind_harness.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/casing_design.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog_map.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/catalog_register.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/certificates.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/cli.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/client_reports.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/collision.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/config_versioning.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/conformance.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/control_room.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/correlation.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/dashboard.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/declarations.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/decline.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/deviation.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/dlis.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/dnp3.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/doctor.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/dossier.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/dossier_verify.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/drill_string.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/earth_model.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/economics.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/ed25519.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/elevation.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/etp.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/example_register_map.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/fat_sat.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/field_pull.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/files.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/follower.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/forward_model.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/frac_hits.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/friction_priors.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/gamma.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/geodesy.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/geomag.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/hartip.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/alarms.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/alerts.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/backup.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/bit-image.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/casing.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/catalog.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/charts.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/collision.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/company-sign-in.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/conformance.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/control-room.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/data-in.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/declare.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/decline.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/dlis.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/doctor.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/dossier.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/drift.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/drillstring.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/economics.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/edr.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/etp.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/files.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/frac-hits.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/historian.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/https.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/hydraulics.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/instruments.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/interval.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/invoice-check.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/map.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/month-end.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/notifications.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/opseis.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/osdu.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/pad-summary.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/patches.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/penetrator.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/plan.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/pore-pressure.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/quality.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/records.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/report-history.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/rig-time.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/rocks.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/seismic.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/setup.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/simulation.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/site.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/sites.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/slip.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/soak.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/sra.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/start.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/structure.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tools.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/torque-drag.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/transients.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/02-sign-in.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/03-site.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/04-add-well.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/05-records.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/06-well-page.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/07-declare.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/08-patches.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/09-alarms.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/10-control-room.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/11-summary-month.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/12-where-the-well-is.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/13-drilling-pages.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/14-production-pages.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/15-seismic.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/16-seismicity.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/17-recorder-office.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/18-reports.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/20-admin.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/21-upkeep.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/23-vpn.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/24-the-box.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/26-monitor.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/tutorial/27-mirror-vpn.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/vibration.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/vpn.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/well-report.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/help/well3d.md +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/helplib.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/housekeeping.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/hydraulics.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/icon.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/interval.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/invoice_check.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/jobs.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/launcher.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/live_ports.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/loadtest.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/modbus.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/model_card.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/month_end.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/notify.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/oidc.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/oidc_mock.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/op_seismicity.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/opcua_port.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/operator_app.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/osdu.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/pad_summary.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/patches.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/pdf_report.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/penetrator.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/permits.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/piwebapi.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/pore_pressure.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/ports.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/ppdm.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/project.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/quakeml.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reader.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reader_app.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reconciler.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/recorder_link.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/WMM2025.COF +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/iscwsa_rev4_example.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/iscwsa_rev5_example.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/mahoning_example.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/mud_motor_specs.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/owsg_tool_models.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/penetrator_ratings.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/seismicity_protocols.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/report_history.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/rig_state.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/sample_record.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/sbom.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seedlink.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/segy.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_array.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_film.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_response.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_track.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/seismic_watch.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/series_cache.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/service.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/service_life.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/shapefile.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/shell.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/shut_in.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/simulation.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/site_map.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/sla_report.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/slip.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/soak.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/sra.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/store_forward.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/strata_join.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/structure.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/supervisor.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/survey_view.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/telemetry.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/timestamp.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/tls.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/tool_library.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/torque_drag.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/transient.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/tray.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/tutorial.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/updates.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/vibration.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/vpn.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/web/reader.html +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/well3d.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/well_assembler.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/well_records.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/well_report.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/wellplan.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/wellpos.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/winident.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/wits0.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/witsml.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/workspace.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/workspaces.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/x25519.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea/xlsx_read.py +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea_program.egg-info/SOURCES.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.79.0 → gea_program-0.81.0}/setup.cfg +0 -0
- {gea_program-0.79.0 → gea_program-0.81.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.81.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
|
|
@@ -476,7 +476,17 @@ reasons and the confidence of each choice. A position filter (since
|
|
|
476
476
|
0.78.0) places the bit in the layer the way a geosteerer does, from the
|
|
477
477
|
gamma and the deep-resistivity distance against the model, and the screen
|
|
478
478
|
draws the band it puts the layer in. The driller accepts, rejects or
|
|
479
|
-
records what was done instead, by name
|
|
479
|
+
records what was done instead, by name. The third dimension (since
|
|
480
|
+
0.80.0): the lateral has an azimuth, the agent's sixth choice is where to
|
|
481
|
+
aim it and the recommendation reads as one toolface; the layer is a surface with
|
|
482
|
+
a dip across the plan line as well as along it, the hole is placed by
|
|
483
|
+
minimum curvature through the surveys' azimuths, the corridor the lease
|
|
484
|
+
and the offset wells leave the hole is in the twin and on the screen's
|
|
485
|
+
plan view, and the config's model carries the plan's azimuth, the cross
|
|
486
|
+
dip and the corridor. A vibration index read off the surface torque,
|
|
487
|
+
weight and hook load (since 0.81.0) stands in for the downhole tool a
|
|
488
|
+
string does not carry, and the record says which the agent read. Every
|
|
489
|
+
recommendation, answer and
|
|
480
490
|
command is on record with its hash, and GEA pulls the records into the
|
|
481
491
|
well from the **Drill string** tab's card (`gea workspace --path C:\site
|
|
482
492
|
--action driller-pull --box http://192.168.1.60:8790 --well SWD-1`). The
|
|
@@ -830,7 +840,7 @@ unsigned, the key hand-overs - and shows the reports as they were signed. A
|
|
|
830
840
|
reader made from the workspace knows the site's key fingerprints, so it
|
|
831
841
|
says whether a dossier is the site's; it allows no network request, and it
|
|
832
842
|
leaves the trusted time-stamp to `verify_dossier.py`. For a client who wants
|
|
833
|
-
it installed, every release also carries `GEA-Reader-Setup-0.
|
|
843
|
+
it installed, every release also carries `GEA-Reader-Setup-0.81.0.exe`: GEA
|
|
834
844
|
Reader as a Windows program of its own - its own icon and taskbar button,
|
|
835
845
|
Check with GEA Reader on a dossier's right-click menu, fingerprints the client
|
|
836
846
|
saves kept on that computer, nothing of GEA-Program. `gea help reader`.
|
|
@@ -1595,7 +1605,7 @@ attaches the zips to the GitHub release of that tag.
|
|
|
1595
1605
|
|
|
1596
1606
|
### The Windows installer
|
|
1597
1607
|
|
|
1598
|
-
Every release also carries `GEA-Program-Setup-0.
|
|
1608
|
+
Every release also carries `GEA-Program-Setup-0.81.0.exe` (for 0.81.0): the
|
|
1599
1609
|
same Windows kit installed like any Windows program, for the signed-in user
|
|
1600
1610
|
alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
|
|
1601
1611
|
It asks for the site workspace (an existing one such as `C:\site` is used as
|
|
@@ -1614,7 +1624,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
|
|
|
1614
1624
|
software deployment:
|
|
1615
1625
|
|
|
1616
1626
|
```
|
|
1617
|
-
GEA-Program-Setup-0.
|
|
1627
|
+
GEA-Program-Setup-0.81.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
|
|
1618
1628
|
```
|
|
1619
1629
|
|
|
1620
1630
|
The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
|
|
@@ -437,7 +437,17 @@ reasons and the confidence of each choice. A position filter (since
|
|
|
437
437
|
0.78.0) places the bit in the layer the way a geosteerer does, from the
|
|
438
438
|
gamma and the deep-resistivity distance against the model, and the screen
|
|
439
439
|
draws the band it puts the layer in. The driller accepts, rejects or
|
|
440
|
-
records what was done instead, by name
|
|
440
|
+
records what was done instead, by name. The third dimension (since
|
|
441
|
+
0.80.0): the lateral has an azimuth, the agent's sixth choice is where to
|
|
442
|
+
aim it and the recommendation reads as one toolface; the layer is a surface with
|
|
443
|
+
a dip across the plan line as well as along it, the hole is placed by
|
|
444
|
+
minimum curvature through the surveys' azimuths, the corridor the lease
|
|
445
|
+
and the offset wells leave the hole is in the twin and on the screen's
|
|
446
|
+
plan view, and the config's model carries the plan's azimuth, the cross
|
|
447
|
+
dip and the corridor. A vibration index read off the surface torque,
|
|
448
|
+
weight and hook load (since 0.81.0) stands in for the downhole tool a
|
|
449
|
+
string does not carry, and the record says which the agent read. Every
|
|
450
|
+
recommendation, answer and
|
|
441
451
|
command is on record with its hash, and GEA pulls the records into the
|
|
442
452
|
well from the **Drill string** tab's card (`gea workspace --path C:\site
|
|
443
453
|
--action driller-pull --box http://192.168.1.60:8790 --well SWD-1`). The
|
|
@@ -791,7 +801,7 @@ unsigned, the key hand-overs - and shows the reports as they were signed. A
|
|
|
791
801
|
reader made from the workspace knows the site's key fingerprints, so it
|
|
792
802
|
says whether a dossier is the site's; it allows no network request, and it
|
|
793
803
|
leaves the trusted time-stamp to `verify_dossier.py`. For a client who wants
|
|
794
|
-
it installed, every release also carries `GEA-Reader-Setup-0.
|
|
804
|
+
it installed, every release also carries `GEA-Reader-Setup-0.81.0.exe`: GEA
|
|
795
805
|
Reader as a Windows program of its own - its own icon and taskbar button,
|
|
796
806
|
Check with GEA Reader on a dossier's right-click menu, fingerprints the client
|
|
797
807
|
saves kept on that computer, nothing of GEA-Program. `gea help reader`.
|
|
@@ -1556,7 +1566,7 @@ attaches the zips to the GitHub release of that tag.
|
|
|
1556
1566
|
|
|
1557
1567
|
### The Windows installer
|
|
1558
1568
|
|
|
1559
|
-
Every release also carries `GEA-Program-Setup-0.
|
|
1569
|
+
Every release also carries `GEA-Program-Setup-0.81.0.exe` (for 0.81.0): the
|
|
1560
1570
|
same Windows kit installed like any Windows program, for the signed-in user
|
|
1561
1571
|
alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
|
|
1562
1572
|
It asks for the site workspace (an existing one such as `C:\site` is used as
|
|
@@ -1575,7 +1585,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
|
|
|
1575
1585
|
software deployment:
|
|
1576
1586
|
|
|
1577
1587
|
```
|
|
1578
|
-
GEA-Program-Setup-0.
|
|
1588
|
+
GEA-Program-Setup-0.81.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
|
|
1579
1589
|
```
|
|
1580
1590
|
|
|
1581
1591
|
The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
|
|
@@ -16566,6 +16566,7 @@ def section_dv_driller(tmp: str) -> None:
|
|
|
16566
16566
|
time.sleep(0.2)
|
|
16567
16567
|
s_ov, _ = call("/api/answer", {"what": "override", "by": "tester", "note": "held", "override": {"build_deg_100ft": 0.0, "wob_klb": 25}})
|
|
16568
16568
|
md = drl.feed.snapshot()["state"]["hole_depth"]
|
|
16569
|
+
drl.cfg["limits"]["vibration_index"] = 5.0 # the section's end alone ends this arming: the vibration index the feed now derives (0.81.0) may cross 1.2 in the shale
|
|
16569
16570
|
s_long, _ = call("/api/arm", {"by": "tester", "to_md": md + 5000})
|
|
16570
16571
|
s_arm, armed = call("/api/arm", {"by": "tester", "to_md": md + 150})
|
|
16571
16572
|
t_end = time.monotonic() + 30
|
|
@@ -16698,7 +16699,7 @@ def section_dv_driller(tmp: str) -> None:
|
|
|
16698
16699
|
e_ = inf["estimate"]; truth = inf["lithology"]
|
|
16699
16700
|
guess = max(("above", e_["p_above"]), ("in", e_["p_in"]), ("below", e_["p_below"]), key=lambda x: x[1])[0]
|
|
16700
16701
|
conf[truth][guess] += 1; n_steps += 1
|
|
16701
|
-
errs.append(abs(e_["d_top"] - (lt.tvd - sc.layer_top(lt.vs))))
|
|
16702
|
+
errs.append(abs(e_["d_top"] - (lt.tvd - sc.layer_top(lt.vs, getattr(lt, "y", 0.0)))))
|
|
16702
16703
|
right = sum(conf[k][k] for k in conf); total = sum(sum(v.values()) for v in conf.values())
|
|
16703
16704
|
# a filter told nothing but the gamma of a flat layer sits where the model says; told a boundary, it moves
|
|
16704
16705
|
f1 = PF.PositionFilter(9000.0, 0.0, 40.0, 60.0, 130.0, 115.0, seed=3)
|
|
@@ -16841,6 +16842,349 @@ def section_dw_shadow(tmp: str) -> None:
|
|
|
16841
16842
|
"refuses the wrong well; the card, the workspace command and the help page carry it" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
16842
16843
|
|
|
16843
16844
|
|
|
16845
|
+
def section_dx_third_dimension(tmp: str) -> None:
|
|
16846
|
+
"""Section DX - the third dimension of GEA Driller: the directional arithmetic (minimum curvature, the dogleg of a
|
|
16847
|
+
build and a turn, the toolface), the twin in the plan's frame (the turn, the layer as a surface, the corridor,
|
|
16848
|
+
an older five-choice action), the filter's cross dip; the rig side (the bit placed from surveys with azimuths,
|
|
16849
|
+
the twin as the rig publishing the azimuth, a decision with the turn and the toolface, an override with a turn,
|
|
16850
|
+
the limit on both together, the config's refusals, the screen, the help); the policy with six heads holding the
|
|
16851
|
+
corridor on the held-out scenes, and shadow mode judging the turn with the record's azimuth and not without.
|
|
16852
|
+
The sixth choice is where to aim the azimuth (left of the plan, along it, right of it); the tool turns toward it
|
|
16853
|
+
at its own rate, as a directional driller holds the azimuth he is given."""
|
|
16854
|
+
import importlib
|
|
16855
|
+
import math as _m
|
|
16856
|
+
from . import driller_link as DL
|
|
16857
|
+
from . import helplib as H
|
|
16858
|
+
pkg = Path(__file__).parent
|
|
16859
|
+
d = Path(tmp, "dx"); d.mkdir()
|
|
16860
|
+
prog = DL.program()
|
|
16861
|
+
if prog is None:
|
|
16862
|
+
ok(True, "DX the driller's program (gea-driller) is not installed beside GEA in this install; nothing to check here")
|
|
16863
|
+
return
|
|
16864
|
+
if prog["env"] and prog["env"] not in sys.path:
|
|
16865
|
+
sys.path.insert(0, prog["env"])
|
|
16866
|
+
T = importlib.import_module("gea_driller.twin"); G = importlib.import_module("gea_driller.geometry"); PF = importlib.import_module("gea_driller.position")
|
|
16867
|
+
P = importlib.import_module("gea_driller.policy"); A = importlib.import_module("gea_driller.advisor"); C = importlib.import_module("gea_driller.config")
|
|
16868
|
+
S = importlib.import_module("gea_driller.service"); R = importlib.import_module("gea_driller.rig"); SH = importlib.import_module("gea_driller.shadow")
|
|
16869
|
+
np_ = importlib.import_module("numpy")
|
|
16870
|
+
# -- DX1 the arithmetic, the twin, the filter -----------------------------------------------------------------------------
|
|
16871
|
+
dn, de, dv, dl = G.min_curvature(90.0, 30.0, 90.0, 30.0, 1000.0)
|
|
16872
|
+
_, _, dv_b, dl_b = G.min_curvature(90.0, 0.0, 91.5, 0.0, 100.0)
|
|
16873
|
+
_, _, _, dl_t = G.min_curvature(90.0, 0.0, 90.0, 2.0, 100.0)
|
|
16874
|
+
_, _, _, dl_t60 = G.min_curvature(60.0, 0.0, 60.0, 2.0, 100.0)
|
|
16875
|
+
lh = T.Lateral(T.Scene(3)); lh.reset(); lt = T.Lateral(T.Scene(3)); lt.reset(); l5 = T.Lateral(T.Scene(3)); l5.reset()
|
|
16876
|
+
for _ in range(8):
|
|
16877
|
+
lh.step(T.HOLD_ACTION); lt.step((2, 1, 1, 1, 0, 2)); l5.step((2, 1, 1, 1, 0))
|
|
16878
|
+
sc = T.Scene(3)
|
|
16879
|
+
cross = (sc.layer_top(500.0, 100.0) - sc.layer_top(500.0, 0.0)) - 100.0 * _m.tan(_m.radians(sc.cross_dip_deg))
|
|
16880
|
+
# a hole aimed right of the plan and never brought back leaves the corridor and is lost
|
|
16881
|
+
lw = T.Lateral(T.Scene(9)); lw.scene.corridor_ft = 50.0; lw.reset()
|
|
16882
|
+
while not lw.done and lw.steps < 400:
|
|
16883
|
+
lw.step((2, 1, 1, 1, 0, 2))
|
|
16884
|
+
try:
|
|
16885
|
+
T.full_action((1, 2, 3)); short_refused = False
|
|
16886
|
+
except ValueError:
|
|
16887
|
+
short_refused = True
|
|
16888
|
+
# the filter: with a cross dip in the model, the top moves with the offset; the cross dip is read only across the plan line
|
|
16889
|
+
f_along = PF.PositionFilter(9000.0, 0.0, 40.0, 60.0, 130.0, 115.0, seed=3, model_cross_dip_deg=2.0)
|
|
16890
|
+
f_across = PF.PositionFilter(9000.0, 0.0, 40.0, 60.0, 130.0, 115.0, seed=3, model_cross_dip_deg=2.0)
|
|
16891
|
+
for k in range(40):
|
|
16892
|
+
f_along.predict(30.0, 0.0); f_along.update(9020.0, 60.0) # along the plan: the gamma in the layer, nothing learnt of the cross dip
|
|
16893
|
+
f_across.predict(0.0, 10.0); f_across.update(9020.0 + 10.0 * (k + 1) * _m.tan(_m.radians(1.0)), 62.0, 4.0) # across: the top tracks a real cross dip of 1 degree, the tool says so
|
|
16894
|
+
e_along = f_along.estimate(9020.0); e_across = f_across.estimate(9020.0 + 400.0 * _m.tan(_m.radians(1.0)))
|
|
16895
|
+
top_shift = float(f_along.model_top(0.0, 100.0) - f_along.model_top(0.0, 0.0))
|
|
16896
|
+
c1 = {
|
|
16897
|
+
"straight": abs(dn - 1000.0 * _m.cos(_m.radians(30.0))) < 1e-6 and abs(de - 500.0) < 1e-6 and abs(dv) < 1e-6 and abs(dl) < 1e-9,
|
|
16898
|
+
"dogleg": abs(dl_b - 1.5) < 1e-9 and abs(dl_t - 2.0) < 1e-6 and abs(dl_t60 - 2.0 * _m.sin(_m.radians(60.0))) < 0.02 and abs(dv_b + 100.0 * _m.sin(_m.radians(0.75))) < 0.05,
|
|
16899
|
+
"toolface": abs(G.toolface_of(1.5, 0.0, 90.0)) < 1e-9 and abs(G.toolface_of(0.0, 2.0, 90.0) - 90.0) < 1e-9 and abs(G.toolface_of(-1.5, 0.0, 90.0) - 180.0) < 1e-9
|
|
16900
|
+
and abs(G.toolface_of(0.0, -2.0, 90.0) - 270.0) < 1e-9 and abs(G.dls_of(3.0, 4.0, 90.0) - 5.0) < 1e-9,
|
|
16901
|
+
"turns": lt.az > lh.az + 1.5 and lt.y > lh.y + 5.0 and 2.5 < lt.az - lh.az < 6.0 and abs(T.turn_toward(4.0, 0.0) - 2.0) < 1e-9 and abs(T.turn_toward(0.0, 0.5) + 0.75) < 1e-9,
|
|
16902
|
+
"surface": abs(cross) < 1.0 + sc.und_amp and T.Scene(1).cross_dip_deg != T.Scene(2).cross_dip_deg and "cross_dip_deg" in sc.describe() and "corridor_ft" in sc.describe(),
|
|
16903
|
+
"lost": lw.done and lw.ended == "outside the corridor" and lw.summary()["lost"] and lw.max_offset > lw.scene.corridor_ft + T.CORRIDOR_END_FT and any("outside the corridor" in e for e in lw.events),
|
|
16904
|
+
"five_choices": abs(l5.az - lh.az) < 1e-9 and abs(l5.y - lh.y) < 1e-9 and short_refused and len(T.ACTION_SIZES) == 6 and T.ACTION_NAMES[-1] == "aim",
|
|
16905
|
+
"obs": len(T.OBS_NAMES) == T.OBS_SIZE == 34 == lt.observe().shape[0] and all(np_.isfinite(lt.observe())) and "offset_right_of_plan" in T.OBS_NAMES,
|
|
16906
|
+
"filter": abs(top_shift - 100.0 * _m.tan(_m.radians(2.0))) < 1e-9 and abs(e_along["cross_dip_deg"] - 2.0) < 0.3 and e_along["cross_dip_sigma"] > 0.3
|
|
16907
|
+
and abs(e_across["cross_dip_deg"] - 1.0) < 0.6 and e_across["cross_dip_sigma"] < e_along["cross_dip_sigma"],
|
|
16908
|
+
}
|
|
16909
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
16910
|
+
ok(not failed1,
|
|
16911
|
+
"DX1 minimum curvature lands a straight hole 30° off the plan where the sine says, a build's dogleg is the build and a turn's counts by the inclination's sine, "
|
|
16912
|
+
"the toolface of a build is up and of a turn is right; in the twin an aim right of the plan turns the hole toward it at the tool's rate and swings it to the "
|
|
16913
|
+
"right, the layer's top moves across the plan line by the cross dip, a hole aimed out of the corridor is lost and says so, a five-choice action is taken aimed "
|
|
16914
|
+
"along the plan and a shorter one refused, "
|
|
16915
|
+
"the observation is 34 named and finite; the filter moves the model's top by its cross dip and reads the cross dip only across the plan line"
|
|
16916
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
16917
|
+
# -- DX2 the rig side --------------------------------------------------------------------------------------------------------
|
|
16918
|
+
shipped = P.Policy.load(S.SHIPPED_POLICY)
|
|
16919
|
+
cfg = json.loads(json.dumps(C.EXAMPLE)); cfg["output"]["dir"] = str(d / "rig"); cfg["status"]["port"] = 0; cfg["status"]["bind"] = "127.0.0.1"
|
|
16920
|
+
cfg["rig"]["transport"] = "sim"; cfg["model"]["plan_azimuth_deg"] = 40.0; cfg["model"]["corridor_ft"] = 120.0; cfg["model"]["cross_dip_deg"] = 1.0
|
|
16921
|
+
cfg = C.check(cfg)
|
|
16922
|
+
heel_tvd0 = float(cfg["model"]["heel_tvd_ft"]) # the twin as the rig rewrites the model block in place below
|
|
16923
|
+
try:
|
|
16924
|
+
C.check(json.loads(json.dumps(cfg)) | {"model": {**cfg["model"], "cross_dip_deg": 20.0}}); cross_refused = False
|
|
16925
|
+
except C.ConfigError:
|
|
16926
|
+
cross_refused = True
|
|
16927
|
+
try:
|
|
16928
|
+
C.check(json.loads(json.dumps(cfg)) | {"model": {**cfg["model"], "corridor_ft": 2.0}}); corridor_refused = False
|
|
16929
|
+
except C.ConfigError:
|
|
16930
|
+
corridor_refused = True
|
|
16931
|
+
# the bit placed from surveys: a feed that holds 90 degrees at the plan's azimuth plus 30 for 600 ft
|
|
16932
|
+
feed = R.Feed(cfg); feed.state.update({"hole_depth": 10800.0, "gamma": 60.0, "continuous_inc": 90.0, "continuous_azi": 70.0}); feed.last_utc = time.time(); feed.connected = True
|
|
16933
|
+
for md_ in (10200.0, 10290.0, 10380.0, 10470.0, 10560.0, 10650.0):
|
|
16934
|
+
feed.add_survey(md_, 90.0, 70.0, "test")
|
|
16935
|
+
adv = A.Advisor(cfg, shipped, feed, A.Records(cfg))
|
|
16936
|
+
obs_, ctx = adv.observation(feed.snapshot())
|
|
16937
|
+
rec = adv.decide(feed.snapshot())
|
|
16938
|
+
words_turn = adv._words(1.5, 25.0, 120.0, 500.0, "drill", {"inc": 90.0, "bed": 0.1, "balling": 0.0, "plan_az": 40.0}, {}, 2.0)
|
|
16939
|
+
words_aim = adv._words(1.5, 25.0, 120.0, 500.0, "drill", {"inc": 90.0, "bed": 0.1, "balling": 0.0, "plan_az": 40.0}, {}, 2.0, 4.0)
|
|
16940
|
+
words_hold = adv._words(0.0, 25.0, 120.0, 500.0, "drill", {"inc": 90.0, "bed": 0.1, "balling": 0.0, "plan_az": 40.0}, {}, 0.0, 0.0)
|
|
16941
|
+
ov = adv._action_of({"turn_deg_100ft": -2.0, "build_deg_100ft": 1.5}, [2, 1, 1, 1, 0])
|
|
16942
|
+
# the limit holds the build and the turn together: a 4 deg/100 ft limit on a 3 build with a 2 turn clips both
|
|
16943
|
+
cfg_l = json.loads(json.dumps(cfg)); cfg_l["limits"]["max_dls_deg_100ft"] = 3.0
|
|
16944
|
+
adv_l = A.Advisor(C.check(cfg_l), shipped, feed, A.Records(cfg_l))
|
|
16945
|
+
rl = adv_l.decide(feed.snapshot())
|
|
16946
|
+
# the twin as the rig publishes the azimuth and the plan view has what it needs
|
|
16947
|
+
drl = S.Driller(cfg, policy=shipped, twin_seed=11, twin_pace_s=0.5)
|
|
16948
|
+
drl.start()
|
|
16949
|
+
try:
|
|
16950
|
+
t_end = time.monotonic() + 20
|
|
16951
|
+
while time.monotonic() < t_end and len(drl.advisor.history) < 2:
|
|
16952
|
+
time.sleep(0.2)
|
|
16953
|
+
live = drl.live(); snap = drl.feed.snapshot()
|
|
16954
|
+
import urllib.request
|
|
16955
|
+
with urllib.request.urlopen(drl.url + "/live", timeout=5) as r_:
|
|
16956
|
+
screen = r_.read().decode("utf-8")
|
|
16957
|
+
cur = dict(drl.advisor.history[-1]) if drl.advisor.history else None
|
|
16958
|
+
finally:
|
|
16959
|
+
drl.stop()
|
|
16960
|
+
sv_text = Path(cfg["output"]["dir"], "surveys.csv").read_text(encoding="utf-8")
|
|
16961
|
+
c2 = {
|
|
16962
|
+
"placed": abs(ctx["vs"] - 600.0 * _m.cos(_m.radians(30.0))) < 0.5 and abs(ctx["y"] - 600.0 * _m.sin(_m.radians(30.0))) < 0.5 and abs(ctx["az"] - 30.0) < 1e-9
|
|
16963
|
+
and abs(ctx["tvd"] - heel_tvd0) < 0.5 and ctx["corridor"] == 120.0 and obs_.shape[0] == T.OBS_SIZE and abs(obs_[29] * 50.0 - ctx["y"]) < 1e-6,
|
|
16964
|
+
"decision": rec is not None and rec["recommend"].get("aim_deg") in T.AIM_CHOICES and abs(rec["recommend"]["turn_deg_100ft"]) <= T.TURN_RATE + 1e-9
|
|
16965
|
+
and abs(rec["recommend"]["turn_deg_100ft"] - T.turn_toward(rec["recommend"]["aim_deg"], 30.0)) < 1e-6 and rec["recommend"]["steer_az"] in ("hold", "turn right", "turn left")
|
|
16966
|
+
and abs(rec["recommend"]["aim_azimuth_deg"] - (40.0 + rec["recommend"]["aim_deg"])) < 1e-9
|
|
16967
|
+
and 0.0 <= rec["recommend"]["toolface_deg"] < 360.0 and any("plan line" in r_ for r_ in rec["reasons"]) and "aim" in " ".join(rec["reasons"]) and len(rec["action"]) == 6,
|
|
16968
|
+
"words": "turn right 2.0°/100 ft" in words_turn and "toolface 53°" in words_turn and "Build +1.5" in words_turn and words_hold.startswith("Hold angle and azimuth")
|
|
16969
|
+
and "aim 4° right of the plan (azimuth 44.0°), turning right 2.0°/100 ft" in words_aim,
|
|
16970
|
+
"override": ov == (3, 1, 1, 1, 0, 0),
|
|
16971
|
+
"limit": rl is not None and G.dls_of(rl["recommend"]["build_deg_100ft"], rl["recommend"]["turn_deg_100ft"], 90.0) <= 3.0 + 1e-9,
|
|
16972
|
+
"refusals": cross_refused and corridor_refused,
|
|
16973
|
+
"twin_rig": snap["state"].get("continuous_azi") is not None and live["plan_azimuth_deg"] is not None and live["model"]["corridor_ft"] > 0 and live["path"] and "y" in live["path"][-1]
|
|
16974
|
+
and live["advisor"]["position"] is not None and "true_y" in live["twin"] and cur is not None and "turn_deg_100ft" in cur["recommend"]
|
|
16975
|
+
and all(s_.get("azi") is not None for s_ in snap["surveys"]),
|
|
16976
|
+
"screen": "The plan view" in screen and "drawPlan" in screen and "turn_deg_100ft" in screen and "off the plan line" in screen and 'placeholder="aim' in screen,
|
|
16977
|
+
"surveys_file": "azimuth_deg" in sv_text.splitlines()[0] and len(sv_text.splitlines()) >= 2 and sv_text.splitlines()[1].split(",")[2] not in ("", "0.00"),
|
|
16978
|
+
"help": not H.check("driller") and "The third dimension" in (pkg / "help" / "driller.md").read_text(encoding="utf-8") and "plan view" in (pkg / "help" / "tutorial" / "28-driller-office.md").read_text(encoding="utf-8"),
|
|
16979
|
+
}
|
|
16980
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
16981
|
+
ok(not failed2,
|
|
16982
|
+
"DX2 the advisor places the bit from surveys 30° off the plan where the arithmetic says (along, right, depth), a decision carries the aim, the turn it asks "
|
|
16983
|
+
"from where the hole points and the toolface, with the azimuth and the corridor among its reasons, the words name a build and an aim as one toolface, an "
|
|
16984
|
+
"override takes the aim or the turn made, the DLS limit "
|
|
16985
|
+
"holds the build and the turn together, a cross dip and a corridor out of range are refused, the twin as the rig publishes the azimuth and surveys with it, "
|
|
16986
|
+
"the screen carries the plan view, and the help and the tutorial say so" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
16987
|
+
# -- DX3 the policy and shadow mode -------------------------------------------------------------------------------------------
|
|
16988
|
+
ev = P.evaluate(P.policy_act_fn(shipped), range(20), "agent"); tb = P.evaluate(T.textbook_action, range(20), "textbook")
|
|
16989
|
+
lat_az = str(d / "lat_az.csv"); lat_no = str(d / "lat_no.csv")
|
|
16990
|
+
info = SH.twin_lateral(lat_az, seed=5, act_fn=P.policy_act_fn(shipped)); SH.twin_lateral(lat_no, seed=5, act_fn=P.policy_act_fn(shipped), with_azimuth=False)
|
|
16991
|
+
sc5 = info["scene"]
|
|
16992
|
+
model5 = {"layer_top_tvd_ft": sc5["layer_top_heel_ft"], "thickness_ft": sc5["thickness_ft"], "dip_deg": sc5["dip_deg"], "cross_dip_deg": sc5["cross_dip_deg"],
|
|
16993
|
+
"gamma_in": sc5["gamma_in_above_below"][0], "gamma_above": sc5["gamma_in_above_below"][1], "gamma_below": sc5["gamma_in_above_below"][2],
|
|
16994
|
+
"heel_md_ft": info["heel_md"], "plan_azimuth_deg": sc5["plan_azimuth_deg"], "corridor_ft": sc5["corridor_ft"]}
|
|
16995
|
+
r_az = SH.run(lat_az, shipped, model=model5, out_dir=str(d / "sh_az"), well="Twin 5"); r_no = SH.run(lat_no, shipped, model=model5, well="Twin 5")
|
|
16996
|
+
md_az = Path(r_az["paths"]["md"]).read_text(encoding="utf-8")
|
|
16997
|
+
c3 = {
|
|
16998
|
+
"heads": list(shipped.to_json()["action_sizes"]) == list(T.ACTION_SIZES) and shipped.to_json()["obs_size"] == 34 and int(shipped.meta.get("trained_steps", 0)) >= 1000000,
|
|
16999
|
+
"corridor": ev["corridor_pct"] >= 90.0 and ev["lost"] == 0 and ev["max_offset_ft"] < 200.0,
|
|
17000
|
+
"textbook_corridor": tb["corridor_pct"] >= 95.0 and tb["lost"] == 0,
|
|
17001
|
+
"layer": ev["in_layer_pct"] > tb["in_layer_pct"] and ev["reward"] > tb["reward"],
|
|
17002
|
+
"shadow_turn": r_az["summary"]["azimuth_in_record"] and r_az["summary"]["turn_agree_pct"] is not None and r_az["summary"]["turn_agree_pct"] >= 30.0
|
|
17003
|
+
and (r_az["summary"]["turn_oppose_pct"] or 0.0) <= 30.0 and "The turn (right, hold or left" in md_az,
|
|
17004
|
+
"shadow_without": not r_no["summary"]["azimuth_in_record"] and r_no["summary"]["turn_agree_pct"] is None and all(x["actual"]["turn"] is None for x in r_no["decisions"]),
|
|
17005
|
+
"model_through": r_az["model"]["plan_azimuth_deg"] == sc5["plan_azimuth_deg"] and r_az["model"]["cross_dip_deg"] == sc5["cross_dip_deg"],
|
|
17006
|
+
}
|
|
17007
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
17008
|
+
ok(not failed3,
|
|
17009
|
+
f"DX3 the shipped policy has six heads over 34 observations; on twenty held-out scenes it keeps {ev['corridor_pct']:.0f}% of the hole in the corridor "
|
|
17010
|
+
f"(none lost, {ev['in_layer_pct']:.0f}% in the layer against the textbook's {tb['in_layer_pct']:.0f}%, SIMULATION) and the textbook holds the corridor; shadow mode "
|
|
17011
|
+
f"judges the turn from a record with the azimuth ({r_az['summary']['turn_agree_pct'] or 0:.0f}% agreeing) and leaves it unjudged without one, and a given model's "
|
|
17012
|
+
"plan azimuth and cross dip go through" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
17013
|
+
|
|
17014
|
+
|
|
17015
|
+
def section_dy_vibration(tmp: str) -> None:
|
|
17016
|
+
"""Section DY - the vibration index from the feed: the index off the surface channels (a steady feed near zero, a
|
|
17017
|
+
torque swinging as the twin's limit swings it reading the limit, a weight swinging reading lateral, the statistics
|
|
17018
|
+
path equal to the samples path, pooled statistics, a window without torque or weight reading nothing); on the twin
|
|
17019
|
+
the derived index tracking the twin's own; the rig side (the twin as the rig publishing the surface channels and
|
|
17020
|
+
no downhole index, the advisor reading the derived one and saying so, the downhole item winning, a feed with
|
|
17021
|
+
neither reading none, the loop disarming on the derived index, the screen, the help); GEA's side (the lateral
|
|
17022
|
+
file from the self-test export with the statistics, the replay reading the stick-slip stretch over the limit
|
|
17023
|
+
and the advisor easing there, a record without the statistics unjudged, the card)."""
|
|
17024
|
+
import http.cookiejar
|
|
17025
|
+
import importlib
|
|
17026
|
+
import math as _m
|
|
17027
|
+
import urllib.error
|
|
17028
|
+
import urllib.request
|
|
17029
|
+
from . import driller_link as DL
|
|
17030
|
+
from . import edr as E
|
|
17031
|
+
from . import helplib as H
|
|
17032
|
+
from .service import Service, Users
|
|
17033
|
+
from .workspace import Workspace
|
|
17034
|
+
pkg = Path(__file__).parent
|
|
17035
|
+
d = Path(tmp, "dy"); d.mkdir()
|
|
17036
|
+
prog = DL.program()
|
|
17037
|
+
if prog is None:
|
|
17038
|
+
ok(True, "DY the driller's program (gea-driller) is not installed beside GEA in this install; nothing to check here")
|
|
17039
|
+
return
|
|
17040
|
+
if prog["env"] and prog["env"] not in sys.path:
|
|
17041
|
+
sys.path.insert(0, prog["env"])
|
|
17042
|
+
T = importlib.import_module("gea_driller.twin"); V = importlib.import_module("gea_driller.vibration"); P = importlib.import_module("gea_driller.policy")
|
|
17043
|
+
A = importlib.import_module("gea_driller.advisor"); C = importlib.import_module("gea_driller.config"); S = importlib.import_module("gea_driller.service")
|
|
17044
|
+
R = importlib.import_module("gea_driller.rig"); SH = importlib.import_module("gea_driller.shadow")
|
|
17045
|
+
np_ = importlib.import_module("numpy")
|
|
17046
|
+
# -- DY1 the index --------------------------------------------------------------------------------------------------------
|
|
17047
|
+
tt = np_.arange(60)
|
|
17048
|
+
steady = {"torque": 10 + 0.02 * np_.sin(tt), "rpm": 120 + 0 * tt, "wob": 25 + 0.05 * np_.cos(tt), "hkld": 175 - 0.05 * np_.cos(tt), "spp": 2800 + 0 * tt}
|
|
17049
|
+
ss = {"torque": 10 * (1 + 0.48 * np_.sin(2 * _m.pi * tt / 8)), "rpm": 120 + 0 * tt, "wob": 25 + 0 * tt, "hkld": 175 + 0 * tt, "spp": 2800 + 0 * tt}
|
|
17050
|
+
lat = {"torque": 10 + 0 * tt, "rpm": 120 + 0 * tt, "wob": 25 * (1 + 0.24 * np_.sin(2 * _m.pi * tt / 5)), "hkld": 175 - 25 * 0.24 * np_.sin(2 * _m.pi * tt / 5), "spp": 2800 + 0 * tt}
|
|
17051
|
+
v0 = V.index(steady); v1 = V.index(ss); v2 = V.index(lat)
|
|
17052
|
+
v1s = V.index_from_stats(V.stats(ss))
|
|
17053
|
+
pooled = V.index_from_stats(V.combine_stats([V.stats({k: v[:30] for k, v in ss.items()}), V.stats({k: v[30:] for k, v in ss.items()})]))
|
|
17054
|
+
none = V.index({"rpm": 120 + 0 * tt, "spp": 2800 + 0 * tt})
|
|
17055
|
+
short = V.index({k: v[:5] for k, v in ss.items()})
|
|
17056
|
+
# on the twin: the derived index against the twin's own, step by step over twenty scenes
|
|
17057
|
+
tr, de = [], []
|
|
17058
|
+
for sd in range(20):
|
|
17059
|
+
lt = T.Lateral(T.Scene(sd)); o = lt.reset()
|
|
17060
|
+
while not lt.done:
|
|
17061
|
+
o, _, _, inf = lt.step(T.textbook_action(o))
|
|
17062
|
+
if inf.get("conditioning") != "drill":
|
|
17063
|
+
continue
|
|
17064
|
+
smp = [lt.surface_sample() for _ in range(20)]
|
|
17065
|
+
v = V.index({"torque": [x["torque"] for x in smp], "rpm": [x["rpm"] for x in smp], "wob": [x["wob"] for x in smp], "hkld": [x["hookload"] for x in smp], "spp": [x["spp"] for x in smp]})
|
|
17066
|
+
tr.append(lt.vib); de.append(v["index"])
|
|
17067
|
+
tr_, de_ = np_.array(tr), np_.array(de)
|
|
17068
|
+
corr = float(np_.corrcoef(tr_, de_)[0, 1]); agree = float(((tr_ > T.VIB_LIMIT) == (de_ > T.VIB_LIMIT)).mean())
|
|
17069
|
+
c1 = {
|
|
17070
|
+
"steady": v0 is not None and v0["index"] < 0.1,
|
|
17071
|
+
"stick_slip": v1 is not None and abs(v1["index"] - 1.2) < 0.08 and v1["kind"] == "stick-slip" and abs(v1["torque_amplitude"] - 0.48) < 0.03,
|
|
17072
|
+
"lateral": v2 is not None and abs(v2["index"] - 1.2) < 0.1 and v2["kind"] == "lateral" and v2["stick_slip"] < 0.1,
|
|
17073
|
+
"paths": v1s["index"] == v1["index"] and abs(pooled["index"] - v1["index"]) < 0.08,
|
|
17074
|
+
"nothing": none is None and short is None,
|
|
17075
|
+
"twin": len(tr) > 1500 and corr > 0.95 and agree >= 0.95 and float(np_.median(np_.abs(tr_ - de_))) < 0.05,
|
|
17076
|
+
"words": "stick-slip" in V.words(v1, 1.2) and "over the limit" in V.words(v1, 1.0) and "over the limit" not in V.words(v0, 1.2),
|
|
17077
|
+
}
|
|
17078
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
17079
|
+
ok(not failed1,
|
|
17080
|
+
f"DY1 the vibration index off the surface channels: a steady feed reads {v0['index'] if v0 else 'none'}, a torque swinging ±48% reads {v1['index'] if v1 else 'none'} as stick-slip, "
|
|
17081
|
+
f"a weight swinging ±24% reads {v2['index'] if v2 else 'none'} as lateral, the statistics path equals the samples path and pooled windows agree, a window without torque "
|
|
17082
|
+
f"or weight reads nothing; on twenty twin scenes the derived index tracks the twin's own (correlation {corr:.3f}, the limit's crossings agreeing {100 * agree:.0f}%, "
|
|
17083
|
+
"SIMULATION - the twin's calibration, not a string's)" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
17084
|
+
# -- DY2 the rig side ---------------------------------------------------------------------------------------------------------
|
|
17085
|
+
shipped = P.Policy.load(S.SHIPPED_POLICY)
|
|
17086
|
+
cfg = json.loads(json.dumps(C.EXAMPLE)); cfg["output"]["dir"] = str(d / "rig"); cfg["status"]["port"] = 0; cfg["status"]["bind"] = "127.0.0.1"
|
|
17087
|
+
cfg["rig"]["transport"] = "sim"; cfg["closed_loop"]["allowed"] = True; cfg["closed_loop"]["stale_s"] = 30
|
|
17088
|
+
cfg = C.check(cfg)
|
|
17089
|
+
try:
|
|
17090
|
+
C.check(json.loads(json.dumps(cfg)) | {"rig": {**cfg["rig"], "vibration_window_s": 2}}); win_refused = False
|
|
17091
|
+
except C.ConfigError:
|
|
17092
|
+
win_refused = True
|
|
17093
|
+
drl = S.Driller(cfg, policy=shipped, twin_seed=11, twin_pace_s=0.6)
|
|
17094
|
+
drl.start()
|
|
17095
|
+
try:
|
|
17096
|
+
t_end = time.monotonic() + 25
|
|
17097
|
+
while time.monotonic() < t_end and len(drl.advisor.history) < 3:
|
|
17098
|
+
time.sleep(0.2)
|
|
17099
|
+
snap = drl.feed.snapshot(); live = drl.live()
|
|
17100
|
+
cur = dict(drl.advisor.history[-1]) if drl.advisor.history else None
|
|
17101
|
+
md = snap["state"].get("hole_depth")
|
|
17102
|
+
rq = urllib.request.Request(drl.url + "/api/arm", data=json.dumps({"by": "tester", "to_md": md + 400}).encode(), headers={"Content-Type": "application/json"}, method="POST")
|
|
17103
|
+
with urllib.request.urlopen(rq, timeout=5) as r_:
|
|
17104
|
+
armed = json.load(r_)
|
|
17105
|
+
drl.cfg["limits"]["vibration_index"] = 0.01 # the limit lowered under the loop: the derived index crosses it at the next look
|
|
17106
|
+
t_end = time.monotonic() + 8
|
|
17107
|
+
while time.monotonic() < t_end and drl.advisor.armed is not None:
|
|
17108
|
+
time.sleep(0.2)
|
|
17109
|
+
drl.cfg["limits"]["vibration_index"] = 1.2
|
|
17110
|
+
last = drl.advisor.arm_log[-1]
|
|
17111
|
+
with urllib.request.urlopen(drl.url + "/live", timeout=5) as r_:
|
|
17112
|
+
screen = r_.read().decode("utf-8")
|
|
17113
|
+
finally:
|
|
17114
|
+
drl.stop()
|
|
17115
|
+
# the downhole item wins; a feed with neither reads none
|
|
17116
|
+
feed = R.Feed(cfg); feed.state.update({"hole_depth": 10500.0, "gamma": 60.0, "continuous_inc": 90.0, "vibration": 1.7}); feed.last_utc = time.time(); feed.connected = True
|
|
17117
|
+
for k in range(30):
|
|
17118
|
+
feed.window.append(float(k), torque=10 * (1 + 0.1 * _m.sin(k)), rpm=120.0, wob=25.0, hkld=175.0, spp=2800.0)
|
|
17119
|
+
v_tool = A.Advisor.vibration_of(feed.snapshot())
|
|
17120
|
+
feed.state["vibration"] = None
|
|
17121
|
+
v_der = A.Advisor.vibration_of(feed.snapshot())
|
|
17122
|
+
feed2 = R.Feed(cfg); feed2.state.update({"hole_depth": 10500.0, "gamma": 60.0}); feed2.last_utc = time.time(); feed2.connected = True
|
|
17123
|
+
v_none = A.Advisor.vibration_of(feed2.snapshot())
|
|
17124
|
+
adv2 = A.Advisor(cfg, shipped, feed2, A.Records(json.loads(json.dumps(cfg)) | {"output": {"dir": str(d / "rig2")}}))
|
|
17125
|
+
rec2 = adv2.decide(feed2.snapshot())
|
|
17126
|
+
decs = [json.loads(l) for l in open(d / "rig" / "decisions.jsonl", encoding="utf-8")]
|
|
17127
|
+
c2 = {
|
|
17128
|
+
"twin_rig": snap["state"].get("torque") is not None and snap["state"].get("hookload") is not None and snap["state"].get("vibration") is None and snap.get("vibration") is not None
|
|
17129
|
+
and snap["vibration"]["index"] > 0 and snap.get("window_n", 0) >= 10 and abs(snap["vibration"]["index"] - drl.feed.lat.vib) < 0.25,
|
|
17130
|
+
"advisor": cur is not None and cur["context"]["vibration_source"] == V.SOURCE and cur["context"]["vibration"] > 0 and any("derived from the surface data" in r_ for r_ in cur["reasons"])
|
|
17131
|
+
and live["vibration"]["source"] == V.SOURCE and live["vibration"]["parts"]["kind"] in ("stick-slip", "lateral"),
|
|
17132
|
+
"wins": v_tool[0] == 1.7 and v_tool[1] == "the downhole tool" and v_der[1] == V.SOURCE and 0 < v_der[0] < 1.0,
|
|
17133
|
+
"none": v_none[0] == 0.0 and v_none[1].startswith("none") and rec2 is not None and any("vibration unread" in r_ for r_ in rec2["reasons"]),
|
|
17134
|
+
"disarms": armed.get("ok") and last["event"] == "disarmed" and "vibration" in last["reason"] and "surface data" in last["reason"],
|
|
17135
|
+
"records": any((x.get("context") or {}).get("vibration_source") == V.SOURCE for x in decs),
|
|
17136
|
+
"screen": "from surface data" in screen and "downhole tool" in screen,
|
|
17137
|
+
"refusal": win_refused,
|
|
17138
|
+
"help": not H.check("driller") and "The vibration index from the feed" in (pkg / "help" / "driller.md").read_text(encoding="utf-8")
|
|
17139
|
+
and "surface" in (pkg / "help" / "tutorial" / "29-driller-rig.md").read_text(encoding="utf-8"),
|
|
17140
|
+
}
|
|
17141
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
17142
|
+
ok(not failed2,
|
|
17143
|
+
"DY2 the twin as the rig publishes the surface torque, weight and hook load and no downhole index; the feed's window gives the derived index close to the "
|
|
17144
|
+
"twin's own, the advisor reads it and says so in its reasons, the records and the screen; the downhole tool's item wins when it comes, a feed with neither "
|
|
17145
|
+
"reads none and says so; the closed loop disarms on the derived index crossing the limit; a window too short is refused; the help and the tutorial say so"
|
|
17146
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
17147
|
+
# -- DY3 GEA's side: the lateral file with the statistics, the replay over the limit, the card ---------------------------------------
|
|
17148
|
+
ws = Workspace.create(str(d / "ws"), "Pad 3 vibration", actor="tester")
|
|
17149
|
+
exp = E.selftest(str(d), 3000)
|
|
17150
|
+
w = ws.add_well_file(exp["path"], display="Well EDR", actor="tester")
|
|
17151
|
+
lat_csv = str(d / "lat.csv")
|
|
17152
|
+
info = DL.lateral_from_edr(ws, w["id"], lat_csv)
|
|
17153
|
+
head = Path(lat_csv).read_text(encoding="utf-8").splitlines()[0].split(",")
|
|
17154
|
+
rec = DL.shadow(ws, w["id"], actor="tester")
|
|
17155
|
+
sm = rec["summary"]
|
|
17156
|
+
# the twin's lateral with and without the statistics
|
|
17157
|
+
tw_csv = str(d / "tw.csv"); tw_no = str(d / "tw_no.csv")
|
|
17158
|
+
tinfo = SH.twin_lateral(tw_csv, seed=5, act_fn=P.policy_act_fn(shipped)); SH.twin_lateral(tw_no, seed=5, act_fn=P.policy_act_fn(shipped), with_stats=False)
|
|
17159
|
+
r_tw = SH.run(tw_csv, shipped, well="Twin 5"); r_no = SH.run(tw_no, shipped, well="Twin 5")
|
|
17160
|
+
tv = dict(tinfo["true_vibration"]); mds = np_.array(sorted(tv))
|
|
17161
|
+
tvv = np_.array([tv[m_] for m_ in mds])
|
|
17162
|
+
def true_over(md_): # the twin's own index averaged over the 30 ft the replay's window pooled
|
|
17163
|
+
j0, j1 = int(np_.searchsorted(mds, md_ - 30.0)), int(np_.searchsorted(mds, md_, side="right"))
|
|
17164
|
+
return float(tvv[j0:j1].mean()) if j1 > j0 else float("nan")
|
|
17165
|
+
pairs = np_.array([(x["vibration"], true_over(x["md"])) for x in r_tw["decisions"] if x["vibration"] is not None])
|
|
17166
|
+
pairs = pairs[np_.isfinite(pairs[:, 1])]
|
|
17167
|
+
corr_tw = float(np_.corrcoef(pairs[:, 0], pairs[:, 1])[0, 1]) if len(pairs) > 10 else 0.0
|
|
17168
|
+
md_text = Path(ws.reports_dir, rec["report"]["md"]).read_text(encoding="utf-8")
|
|
17169
|
+
app_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
17170
|
+
c3 = {
|
|
17171
|
+
"lateral_file": all(c in head for c in DL.LATERAL_STATS) and info["vibration_rows"] >= 1000,
|
|
17172
|
+
"over_limit": sm["vibration_in_record"] and sm["vibration_over_limit_pct"] is not None and 5.0 <= sm["vibration_over_limit_pct"] <= 40.0 and sm["vibration_mean"] > 0.3
|
|
17173
|
+
and sm["eased_when_over_pct"] is not None and sm["eased_when_over_pct"] >= 50.0,
|
|
17174
|
+
"report": "stood over the limit" in md_text and "| Vib |" in md_text,
|
|
17175
|
+
"twin_lateral": r_tw["summary"]["vibration_in_record"] and len(pairs) > 50 and corr_tw > 0.7 and float(np_.median(np_.abs(pairs[:, 0] - pairs[:, 1]))) < 0.12,
|
|
17176
|
+
"without": not r_no["summary"]["vibration_in_record"] and r_no["summary"]["vibration_over_limit_pct"] is None and all(x["vibration"] is None for x in r_no["decisions"]),
|
|
17177
|
+
"selftest_export": any(abs(float(r_.split(",")[12]) - 10.0) > 4.0 for r_ in Path(exp["path"]).read_text(encoding="utf-8").splitlines()[1:] if r_.split(",")[12] not in ("",)),
|
|
17178
|
+
"card": "Vibration over limit" in app_src and "eased" in app_src,
|
|
17179
|
+
}
|
|
17180
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
17181
|
+
ok(not failed3,
|
|
17182
|
+
f"DY3 GEA's lateral file carries the surface channels' statistics per foot ({info['vibration_rows']} feet of them from the self-test export), the replay reads the "
|
|
17183
|
+
f"stick-slip stretch over the limit at {sm['vibration_over_limit_pct'] or 0:.0f}% of the decisions and the advisor eases there {sm['eased_when_over_pct'] or 0:.0f}% of the "
|
|
17184
|
+
f"time, the report says so; on the twin's lateral the replayed index tracks the twin's own (correlation {corr_tw:.2f}); a record without the statistics is unjudged; "
|
|
17185
|
+
"the card carries the column" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
17186
|
+
|
|
17187
|
+
|
|
16844
17188
|
def main() -> int:
|
|
16845
17189
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
16846
17190
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -16971,6 +17315,8 @@ def main() -> int:
|
|
|
16971
17315
|
section_du_tutorial(tmp)
|
|
16972
17316
|
section_dv_driller(tmp)
|
|
16973
17317
|
section_dw_shadow(tmp)
|
|
17318
|
+
section_dx_third_dimension(tmp)
|
|
17319
|
+
section_dy_vibration(tmp)
|
|
16974
17320
|
if _FAILS:
|
|
16975
17321
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
16976
17322
|
for f in _FAILS:
|
|
@@ -281,6 +281,11 @@ def wells_with_records(ws) -> List[dict]:
|
|
|
281
281
|
# -- shadow mode: a drilled well of this workspace replayed through the advisor ---------------------------------------------
|
|
282
282
|
|
|
283
283
|
LATERAL_COLUMNS = ('md_ft', 'tvd_ft', 'gamma_api', 'wob_klb', 'rpm', 'flow_gpm', 'rop_ft_hr', 'spp_psi', 'hours')
|
|
284
|
+
# per foot, the surface channels' mean and RMS about it over the seconds since the foot before (since 0.81.0): what the
|
|
285
|
+
# driller's vibration index is read off when the string had no downhole tool - the same names the driller's program reads
|
|
286
|
+
LATERAL_STATS = ('torque_mean', 'torque_rms', 'rpm_mean', 'rpm_rms', 'wob_mean', 'wob_rms', 'hkld_mean', 'hkld_rms', 'spp_mean', 'spp_rms')
|
|
287
|
+
_STAT_CH = {'torque': 'torque', 'rpm': 'rpm', 'wob': 'wob', 'hkld': 'hkld', 'spp': 'spp'}
|
|
288
|
+
_STAT_MIN = 5 # seconds of a foot at least; the index pools the feet of a decision
|
|
284
289
|
|
|
285
290
|
|
|
286
291
|
def lateral_from_edr(ws, well_id: str, out_csv: str) -> dict:
|
|
@@ -296,7 +301,7 @@ def lateral_from_edr(ws, well_id: str, out_csv: str) -> dict:
|
|
|
296
301
|
if not src or not E.is_edr(src):
|
|
297
302
|
raise ValueError(f"{well_id} has no drilling-recorder export (an EDR CSV) under its sources")
|
|
298
303
|
st = E.read(src)
|
|
299
|
-
ch = {k: E._ch(st, k) for k in ('hole', 'tvd', 'gamma', 'wob', 'rpm', 'flow', 'rop', 'spp', 'onb')}
|
|
304
|
+
ch = {k: E._ch(st, k) for k in ('hole', 'tvd', 'gamma', 'wob', 'rpm', 'flow', 'rop', 'spp', 'onb', 'torque', 'hkld')}
|
|
300
305
|
hole = ch['hole']
|
|
301
306
|
if hole is None or ch['tvd'] is None:
|
|
302
307
|
raise ValueError('the export carries no hole depth or no interpolated TVD; the lateral cannot be placed')
|
|
@@ -308,32 +313,47 @@ def lateral_from_edr(ws, well_id: str, out_csv: str) -> dict:
|
|
|
308
313
|
n = len(hole)
|
|
309
314
|
rows = []
|
|
310
315
|
seen = set(); g_acc = []
|
|
316
|
+
s_acc = {k: [] for k in _STAT_CH}
|
|
311
317
|
for i in range(n):
|
|
312
318
|
h = hole[i]
|
|
313
319
|
if h is None or (isinstance(h, float) and (h != h)) or not keep_h[i]:
|
|
314
320
|
continue
|
|
315
321
|
if ch['gamma'] is not None and not (ch['gamma'][i] != ch['gamma'][i]):
|
|
316
322
|
g_acc.append(float(ch['gamma'][i]))
|
|
323
|
+
for k, c in _STAT_CH.items():
|
|
324
|
+
v = _v(ch[c], i)
|
|
325
|
+
if v == v: # not NaN
|
|
326
|
+
s_acc[k].append(v)
|
|
317
327
|
foot = int(h)
|
|
318
328
|
if foot in seen:
|
|
319
329
|
continue
|
|
320
330
|
seen.add(foot)
|
|
321
331
|
tvd = ch['tvd'][i]
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
332
|
+
row = {'md_ft': float(h), 'tvd_ft': float(tvd) if not (tvd != tvd) else float('nan'),
|
|
333
|
+
'gamma_api': float(sorted(g_acc)[len(g_acc) // 2]) if g_acc else float('nan'),
|
|
334
|
+
'wob_klb': _v(ch['wob'], i), 'rpm': _v(ch['rpm'], i), 'flow_gpm': _v(ch['flow'], i), 'rop_ft_hr': _v(ch['rop'], i), 'spp_psi': _v(ch['spp'], i),
|
|
335
|
+
'hours': float(secs[i]) / 3600.0}
|
|
336
|
+
for k in _STAT_CH:
|
|
337
|
+
a = s_acc[k]
|
|
338
|
+
if len(a) >= _STAT_MIN:
|
|
339
|
+
m = sum(a) / len(a)
|
|
340
|
+
row[f'{k}_mean'] = m; row[f'{k}_rms'] = (sum((x - m) ** 2 for x in a) / len(a)) ** 0.5
|
|
341
|
+
else:
|
|
342
|
+
row[f'{k}_mean'] = float('nan'); row[f'{k}_rms'] = float('nan')
|
|
343
|
+
rows.append(row)
|
|
344
|
+
g_acc = []; s_acc = {k: [] for k in _STAT_CH}
|
|
327
345
|
if len(rows) < 100:
|
|
328
346
|
raise ValueError(f'the export yields only {len(rows)} feet of new hole; a lateral is thousands')
|
|
329
347
|
rows.sort(key=lambda r: r['md_ft'])
|
|
330
348
|
os.makedirs(os.path.dirname(out_csv), exist_ok=True)
|
|
349
|
+
cols = list(LATERAL_COLUMNS) + list(LATERAL_STATS)
|
|
331
350
|
with open(out_csv, 'w', encoding='utf-8', newline='') as f:
|
|
332
|
-
w = _csv.DictWriter(f, fieldnames=
|
|
351
|
+
w = _csv.DictWriter(f, fieldnames=cols); w.writeheader()
|
|
333
352
|
for r in rows:
|
|
334
|
-
w.writerow({c: ('' if (isinstance(r[c], float) and r[c] != r[c]) or r[c] is None else f'{r[c]:.3f}') for c in
|
|
353
|
+
w.writerow({c: ('' if (isinstance(r[c], float) and r[c] != r[c]) or r[c] is None else f'{r[c]:.3f}') for c in cols})
|
|
335
354
|
rows.sort(key=lambda r: r['md_ft'])
|
|
336
355
|
return {'rows': len(rows), 'source': src, 'md_from': rows[0]['md_ft'], 'md_to': rows[-1]['md_ft'], 'gamma_rows': sum(1 for r in rows if r['gamma_api'] == r['gamma_api']),
|
|
356
|
+
'vibration_rows': sum(1 for r in rows if r['torque_mean'] == r['torque_mean'] or r['wob_mean'] == r['wob_mean']),
|
|
337
357
|
'seconds': int(n), 'excluded_rows': int((~keep_h).sum()), 'depth_events': {k: len(v) for k, v in de.items() if isinstance(v, list)}}
|
|
338
358
|
|
|
339
359
|
|
|
@@ -748,8 +748,10 @@ def selftest(tmp: str, rows_per_section: int = 3000) -> dict:
|
|
|
748
748
|
if on:
|
|
749
749
|
depth += 600.0 / 3600.0 # 600 ft/h while on bottom (a self-test cadence, not a rig's)
|
|
750
750
|
tvd = depth if depth < 2000.0 else 2000.0 + (depth - 2000.0) * 0.02 # a lateral past 2,000 ft
|
|
751
|
+
# a stretch of stick-slip in the lateral: the surface torque swinging ±60% with an eight-second period (since 0.81.0)
|
|
752
|
+
tq = 10.0 * (1.0 + 0.6 * math.sin(2 * math.pi * i / 8.0)) if (s == 2 and 1000 <= i < 2000 and on) else 10.0
|
|
751
753
|
row = [t.strftime('%Y/%m/%d'), t.strftime('%H:%M:%S'), f'{depth:.1f}', f'{depth - (0 if on else 30):.1f}', '600.00' if on else '0.00',
|
|
752
|
-
'20.0' if on else '0.0', '60.00' if on else '0.00', '2500.00', '150.0', '400.00', '500.00', '50.00', '
|
|
754
|
+
'20.0' if on else '0.0', '60.00' if on else '0.00', '2500.00', '150.0', '400.00', '500.00', '50.00', f'{tq:.3f}', str(on),
|
|
753
755
|
'-999.25' if s == 0 else f'{(45.0 if depth < 2300.0 else 115.0) + (i % 7):.2f}', '100', '0.00' if i % 500 == 0 else '45.00', '' if (i % 10 == 0) else f'{tvd:.2f}']
|
|
754
756
|
w.writerow(row); t += timedelta(seconds=1)
|
|
755
757
|
st = read(p)
|
|
@@ -180,7 +180,10 @@ declared extras.
|
|
|
180
180
|
feed, a crossed limit or the section's end; this version's only downlink
|
|
181
181
|
keeps every command on record and sends none. The records carry no
|
|
182
182
|
secret; GEA pulls them checked against their hashes. The twin as the rig
|
|
183
|
-
on the office PC binds to 127.0.0.1 only.
|
|
183
|
+
on the office PC binds to 127.0.0.1 only. Since 0.80.0 the records carry
|
|
184
|
+
the hole's azimuth and its offset from the plan line, as the surveys
|
|
185
|
+
give them - a position, not a coordinate: the plan's azimuth and the
|
|
186
|
+
heel are in the rig PC's config, no surface location is.
|
|
184
187
|
|
|
185
188
|
## What it records
|
|
186
189
|
|