gea-program 0.80.0__tar.gz → 0.82.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.
Files changed (428) hide show
  1. {gea_program-0.80.0/gea_program.egg-info → gea_program-0.82.0}/PKG-INFO +13 -7
  2. {gea_program-0.80.0 → gea_program-0.82.0}/README.md +12 -6
  3. {gea_program-0.80.0 → gea_program-0.82.0}/gea/__init__.py +1 -1
  4. {gea_program-0.80.0 → gea_program-0.82.0}/gea/acceptance_tests.py +275 -4
  5. {gea_program-0.80.0 → gea_program-0.82.0}/gea/driller_link.py +28 -8
  6. {gea_program-0.80.0 → gea_program-0.82.0}/gea/edr.py +3 -1
  7. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/driller.md +3 -3
  8. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/install.md +2 -2
  9. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/reader.md +1 -1
  10. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/recorder.md +1 -1
  11. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/01-install.md +2 -2
  12. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/19-dossier-reader.md +1 -1
  13. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/22-updates.md +1 -1
  14. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/25-install-recorder.md +2 -2
  15. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/28-driller-office.md +3 -3
  16. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/29-driller-rig.md +2 -2
  17. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial.md +2 -2
  18. {gea_program-0.80.0 → gea_program-0.82.0}/gea/web/app.html +1 -1
  19. {gea_program-0.80.0 → gea_program-0.82.0/gea_program.egg-info}/PKG-INFO +13 -7
  20. {gea_program-0.80.0 → gea_program-0.82.0}/pyproject.toml +1 -1
  21. {gea_program-0.80.0 → gea_program-0.82.0}/LICENSE +0 -0
  22. {gea_program-0.80.0 → gea_program-0.82.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  23. {gea_program-0.80.0 → gea_program-0.82.0}/gea/__main__.py +0 -0
  24. {gea_program-0.80.0 → gea_program-0.82.0}/gea/accuracy_statement.py +0 -0
  25. {gea_program-0.80.0 → gea_program-0.82.0}/gea/alarm_engine.py +0 -0
  26. {gea_program-0.80.0 → gea_program-0.82.0}/gea/alerts.py +0 -0
  27. {gea_program-0.80.0 → gea_program-0.82.0}/gea/backup.py +0 -0
  28. {gea_program-0.80.0 → gea_program-0.82.0}/gea/bench.py +0 -0
  29. {gea_program-0.80.0 → gea_program-0.82.0}/gea/bit_seismic.py +0 -0
  30. {gea_program-0.80.0 → gea_program-0.82.0}/gea/blind_harness.py +0 -0
  31. {gea_program-0.80.0 → gea_program-0.82.0}/gea/casing_design.py +0 -0
  32. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  33. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  34. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  35. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  36. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  37. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  38. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  39. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  40. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  41. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  42. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  43. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  44. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  45. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  46. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  47. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  48. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  49. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  50. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  51. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  52. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  53. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  54. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  55. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  56. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  57. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  58. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  59. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  60. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  61. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  62. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  63. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  64. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  65. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  66. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  67. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  68. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  69. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  70. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  71. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  72. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  73. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  74. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  75. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  76. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  77. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  78. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  79. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  80. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  81. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  82. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  83. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  84. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  85. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  86. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  87. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  88. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  89. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  90. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  91. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  92. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  93. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  94. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  95. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  96. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  97. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  98. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  99. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/med_160_sapropels.txt +0 -0
  100. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  101. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  102. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  103. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  104. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  105. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  106. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  107. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  108. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  109. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  110. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  111. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  112. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  113. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  114. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  115. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  116. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  117. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  118. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  119. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  120. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  121. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  122. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  123. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  124. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  125. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  126. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  127. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  128. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  129. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  130. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  131. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  132. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  133. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  134. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  135. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  136. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog_map.py +0 -0
  137. {gea_program-0.80.0 → gea_program-0.82.0}/gea/catalog_register.py +0 -0
  138. {gea_program-0.80.0 → gea_program-0.82.0}/gea/certificates.py +0 -0
  139. {gea_program-0.80.0 → gea_program-0.82.0}/gea/cli.py +0 -0
  140. {gea_program-0.80.0 → gea_program-0.82.0}/gea/client_reports.py +0 -0
  141. {gea_program-0.80.0 → gea_program-0.82.0}/gea/collision.py +0 -0
  142. {gea_program-0.80.0 → gea_program-0.82.0}/gea/config_versioning.py +0 -0
  143. {gea_program-0.80.0 → gea_program-0.82.0}/gea/conformance.py +0 -0
  144. {gea_program-0.80.0 → gea_program-0.82.0}/gea/control_room.py +0 -0
  145. {gea_program-0.80.0 → gea_program-0.82.0}/gea/correlation.py +0 -0
  146. {gea_program-0.80.0 → gea_program-0.82.0}/gea/dashboard.py +0 -0
  147. {gea_program-0.80.0 → gea_program-0.82.0}/gea/declarations.py +0 -0
  148. {gea_program-0.80.0 → gea_program-0.82.0}/gea/decline.py +0 -0
  149. {gea_program-0.80.0 → gea_program-0.82.0}/gea/deviation.py +0 -0
  150. {gea_program-0.80.0 → gea_program-0.82.0}/gea/dlis.py +0 -0
  151. {gea_program-0.80.0 → gea_program-0.82.0}/gea/dnp3.py +0 -0
  152. {gea_program-0.80.0 → gea_program-0.82.0}/gea/doctor.py +0 -0
  153. {gea_program-0.80.0 → gea_program-0.82.0}/gea/dossier.py +0 -0
  154. {gea_program-0.80.0 → gea_program-0.82.0}/gea/dossier_verify.py +0 -0
  155. {gea_program-0.80.0 → gea_program-0.82.0}/gea/downhole_engine.py +0 -0
  156. {gea_program-0.80.0 → gea_program-0.82.0}/gea/drift_monitor.py +0 -0
  157. {gea_program-0.80.0 → gea_program-0.82.0}/gea/drill_string.py +0 -0
  158. {gea_program-0.80.0 → gea_program-0.82.0}/gea/earth_model.py +0 -0
  159. {gea_program-0.80.0 → gea_program-0.82.0}/gea/economics.py +0 -0
  160. {gea_program-0.80.0 → gea_program-0.82.0}/gea/ed25519.py +0 -0
  161. {gea_program-0.80.0 → gea_program-0.82.0}/gea/elevation.py +0 -0
  162. {gea_program-0.80.0 → gea_program-0.82.0}/gea/etp.py +0 -0
  163. {gea_program-0.80.0 → gea_program-0.82.0}/gea/etp12_schemas.json +0 -0
  164. {gea_program-0.80.0 → gea_program-0.82.0}/gea/example_register_map.json +0 -0
  165. {gea_program-0.80.0 → gea_program-0.82.0}/gea/fat_sat.py +0 -0
  166. {gea_program-0.80.0 → gea_program-0.82.0}/gea/field_pull.py +0 -0
  167. {gea_program-0.80.0 → gea_program-0.82.0}/gea/files.py +0 -0
  168. {gea_program-0.80.0 → gea_program-0.82.0}/gea/fixtures/README.md +0 -0
  169. {gea_program-0.80.0 → gea_program-0.82.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  170. {gea_program-0.80.0 → gea_program-0.82.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  171. {gea_program-0.80.0 → gea_program-0.82.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  172. {gea_program-0.80.0 → gea_program-0.82.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  173. {gea_program-0.80.0 → gea_program-0.82.0}/gea/follower.py +0 -0
  174. {gea_program-0.80.0 → gea_program-0.82.0}/gea/forward_model.py +0 -0
  175. {gea_program-0.80.0 → gea_program-0.82.0}/gea/frac_hits.py +0 -0
  176. {gea_program-0.80.0 → gea_program-0.82.0}/gea/friction_priors.py +0 -0
  177. {gea_program-0.80.0 → gea_program-0.82.0}/gea/gamma.py +0 -0
  178. {gea_program-0.80.0 → gea_program-0.82.0}/gea/gauge_aging.py +0 -0
  179. {gea_program-0.80.0 → gea_program-0.82.0}/gea/gauge_specs.py +0 -0
  180. {gea_program-0.80.0 → gea_program-0.82.0}/gea/geodesy.py +0 -0
  181. {gea_program-0.80.0 → gea_program-0.82.0}/gea/geomag.py +0 -0
  182. {gea_program-0.80.0 → gea_program-0.82.0}/gea/gravity_reference.py +0 -0
  183. {gea_program-0.80.0 → gea_program-0.82.0}/gea/hartip.py +0 -0
  184. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/IT_SECURITY.md +0 -0
  185. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/TESTER_GUIDE.md +0 -0
  186. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/alarms.md +0 -0
  187. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/alerts.md +0 -0
  188. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/audit-update.md +0 -0
  189. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/backup.md +0 -0
  190. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/bit-image.md +0 -0
  191. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/casing.md +0 -0
  192. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/catalog.md +0 -0
  193. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/charts.md +0 -0
  194. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/collision.md +0 -0
  195. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/company-sign-in.md +0 -0
  196. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/conformance.md +0 -0
  197. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/control-room.md +0 -0
  198. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/data-in.md +0 -0
  199. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/declare.md +0 -0
  200. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/decline.md +0 -0
  201. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/dlis.md +0 -0
  202. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/doctor.md +0 -0
  203. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/dossier.md +0 -0
  204. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/drift.md +0 -0
  205. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/drillstring.md +0 -0
  206. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/economics.md +0 -0
  207. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/edr.md +0 -0
  208. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/etp.md +0 -0
  209. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/field-ports.md +0 -0
  210. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/files.md +0 -0
  211. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/frac-hits.md +0 -0
  212. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/historian.md +0 -0
  213. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/https.md +0 -0
  214. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/hydraulics.md +0 -0
  215. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/instruments.md +0 -0
  216. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/interval.md +0 -0
  217. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/invoice-check.md +0 -0
  218. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/map.md +0 -0
  219. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/month-end.md +0 -0
  220. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/notifications.md +0 -0
  221. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/opseis.md +0 -0
  222. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/osdu.md +0 -0
  223. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/pad-summary.md +0 -0
  224. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/patches.md +0 -0
  225. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/penetrator.md +0 -0
  226. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/plan.md +0 -0
  227. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/pore-pressure.md +0 -0
  228. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/ppdm.md +0 -0
  229. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/quakeml.md +0 -0
  230. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/quality.md +0 -0
  231. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/records.md +0 -0
  232. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/report-history.md +0 -0
  233. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/rig-time.md +0 -0
  234. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/rocks.md +0 -0
  235. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/seedlink.md +0 -0
  236. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/seismic.md +0 -0
  237. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/setup.md +0 -0
  238. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/simulation.md +0 -0
  239. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/site.md +0 -0
  240. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/sites.md +0 -0
  241. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/slip.md +0 -0
  242. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/soak.md +0 -0
  243. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/sra.md +0 -0
  244. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/start.md +0 -0
  245. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/structure.md +0 -0
  246. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tools.md +0 -0
  247. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/torque-drag.md +0 -0
  248. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/transients.md +0 -0
  249. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/02-sign-in.md +0 -0
  250. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/03-site.md +0 -0
  251. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/04-add-well.md +0 -0
  252. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/05-records.md +0 -0
  253. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/06-well-page.md +0 -0
  254. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/07-declare.md +0 -0
  255. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/08-patches.md +0 -0
  256. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/09-alarms.md +0 -0
  257. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/10-control-room.md +0 -0
  258. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/11-summary-month.md +0 -0
  259. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/12-where-the-well-is.md +0 -0
  260. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/13-drilling-pages.md +0 -0
  261. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/14-production-pages.md +0 -0
  262. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/15-seismic.md +0 -0
  263. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/16-seismicity.md +0 -0
  264. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/17-recorder-office.md +0 -0
  265. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/18-reports.md +0 -0
  266. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/20-admin.md +0 -0
  267. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/21-upkeep.md +0 -0
  268. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/23-vpn.md +0 -0
  269. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/24-the-box.md +0 -0
  270. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/26-monitor.md +0 -0
  271. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/tutorial/27-mirror-vpn.md +0 -0
  272. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/upkeep.md +0 -0
  273. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/vibration.md +0 -0
  274. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/vpn.md +0 -0
  275. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/well-report.md +0 -0
  276. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/well-tests.md +0 -0
  277. {gea_program-0.80.0 → gea_program-0.82.0}/gea/help/well3d.md +0 -0
  278. {gea_program-0.80.0 → gea_program-0.82.0}/gea/helplib.py +0 -0
  279. {gea_program-0.80.0 → gea_program-0.82.0}/gea/housekeeping.py +0 -0
  280. {gea_program-0.80.0 → gea_program-0.82.0}/gea/hydraulics.py +0 -0
  281. {gea_program-0.80.0 → gea_program-0.82.0}/gea/icon.py +0 -0
  282. {gea_program-0.80.0 → gea_program-0.82.0}/gea/interval.py +0 -0
  283. {gea_program-0.80.0 → gea_program-0.82.0}/gea/inverse_engine.py +0 -0
  284. {gea_program-0.80.0 → gea_program-0.82.0}/gea/invoice_check.py +0 -0
  285. {gea_program-0.80.0 → gea_program-0.82.0}/gea/jobs.py +0 -0
  286. {gea_program-0.80.0 → gea_program-0.82.0}/gea/launcher.py +0 -0
  287. {gea_program-0.80.0 → gea_program-0.82.0}/gea/live_ports.py +0 -0
  288. {gea_program-0.80.0 → gea_program-0.82.0}/gea/loadtest.py +0 -0
  289. {gea_program-0.80.0 → gea_program-0.82.0}/gea/matplotlib_demo.py +0 -0
  290. {gea_program-0.80.0 → gea_program-0.82.0}/gea/modbus.py +0 -0
  291. {gea_program-0.80.0 → gea_program-0.82.0}/gea/modbus_rtu.py +0 -0
  292. {gea_program-0.80.0 → gea_program-0.82.0}/gea/model_card.py +0 -0
  293. {gea_program-0.80.0 → gea_program-0.82.0}/gea/month_end.py +0 -0
  294. {gea_program-0.80.0 → gea_program-0.82.0}/gea/mqtt_port.py +0 -0
  295. {gea_program-0.80.0 → gea_program-0.82.0}/gea/notify.py +0 -0
  296. {gea_program-0.80.0 → gea_program-0.82.0}/gea/oidc.py +0 -0
  297. {gea_program-0.80.0 → gea_program-0.82.0}/gea/oidc_mock.py +0 -0
  298. {gea_program-0.80.0 → gea_program-0.82.0}/gea/op_seismicity.py +0 -0
  299. {gea_program-0.80.0 → gea_program-0.82.0}/gea/opcua_port.py +0 -0
  300. {gea_program-0.80.0 → gea_program-0.82.0}/gea/operator_app.py +0 -0
  301. {gea_program-0.80.0 → gea_program-0.82.0}/gea/osdu.py +0 -0
  302. {gea_program-0.80.0 → gea_program-0.82.0}/gea/pad_summary.py +0 -0
  303. {gea_program-0.80.0 → gea_program-0.82.0}/gea/patches.py +0 -0
  304. {gea_program-0.80.0 → gea_program-0.82.0}/gea/pdf_report.py +0 -0
  305. {gea_program-0.80.0 → gea_program-0.82.0}/gea/penetrator.py +0 -0
  306. {gea_program-0.80.0 → gea_program-0.82.0}/gea/permits.py +0 -0
  307. {gea_program-0.80.0 → gea_program-0.82.0}/gea/piwebapi.py +0 -0
  308. {gea_program-0.80.0 → gea_program-0.82.0}/gea/pore_pressure.py +0 -0
  309. {gea_program-0.80.0 → gea_program-0.82.0}/gea/ports.py +0 -0
  310. {gea_program-0.80.0 → gea_program-0.82.0}/gea/ppdm.py +0 -0
  311. {gea_program-0.80.0 → gea_program-0.82.0}/gea/profile_catalog.py +0 -0
  312. {gea_program-0.80.0 → gea_program-0.82.0}/gea/project.py +0 -0
  313. {gea_program-0.80.0 → gea_program-0.82.0}/gea/qt6_downhole_app.py +0 -0
  314. {gea_program-0.80.0 → gea_program-0.82.0}/gea/quakeml.py +0 -0
  315. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reader.py +0 -0
  316. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reader_app.py +0 -0
  317. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reconciler.py +0 -0
  318. {gea_program-0.80.0 → gea_program-0.82.0}/gea/recorder_link.py +0 -0
  319. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  320. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  321. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/WMM2025.COF +0 -0
  322. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  323. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/eia_prices.provenance.json +0 -0
  324. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  325. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/iscwsa_rev4_example.json +0 -0
  326. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/iscwsa_rev5_example.json +0 -0
  327. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
  328. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
  329. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  330. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  331. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  332. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  333. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/mahoning_example.json +0 -0
  334. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/mud_motor_specs.json +0 -0
  335. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/owsg_tool_models.json +0 -0
  336. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/penetrator_ratings.json +0 -0
  337. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/seismicity_protocols.json +0 -0
  338. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/tool_sources.json +0 -0
  339. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/us_outline.json +0 -0
  340. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/us_outline.provenance.json +0 -0
  341. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_3d.provenance.json +0 -0
  342. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  343. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  344. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  345. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_surfaces.json.gz +0 -0
  346. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  347. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  348. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  349. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  350. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  351. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  352. {gea_program-0.80.0 → gea_program-0.82.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  353. {gea_program-0.80.0 → gea_program-0.82.0}/gea/report_history.py +0 -0
  354. {gea_program-0.80.0 → gea_program-0.82.0}/gea/rig_state.py +0 -0
  355. {gea_program-0.80.0 → gea_program-0.82.0}/gea/rock_inventory.py +0 -0
  356. {gea_program-0.80.0 → gea_program-0.82.0}/gea/sample_record.py +0 -0
  357. {gea_program-0.80.0 → gea_program-0.82.0}/gea/sample_well_profile.csv +0 -0
  358. {gea_program-0.80.0 → gea_program-0.82.0}/gea/sbom.py +0 -0
  359. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seedlink.py +0 -0
  360. {gea_program-0.80.0 → gea_program-0.82.0}/gea/segy.py +0 -0
  361. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic.py +0 -0
  362. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_array.py +0 -0
  363. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_assoc.py +0 -0
  364. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_detect.py +0 -0
  365. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_film.py +0 -0
  366. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_harmonic.py +0 -0
  367. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_qc.py +0 -0
  368. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_response.py +0 -0
  369. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_signature.py +0 -0
  370. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_track.py +0 -0
  371. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_uncertainty.py +0 -0
  372. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_unlisted.py +0 -0
  373. {gea_program-0.80.0 → gea_program-0.82.0}/gea/seismic_watch.py +0 -0
  374. {gea_program-0.80.0 → gea_program-0.82.0}/gea/sensor_swap.py +0 -0
  375. {gea_program-0.80.0 → gea_program-0.82.0}/gea/series_cache.py +0 -0
  376. {gea_program-0.80.0 → gea_program-0.82.0}/gea/service.py +0 -0
  377. {gea_program-0.80.0 → gea_program-0.82.0}/gea/service_life.py +0 -0
  378. {gea_program-0.80.0 → gea_program-0.82.0}/gea/setup_checklist.py +0 -0
  379. {gea_program-0.80.0 → gea_program-0.82.0}/gea/shapefile.py +0 -0
  380. {gea_program-0.80.0 → gea_program-0.82.0}/gea/shell.py +0 -0
  381. {gea_program-0.80.0 → gea_program-0.82.0}/gea/shut_in.py +0 -0
  382. {gea_program-0.80.0 → gea_program-0.82.0}/gea/simulation.py +0 -0
  383. {gea_program-0.80.0 → gea_program-0.82.0}/gea/site_map.py +0 -0
  384. {gea_program-0.80.0 → gea_program-0.82.0}/gea/sla_report.py +0 -0
  385. {gea_program-0.80.0 → gea_program-0.82.0}/gea/slip.py +0 -0
  386. {gea_program-0.80.0 → gea_program-0.82.0}/gea/soak.py +0 -0
  387. {gea_program-0.80.0 → gea_program-0.82.0}/gea/sra.py +0 -0
  388. {gea_program-0.80.0 → gea_program-0.82.0}/gea/store_forward.py +0 -0
  389. {gea_program-0.80.0 → gea_program-0.82.0}/gea/strata_join.py +0 -0
  390. {gea_program-0.80.0 → gea_program-0.82.0}/gea/structure.py +0 -0
  391. {gea_program-0.80.0 → gea_program-0.82.0}/gea/supervisor.py +0 -0
  392. {gea_program-0.80.0 → gea_program-0.82.0}/gea/survey_cmd.py +0 -0
  393. {gea_program-0.80.0 → gea_program-0.82.0}/gea/survey_view.py +0 -0
  394. {gea_program-0.80.0 → gea_program-0.82.0}/gea/telemetry.py +0 -0
  395. {gea_program-0.80.0 → gea_program-0.82.0}/gea/timestamp.py +0 -0
  396. {gea_program-0.80.0 → gea_program-0.82.0}/gea/tls.py +0 -0
  397. {gea_program-0.80.0 → gea_program-0.82.0}/gea/tool_catalog.py +0 -0
  398. {gea_program-0.80.0 → gea_program-0.82.0}/gea/tool_library.py +0 -0
  399. {gea_program-0.80.0 → gea_program-0.82.0}/gea/tool_taxonomy.py +0 -0
  400. {gea_program-0.80.0 → gea_program-0.82.0}/gea/torque_drag.py +0 -0
  401. {gea_program-0.80.0 → gea_program-0.82.0}/gea/transient.py +0 -0
  402. {gea_program-0.80.0 → gea_program-0.82.0}/gea/tray.py +0 -0
  403. {gea_program-0.80.0 → gea_program-0.82.0}/gea/tutorial.py +0 -0
  404. {gea_program-0.80.0 → gea_program-0.82.0}/gea/updates.py +0 -0
  405. {gea_program-0.80.0 → gea_program-0.82.0}/gea/vibration.py +0 -0
  406. {gea_program-0.80.0 → gea_program-0.82.0}/gea/vpn.py +0 -0
  407. {gea_program-0.80.0 → gea_program-0.82.0}/gea/web/reader.html +0 -0
  408. {gea_program-0.80.0 → gea_program-0.82.0}/gea/well3d.py +0 -0
  409. {gea_program-0.80.0 → gea_program-0.82.0}/gea/well_assembler.py +0 -0
  410. {gea_program-0.80.0 → gea_program-0.82.0}/gea/well_records.py +0 -0
  411. {gea_program-0.80.0 → gea_program-0.82.0}/gea/well_report.py +0 -0
  412. {gea_program-0.80.0 → gea_program-0.82.0}/gea/well_test_validation.py +0 -0
  413. {gea_program-0.80.0 → gea_program-0.82.0}/gea/wellplan.py +0 -0
  414. {gea_program-0.80.0 → gea_program-0.82.0}/gea/wellpos.py +0 -0
  415. {gea_program-0.80.0 → gea_program-0.82.0}/gea/winident.py +0 -0
  416. {gea_program-0.80.0 → gea_program-0.82.0}/gea/wits0.py +0 -0
  417. {gea_program-0.80.0 → gea_program-0.82.0}/gea/witsml.py +0 -0
  418. {gea_program-0.80.0 → gea_program-0.82.0}/gea/workspace.py +0 -0
  419. {gea_program-0.80.0 → gea_program-0.82.0}/gea/workspaces.py +0 -0
  420. {gea_program-0.80.0 → gea_program-0.82.0}/gea/x25519.py +0 -0
  421. {gea_program-0.80.0 → gea_program-0.82.0}/gea/xlsx_read.py +0 -0
  422. {gea_program-0.80.0 → gea_program-0.82.0}/gea_program.egg-info/SOURCES.txt +0 -0
  423. {gea_program-0.80.0 → gea_program-0.82.0}/gea_program.egg-info/dependency_links.txt +0 -0
  424. {gea_program-0.80.0 → gea_program-0.82.0}/gea_program.egg-info/entry_points.txt +0 -0
  425. {gea_program-0.80.0 → gea_program-0.82.0}/gea_program.egg-info/requires.txt +0 -0
  426. {gea_program-0.80.0 → gea_program-0.82.0}/gea_program.egg-info/top_level.txt +0 -0
  427. {gea_program-0.80.0 → gea_program-0.82.0}/setup.cfg +0 -0
  428. {gea_program-0.80.0 → gea_program-0.82.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.80.0
3
+ Version: 0.82.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
@@ -483,7 +483,10 @@ a dip across the plan line as well as along it, the hole is placed by
483
483
  minimum curvature through the surveys' azimuths, the corridor the lease
484
484
  and the offset wells leave the hole is in the twin and on the screen's
485
485
  plan view, and the config's model carries the plan's azimuth, the cross
486
- dip and the corridor. Every recommendation, answer and
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
487
490
  command is on record with its hash, and GEA pulls the records into the
488
491
  well from the **Drill string** tab's card (`gea workspace --path C:\site
489
492
  --action driller-pull --box http://192.168.1.60:8790 --well SWD-1`). The
@@ -491,8 +494,11 @@ agent is a policy trained by proximal policy optimization in a physics
491
494
  twin of the lateral - the layer rolling with dip, undulation and faults,
492
495
  the gamma and the deep-resistivity boundary the tools read of it, the
493
496
  steering tool, the bit, the hydraulics, the hole - on scenes drawn fresh,
494
- judged against a textbook driller on scenes it never saw; the figures of
495
- that judgement are a simulation's and are labelled so. Advisory by
497
+ judged on scenes it never saw between a textbook driller (the bar: what a
498
+ careful driller does by the book from the same observation) and an oracle
499
+ told the twin's own layer (the ceiling no policy can beat); `gea-driller
500
+ evaluate` prints the three rows and the agent's standing between them.
501
+ The figures of that judgement are a simulation's and are labelled so. Advisory by
496
502
  default; the closed loop ships as a design with a switch, armed by a named
497
503
  operator for one section, disarming itself on a stale feed, a crossed
498
504
  limit or the section's end, through a downlink that keeps every command and
@@ -837,7 +843,7 @@ unsigned, the key hand-overs - and shows the reports as they were signed. A
837
843
  reader made from the workspace knows the site's key fingerprints, so it
838
844
  says whether a dossier is the site's; it allows no network request, and it
839
845
  leaves the trusted time-stamp to `verify_dossier.py`. For a client who wants
840
- it installed, every release also carries `GEA-Reader-Setup-0.80.0.exe`: GEA
846
+ it installed, every release also carries `GEA-Reader-Setup-0.82.0.exe`: GEA
841
847
  Reader as a Windows program of its own - its own icon and taskbar button,
842
848
  Check with GEA Reader on a dossier's right-click menu, fingerprints the client
843
849
  saves kept on that computer, nothing of GEA-Program. `gea help reader`.
@@ -1602,7 +1608,7 @@ attaches the zips to the GitHub release of that tag.
1602
1608
 
1603
1609
  ### The Windows installer
1604
1610
 
1605
- Every release also carries `GEA-Program-Setup-0.80.0.exe` (for 0.80.0): the
1611
+ Every release also carries `GEA-Program-Setup-0.82.0.exe` (for 0.82.0): the
1606
1612
  same Windows kit installed like any Windows program, for the signed-in user
1607
1613
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1608
1614
  It asks for the site workspace (an existing one such as `C:\site` is used as
@@ -1621,7 +1627,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1621
1627
  software deployment:
1622
1628
 
1623
1629
  ```
1624
- GEA-Program-Setup-0.80.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1630
+ GEA-Program-Setup-0.82.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1625
1631
  ```
1626
1632
 
1627
1633
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -444,7 +444,10 @@ a dip across the plan line as well as along it, the hole is placed by
444
444
  minimum curvature through the surveys' azimuths, the corridor the lease
445
445
  and the offset wells leave the hole is in the twin and on the screen's
446
446
  plan view, and the config's model carries the plan's azimuth, the cross
447
- dip and the corridor. Every recommendation, answer and
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
448
451
  command is on record with its hash, and GEA pulls the records into the
449
452
  well from the **Drill string** tab's card (`gea workspace --path C:\site
450
453
  --action driller-pull --box http://192.168.1.60:8790 --well SWD-1`). The
@@ -452,8 +455,11 @@ agent is a policy trained by proximal policy optimization in a physics
452
455
  twin of the lateral - the layer rolling with dip, undulation and faults,
453
456
  the gamma and the deep-resistivity boundary the tools read of it, the
454
457
  steering tool, the bit, the hydraulics, the hole - on scenes drawn fresh,
455
- judged against a textbook driller on scenes it never saw; the figures of
456
- that judgement are a simulation's and are labelled so. Advisory by
458
+ judged on scenes it never saw between a textbook driller (the bar: what a
459
+ careful driller does by the book from the same observation) and an oracle
460
+ told the twin's own layer (the ceiling no policy can beat); `gea-driller
461
+ evaluate` prints the three rows and the agent's standing between them.
462
+ The figures of that judgement are a simulation's and are labelled so. Advisory by
457
463
  default; the closed loop ships as a design with a switch, armed by a named
458
464
  operator for one section, disarming itself on a stale feed, a crossed
459
465
  limit or the section's end, through a downlink that keeps every command and
@@ -798,7 +804,7 @@ unsigned, the key hand-overs - and shows the reports as they were signed. A
798
804
  reader made from the workspace knows the site's key fingerprints, so it
799
805
  says whether a dossier is the site's; it allows no network request, and it
800
806
  leaves the trusted time-stamp to `verify_dossier.py`. For a client who wants
801
- it installed, every release also carries `GEA-Reader-Setup-0.80.0.exe`: GEA
807
+ it installed, every release also carries `GEA-Reader-Setup-0.82.0.exe`: GEA
802
808
  Reader as a Windows program of its own - its own icon and taskbar button,
803
809
  Check with GEA Reader on a dossier's right-click menu, fingerprints the client
804
810
  saves kept on that computer, nothing of GEA-Program. `gea help reader`.
@@ -1563,7 +1569,7 @@ attaches the zips to the GitHub release of that tag.
1563
1569
 
1564
1570
  ### The Windows installer
1565
1571
 
1566
- Every release also carries `GEA-Program-Setup-0.80.0.exe` (for 0.80.0): the
1572
+ Every release also carries `GEA-Program-Setup-0.82.0.exe` (for 0.82.0): the
1567
1573
  same Windows kit installed like any Windows program, for the signed-in user
1568
1574
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1569
1575
  It asks for the site workspace (an existing one such as `C:\site` is used as
@@ -1582,7 +1588,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1582
1588
  software deployment:
1583
1589
 
1584
1590
  ```
1585
- GEA-Program-Setup-0.80.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1591
+ GEA-Program-Setup-0.82.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1586
1592
  ```
1587
1593
 
1588
1594
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.80.0"
126
+ __version__ = "0.82.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -16511,7 +16511,7 @@ def section_dv_driller(tmp: str) -> None:
16511
16511
  num = (lp_ - lm_) / (2 * eps)
16512
16512
  grads_ok = grads_ok and abs(num - g[k][i]) <= 1e-5 * max(1.0, abs(num))
16513
16513
  shipped = P.Policy.load(S.SHIPPED_POLICY)
16514
- ev = P.evaluate(P.policy_act_fn(shipped), range(20), "agent"); tb = P.evaluate(T.textbook_action, range(20), "textbook")
16514
+ three = P.evaluate_all(shipped, range(20)); ev = three["agent"]; tb = three["textbook"]; orc = three["oracle"]
16515
16515
  fresh = P.Policy(seed=5); before = P.evaluate(P.policy_act_fn(fresh), range(10, 14), "fresh")
16516
16516
  P.train(fresh, seconds=10.0, batch_steps=2048, n_envs=8, scene_seed0=600000)
16517
16517
  after = P.evaluate(P.policy_act_fn(fresh), range(10, 14), "trained")
@@ -16524,7 +16524,10 @@ def section_dv_driller(tmp: str) -> None:
16524
16524
  ex = shipped.explain(T.Lateral(T.Scene(2)).reset())
16525
16525
  c2 = {
16526
16526
  "gradients": grads_ok,
16527
- "beats_textbook": ev["reward"] > tb["reward"] and ev["in_layer_pct"] > tb["in_layer_pct"] + 5.0 and ev["stuck"] <= tb["stuck"],
16527
+ # the bar is a strong driller now (0.82.0): the agent is held above it in the hole in the layer by more than a point and within the
16528
+ # reward's own noise of it, and the gate says how far it stands toward the oracle rather than claiming a wide margin
16529
+ "beats_textbook": ev["reward"] > tb["reward"] - 3.0 and ev["in_layer_pct"] > tb["in_layer_pct"] + 1.0 and ev["stuck"] <= tb["stuck"],
16530
+ "under_oracle": orc["in_layer_pct"] > ev["in_layer_pct"] and orc["reward"] > ev["reward"] and 0.0 < three["standing"]["in_layer_pct_of_the_way"] < 100.0,
16528
16531
  "learns": after["reward"] > before["reward"],
16529
16532
  "file": same and layout_refused and int(shipped.meta.get("trained_steps", 0)) >= 1000000,
16530
16533
  "explains": set(ex["choices"]) == set(T.ACTION_NAMES) and all(0 < c["confidence"] <= 1 for c in ex["choices"].values()) and ex["choices"]["wob"]["choice"] in T.WOB_CHOICES,
@@ -16532,8 +16535,9 @@ def section_dv_driller(tmp: str) -> None:
16532
16535
  failed2 = [k for k, v in c2.items() if not v]
16533
16536
  ok(not failed2,
16534
16537
  f"DV2 the policy's written-out gradients agree with finite differences; the shipped policy ({int(shipped.meta.get('trained_steps', 0)):,} twin steps) beats the "
16535
- f"textbook driller on twenty held-out scenes (in the layer {ev['in_layer_pct']:.0f}% against {tb['in_layer_pct']:.0f}%, reward {ev['reward']:.0f} against {tb['reward']:.0f}, "
16536
- "SIMULATION); ten seconds of training raise a fresh policy's return; the file round-trips and another layout is refused; a decision explains itself in "
16538
+ f"textbook driller on twenty held-out scenes (in the layer {ev['in_layer_pct']:.0f}% against {tb['in_layer_pct']:.0f}%, reward {ev['reward']:.0f} against {tb['reward']:.0f}) "
16539
+ f"and stands under the oracle told the layer ({orc['in_layer_pct']:.0f}%, {orc['reward']:.0f}: {three['standing']['in_layer_pct_of_the_way']:.0f}% of the way, SIMULATION); "
16540
+ "ten seconds of training raise a fresh policy's return; the file round-trips and another layout is refused; a decision explains itself in "
16537
16541
  "operational units with a confidence per choice" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
16538
16542
  # -- DV3 the service with the twin as the rig ------------------------------------------------------------------------
16539
16543
  cfg = json.loads(json.dumps(C.EXAMPLE)); cfg["output"]["dir"] = str(d / "rig"); cfg["status"]["port"] = 0; cfg["status"]["bind"] = "127.0.0.1"
@@ -16566,6 +16570,7 @@ def section_dv_driller(tmp: str) -> None:
16566
16570
  time.sleep(0.2)
16567
16571
  s_ov, _ = call("/api/answer", {"what": "override", "by": "tester", "note": "held", "override": {"build_deg_100ft": 0.0, "wob_klb": 25}})
16568
16572
  md = drl.feed.snapshot()["state"]["hole_depth"]
16573
+ 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
16574
  s_long, _ = call("/api/arm", {"by": "tester", "to_md": md + 5000})
16570
16575
  s_arm, armed = call("/api/arm", {"by": "tester", "to_md": md + 150})
16571
16576
  t_end = time.monotonic() + 30
@@ -17011,6 +17016,270 @@ def section_dx_third_dimension(tmp: str) -> None:
17011
17016
  "plan azimuth and cross dip go through" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
17012
17017
 
17013
17018
 
17019
+ def section_dy_vibration(tmp: str) -> None:
17020
+ """Section DY - the vibration index from the feed: the index off the surface channels (a steady feed near zero, a
17021
+ torque swinging as the twin's limit swings it reading the limit, a weight swinging reading lateral, the statistics
17022
+ path equal to the samples path, pooled statistics, a window without torque or weight reading nothing); on the twin
17023
+ the derived index tracking the twin's own; the rig side (the twin as the rig publishing the surface channels and
17024
+ no downhole index, the advisor reading the derived one and saying so, the downhole item winning, a feed with
17025
+ neither reading none, the loop disarming on the derived index, the screen, the help); GEA's side (the lateral
17026
+ file from the self-test export with the statistics, the replay reading the stick-slip stretch over the limit
17027
+ and the advisor easing there, a record without the statistics unjudged, the card)."""
17028
+ import http.cookiejar
17029
+ import importlib
17030
+ import math as _m
17031
+ import urllib.error
17032
+ import urllib.request
17033
+ from . import driller_link as DL
17034
+ from . import edr as E
17035
+ from . import helplib as H
17036
+ from .service import Service, Users
17037
+ from .workspace import Workspace
17038
+ pkg = Path(__file__).parent
17039
+ d = Path(tmp, "dy"); d.mkdir()
17040
+ prog = DL.program()
17041
+ if prog is None:
17042
+ ok(True, "DY the driller's program (gea-driller) is not installed beside GEA in this install; nothing to check here")
17043
+ return
17044
+ if prog["env"] and prog["env"] not in sys.path:
17045
+ sys.path.insert(0, prog["env"])
17046
+ T = importlib.import_module("gea_driller.twin"); V = importlib.import_module("gea_driller.vibration"); P = importlib.import_module("gea_driller.policy")
17047
+ A = importlib.import_module("gea_driller.advisor"); C = importlib.import_module("gea_driller.config"); S = importlib.import_module("gea_driller.service")
17048
+ R = importlib.import_module("gea_driller.rig"); SH = importlib.import_module("gea_driller.shadow")
17049
+ np_ = importlib.import_module("numpy")
17050
+ # -- DY1 the index --------------------------------------------------------------------------------------------------------
17051
+ tt = np_.arange(60)
17052
+ 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}
17053
+ 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}
17054
+ 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}
17055
+ v0 = V.index(steady); v1 = V.index(ss); v2 = V.index(lat)
17056
+ v1s = V.index_from_stats(V.stats(ss))
17057
+ 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()})]))
17058
+ none = V.index({"rpm": 120 + 0 * tt, "spp": 2800 + 0 * tt})
17059
+ short = V.index({k: v[:5] for k, v in ss.items()})
17060
+ # on the twin: the derived index against the twin's own, step by step over twenty scenes
17061
+ tr, de = [], []
17062
+ for sd in range(20):
17063
+ lt = T.Lateral(T.Scene(sd)); o = lt.reset()
17064
+ while not lt.done:
17065
+ o, _, _, inf = lt.step(T.textbook_action(o))
17066
+ if inf.get("conditioning") != "drill":
17067
+ continue
17068
+ smp = [lt.surface_sample() for _ in range(20)]
17069
+ 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]})
17070
+ tr.append(lt.vib); de.append(v["index"])
17071
+ tr_, de_ = np_.array(tr), np_.array(de)
17072
+ corr = float(np_.corrcoef(tr_, de_)[0, 1]); agree = float(((tr_ > T.VIB_LIMIT) == (de_ > T.VIB_LIMIT)).mean())
17073
+ c1 = {
17074
+ "steady": v0 is not None and v0["index"] < 0.1,
17075
+ "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,
17076
+ "lateral": v2 is not None and abs(v2["index"] - 1.2) < 0.1 and v2["kind"] == "lateral" and v2["stick_slip"] < 0.1,
17077
+ "paths": v1s["index"] == v1["index"] and abs(pooled["index"] - v1["index"]) < 0.08,
17078
+ "nothing": none is None and short is None,
17079
+ "twin": len(tr) > 1500 and corr > 0.95 and agree >= 0.95 and float(np_.median(np_.abs(tr_ - de_))) < 0.05,
17080
+ "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),
17081
+ }
17082
+ failed1 = [k for k, v in c1.items() if not v]
17083
+ ok(not failed1,
17084
+ 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, "
17085
+ 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 "
17086
+ 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}%, "
17087
+ "SIMULATION - the twin's calibration, not a string's)" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
17088
+ # -- DY2 the rig side ---------------------------------------------------------------------------------------------------------
17089
+ shipped = P.Policy.load(S.SHIPPED_POLICY)
17090
+ cfg = json.loads(json.dumps(C.EXAMPLE)); cfg["output"]["dir"] = str(d / "rig"); cfg["status"]["port"] = 0; cfg["status"]["bind"] = "127.0.0.1"
17091
+ cfg["rig"]["transport"] = "sim"; cfg["closed_loop"]["allowed"] = True; cfg["closed_loop"]["stale_s"] = 30
17092
+ cfg = C.check(cfg)
17093
+ try:
17094
+ C.check(json.loads(json.dumps(cfg)) | {"rig": {**cfg["rig"], "vibration_window_s": 2}}); win_refused = False
17095
+ except C.ConfigError:
17096
+ win_refused = True
17097
+ drl = S.Driller(cfg, policy=shipped, twin_seed=11, twin_pace_s=0.6)
17098
+ drl.start()
17099
+ try:
17100
+ t_end = time.monotonic() + 25
17101
+ while time.monotonic() < t_end and len(drl.advisor.history) < 3:
17102
+ time.sleep(0.2)
17103
+ snap = drl.feed.snapshot(); live = drl.live()
17104
+ cur = dict(drl.advisor.history[-1]) if drl.advisor.history else None
17105
+ md = snap["state"].get("hole_depth")
17106
+ 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")
17107
+ with urllib.request.urlopen(rq, timeout=5) as r_:
17108
+ armed = json.load(r_)
17109
+ drl.cfg["limits"]["vibration_index"] = 0.01 # the limit lowered under the loop: the derived index crosses it at the next look
17110
+ t_end = time.monotonic() + 8
17111
+ while time.monotonic() < t_end and drl.advisor.armed is not None:
17112
+ time.sleep(0.2)
17113
+ drl.cfg["limits"]["vibration_index"] = 1.2
17114
+ last = drl.advisor.arm_log[-1]
17115
+ with urllib.request.urlopen(drl.url + "/live", timeout=5) as r_:
17116
+ screen = r_.read().decode("utf-8")
17117
+ finally:
17118
+ drl.stop()
17119
+ # the downhole item wins; a feed with neither reads none
17120
+ 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
17121
+ for k in range(30):
17122
+ 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)
17123
+ v_tool = A.Advisor.vibration_of(feed.snapshot())
17124
+ feed.state["vibration"] = None
17125
+ v_der = A.Advisor.vibration_of(feed.snapshot())
17126
+ feed2 = R.Feed(cfg); feed2.state.update({"hole_depth": 10500.0, "gamma": 60.0}); feed2.last_utc = time.time(); feed2.connected = True
17127
+ v_none = A.Advisor.vibration_of(feed2.snapshot())
17128
+ adv2 = A.Advisor(cfg, shipped, feed2, A.Records(json.loads(json.dumps(cfg)) | {"output": {"dir": str(d / "rig2")}}))
17129
+ rec2 = adv2.decide(feed2.snapshot())
17130
+ decs = [json.loads(l) for l in open(d / "rig" / "decisions.jsonl", encoding="utf-8")]
17131
+ c2 = {
17132
+ "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
17133
+ and snap["vibration"]["index"] > 0 and snap.get("window_n", 0) >= 10 and abs(snap["vibration"]["index"] - drl.feed.lat.vib) < 0.25,
17134
+ "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"])
17135
+ and live["vibration"]["source"] == V.SOURCE and live["vibration"]["parts"]["kind"] in ("stick-slip", "lateral"),
17136
+ "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,
17137
+ "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"]),
17138
+ "disarms": armed.get("ok") and last["event"] == "disarmed" and "vibration" in last["reason"] and "surface data" in last["reason"],
17139
+ "records": any((x.get("context") or {}).get("vibration_source") == V.SOURCE for x in decs),
17140
+ "screen": "from surface data" in screen and "downhole tool" in screen,
17141
+ "refusal": win_refused,
17142
+ "help": not H.check("driller") and "The vibration index from the feed" in (pkg / "help" / "driller.md").read_text(encoding="utf-8")
17143
+ and "surface" in (pkg / "help" / "tutorial" / "29-driller-rig.md").read_text(encoding="utf-8"),
17144
+ }
17145
+ failed2 = [k for k, v in c2.items() if not v]
17146
+ ok(not failed2,
17147
+ "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 "
17148
+ "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 "
17149
+ "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"
17150
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
17151
+ # -- DY3 GEA's side: the lateral file with the statistics, the replay over the limit, the card ---------------------------------------
17152
+ ws = Workspace.create(str(d / "ws"), "Pad 3 vibration", actor="tester")
17153
+ exp = E.selftest(str(d), 3000)
17154
+ w = ws.add_well_file(exp["path"], display="Well EDR", actor="tester")
17155
+ lat_csv = str(d / "lat.csv")
17156
+ info = DL.lateral_from_edr(ws, w["id"], lat_csv)
17157
+ head = Path(lat_csv).read_text(encoding="utf-8").splitlines()[0].split(",")
17158
+ rec = DL.shadow(ws, w["id"], actor="tester")
17159
+ sm = rec["summary"]
17160
+ # the twin's lateral with and without the statistics
17161
+ tw_csv = str(d / "tw.csv"); tw_no = str(d / "tw_no.csv")
17162
+ 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)
17163
+ r_tw = SH.run(tw_csv, shipped, well="Twin 5"); r_no = SH.run(tw_no, shipped, well="Twin 5")
17164
+ tv = dict(tinfo["true_vibration"]); mds = np_.array(sorted(tv))
17165
+ tvv = np_.array([tv[m_] for m_ in mds])
17166
+ def true_over(md_): # the twin's own index averaged over the 30 ft the replay's window pooled
17167
+ j0, j1 = int(np_.searchsorted(mds, md_ - 30.0)), int(np_.searchsorted(mds, md_, side="right"))
17168
+ return float(tvv[j0:j1].mean()) if j1 > j0 else float("nan")
17169
+ pairs = np_.array([(x["vibration"], true_over(x["md"])) for x in r_tw["decisions"] if x["vibration"] is not None])
17170
+ pairs = pairs[np_.isfinite(pairs[:, 1])]
17171
+ corr_tw = float(np_.corrcoef(pairs[:, 0], pairs[:, 1])[0, 1]) if len(pairs) > 10 else 0.0
17172
+ md_text = Path(ws.reports_dir, rec["report"]["md"]).read_text(encoding="utf-8")
17173
+ app_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
17174
+ c3 = {
17175
+ "lateral_file": all(c in head for c in DL.LATERAL_STATS) and info["vibration_rows"] >= 1000,
17176
+ "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
17177
+ and sm["eased_when_over_pct"] is not None and sm["eased_when_over_pct"] >= 50.0,
17178
+ "report": "stood over the limit" in md_text and "| Vib |" in md_text,
17179
+ "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,
17180
+ "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"]),
17181
+ "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 ("",)),
17182
+ "card": "Vibration over limit" in app_src and "eased" in app_src,
17183
+ }
17184
+ failed3 = [k for k, v in c3.items() if not v]
17185
+ ok(not failed3,
17186
+ 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 "
17187
+ 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 "
17188
+ 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; "
17189
+ "the card carries the column" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
17190
+
17191
+
17192
+ def section_dz_textbook_oracle(tmp: str) -> None:
17193
+ """Section DZ - the stronger textbook driller and the oracle: the textbook's behaviours from the observation
17194
+ alone (steering back hard when the filter says the bit is out, the weight the bit is rated for in a calm hole
17195
+ and not while the hole fills, the vibration trial - lighter and faster first, heavier and slower when that did
17196
+ not quiet it - circulating at 40%, a new lateral a new memory); the oracle told the twin's layer refusing to run
17197
+ unbound and beating the textbook and the agent; the three rows and the standing; the command line, the self-test
17198
+ and the help."""
17199
+ import importlib
17200
+ import subprocess as _sp
17201
+ from . import driller_link as DL
17202
+ from . import helplib as H
17203
+ pkg = Path(__file__).parent
17204
+ d = Path(tmp, "dz"); d.mkdir()
17205
+ prog = DL.program()
17206
+ if prog is None:
17207
+ ok(True, "DZ the driller's program (gea-driller) is not installed beside GEA in this install; nothing to check here")
17208
+ return
17209
+ if prog["env"] and prog["env"] not in sys.path:
17210
+ sys.path.insert(0, prog["env"])
17211
+ T = importlib.import_module("gea_driller.twin"); P = importlib.import_module("gea_driller.policy"); S = importlib.import_module("gea_driller.service")
17212
+ np_ = importlib.import_module("numpy")
17213
+ # -- DZ1 the textbook and the oracle ---------------------------------------------------------------------------------------
17214
+ lat = T.Lateral(T.Scene(2)); base = lat.reset().copy()
17215
+ i = {n: k for k, n in enumerate(T.OBS_NAMES)}
17216
+
17217
+ def obs(**kw):
17218
+ o = base.copy()
17219
+ o[i["filter_p_in"]], o[i["filter_p_above"]], o[i["filter_p_below"]] = 0.95, 0.03, 0.02
17220
+ o[i["filter_dist_top"]], o[i["filter_dist_base"]] = 20.0 / 30, 20.0 / 30
17221
+ o[i["vibration"]] = 0.5 / 1.5; o[i["cuttings_bed"]] = 0.1; o[i["balling"]] = 0.0; o[i["gamma_trend"]] = 0.0; o[i["deep_res_available"]] = 0.0
17222
+ o[i["inclination_minus_90"]] = 0.0; o[i["progress"]] = 0.5
17223
+ for k, v in kw.items():
17224
+ o[i[k]] = v
17225
+ return o
17226
+ tb = T.TextbookDriller()
17227
+ a_calm = tb(obs())
17228
+ a_above = tb(obs(filter_p_in=0.1, filter_p_above=0.85, filter_p_below=0.05, filter_dist_top=-15.0 / 30))
17229
+ a_below = tb(obs(filter_p_in=0.1, filter_p_above=0.05, filter_p_below=0.85, filter_dist_base=-15.0 / 30))
17230
+ a_fill = tb(obs(cuttings_bed=0.35))
17231
+ a_circ = tb(obs(cuttings_bed=0.45)); a_ream = tb(obs(balling=0.45))
17232
+ tb2 = T.TextbookDriller()
17233
+ v1 = tb2(obs(vibration=1.4 / 1.5)); v2 = tb2(obs(vibration=1.45 / 1.5)); v3 = tb2(obs(vibration=1.5 / 1.5)); v4 = tb2(obs(vibration=0.6 / 1.5)); v5 = tb2(obs(vibration=1.4 / 1.5))
17234
+ tb3 = T.TextbookDriller()
17235
+ tb3(obs(vibration=1.4 / 1.5)); tb3(obs(vibration=1.5 / 1.5)); trial_before = tb3.vib_trial # a trial that did not quiet: the second cure
17236
+ tb3(obs(vibration=1.4 / 1.5, progress=0.0)); trial_after = tb3.vib_trial # a new lateral (the progress fell): a new memory
17237
+ try:
17238
+ T.OracleDriller()(base); unbound = False
17239
+ except RuntimeError:
17240
+ unbound = True
17241
+ three = P.evaluate_all(P.Policy.load(S.SHIPPED_POLICY), range(20))
17242
+ tbr, agr, orr = three["textbook"], three["agent"], three["oracle"]
17243
+ old = T.run_episode(T.Lateral(T.Scene(0)), T.textbook_action); new = T.run_episode(T.Lateral(T.Scene(0)), T.TextbookDriller())
17244
+ c1 = {
17245
+ "calm": a_calm[1] == 2 and a_calm[2] == 1 and a_calm[4] == 0 and a_fill[1] == 1,
17246
+ "out": a_above[0] == 0 and a_below[0] == 4,
17247
+ "hole": a_circ[4] == 1 and a_ream[4] == 2,
17248
+ "trial": (v1[1], v1[2]) == (0, 2) and (v2[1], v2[2]) == (1, 0) and (v3[1], v3[2]) == (1, 0) and v4[1] == 2 and (v5[1], v5[2]) == (0, 2),
17249
+ "memory": trial_before == 2 and trial_after == 1 and abs(old["reward"] - new["reward"]) < 1e-9,
17250
+ "oracle": unbound and orr["in_layer_pct"] > agr["in_layer_pct"] > tbr["in_layer_pct"] and orr["reward"] > agr["reward"] > tbr["reward"] and orr["in_layer_pct"] >= 90.0,
17251
+ "standing": 0.0 < three["standing"]["in_layer_pct_of_the_way"] < 100.0 and three["label"] == "SIMULATION" and three["scenes"] == list(range(20)),
17252
+ }
17253
+ failed1 = [k for k, v in c1.items() if not v]
17254
+ ok(not failed1,
17255
+ f"DZ1 the textbook driller from the observation alone: the rated weight in a calm hole in the layer and not while the hole fills, a hard drop when the filter says "
17256
+ f"above and a hard build when below, circulating at 40% and reaming a balled bit, the vibration trial lighter and faster first then heavier and slower, a new "
17257
+ f"lateral a new memory; the oracle refuses to run unbound and, told the layer, holds {orr['in_layer_pct']:.0f}% of the hole in it against the agent's {agr['in_layer_pct']:.0f}% "
17258
+ f"and the textbook's {tbr['in_layer_pct']:.0f}% on twenty held-out scenes (SIMULATION); the standing reads between them" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
17259
+ # -- DZ2 the command line, the self-test and the help --------------------------------------------------------------------------
17260
+ env = dict(os.environ)
17261
+ if prog["env"]:
17262
+ env["PYTHONPATH"] = prog["env"] + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
17263
+ r_ev = _sp.run([sys.executable, "-m", "gea_driller", "evaluate", "--scenes", "4", "--json"], capture_output=True, text=True, env=env, cwd=str(d))
17264
+ r_tx = _sp.run([sys.executable, "-m", "gea_driller", "evaluate", "--scenes", "3"], capture_output=True, text=True, env=env, cwd=str(d))
17265
+ try:
17266
+ j = json.loads(r_ev.stdout)
17267
+ except ValueError:
17268
+ j = {}
17269
+ main_src = (Path(prog["path"]) / "__main__.py").read_text(encoding="utf-8")
17270
+ help_text = (pkg / "help" / "driller.md").read_text(encoding="utf-8")
17271
+ c2 = {
17272
+ "json": r_ev.returncode == 0 and set(j) >= {"textbook", "agent", "oracle", "standing"} and j["scenes"] == [0, 1, 2, 3] and "episodes" not in j["agent"],
17273
+ "text": r_tx.returncode == 0 and "oracle" in r_tx.stdout and "textbook" in r_tx.stdout and "of the way from the textbook to the oracle" in r_tx.stdout and "SIMULATION" in r_tx.stdout,
17274
+ "selftest": "evaluate_all" in main_src and "oracle" in main_src,
17275
+ "help": not H.check("driller") and "oracle" in help_text and "the bar" in help_text,
17276
+ }
17277
+ failed2 = [k for k, v in c2.items() if not v]
17278
+ ok(not failed2,
17279
+ "DZ2 `gea-driller evaluate` prints the three rows - the textbook, the agent, the oracle - and where the agent stands between them, as text and as JSON; "
17280
+ "the self-test holds the policy to the oracle as well; the help names the bar and the ceiling" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
17281
+
17282
+
17014
17283
  def main() -> int:
17015
17284
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
17016
17285
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -17142,6 +17411,8 @@ def main() -> int:
17142
17411
  section_dv_driller(tmp)
17143
17412
  section_dw_shadow(tmp)
17144
17413
  section_dx_third_dimension(tmp)
17414
+ section_dy_vibration(tmp)
17415
+ section_dz_textbook_oracle(tmp)
17145
17416
  if _FAILS:
17146
17417
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
17147
17418
  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
- rows.append({'md_ft': float(h), 'tvd_ft': float(tvd) if not (tvd != tvd) else float('nan'),
323
- 'gamma_api': float(sorted(g_acc)[len(g_acc) // 2]) if g_acc else float('nan'),
324
- '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),
325
- 'hours': float(secs[i]) / 3600.0})
326
- g_acc = []
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=list(LATERAL_COLUMNS)); w.writeheader()
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 LATERAL_COLUMNS})
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', '10.000', str(on),
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)
@@ -2,12 +2,12 @@
2
2
 
3
3
  > A program on the rig PC that reads the rig's own feed and the pre-drill model, places the bit in the layer with a position filter, and, every 30 ft, recommends in the driller's units what to do next - a policy trained by reinforcement learning in a physics twin of the lateral - with the driller's answer, every command, and the records pulled into GEA.
4
4
 
5
- **The command**: on the rig PC, `GEA-Driller-0.80.0.tar.gz` from the release (or the kit's `installers` folder, fetched on the Audit / Update tab): unpack it and run `sudo sh install.sh` on Linux or `install.cmd` on Windows, once; edit its config - the EDR's WITS0 feed (`tcp`, the host and port; `serial`, the COM port), the pre-drill model (the target layer's top TVD at the heel, its thickness and dip, the gamma in, above and below, the deep-resistivity tool, the heel's MD and TVD), the tool's cap, the limits - and restart it. The driller's screen is at port 8790 of the rig PC: `http://192.168.1.60:8790/live`. It shows the section with the model's layer window, the hole drilled through it and - since 0.78.0 - the **position filter**'s band: where the layer is from the readings, as a geosteerer places it (a particle filter over the layer's offset from the model, its dip error and its thickness, moved every decision and kept to the guesses that explain the gamma and the deep-resistivity distance), with the probability the bit is in the layer, above or below it and the distance to the top and the base with its spread, sixty feet ahead; the agent reads the same estimate, and so does the textbook driller it is judged against. Then the recommendation in words - *Build +1.5°/100 ft (toolface up, 25% steering); 25 klb WOB, 120 rpm, 500 gpm* - with its reasons (what the deep-resistivity tool says, the gamma against the model's values, the model's bracket, the limits) and the confidence of each choice, the rig's numbers against the limits, and **Accept**, **Reject** or **Did something else…** with the operator's name. In GEA: the **Drill string** tab's card **GEA Driller** - the rig PC's address and the well, **Pull**: the records come in checked against their hashes, and the table says how many decisions, accepted, rejected, done otherwise, the hole under advice, the armed sections and the commands. **Open the driller's screen** opens the rig PC's screen from its address. Before there is a rig PC: **Start the twin as the rig** runs the driller's program on this computer with a scene drilling itself under the agent - **Open its screen** is the driller's screen with the twin behind it, labelled SIMULATION - and **Pull from it** brings its records in; **Stop it** closes them. **Shadow a drilled well** (since 0.79.0): a well of this workspace with a drilling-recorder export is replayed through the advisor - every 30 ft the agent sees what the rig had shown to that point and nothing past it, and its recommendation is set beside what the record did next: the build actually made over the following 90 ft, the weight, speed and flow used, and at each stretch the gamma marks as out of the layer, whether the advisor called the way back at the first reading. The report, **SHADOW MODE**, is under the well's reports with a drawing of the lateral against the layer; the layer model is read off the well's own gamma in hindsight and says so - and grades how well it fits the well (good, fair, poor: a well steered to a rock that is not the low gamma reads poor, and the agent's calls against a poor model mean little) - unless the site's pre-drill model is given: a file `wells\Well-30636033\driller\model.json` with the config's `model` block (the target layer's top TVD at the heel, its thickness and dip, the gamma in, above and below, the heel's MD and TVD) is used whenever it is there (`gea workspace --path C:\site --action driller-shadow --well Well-30636033`). The export is read on this computer and nothing of it leaves it. Agreement with the driller says the two would have done alike, not that either was right. **The third dimension** (since 0.80.0): the lateral has an azimuth. The config's model carries the plan's azimuth (`plan_azimuth_deg`; empty, the first survey's at the heel is taken), the layer's dip across the plan line (`cross_dip_deg`, positive deepening to the right), the corridor the hole may run in to the right and left of the plan line (`corridor_ft`, the lease line and the offset wells) and where the heel sits across it (`heel_offset_ft`); the feed's surveys and the continuous azimuth (WITS item 0832, or the config's mapping) place the bit by minimum curvature along the plan, right of its line and in depth, and the screen draws the plan view - the corridor as a band, the hole against the plan line - under the section. The recommendation gains the aim - where to point the azimuth, along the plan or 4° left or right of it, and the turn that asks from where the hole points: *Build +1.5°/100 ft and aim 4° right of the plan (azimuth 44.0°), turning right 2.0°/100 ft (toolface 53°, 42% steering)* - with the build and the turn held together inside the DLS limit, and **Did something else…** takes the aim, or the turn made instead; the position filter carries the cross dip and reads it only when the hole moves across the plan line, leaving the model's value standing until then. In the twin the layer is a surface, the faults have a strike, the tool walks, and a hole that leaves the corridor by 150 ft is lost; the agent is judged on the hole in the corridor as well as in the layer (`gea-driller evaluate` prints both). Shadow mode reads the record's `azimuth_deg` column when it has one and sets the turn the advisor would have asked beside the turn the record made; without it the record's turns are not judged, and the report says so. Over a cellular modem the rig PC joins GEA-VPN as a member of kind `driller` and GEA pulls from `http://10.77.0.4:8790`. The closed loop: `closed_loop.allowed` in the rig PC's config puts the switch on its screen; an operator arms it by name for one section of at most 500 ft (**Arm for this section**), every decision then goes to the downlink as a command, and the loop disarms itself on a stale feed, a crossed limit, the section's end or **Disarm now** - and this version's only downlink is the null one, which keeps every command on record and sends none, because no vendor's downlink protocol ships with it. From a terminal: `gea workspace --path C:\site --action driller-pull --box http://192.168.1.60:8790 --well SWD-1`; on the rig PC `gea-driller sim --seed 7 --pace 3 --port 8790`, `gea-driller evaluate --scenes 20`, `gea-driller train --seconds 600 --out policy.json`, `gea-driller selftest`.
5
+ **The command**: on the rig PC, `GEA-Driller-0.82.0.tar.gz` from the release (or the kit's `installers` folder, fetched on the Audit / Update tab): unpack it and run `sudo sh install.sh` on Linux or `install.cmd` on Windows, once; edit its config - the EDR's WITS0 feed (`tcp`, the host and port; `serial`, the COM port), the pre-drill model (the target layer's top TVD at the heel, its thickness and dip, the gamma in, above and below, the deep-resistivity tool, the heel's MD and TVD), the tool's cap, the limits - and restart it. The driller's screen is at port 8790 of the rig PC: `http://192.168.1.60:8790/live`. It shows the section with the model's layer window, the hole drilled through it and - since 0.78.0 - the **position filter**'s band: where the layer is from the readings, as a geosteerer places it (a particle filter over the layer's offset from the model, its dip error and its thickness, moved every decision and kept to the guesses that explain the gamma and the deep-resistivity distance), with the probability the bit is in the layer, above or below it and the distance to the top and the base with its spread, sixty feet ahead; the agent reads the same estimate, and so does the textbook driller it is judged against. Then the recommendation in words - *Build +1.5°/100 ft (toolface up, 25% steering); 25 klb WOB, 120 rpm, 500 gpm* - with its reasons (what the deep-resistivity tool says, the gamma against the model's values, the model's bracket, the limits) and the confidence of each choice, the rig's numbers against the limits, and **Accept**, **Reject** or **Did something else…** with the operator's name. In GEA: the **Drill string** tab's card **GEA Driller** - the rig PC's address and the well, **Pull**: the records come in checked against their hashes, and the table says how many decisions, accepted, rejected, done otherwise, the hole under advice, the armed sections and the commands. **Open the driller's screen** opens the rig PC's screen from its address. Before there is a rig PC: **Start the twin as the rig** runs the driller's program on this computer with a scene drilling itself under the agent - **Open its screen** is the driller's screen with the twin behind it, labelled SIMULATION - and **Pull from it** brings its records in; **Stop it** closes them. **Shadow a drilled well** (since 0.79.0): a well of this workspace with a drilling-recorder export is replayed through the advisor - every 30 ft the agent sees what the rig had shown to that point and nothing past it, and its recommendation is set beside what the record did next: the build actually made over the following 90 ft, the weight, speed and flow used, and at each stretch the gamma marks as out of the layer, whether the advisor called the way back at the first reading. The report, **SHADOW MODE**, is under the well's reports with a drawing of the lateral against the layer; the layer model is read off the well's own gamma in hindsight and says so - and grades how well it fits the well (good, fair, poor: a well steered to a rock that is not the low gamma reads poor, and the agent's calls against a poor model mean little) - unless the site's pre-drill model is given: a file `wells\Well-30636033\driller\model.json` with the config's `model` block (the target layer's top TVD at the heel, its thickness and dip, the gamma in, above and below, the heel's MD and TVD) is used whenever it is there (`gea workspace --path C:\site --action driller-shadow --well Well-30636033`). The export is read on this computer and nothing of it leaves it. Agreement with the driller says the two would have done alike, not that either was right. **The third dimension** (since 0.80.0): the lateral has an azimuth. The config's model carries the plan's azimuth (`plan_azimuth_deg`; empty, the first survey's at the heel is taken), the layer's dip across the plan line (`cross_dip_deg`, positive deepening to the right), the corridor the hole may run in to the right and left of the plan line (`corridor_ft`, the lease line and the offset wells) and where the heel sits across it (`heel_offset_ft`); the feed's surveys and the continuous azimuth (WITS item 0832, or the config's mapping) place the bit by minimum curvature along the plan, right of its line and in depth, and the screen draws the plan view - the corridor as a band, the hole against the plan line - under the section. The recommendation gains the aim - where to point the azimuth, along the plan or 4° left or right of it, and the turn that asks from where the hole points: *Build +1.5°/100 ft and aim 4° right of the plan (azimuth 44.0°), turning right 2.0°/100 ft (toolface 53°, 42% steering)* - with the build and the turn held together inside the DLS limit, and **Did something else…** takes the aim, or the turn made instead; the position filter carries the cross dip and reads it only when the hole moves across the plan line, leaving the model's value standing until then. In the twin the layer is a surface, the faults have a strike, the tool walks, and a hole that leaves the corridor by 150 ft is lost; the agent is judged on the hole in the corridor as well as in the layer (`gea-driller evaluate` prints both). Shadow mode reads the record's `azimuth_deg` column when it has one and sets the turn the advisor would have asked beside the turn the record made; without it the record's turns are not judged, and the report says so. **The vibration index from the feed** (since 0.81.0): a rig without a downhole vibration tool left the agent blind to the stick-slip and the whirl it was trained to ease, and the closed loop's vibration limit could never trip. Now the rig PC reads an index off the surface channels every EDR sends: over the last minute (`rig.vibration_window_s`), the surface torque's RMS about its mean as a fraction of the mean - the measure the Drill string page calls the oscillation index - times root two for the amplitude, and the same of the weight on bit and the hook load; stick-slip is the torque amplitude over 0.4, lateral vibration the weight amplitude over 0.2 (the twin's scale, where 1.2 is the limit), and the index is the larger. The downhole tool's item (WITS 0830) is taken instead whenever it comes, and every decision records which it read: the tile on the screen says *downhole tool* or *from surface data, stick-slip*, the reasons say *vibration 1.35 derived from the surface data (stick-slip, the surface torque swinging ±54%) over the limit 1.2*, and the limit disarms the loop on either. A surface proxy, labelled; on the twin as the rig it tracks the twin's own index within a few hundredths. The lateral file GEA writes for shadow mode carries, per foot, the surface channels' mean and RMS over the seconds of that foot (`torque_mean`, `torque_rms`, `wob_mean`, `wob_rms`, `hkld_mean`, `hkld_rms`, `rpm_mean`, `rpm_rms`, `spp_mean`, `spp_rms`), the replay reads the index off them for every decision, and the report says how often it stood over the limit and how often the advisor asked for less weight or more speed there than the record used; a record without torque or weight statistics says so and the advisor sees none. **The bar and the ceiling** (since 0.82.0): the agent is judged between two drillers on the same held-out scenes. The textbook driller is the bar - what a careful directional driller and geosteerer do by the book from the same observation the agent gets and nothing more: aim at the middle of the layer as the filter places it, steer back hard when the filter says the bit is out, lean away from a boundary the gamma's trend is running at, the rated weight in a calm hole in the layer and not while the hole fills, over the vibration limit lighter and faster first and heavier and slower when that did not quiet it, circulate at 40% of a bed, ream a balled bit, hold the plan line; it has a driller's memory of its own last decisions and none of the twin's secret. The oracle is the ceiling - the same driller told the twin's own layer at the bit and 120 ft ahead of it, which no policy can beat because it sees what no tool measures. `gea-driller evaluate` prints the three rows and how far the agent stands from the bar toward the ceiling; the self-test refuses a policy that does not beat the textbook, and the gate holds it under the oracle. The figures are a simulation's, labelled; a stronger textbook lowers the agent's standing without changing the agent, and that is the point of it. Over a cellular modem the rig PC joins GEA-VPN as a member of kind `driller` and GEA pulls from `http://10.77.0.4:8790`. The closed loop: `closed_loop.allowed` in the rig PC's config puts the switch on its screen; an operator arms it by name for one section of at most 500 ft (**Arm for this section**), every decision then goes to the downlink as a command, and the loop disarms itself on a stale feed, a crossed limit, the section's end or **Disarm now** - and this version's only downlink is the null one, which keeps every command on record and sends none, because no vendor's downlink protocol ships with it. From a terminal: `gea workspace --path C:\site --action driller-pull --box http://192.168.1.60:8790 --well SWD-1`; on the rig PC `gea-driller sim --seed 7 --pace 3 --port 8790`, `gea-driller evaluate --scenes 20`, `gea-driller train --seconds 600 --out policy.json`, `gea-driller selftest`.
6
6
 
7
7
  **What it writes**: on the rig PC, `decisions.jsonl` (every observation, recommendation, confidence, reasons and answer), `commands.jsonl` (every arming, command and disarming with its reason) and `surveys.csv`, each with a `.sha256` beside it, and `manifest.json`. In GEA, `wells\SWD-1\driller\` holds the same files as pulled, `manifest.json` and `last_pull.json` with the summary; the audit lines `driller.pull`, `driller.sim_start`, `driller.sim_stop`. The twin as the rig writes under `field\sim-driller\data`.
8
8
 
9
- **The number to check**: on the rig PC's screen, the feed's age (a decision is made only on a live feed) and, in GEA's table, accepted against rejected and done otherwise - an agent whose advice is rejected more than taken is not helping, and the records say so. For the agent itself, `gea-driller evaluate` prints the policy against a textbook driller on twenty held-out twin scenes - the hole in the layer, the sweet spot, feet a day, dogleg, stuck pipe, losses - labelled SIMULATION; the self-test refuses a policy that does not beat the textbook.
9
+ **The number to check**: on the rig PC's screen, the feed's age (a decision is made only on a live feed) and, in GEA's table, accepted against rejected and done otherwise - an agent whose advice is rejected more than taken is not helping, and the records say so. For the agent itself, `gea-driller evaluate` prints the policy between the textbook driller and the oracle on sixty held-out twin scenes - the hole in the layer, the sweet spot, the corridor, feet a day, dogleg, stuck pipe, losses, and the agent's standing from the one toward the other - labelled SIMULATION; the self-test refuses a policy that does not beat the textbook.
10
10
 
11
- **What this page will not call a measurement**: the twin's figures are a simulation's, however many scenes, and say nothing of a real formation; a recommendation is the agent's reading of the feed, the model and the filter's estimate, not a survey; the filter's band is a probability, not a log, and the hole's position on its screen is projected from the surveys and the continuous inclination and azimuth, not measured at the bit, and its offset from the plan line inherits every survey's azimuth error; the hole-cleaning and balling the agent reasons from are the model's estimates; and "commands sent" is zero in this version by construction.
11
+ **What this page will not call a measurement**: the twin's figures are a simulation's, however many scenes, and say nothing of a real formation; a recommendation is the agent's reading of the feed, the model and the filter's estimate, not a survey; the filter's band is a probability, not a log, and the hole's position on its screen is projected from the surveys and the continuous inclination and azimuth, not measured at the bit, and its offset from the plan line inherits every survey's azimuth error; the hole-cleaning and balling the agent reasons from are the model's estimates; the vibration index read off the surface data is a proxy for what a downhole tool would measure, calibrated on the twin, not on a string; and "commands sent" is zero in this version by construction.
12
12
 
13
13
  Related: `gea help drillstring`, `gea help recorder`, `gea help vpn`.