gea-program 0.51.0__tar.gz → 0.53.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 (360) hide show
  1. {gea_program-0.51.0 → gea_program-0.53.0}/PKG-INFO +28 -1
  2. {gea_program-0.51.0 → gea_program-0.53.0}/README.md +27 -0
  3. {gea_program-0.51.0 → gea_program-0.53.0}/gea/__init__.py +1 -1
  4. {gea_program-0.51.0 → gea_program-0.53.0}/gea/__main__.py +96 -0
  5. {gea_program-0.51.0 → gea_program-0.53.0}/gea/acceptance_tests.py +350 -0
  6. {gea_program-0.51.0 → gea_program-0.53.0}/gea/dossier.py +26 -3
  7. gea_program-0.53.0/gea/help/pore-pressure.md +17 -0
  8. gea_program-0.53.0/gea/help/torque-drag.md +17 -0
  9. {gea_program-0.51.0 → gea_program-0.53.0}/gea/helplib.py +3 -1
  10. gea_program-0.53.0/gea/pore_pressure.py +682 -0
  11. gea_program-0.53.0/gea/reference/l0701_sonic_gr.las.gz +0 -0
  12. gea_program-0.53.0/gea/reference/l0701_sonic_gr.provenance.json +14 -0
  13. {gea_program-0.51.0 → gea_program-0.53.0}/gea/service.py +37 -0
  14. gea_program-0.53.0/gea/torque_drag.py +509 -0
  15. {gea_program-0.51.0 → gea_program-0.53.0}/gea/web/app.html +129 -1
  16. {gea_program-0.51.0 → gea_program-0.53.0}/gea_program.egg-info/PKG-INFO +28 -1
  17. {gea_program-0.51.0 → gea_program-0.53.0}/gea_program.egg-info/SOURCES.txt +6 -0
  18. {gea_program-0.51.0 → gea_program-0.53.0}/pyproject.toml +1 -1
  19. {gea_program-0.51.0 → gea_program-0.53.0}/LICENSE +0 -0
  20. {gea_program-0.51.0 → gea_program-0.53.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  21. {gea_program-0.51.0 → gea_program-0.53.0}/gea/accuracy_statement.py +0 -0
  22. {gea_program-0.51.0 → gea_program-0.53.0}/gea/alarm_engine.py +0 -0
  23. {gea_program-0.51.0 → gea_program-0.53.0}/gea/alerts.py +0 -0
  24. {gea_program-0.51.0 → gea_program-0.53.0}/gea/backup.py +0 -0
  25. {gea_program-0.51.0 → gea_program-0.53.0}/gea/bench.py +0 -0
  26. {gea_program-0.51.0 → gea_program-0.53.0}/gea/blind_harness.py +0 -0
  27. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  28. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  29. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  30. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  31. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  32. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  33. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  34. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  35. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  36. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  37. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  38. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  39. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  40. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  41. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  42. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  43. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  44. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  45. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  46. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  47. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  48. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  49. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  50. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  51. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  52. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  53. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  54. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  55. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  56. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  57. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  58. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  59. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  60. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  61. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  62. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  63. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  64. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  65. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  66. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  67. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  68. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  69. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  70. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  71. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  72. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  73. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  74. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  75. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  76. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  77. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  78. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  79. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  80. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  81. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  82. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  83. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  84. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  85. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  86. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  87. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  88. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  89. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  90. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  91. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  92. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  93. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  94. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/med_160_sapropels.txt +0 -0
  95. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  96. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  97. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  98. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  99. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  100. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  101. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  102. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  103. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  104. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  105. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  106. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  107. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  108. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  109. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  110. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  111. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  112. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  113. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  114. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  115. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  116. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  117. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  118. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  119. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  120. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  121. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  122. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  123. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  124. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  125. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  126. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  127. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  128. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  129. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  130. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  131. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog_map.py +0 -0
  132. {gea_program-0.51.0 → gea_program-0.53.0}/gea/catalog_register.py +0 -0
  133. {gea_program-0.51.0 → gea_program-0.53.0}/gea/certificates.py +0 -0
  134. {gea_program-0.51.0 → gea_program-0.53.0}/gea/cli.py +0 -0
  135. {gea_program-0.51.0 → gea_program-0.53.0}/gea/client_reports.py +0 -0
  136. {gea_program-0.51.0 → gea_program-0.53.0}/gea/collision.py +0 -0
  137. {gea_program-0.51.0 → gea_program-0.53.0}/gea/config_versioning.py +0 -0
  138. {gea_program-0.51.0 → gea_program-0.53.0}/gea/conformance.py +0 -0
  139. {gea_program-0.51.0 → gea_program-0.53.0}/gea/control_room.py +0 -0
  140. {gea_program-0.51.0 → gea_program-0.53.0}/gea/correlation.py +0 -0
  141. {gea_program-0.51.0 → gea_program-0.53.0}/gea/dashboard.py +0 -0
  142. {gea_program-0.51.0 → gea_program-0.53.0}/gea/declarations.py +0 -0
  143. {gea_program-0.51.0 → gea_program-0.53.0}/gea/deviation.py +0 -0
  144. {gea_program-0.51.0 → gea_program-0.53.0}/gea/dlis.py +0 -0
  145. {gea_program-0.51.0 → gea_program-0.53.0}/gea/dnp3.py +0 -0
  146. {gea_program-0.51.0 → gea_program-0.53.0}/gea/doctor.py +0 -0
  147. {gea_program-0.51.0 → gea_program-0.53.0}/gea/dossier_verify.py +0 -0
  148. {gea_program-0.51.0 → gea_program-0.53.0}/gea/downhole_engine.py +0 -0
  149. {gea_program-0.51.0 → gea_program-0.53.0}/gea/drift_monitor.py +0 -0
  150. {gea_program-0.51.0 → gea_program-0.53.0}/gea/drill_string.py +0 -0
  151. {gea_program-0.51.0 → gea_program-0.53.0}/gea/earth_model.py +0 -0
  152. {gea_program-0.51.0 → gea_program-0.53.0}/gea/economics.py +0 -0
  153. {gea_program-0.51.0 → gea_program-0.53.0}/gea/ed25519.py +0 -0
  154. {gea_program-0.51.0 → gea_program-0.53.0}/gea/edr.py +0 -0
  155. {gea_program-0.51.0 → gea_program-0.53.0}/gea/elevation.py +0 -0
  156. {gea_program-0.51.0 → gea_program-0.53.0}/gea/etp.py +0 -0
  157. {gea_program-0.51.0 → gea_program-0.53.0}/gea/etp12_schemas.json +0 -0
  158. {gea_program-0.51.0 → gea_program-0.53.0}/gea/example_register_map.json +0 -0
  159. {gea_program-0.51.0 → gea_program-0.53.0}/gea/fat_sat.py +0 -0
  160. {gea_program-0.51.0 → gea_program-0.53.0}/gea/files.py +0 -0
  161. {gea_program-0.51.0 → gea_program-0.53.0}/gea/fixtures/README.md +0 -0
  162. {gea_program-0.51.0 → gea_program-0.53.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  163. {gea_program-0.51.0 → gea_program-0.53.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  164. {gea_program-0.51.0 → gea_program-0.53.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  165. {gea_program-0.51.0 → gea_program-0.53.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  166. {gea_program-0.51.0 → gea_program-0.53.0}/gea/follower.py +0 -0
  167. {gea_program-0.51.0 → gea_program-0.53.0}/gea/forward_model.py +0 -0
  168. {gea_program-0.51.0 → gea_program-0.53.0}/gea/frac_hits.py +0 -0
  169. {gea_program-0.51.0 → gea_program-0.53.0}/gea/friction_priors.py +0 -0
  170. {gea_program-0.51.0 → gea_program-0.53.0}/gea/gamma.py +0 -0
  171. {gea_program-0.51.0 → gea_program-0.53.0}/gea/gauge_aging.py +0 -0
  172. {gea_program-0.51.0 → gea_program-0.53.0}/gea/gauge_specs.py +0 -0
  173. {gea_program-0.51.0 → gea_program-0.53.0}/gea/geodesy.py +0 -0
  174. {gea_program-0.51.0 → gea_program-0.53.0}/gea/geomag.py +0 -0
  175. {gea_program-0.51.0 → gea_program-0.53.0}/gea/gravity_reference.py +0 -0
  176. {gea_program-0.51.0 → gea_program-0.53.0}/gea/hartip.py +0 -0
  177. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/TESTER_GUIDE.md +0 -0
  178. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/alarms.md +0 -0
  179. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/alerts.md +0 -0
  180. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/audit-update.md +0 -0
  181. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/backup.md +0 -0
  182. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/catalog.md +0 -0
  183. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/charts.md +0 -0
  184. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/collision.md +0 -0
  185. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/conformance.md +0 -0
  186. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/control-room.md +0 -0
  187. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/data-in.md +0 -0
  188. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/declare.md +0 -0
  189. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/dlis.md +0 -0
  190. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/doctor.md +0 -0
  191. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/dossier.md +0 -0
  192. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/drift.md +0 -0
  193. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/drillstring.md +0 -0
  194. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/economics.md +0 -0
  195. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/edr.md +0 -0
  196. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/etp.md +0 -0
  197. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/field-ports.md +0 -0
  198. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/files.md +0 -0
  199. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/frac-hits.md +0 -0
  200. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/historian.md +0 -0
  201. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/instruments.md +0 -0
  202. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/interval.md +0 -0
  203. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/invoice-check.md +0 -0
  204. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/map.md +0 -0
  205. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/month-end.md +0 -0
  206. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/notifications.md +0 -0
  207. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/opseis.md +0 -0
  208. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/osdu.md +0 -0
  209. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/pad-summary.md +0 -0
  210. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/patches.md +0 -0
  211. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/penetrator.md +0 -0
  212. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/plan.md +0 -0
  213. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/ppdm.md +0 -0
  214. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/quakeml.md +0 -0
  215. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/quality.md +0 -0
  216. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/report-history.md +0 -0
  217. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/rig-time.md +0 -0
  218. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/seedlink.md +0 -0
  219. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/seismic.md +0 -0
  220. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/setup.md +0 -0
  221. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/simulation.md +0 -0
  222. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/site.md +0 -0
  223. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/sites.md +0 -0
  224. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/slip.md +0 -0
  225. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/soak.md +0 -0
  226. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/sra.md +0 -0
  227. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/start.md +0 -0
  228. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/structure.md +0 -0
  229. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/tools.md +0 -0
  230. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/transients.md +0 -0
  231. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/upkeep.md +0 -0
  232. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/vibration.md +0 -0
  233. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/well-report.md +0 -0
  234. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/well-tests.md +0 -0
  235. {gea_program-0.51.0 → gea_program-0.53.0}/gea/help/well3d.md +0 -0
  236. {gea_program-0.51.0 → gea_program-0.53.0}/gea/housekeeping.py +0 -0
  237. {gea_program-0.51.0 → gea_program-0.53.0}/gea/interval.py +0 -0
  238. {gea_program-0.51.0 → gea_program-0.53.0}/gea/inverse_engine.py +0 -0
  239. {gea_program-0.51.0 → gea_program-0.53.0}/gea/invoice_check.py +0 -0
  240. {gea_program-0.51.0 → gea_program-0.53.0}/gea/jobs.py +0 -0
  241. {gea_program-0.51.0 → gea_program-0.53.0}/gea/live_ports.py +0 -0
  242. {gea_program-0.51.0 → gea_program-0.53.0}/gea/loadtest.py +0 -0
  243. {gea_program-0.51.0 → gea_program-0.53.0}/gea/matplotlib_demo.py +0 -0
  244. {gea_program-0.51.0 → gea_program-0.53.0}/gea/modbus.py +0 -0
  245. {gea_program-0.51.0 → gea_program-0.53.0}/gea/modbus_rtu.py +0 -0
  246. {gea_program-0.51.0 → gea_program-0.53.0}/gea/model_card.py +0 -0
  247. {gea_program-0.51.0 → gea_program-0.53.0}/gea/month_end.py +0 -0
  248. {gea_program-0.51.0 → gea_program-0.53.0}/gea/mqtt_port.py +0 -0
  249. {gea_program-0.51.0 → gea_program-0.53.0}/gea/notify.py +0 -0
  250. {gea_program-0.51.0 → gea_program-0.53.0}/gea/op_seismicity.py +0 -0
  251. {gea_program-0.51.0 → gea_program-0.53.0}/gea/opcua_port.py +0 -0
  252. {gea_program-0.51.0 → gea_program-0.53.0}/gea/operator_app.py +0 -0
  253. {gea_program-0.51.0 → gea_program-0.53.0}/gea/osdu.py +0 -0
  254. {gea_program-0.51.0 → gea_program-0.53.0}/gea/pad_summary.py +0 -0
  255. {gea_program-0.51.0 → gea_program-0.53.0}/gea/patches.py +0 -0
  256. {gea_program-0.51.0 → gea_program-0.53.0}/gea/pdf_report.py +0 -0
  257. {gea_program-0.51.0 → gea_program-0.53.0}/gea/penetrator.py +0 -0
  258. {gea_program-0.51.0 → gea_program-0.53.0}/gea/permits.py +0 -0
  259. {gea_program-0.51.0 → gea_program-0.53.0}/gea/piwebapi.py +0 -0
  260. {gea_program-0.51.0 → gea_program-0.53.0}/gea/ports.py +0 -0
  261. {gea_program-0.51.0 → gea_program-0.53.0}/gea/ppdm.py +0 -0
  262. {gea_program-0.51.0 → gea_program-0.53.0}/gea/profile_catalog.py +0 -0
  263. {gea_program-0.51.0 → gea_program-0.53.0}/gea/project.py +0 -0
  264. {gea_program-0.51.0 → gea_program-0.53.0}/gea/qt6_downhole_app.py +0 -0
  265. {gea_program-0.51.0 → gea_program-0.53.0}/gea/quakeml.py +0 -0
  266. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reconciler.py +0 -0
  267. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  268. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  269. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/WMM2025.COF +0 -0
  270. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  271. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/eia_prices.provenance.json +0 -0
  272. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  273. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/iscwsa_rev4_example.json +0 -0
  274. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/iscwsa_rev5_example.json +0 -0
  275. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  276. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  277. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  278. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  279. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/mahoning_example.json +0 -0
  280. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/mud_motor_specs.json +0 -0
  281. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/owsg_tool_models.json +0 -0
  282. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/penetrator_ratings.json +0 -0
  283. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/seismicity_protocols.json +0 -0
  284. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/tool_sources.json +0 -0
  285. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/us_outline.json +0 -0
  286. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/us_outline.provenance.json +0 -0
  287. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_3d.provenance.json +0 -0
  288. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  289. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  290. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  291. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_surfaces.json.gz +0 -0
  292. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  293. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  294. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  295. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  296. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  297. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  298. {gea_program-0.51.0 → gea_program-0.53.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  299. {gea_program-0.51.0 → gea_program-0.53.0}/gea/report_history.py +0 -0
  300. {gea_program-0.51.0 → gea_program-0.53.0}/gea/rig_state.py +0 -0
  301. {gea_program-0.51.0 → gea_program-0.53.0}/gea/rock_inventory.py +0 -0
  302. {gea_program-0.51.0 → gea_program-0.53.0}/gea/sample_record.py +0 -0
  303. {gea_program-0.51.0 → gea_program-0.53.0}/gea/sample_well_profile.csv +0 -0
  304. {gea_program-0.51.0 → gea_program-0.53.0}/gea/sbom.py +0 -0
  305. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seedlink.py +0 -0
  306. {gea_program-0.51.0 → gea_program-0.53.0}/gea/segy.py +0 -0
  307. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic.py +0 -0
  308. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_array.py +0 -0
  309. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_assoc.py +0 -0
  310. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_detect.py +0 -0
  311. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_film.py +0 -0
  312. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_harmonic.py +0 -0
  313. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_qc.py +0 -0
  314. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_response.py +0 -0
  315. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_signature.py +0 -0
  316. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_track.py +0 -0
  317. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_uncertainty.py +0 -0
  318. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_unlisted.py +0 -0
  319. {gea_program-0.51.0 → gea_program-0.53.0}/gea/seismic_watch.py +0 -0
  320. {gea_program-0.51.0 → gea_program-0.53.0}/gea/sensor_swap.py +0 -0
  321. {gea_program-0.51.0 → gea_program-0.53.0}/gea/series_cache.py +0 -0
  322. {gea_program-0.51.0 → gea_program-0.53.0}/gea/service_life.py +0 -0
  323. {gea_program-0.51.0 → gea_program-0.53.0}/gea/setup_checklist.py +0 -0
  324. {gea_program-0.51.0 → gea_program-0.53.0}/gea/shapefile.py +0 -0
  325. {gea_program-0.51.0 → gea_program-0.53.0}/gea/shell.py +0 -0
  326. {gea_program-0.51.0 → gea_program-0.53.0}/gea/shut_in.py +0 -0
  327. {gea_program-0.51.0 → gea_program-0.53.0}/gea/simulation.py +0 -0
  328. {gea_program-0.51.0 → gea_program-0.53.0}/gea/site_map.py +0 -0
  329. {gea_program-0.51.0 → gea_program-0.53.0}/gea/sla_report.py +0 -0
  330. {gea_program-0.51.0 → gea_program-0.53.0}/gea/slip.py +0 -0
  331. {gea_program-0.51.0 → gea_program-0.53.0}/gea/soak.py +0 -0
  332. {gea_program-0.51.0 → gea_program-0.53.0}/gea/sra.py +0 -0
  333. {gea_program-0.51.0 → gea_program-0.53.0}/gea/store_forward.py +0 -0
  334. {gea_program-0.51.0 → gea_program-0.53.0}/gea/strata_join.py +0 -0
  335. {gea_program-0.51.0 → gea_program-0.53.0}/gea/structure.py +0 -0
  336. {gea_program-0.51.0 → gea_program-0.53.0}/gea/supervisor.py +0 -0
  337. {gea_program-0.51.0 → gea_program-0.53.0}/gea/survey_cmd.py +0 -0
  338. {gea_program-0.51.0 → gea_program-0.53.0}/gea/survey_view.py +0 -0
  339. {gea_program-0.51.0 → gea_program-0.53.0}/gea/telemetry.py +0 -0
  340. {gea_program-0.51.0 → gea_program-0.53.0}/gea/tool_catalog.py +0 -0
  341. {gea_program-0.51.0 → gea_program-0.53.0}/gea/tool_library.py +0 -0
  342. {gea_program-0.51.0 → gea_program-0.53.0}/gea/tool_taxonomy.py +0 -0
  343. {gea_program-0.51.0 → gea_program-0.53.0}/gea/transient.py +0 -0
  344. {gea_program-0.51.0 → gea_program-0.53.0}/gea/vibration.py +0 -0
  345. {gea_program-0.51.0 → gea_program-0.53.0}/gea/well3d.py +0 -0
  346. {gea_program-0.51.0 → gea_program-0.53.0}/gea/well_assembler.py +0 -0
  347. {gea_program-0.51.0 → gea_program-0.53.0}/gea/well_report.py +0 -0
  348. {gea_program-0.51.0 → gea_program-0.53.0}/gea/well_test_validation.py +0 -0
  349. {gea_program-0.51.0 → gea_program-0.53.0}/gea/wellplan.py +0 -0
  350. {gea_program-0.51.0 → gea_program-0.53.0}/gea/wellpos.py +0 -0
  351. {gea_program-0.51.0 → gea_program-0.53.0}/gea/wits0.py +0 -0
  352. {gea_program-0.51.0 → gea_program-0.53.0}/gea/witsml.py +0 -0
  353. {gea_program-0.51.0 → gea_program-0.53.0}/gea/workspace.py +0 -0
  354. {gea_program-0.51.0 → gea_program-0.53.0}/gea/xlsx_read.py +0 -0
  355. {gea_program-0.51.0 → gea_program-0.53.0}/gea_program.egg-info/dependency_links.txt +0 -0
  356. {gea_program-0.51.0 → gea_program-0.53.0}/gea_program.egg-info/entry_points.txt +0 -0
  357. {gea_program-0.51.0 → gea_program-0.53.0}/gea_program.egg-info/requires.txt +0 -0
  358. {gea_program-0.51.0 → gea_program-0.53.0}/gea_program.egg-info/top_level.txt +0 -0
  359. {gea_program-0.51.0 → gea_program-0.53.0}/setup.cfg +0 -0
  360. {gea_program-0.51.0 → gea_program-0.53.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.51.0
3
+ Version: 0.53.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
@@ -648,6 +648,33 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
648
648
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
649
649
  `rtserve.iris.washington.edu:18000`, network `TX`.
650
650
 
651
+ ### Torque and drag
652
+
653
+ The hook load and torque the drill string should take at each bit depth, by
654
+ the soft-string model of Johancsik et al.: pick-up, slack-off and rotating
655
+ off bottom for the declared friction factors and a fan of others (the
656
+ broomstick), the torque rotating off bottom. Against them, the rig's own
657
+ hook loads and torque from the drilling record, read by the rig states -
658
+ tripping out and in, rotating off bottom - and the friction factors that fit
659
+ them, cased and open hole apart. Volve 15/9-F-10 with an example string is
660
+ the worked example. The **Torque & drag** tab, `gea torque-drag`,
661
+ `gea help torque-drag`.
662
+
663
+ ### Pore pressure
664
+
665
+ The mud-weight window from a well's logs or its drilling record. The
666
+ overburden from the density log (or Gardner's density from the sonic); the
667
+ shale's normal compaction trend over an interval the operator declares
668
+ normally pressured; the pore pressure by Eaton's method from the sonic, the
669
+ deep resistivity or the corrected d-exponent computed from the drilling
670
+ record; the fracture gradient by Eaton's relation and the Hubbert-Willis
671
+ minimum - all as equivalent mud weights against true vertical depth, with
672
+ formation tests, kicks and leak-off tests beside them, the Eaton exponent
673
+ that fits the tests, and the mud weight used flagged where it falls outside
674
+ the window. The Dutch offshore well L07-01 (NLOG open data) is the worked
675
+ example. The **Pore pressure** tab, `gea pore-pressure`,
676
+ `gea help pore-pressure`.
677
+
651
678
  ### Frac hits
652
679
 
653
680
  What each fracturing stage did to the pressure in the wells around it. A
@@ -609,6 +609,33 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
609
609
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
610
610
  `rtserve.iris.washington.edu:18000`, network `TX`.
611
611
 
612
+ ### Torque and drag
613
+
614
+ The hook load and torque the drill string should take at each bit depth, by
615
+ the soft-string model of Johancsik et al.: pick-up, slack-off and rotating
616
+ off bottom for the declared friction factors and a fan of others (the
617
+ broomstick), the torque rotating off bottom. Against them, the rig's own
618
+ hook loads and torque from the drilling record, read by the rig states -
619
+ tripping out and in, rotating off bottom - and the friction factors that fit
620
+ them, cased and open hole apart. Volve 15/9-F-10 with an example string is
621
+ the worked example. The **Torque & drag** tab, `gea torque-drag`,
622
+ `gea help torque-drag`.
623
+
624
+ ### Pore pressure
625
+
626
+ The mud-weight window from a well's logs or its drilling record. The
627
+ overburden from the density log (or Gardner's density from the sonic); the
628
+ shale's normal compaction trend over an interval the operator declares
629
+ normally pressured; the pore pressure by Eaton's method from the sonic, the
630
+ deep resistivity or the corrected d-exponent computed from the drilling
631
+ record; the fracture gradient by Eaton's relation and the Hubbert-Willis
632
+ minimum - all as equivalent mud weights against true vertical depth, with
633
+ formation tests, kicks and leak-off tests beside them, the Eaton exponent
634
+ that fits the tests, and the mud weight used flagged where it falls outside
635
+ the window. The Dutch offshore well L07-01 (NLOG open data) is the worked
636
+ example. The **Pore pressure** tab, `gea pore-pressure`,
637
+ `gea help pore-pressure`.
638
+
612
639
  ### Frac hits
613
640
 
614
641
  What each fracturing stage did to the pressure in the wells around it. A
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.51.0"
126
+ __version__ = "0.53.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -622,6 +622,36 @@ def main(argv=None) -> int:
622
622
  p_pl.add_argument("--actor", type=str, default="cli")
623
623
  p_pl.add_argument("--json", action="store_true")
624
624
 
625
+ p_td = sub.add_parser("torque-drag", help="the hook load and torque the string should take at each bit depth (soft-string model), the broomstick, and the friction factors that fit the rig's own trips")
626
+ p_td.add_argument("--workspace", type=str, default=None)
627
+ p_td.add_argument("--well", type=str, default=None)
628
+ p_td.add_argument("--example", action="store_true", help="the worked example (Volve 15/9-F-10 with an example string)")
629
+ p_td.add_argument("--string", type=str, default=None, help="sections from the bit up: 'BHA:300:6.75:95; drill pipe::5:22.6:6.625' (what:length_ft:od_in:lb_ft[:contact_od_in])")
630
+ p_td.add_argument("--mud", type=str, default=None, help="mud density, ppg")
631
+ p_td.add_argument("--mu-cased", type=str, default=None)
632
+ p_td.add_argument("--mu-open", type=str, default=None)
633
+ p_td.add_argument("--block", type=str, default=None, help="the travelling block's weight, klbf (estimated from the record when not declared)")
634
+ p_td.add_argument("--source", type=str, default="", help="where the string and its figures came from")
635
+ p_td.add_argument("--write", action="store_true")
636
+ p_td.add_argument("--actor", type=str, default="cli")
637
+ p_td.add_argument("--json", action="store_true")
638
+
639
+ p_pp = sub.add_parser("pore-pressure", help="the mud-weight window: overburden, pore pressure by Eaton from the sonic, the resistivity or the d-exponent, and the fracture gradient")
640
+ p_pp.add_argument("--workspace", type=str, default=None)
641
+ p_pp.add_argument("--well", type=str, default=None)
642
+ p_pp.add_argument("--example", action="store_true", help="the worked example (L07-01, public data)")
643
+ p_pp.add_argument("--indicator", type=str, default=None, help="auto, sonic, resistivity or dexponent")
644
+ for k_, h_ in (("exponent", "Eaton's exponent"), ("nct-top", "top of the normally pressured interval, m TVD"), ("nct-bottom", "its bottom, m TVD"),
645
+ ("gr-cutoff", "the shale cut-off, gAPI"), ("normal-gradient", "the normal pore gradient, psi/ft"), ("poisson", "Poisson's ratio"),
646
+ ("density-above", "the mean density above the logs, g/cm3"), ("water-depth", "the water depth, m"), ("air-gap", "the air gap, m"),
647
+ ("bit", "the bit size for the d-exponent, in")):
648
+ p_pp.add_argument("--" + k_, type=str, default=None, help=h_)
649
+ p_pp.add_argument("--measure", type=str, default=None, help="add a measurement DEPTH_M,VALUE[,psi|ppg[,pore|kick|lot|fit[,tvd|md]]]")
650
+ p_pp.add_argument("--source", type=str, default="", help="where the values came from")
651
+ p_pp.add_argument("--write", action="store_true", help="write the report")
652
+ p_pp.add_argument("--actor", type=str, default="cli")
653
+ p_pp.add_argument("--json", action="store_true")
654
+
625
655
  p_fh = sub.add_parser("frac-hits", help="what each fracturing stage did to the pressure in the wells around it: none, poroelastic, mixed or a frac hit, the first response and the volume pumped by then")
626
656
  p_fh.add_argument("--workspace", type=str, required=True)
627
657
  p_fh.add_argument("--well", type=str, default=None, help="the treatment well (with --stages or --remove) or the monitored well (with --channel)")
@@ -2456,6 +2486,72 @@ def main(argv=None) -> int:
2456
2486
  raise SystemExit(f"collision: {e.args[0] if e.args else e}")
2457
2487
  print(_json.dumps({k: v for k, v in r.items()}, default=str) if a.json else _CO.text(r))
2458
2488
  return 0
2489
+ elif a.cmd == "torque-drag":
2490
+ import json as _json
2491
+ from . import torque_drag as _TND
2492
+ from .cli import _safe_stdout
2493
+ from .workspace import Workspace, WorkspaceError
2494
+ _safe_stdout()
2495
+ try:
2496
+ ws = Workspace(a.workspace) if a.workspace else None
2497
+ scene = "example" if (a.example or ws is None) else "workspace"
2498
+ if ws is not None and any(v is not None for v in (a.string, a.mud, a.mu_cased, a.mu_open, a.block)):
2499
+ if not a.well:
2500
+ raise ValueError("--well is needed")
2501
+ c_ = _TND.set_well(ws, a.actor, a.well, a.source, _TND.parse_string(a.string) if a.string else None, a.mud, a.mu_cased, a.mu_open, a.block)
2502
+ print(f"{a.well}: " + "; ".join(f"{x['what']} {('to surface' if x['length_ft'] is None else format(x['length_ft'], ',.0f') + ' ft')}" for x in (c_.get('string') or []))
2503
+ + f"; mud {c_['mud_ppg']:g} ppg, mu cased {c_['mu_cased']:g}, open {c_['mu_open']:g}")
2504
+ return 0
2505
+ if a.write:
2506
+ if ws is None:
2507
+ raise ValueError("--write needs --workspace")
2508
+ r_ = _TND.write_report(ws, __version__, a.well, a.actor, scene)
2509
+ print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
2510
+ if r_['paths'].get('pdf'):
2511
+ print(f" printable: {r_['paths']['pdf']}")
2512
+ return 0
2513
+ r = _TND.page(ws, scene, a.well)
2514
+ except (KeyError, ValueError, OSError, WorkspaceError) as e:
2515
+ raise SystemExit(f"torque-drag: {e.args[0] if e.args else e}")
2516
+ print(_json.dumps(r, default=str) if a.json else _TND.text(r))
2517
+ return 0
2518
+ elif a.cmd == "pore-pressure":
2519
+ import json as _json
2520
+ from . import pore_pressure as _PPF
2521
+ from .cli import _safe_stdout
2522
+ from .workspace import Workspace, WorkspaceError
2523
+ _safe_stdout()
2524
+ try:
2525
+ ws = Workspace(a.workspace) if a.workspace else None
2526
+ scene = "example" if (a.example or ws is None) else "workspace"
2527
+ keys = {"exponent": a.exponent, "nct_top_m": a.nct_top, "nct_bottom_m": a.nct_bottom, "gr_cutoff": a.gr_cutoff,
2528
+ "normal_gradient_psi_ft": a.normal_gradient, "poisson": a.poisson, "density_above_gcc": a.density_above,
2529
+ "water_depth_m": a.water_depth, "air_gap_m": a.air_gap, "bit_in": a.bit, "indicator": a.indicator}
2530
+ if ws is not None and (any(v is not None for v in keys.values()) or a.measure):
2531
+ if not a.well:
2532
+ raise ValueError("--well is needed")
2533
+ if a.measure:
2534
+ p_ = [x.strip() for x in a.measure.split(",")]
2535
+ r_ = _PPF.add_measurement(ws, a.actor, a.well, p_[0], p_[1], p_[2] if len(p_) > 2 else "psi", p_[3] if len(p_) > 3 else "pore",
2536
+ p_[4] if len(p_) > 4 else "tvd", a.source)
2537
+ print(f"measurement kept for {a.well}: {r_['kind']} {r_['value']:g} {r_['unit']} at {r_['depth_m']:g} m {r_['depth_kind'].upper()}")
2538
+ return 0
2539
+ c_ = _PPF.set_well(ws, a.actor, a.well, a.source, **{k: v for k, v in keys.items() if v is not None})
2540
+ print(f"{a.well}: {c_['indicator']}, exponent {c_['exponent'] or 'Eaton'}, trend {c_['nct_top_m']} to {c_['nct_bottom_m']} m, normal {c_['normal_gradient_psi_ft']:g} psi/ft, Poisson {c_['poisson']:g}")
2541
+ return 0
2542
+ if a.write:
2543
+ if ws is None:
2544
+ raise ValueError("--write needs --workspace")
2545
+ r_ = _PPF.write_report(ws, __version__, a.well, a.actor, scene)
2546
+ print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
2547
+ if r_['paths'].get('pdf'):
2548
+ print(f" printable: {r_['paths']['pdf']}")
2549
+ return 0
2550
+ r = _PPF.page(ws, scene, a.well)
2551
+ except (KeyError, ValueError, OSError, WorkspaceError) as e:
2552
+ raise SystemExit(f"pore-pressure: {e.args[0] if e.args else e}")
2553
+ print(_json.dumps(r, default=str) if a.json else _PPF.text(r))
2554
+ return 0
2459
2555
  elif a.cmd == "frac-hits":
2460
2556
  import json as _json
2461
2557
  from . import frac_hits as _FH
@@ -11717,6 +11717,354 @@ def section_ct_frac_hits(tmp: str) -> None:
11717
11717
  + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
11718
11718
 
11719
11719
 
11720
+ def section_cu_pore_pressure(tmp: str) -> None:
11721
+ """Section CU - pore pressure and fracture gradient: the arithmetic (overburden, Gardner, Eaton, the d-exponent and
11722
+ its correction, Eaton's and Hubbert-Willis's fracture gradients) by hand; a depth log and a drilling record built so
11723
+ Eaton's method must return a known overpressure, read back by the sonic, the resistivity and the corrected
11724
+ d-exponent; measurements and the fitted exponent; the worked example; the API, the CLI, the report, the dossier,
11725
+ the tab and the help."""
11726
+ import gzip
11727
+ import hashlib as _hl
11728
+ import http.cookiejar
11729
+ import math as _m
11730
+ import subprocess as _sp
11731
+ import urllib.request
11732
+ import urllib.error
11733
+ import zipfile as _zf
11734
+ import numpy as _np
11735
+ from . import __version__ as _ver
11736
+ from . import dossier as DZ
11737
+ from . import helplib as H
11738
+ from . import pore_pressure as PPF
11739
+ from .workspace import Workspace
11740
+ from .service import Service, Users
11741
+ pkg = Path(__file__).parent
11742
+ d = Path(tmp, "porepressure"); d.mkdir()
11743
+
11744
+ # -- CU1 the arithmetic by hand ------------------------------------------------------------------------------------------
11745
+ dd = float(PPF.dexponent([30.0], [100.0], [20000.0], 8.5)[0])
11746
+ d_hand = _m.log10(30.0 / 6000.0) / _m.log10(12.0 * 20000.0 / 8.5e6)
11747
+ dc_ = float(PPF.dc([d_hand], 8.95, [10.0])[0])
11748
+ ob = PPF.overburden_psi(_np.array([1000.0, 2000.0]), _np.array([2.3, 2.3]), 0.0, 0.0, 1.03, 2.3)
11749
+ ob_sea = PPF.overburden_psi(_np.array([137.0, 1137.0]), _np.array([2.0, 2.0]), 100.0, 37.0, 1.03, 2.0)
11750
+ psi_hand = lambda rho, h: rho * 1000.0 * 9.80665 * h / 6894.757293168
11751
+ S_, Ph_ = 10000.0, 4650.0
11752
+ pp_s = float(PPF.eaton(S_, Ph_, 120.0, 100.0, 3.0, -1)) # sonic slower than its trend: overpressure
11753
+ pp_r = float(PPF.eaton(S_, Ph_, 0.8, 1.0, 1.2, 1)) # resistivity below its trend: overpressure
11754
+ c1 = {
11755
+ "dexp": abs(dd - d_hand) < 1e-12 and abs(d_hand - 1.48529) < 1e-4 and abs(dc_ - d_hand * 0.895) < 1e-12 and _np.isnan(PPF.dexponent([0.0], [100.0], [20000.0], 8.5)[0]),
11756
+ "overburden": abs(ob[0] - psi_hand(2.3, 1000.0)) < 1e-6 and abs(ob[1] - psi_hand(2.3, 2000.0)) < 1e-6
11757
+ and abs(ob_sea[0] - psi_hand(1.03, 100.0)) < 1e-6 and abs(ob_sea[1] - psi_hand(1.03, 100.0) - psi_hand(2.0, 1000.0)) < 1e-6,
11758
+ "gardner": abs(float(PPF.gardner(_np.array([100.0]))[0]) - 0.23 * 10000.0 ** 0.25) < 1e-12,
11759
+ "eaton": abs(pp_s - (S_ - (S_ - Ph_) * (100.0 / 120.0) ** 3)) < 1e-9 and pp_s > Ph_ and abs(pp_r - (S_ - (S_ - Ph_) * 0.8 ** 1.2)) < 1e-9
11760
+ and abs(float(PPF.eaton(S_, Ph_, 100.0, 100.0, 3.0, -1)) - Ph_) < 1e-9,
11761
+ "fracture": abs(float(PPF.eaton_fg(S_, 6000.0, 0.25)) - float(PPF.hubbert_willis(S_, 6000.0))) < 1e-9
11762
+ and abs(float(PPF.eaton_fg(S_, 6000.0, 0.4)) - (0.4 / 0.6 * 4000.0 + 6000.0)) < 1e-9,
11763
+ "ppg": abs(float(PPF.ppg(5200.0, 10000.0 * 0.3048)) - 10.0) < 1e-9,
11764
+ }
11765
+ failed1 = [k for k, v in c1.items() if not v]
11766
+ ok(not failed1,
11767
+ "CU1 the arithmetic by hand: Jorden and Shirley's d-exponent (1.4853 for 30 ft/h, 100 rpm, 20 klbf on an 8-1/2 in bit) and "
11768
+ "Rehm and McClendon's correction; the overburden of rock alone and of sea then rock below an air gap; Gardner's density; "
11769
+ "Eaton's ratio for the sonic and the resistivity, returning hydrostatic on the trend; Eaton's fracture gradient equal to "
11770
+ "Hubbert-Willis's at a Poisson's ratio of 0.25; an equivalent mud weight"
11771
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
11772
+
11773
+ # -- CU2 the labelled log and drilling record --------------------------------------------------------------------------
11774
+ f = PPF.selftest_files(str(d))
11775
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
11776
+ wl = ws.add_well_file(f["las"], display="LOG-1", actor="tester")["id"]
11777
+ we = ws.add_well_file(f["edr"], display="EDR-1", actor="tester")["id"]
11778
+
11779
+ def err_of(wid):
11780
+ x = PPF.page(ws, "workspace", wid)["result"]
11781
+ c = x["curves"]
11782
+ z = _np.array(c["tvd_m"], float)
11783
+ pp = _np.array([_np.nan if v is None else v for v in c["pp_psi"]], float)
11784
+ Pt = PPF.truth(z)[2]
11785
+ away = (_np.abs(z - 2200.0) > 10.0) & (_np.abs(z - 2600.0) > 10.0) & _np.isfinite(pp)
11786
+ return x, float(_np.max(_np.abs(pp[away] - Pt[away]) / Pt[away])), int(away.sum())
11787
+ PPF.set_well(ws, "tester", wl, "labelled log (test)", indicator="sonic", nct_top_m=600, nct_bottom_m=2100, smooth_m=5, density_above_gcc=2.3)
11788
+ xs, es, ns = err_of(wl)
11789
+ PPF.set_well(ws, "tester", wl, "labelled log (test)", indicator="resistivity")
11790
+ xr, er, nr = err_of(wl)
11791
+ no_bit = PPF.page(ws, "workspace", we)["detail"]
11792
+ PPF.set_well(ws, "tester", we, "labelled record (test)", bit_in=8.5, nct_top_m=1050, nct_bottom_m=2100, smooth_m=6, density_above_gcc=2.3)
11793
+ xd, ed, nd = err_of(we)
11794
+ c2 = {
11795
+ "sonic": es < 0.002 and ns > 300 and abs(xs["trend"]["a"] - 2.25) < 1e-4 and abs(xs["trend"]["b_per_m"] + 1.2e-4) < 1e-8
11796
+ and xs["overpressure_from_tvd_m"] is not None and abs(xs["overpressure_from_tvd_m"] - 2200.0) <= 5.0 and abs(xs["overburden_td_psi"] - float(PPF.truth(_np.array([3500.0]))[0][0])) < 1.0,
11797
+ "resistivity": er < 0.002 and nr > 300 and xr["indicator"] == "resistivity" and abs(xr["trend"]["b_per_m"] - 1.5e-4) < 1e-8,
11798
+ "dexponent": "bit size" in no_bit and ed < 0.002 and nd > 300 and xd["indicator"] == "dexponent" and "Mud Weight In" in xd["how"] and abs(xd["trend"]["b_per_m"] - 1.0e-4) < 2e-6,
11799
+ "mud_flag": any(fl["what"] == "the mud weight below the pore pressure" and abs(fl["from_tvd_m"] - 2200.0) < 6 and abs(fl["to_tvd_m"] - 2600.0) < 6 for fl in xd["flags"]),
11800
+ }
11801
+ failed2 = [k for k, v in c2.items() if not v]
11802
+ ok(not failed2,
11803
+ "CU2 a depth log and a drilling record built so that Eaton's method must find a pore pressure 40 % of the way from "
11804
+ "hydrostatic to the overburden between 2,200 and 2,600 m: read back by the sonic, the resistivity and the corrected "
11805
+ "d-exponent to within 0.2 % away from the zone's edges, the trends recovered as built, the overburden to the psi; the "
11806
+ "d-exponent refused without a bit size; the 9.5 ppg mud flagged below the pore pressure across the zone"
11807
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
11808
+
11809
+ # -- CU3 measurements, the fitted exponent, refusals and the worked example ----------------------------------------------
11810
+ PPF.set_well(ws, "tester", wl, "labelled log (test)", indicator="sonic")
11811
+ pt = float(PPF.truth(_np.array([2400.0]))[2][0])
11812
+ PPF.add_measurement(ws, "tester", wl, 2400.0, round(pt, 1), "psi", "pore", "tvd", "MDT (test)")
11813
+ PPF.add_measurement(ws, "tester", wl, 1500.0, 16.2, "ppg", "lot", "tvd", "LOT (test)")
11814
+ xm = PPF.page(ws, "workspace", wl)["result"]
11815
+ refused = 0
11816
+ for kw in ({"poisson": 0.6}, {"normal_gradient_psi_ft": 0.3}, {"nct_top_m": 2000, "nct_bottom_m": 1000}, {"indicator": "magic"}, {"exponent": 2.0}):
11817
+ try:
11818
+ PPF.set_well(ws, "tester", wl, "" if "exponent" in kw else "x", **kw)
11819
+ except ValueError:
11820
+ refused += 1
11821
+ for args in ((wl, -5, 100), (wl, 1000, 100, "bar"), (wl, 1000, 100, "psi", "dst"), (wl, 1000, 100, "psi", "pore", "tvd", "")):
11822
+ try:
11823
+ PPF.add_measurement(ws, "tester", args[0], args[1], args[2], *(args[3:5] or ()), **({"depth_kind": args[5], "source": args[6]} if len(args) > 6 else {"source": "s"}))
11824
+ except ValueError:
11825
+ refused += 1
11826
+ ex = PPF.example()
11827
+ raw = gzip.open(pkg / "reference" / "l0701_sonic_gr.las.gz").read()
11828
+ prov = json.loads((pkg / "reference" / "l0701_sonic_gr.provenance.json").read_text(encoding="utf-8"))
11829
+ c3 = {
11830
+ "measured": len(xm["measured"]) == 2 and abs(xm["measured"][0]["computed_psi"] - pt) < 0.005 * pt and xm["fit_exponent"] is not None and abs(xm["fit_exponent"] - 3.0) < 0.06
11831
+ and abs(xm["measured"][1]["psi"] - 16.2 * 0.052 * 1500.0 / 0.3048) < 0.1 and xm["measured"][1]["kind"] == "lot",
11832
+ "refused": refused == 9,
11833
+ "example": ex["indicator"] == "sonic" and ex["channel"] == "DT" and ex["trend"]["points"] > 5000 and 18.0 < ex["overburden_td_ppg"] < 21.0
11834
+ and "Gardner from the sonic" in ex["density_from"] and "density log" in ex["density_from"] and ex["max_pp_ppg"] is not None and "NLOG" in ex["attribution"],
11835
+ "provenance": _hl.sha256(raw).hexdigest() == prov["sha256_uncompressed"] and _hl.sha256((pkg / "reference" / "l0701_sonic_gr.las.gz").read_bytes()).hexdigest() == prov["sha256"],
11836
+ }
11837
+ failed3 = [k for k, v in c3.items() if not v]
11838
+ ok(not failed3,
11839
+ "CU3 a formation test drawn beside the curve it confirms (to half a percent) and the Eaton exponent fitted to it (3); a "
11840
+ "leak-off test in ppg turned to psi at its depth; a Poisson's ratio, a normal gradient, a trend interval or an indicator "
11841
+ "out of range, a value without its source, and a measurement without a depth, a unit, a kind or a source refused; the "
11842
+ "worked example L07-01 read from its sonic with the overburden from its density log and Gardner's, the file as hashed"
11843
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
11844
+
11845
+ # -- CU4 the API, the CLI, the report, the dossier, the tab and the help -------------------------------------------------
11846
+ Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
11847
+ svc = Service(wsp, port=0, scheduler=False).start()
11848
+ base_url = svc.url.rstrip("/")
11849
+
11850
+ def opener(name, pw_):
11851
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
11852
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
11853
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
11854
+ return o
11855
+
11856
+ def call(o, method, path, body=None):
11857
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
11858
+ if method == "POST":
11859
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
11860
+ try:
11861
+ with o.open(rq, timeout=120) as rr_:
11862
+ return rr_.status, json.loads(rr_.read())
11863
+ except urllib.error.HTTPError as e:
11864
+ return e.code, json.loads(e.read())
11865
+ try:
11866
+ V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
11867
+ s_vg, vg = call(V, "GET", "/api/pore-pressure?well=" + wl)
11868
+ s_ve, ve = call(V, "GET", "/api/pore-pressure?scene=example")
11869
+ s_vs, _ = call(V, "POST", "/api/pore-pressure/settings", {"well_id": wl, "poisson": 0.3, "source": "x"})
11870
+ s_os, os_ = call(O, "POST", "/api/pore-pressure/settings", {"well_id": wl, "poisson": "0.3", "source": "frac gradient study (test)"})
11871
+ s_ob, _ = call(O, "POST", "/api/pore-pressure/settings", {"well_id": wl, "poisson": 0.7, "source": "x"})
11872
+ s_om, _ = call(O, "POST", "/api/pore-pressure/measure", {"well_id": wl, "depth_m": 3000, "value": 9.0, "unit": "ppg", "kind": "kick", "source": "kick (test)"})
11873
+ s_bm, _ = call(O, "POST", "/api/pore-pressure/measure", {"well_id": wl, "depth_m": 3000, "value": 9.0, "unit": "ppg", "kind": "kick", "source": ""})
11874
+ s_ur, _ = call(O, "POST", "/api/pore-pressure/unmeasure", {"well_id": wl, "index": 2})
11875
+ s_u2, _ = call(O, "POST", "/api/pore-pressure/unmeasure", {"well_id": wl, "index": 9})
11876
+ s_vw, _ = call(V, "POST", "/api/pore-pressure/write", {"well_id": wl})
11877
+ s_ow, ow = call(O, "POST", "/api/pore-pressure/write", {"well_id": wl})
11878
+ finally:
11879
+ svc.stop()
11880
+ cli_x = _sp.run([sys.executable, "-m", "gea", "pore-pressure", "--example"], capture_output=True, text=True, cwd=str(pkg.parent))
11881
+ cli_s = _sp.run([sys.executable, "-m", "gea", "pore-pressure", "--workspace", wsp, "--well", wl, "--gr-cutoff", "90", "--source", "shale cut-off (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
11882
+ cli_m = _sp.run([sys.executable, "-m", "gea", "pore-pressure", "--workspace", wsp, "--well", wl, "--measure", "2500,13.0,ppg,pore,tvd", "--source", "RFT (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
11883
+ cli_t = _sp.run([sys.executable, "-m", "gea", "pore-pressure", "--workspace", wsp, "--well", wl], capture_output=True, text=True, cwd=str(pkg.parent))
11884
+ dz = DZ.build(ws, "tester", _ver, None)
11885
+ names = _zf.ZipFile(Path(ws.reports_dir, dz["path"])).namelist()
11886
+ audit = Path(ws.audit_path).read_text(encoding="utf-8")
11887
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
11888
+ c4 = {
11889
+ "api": s_vg == 200 and vg["result"]["indicator"] == "sonic" and s_ve == 200 and ve["result"]["well"] == "L07-01" and s_vs == 403 and s_os == 200 and os_["poisson"] == 0.3
11890
+ and s_ob == 400 and s_om == 200 and s_bm == 400 and s_ur == 200 and s_u2 == 404 and s_vw == 403 and s_ow == 200 and ow["report_id"].startswith("PPF-"),
11891
+ "cli": cli_x.returncode == 0 and "L07-01" in cli_x.stdout and cli_s.returncode == 0 and cli_m.returncode == 0 and "pore 13 ppg at 2500 m TVD" in cli_m.stdout
11892
+ and cli_t.returncode == 0 and "gamma ray >= 90" in cli_t.stdout and "the exponent that fits" in cli_t.stdout,
11893
+ "report": PR_read_ok(Path(ws.reports_dir, "wells", wl, "pore_pressure.pdf"))
11894
+ and all(x in audit for x in ("pore_pressure.settings", "pore_pressure.measurement", "pore_pressure.measurement_removed", "report.pore_pressure")),
11895
+ "dossier": "declarations/geomech/pore_pressure.json" in names and f"results/pore_pressure/{wl}.json" in names and f"results/pore_pressure/{we}.json" in names,
11896
+ "page": all(x in page_src for x in ("VIEWS.porepressure", "['#/porepressure', 'Pore pressure']", "/api/pore-pressure/settings", "ppDepthPlot", "ppTrendPlot")),
11897
+ "help": not H.check("pore-pressure") and H.VIEW_TOPIC.get("porepressure") == "pore-pressure",
11898
+ }
11899
+ failed4 = [k for k, v in c4.items() if not v]
11900
+ ok(not failed4,
11901
+ "CU4 on the API (anyone reads a well's window and the worked example; only an operator declares how a well is read - a "
11902
+ "Poisson's ratio of 0.7 refused - adds or removes a measurement - one without its source refused - or writes the report), "
11903
+ "the CLI (the example, a cut-off, a measurement, the well), the report as PDF, a signed dossier carrying the declarations "
11904
+ "and each declared well's window, the tab and the help"
11905
+ + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
11906
+
11907
+
11908
+ def section_cv_torque_drag(tmp: str) -> None:
11909
+ """Section CV - torque and drag: the soft-string model against closed forms (a vertical string, a straight inclined
11910
+ one pulled and rotated, the capstan over a bend in the vertical plane and in azimuth); a drilling record made by the
11911
+ model with known friction factors, read back by the rig states and fitted to them; the worked example; the API, the
11912
+ CLI, the report, the dossier, the tab and the help."""
11913
+ import http.cookiejar
11914
+ import math as _m
11915
+ import subprocess as _sp
11916
+ import urllib.request
11917
+ import urllib.error
11918
+ import zipfile as _zf
11919
+ import numpy as _np
11920
+ from . import __version__ as _ver
11921
+ from . import dossier as DZ
11922
+ from . import helplib as H
11923
+ from . import rig_state as RS
11924
+ from . import torque_drag as TND
11925
+ from . import well3d as W
11926
+ from .workspace import Workspace
11927
+ from .service import Service, Users
11928
+ pkg = Path(__file__).parent
11929
+ d = Path(tmp, "torquedrag"); d.mkdir()
11930
+
11931
+ # -- CV1 the model against closed forms ------------------------------------------------------------------------------
11932
+ md = _np.linspace(0.0, 10000.0, 101); n = len(md)
11933
+ bf = TND.buoyancy(10.0)
11934
+ Fv, _ = TND.soft_string(md, _np.zeros(n), _np.zeros(n), _np.full(n - 1, 20.0 * bf), _np.full(n - 1, 3.3125), _np.full(n - 1, 0.3), "out")
11935
+ s60, c60 = _m.sin(_m.radians(60)), _m.cos(_m.radians(60))
11936
+ Fo, _ = TND.soft_string(md, _np.full(n, 60.0), _np.zeros(n), _np.full(n - 1, 20.0), _np.full(n - 1, 3.0), _np.full(n - 1, 0.3), "out")
11937
+ Fi, _ = TND.soft_string(md, _np.full(n, 60.0), _np.zeros(n), _np.full(n - 1, 20.0), _np.full(n - 1, 3.0), _np.full(n - 1, 0.3), "in")
11938
+ Fr, Mr = TND.soft_string(md, _np.full(n, 60.0), _np.zeros(n), _np.full(n - 1, 20.0), _np.full(n - 1, 3.0), _np.full(n - 1, 0.3), "rotate")
11939
+ N_ = 9001
11940
+ mc = _np.linspace(0.0, 1000.0, N_)
11941
+ Fc, _ = TND.soft_string(mc, _np.linspace(0.0, 90.0, N_), _np.zeros(N_), _np.zeros(N_ - 1), _np.full(N_ - 1, 3.0), _np.full(N_ - 1, 0.3), "out", 10000.0)
11942
+ Fa, _ = TND.soft_string(mc, _np.full(N_, 90.0), _np.linspace(0.0, 90.0, N_), _np.zeros(N_ - 1), _np.full(N_ - 1, 3.0), _np.full(N_ - 1, 0.3), "out", 10000.0)
11943
+ cap = 10000.0 * _m.exp(0.3 * _m.pi / 2)
11944
+ refused = 0
11945
+ for bad in ([], [{"what": "x", "length_ft": None, "od_in": 5, "weight_lb_ft": 20}, {"what": "y", "length_ft": None, "od_in": 5, "weight_lb_ft": 20}],
11946
+ [{"what": "x", "length_ft": 100, "od_in": 0.5, "weight_lb_ft": 20}], [{"what": "x", "length_ft": 100, "od_in": 5, "weight_lb_ft": 900}],
11947
+ [{"what": "x", "length_ft": 100, "od_in": 6, "weight_lb_ft": 20, "contact_od_in": 5}]):
11948
+ try:
11949
+ TND.check_string(bad)
11950
+ except ValueError:
11951
+ refused += 1
11952
+ c1 = {
11953
+ "vertical": abs(Fv[0] - 20.0 * bf * 10000.0) < 1e-6 and abs(bf - (1 - 10.0 / 65.5)) < 1e-12,
11954
+ "inclined": abs(Fo[0] - 200000.0 * (c60 + 0.3 * s60)) < 1e-6 and abs(Fi[0] - 200000.0 * (c60 - 0.3 * s60)) < 1e-6 and abs(Fr[0] - 200000.0 * c60) < 1e-6
11955
+ and abs(Mr[0] - 0.3 * 200000.0 * s60 * 3.0 / 12.0) < 1e-6,
11956
+ "capstan": abs(Fc[0] / cap - 1) < 5e-5 and abs(Fa[0] / cap - 1) < 5e-5,
11957
+ "string": refused == 5 and TND.parse_string("BHA:300:6.75:95; dp::5:22.6:6.625")[1]["length_ft"] is None,
11958
+ }
11959
+ failed1 = [k for k, v in c1.items() if not v]
11960
+ ok(not failed1,
11961
+ "CV1 the soft-string model against closed forms: a vertical string weighs its buoyed weight; a straight 60-degree string "
11962
+ "pulled, run and rotated gives W(cos + mu sin), W(cos - mu sin), W cos and the torque mu W sin r exactly; a weightless string "
11963
+ "over a 90-degree bend, in the vertical plane or in azimuth, comes to the capstan's e^(mu theta) as the elements shrink; a "
11964
+ "string without sections, with two open-ended, or with a diameter, weight or contact diameter out of range refused"
11965
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
11966
+
11967
+ # -- CV2 a record made by the model, read back ---------------------------------------------------------------------------
11968
+ f = TND.selftest_files(str(d))
11969
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
11970
+ wid = ws.add_well_file(f["record"], display="TD-1", actor="tester")["id"]
11971
+ ws.set_well(wid, surface={"lat": 31.42, "lon": -104.10, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
11972
+ no_survey = TND.page(ws, "workspace", wid)["detail"]
11973
+ W.attach_trajectory(ws, wid, "s.csv", Path(f["survey"]).read_bytes(), "tester", "true", "ft", None, None, None, "test survey")
11974
+ no_string = TND.page(ws, "workspace", wid)["detail"]
11975
+ RS.set_casing(ws, "tester", wid, "9-5/8", 4000, "tally (test)")
11976
+ TND.set_well(ws, "tester", wid, "BHA report (test)", f["string"], 10.0, 0.25, 0.30)
11977
+ r = TND.page(ws, "workspace", wid)["result"]
11978
+ fi = r["fits"]
11979
+ m_ = r["measured"]
11980
+ c2 = {
11981
+ "needs": "survey" in no_survey and "string" in no_string,
11982
+ "measured": len(m_["out"]["depth_ft"]) >= 70 and len(m_["in"]["depth_ft"]) >= 70 and len(m_["rotate"]["depth_ft"]) == 9 and abs(r["block_klbf"] - 40.0) < 0.5 and m_["block_estimated"],
11983
+ "open": all(abs(fi[k]["mu"] - 0.24) <= 0.02 for k in ("out:open", "in:open", "torque:open")),
11984
+ "cased": all(abs(fi[k]["mu"] - 0.18) <= 0.02 for k in ("out:cased", "in:cased", "torque:cased")),
11985
+ "weights": abs(r["string_air_klbf"] - (300 * 95.0 + (r["td_ft"] - 300) * 22.6) / 1000.0) < 0.05 and r["shoe_ft"] == 4000.0,
11986
+ }
11987
+ failed2 = [k for k, v in c2.items() if not v]
11988
+ ok(not failed2,
11989
+ "CV2 a drilling record made by the model - a trip out and in stand by stand, the string rotated off bottom every 900 ft, "
11990
+ "friction 0.18 cased above a 4,000 ft shoe and 0.24 open, a 40 klbf block, noise - read back by the rig states: the block "
11991
+ "estimated to half a klbf, the pick-up, slack-off and torque each fitting 0.18 cased and 0.24 open to within 0.02; a well "
11992
+ "without a survey or a string says what it needs"
11993
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
11994
+
11995
+ # -- CV3 the worked example -----------------------------------------------------------------------------------------------
11996
+ ex = TND.example()
11997
+ de = ex["declared"]
11998
+ c3 = {
11999
+ "example": ex["well"] == "15/9-F-10" and abs(ex["td_ft"] - 5300.07 / 0.3048) < 5.0 and ex["max_inc_deg"] > 80
12000
+ and all(de["out"][i] >= de["rotate"][i] >= de["in"][i] for i in range(len(de["depth_ft"])))
12001
+ and de["torque"][-1] > de["torque"][len(de["torque"]) // 2] > 0 and "not from Volve" in ex["config"]["source"],
12002
+ "fan": sorted(ex["fan"]) == ["0.1", "0.2", "0.3", "0.4", "0.5"] and ex["fan"]["0.5"]["out"][-1] > ex["fan"]["0.1"]["out"][-1] and ex["fan"]["0.5"]["in"][-1] < ex["fan"]["0.1"]["in"][-1],
12003
+ }
12004
+ failed3 = [k for k, v in c3.items() if not v]
12005
+ ok(not failed3,
12006
+ "CV3 the worked example, Volve 15/9-F-10's survey to its 5,300 m TD with a string declared for the example: pick-up above "
12007
+ "rotating above slack-off at every depth, the torque growing with depth, the broomstick fanning out with the friction factor"
12008
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
12009
+
12010
+ # -- CV4 the API, the CLI, the report, the dossier, the tab and the help -------------------------------------------------
12011
+ Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
12012
+ svc = Service(wsp, port=0, scheduler=False).start()
12013
+ base_url = svc.url.rstrip("/")
12014
+
12015
+ def opener(name, pw_):
12016
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
12017
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
12018
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
12019
+ return o
12020
+
12021
+ def call(o, method, path, body=None):
12022
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
12023
+ if method == "POST":
12024
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
12025
+ try:
12026
+ with o.open(rq, timeout=120) as rr_:
12027
+ return rr_.status, json.loads(rr_.read())
12028
+ except urllib.error.HTTPError as e:
12029
+ return e.code, json.loads(e.read())
12030
+ try:
12031
+ V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
12032
+ s_vg, vg = call(V, "GET", "/api/torque-drag?well=" + wid)
12033
+ s_ve, ve = call(V, "GET", "/api/torque-drag?scene=example")
12034
+ s_vs, _ = call(V, "POST", "/api/torque-drag/settings", {"well_id": wid, "mud_ppg": 11, "source": "x"})
12035
+ s_os, os_ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "mud_ppg": "11", "string": "BHA:300:6.75:95:6.75; dp::5:22.6:6.625", "source": "mud report (test)"})
12036
+ s_bs, _ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "string": "BHA:300", "source": "x"})
12037
+ s_bm, _ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "mu_open": 0.9, "source": "x"})
12038
+ s_ns, _ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "mud_ppg": 12})
12039
+ s_vw, _ = call(V, "POST", "/api/torque-drag/write", {"well_id": wid})
12040
+ s_ow, ow = call(O, "POST", "/api/torque-drag/write", {"well_id": wid})
12041
+ finally:
12042
+ svc.stop()
12043
+ cli_x = _sp.run([sys.executable, "-m", "gea", "torque-drag", "--example"], capture_output=True, text=True, cwd=str(pkg.parent))
12044
+ cli_s = _sp.run([sys.executable, "-m", "gea", "torque-drag", "--workspace", wsp, "--well", wid, "--mu-open", "0.26", "--source", "offset well (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
12045
+ cli_t = _sp.run([sys.executable, "-m", "gea", "torque-drag", "--workspace", wsp, "--well", wid], capture_output=True, text=True, cwd=str(pkg.parent))
12046
+ dz = DZ.build(ws, "tester", _ver, None)
12047
+ names = _zf.ZipFile(Path(ws.reports_dir, dz["path"])).namelist()
12048
+ audit = Path(ws.audit_path).read_text(encoding="utf-8")
12049
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
12050
+ c4 = {
12051
+ "api": s_vg == 200 and vg["result"]["fits"] and s_ve == 200 and ve["result"]["well"] == "15/9-F-10" and s_vs == 403 and s_os == 200 and os_["mud_ppg"] == 11.0
12052
+ and len(os_["string"]) == 2 and s_bs == 400 and s_bm == 400 and s_ns == 400 and s_vw == 403 and s_ow == 200 and ow["report_id"].startswith("TND-"),
12053
+ "cli": cli_x.returncode == 0 and "15/9-F-10" in cli_x.stdout and cli_s.returncode == 0 and "open 0.26" in cli_s.stdout and cli_t.returncode == 0
12054
+ and "fits mu" in cli_t.stdout and "mud 11 ppg" in cli_t.stdout,
12055
+ "report": PR_read_ok(Path(ws.reports_dir, "wells", wid, "torque_drag.pdf")) and "torque_drag.declare" in audit and "report.torque_drag" in audit,
12056
+ "dossier": "declarations/drillstring/torque_drag.json" in names and f"results/torque_drag/{wid}.json" in names,
12057
+ "page": all(x in page_src for x in ("VIEWS.torquedrag", "['#/torquedrag', 'Torque & drag']", "/api/torque-drag/settings", "tdChart")),
12058
+ "help": not H.check("torque-drag") and H.VIEW_TOPIC.get("torquedrag") == "torque-drag",
12059
+ }
12060
+ failed4 = [k for k, v in c4.items() if not v]
12061
+ ok(not failed4,
12062
+ "CV4 on the API (anyone reads a well's model and the worked example; only an operator declares the string, the mud and the "
12063
+ "friction factors - a section without its figures, a friction factor of 0.9 or a change without its source refused - or "
12064
+ "writes the report), the CLI, the report as PDF, a signed dossier carrying the string and the model, the tab and the help"
12065
+ + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
12066
+
12067
+
11720
12068
  def PR_read_ok(path) -> bool:
11721
12069
  from . import pdf_report as PR
11722
12070
  try:
@@ -11826,6 +12174,8 @@ def main() -> int:
11826
12174
  section_cr_collision(tmp)
11827
12175
  section_cs_plan_and_tools(tmp)
11828
12176
  section_ct_frac_hits(tmp)
12177
+ section_cu_pore_pressure(tmp)
12178
+ section_cv_torque_drag(tmp)
11829
12179
  if _FAILS:
11830
12180
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
11831
12181
  for f in _FAILS:
@@ -277,7 +277,8 @@ def _gather(ws, well_id: Optional[str]) -> Dict[str, List[tuple]]:
277
277
  ('seismicity', 'catalogs.json'), ('seismicity', 'settings.json'), ('config', 'fleet.json'), ('workspace.json',),
278
278
  ('economics', 'invoices', 'terms.json'), ('economics', 'invoices', 'files.json'), ('drillstring', 'rig_time.json'),
279
279
  ('drillstring', 'casing.json'), ('wellpath', 'collision.json'), ('wellpath', 'survey_program.json'),
280
- ('completion', 'frac', 'stages.json'), ('completion', 'frac', 'settings.json')):
280
+ ('completion', 'frac', 'stages.json'), ('completion', 'frac', 'settings.json'), ('geomech', 'pore_pressure.json'),
281
+ ('drillstring', 'torque_drag.json')):
281
282
  p = os.path.join(ws.path, *rel)
282
283
  if os.path.isfile(p):
283
284
  out_list.append((p, 'declarations/' + '/'.join(rel)))
@@ -299,8 +300,8 @@ def _gather(ws, well_id: Optional[str]) -> Dict[str, List[tuple]]:
299
300
 
300
301
  def _results(ws, well_id: Optional[str]) -> List[tuple]:
301
302
  """The invoice cross-check of each well with invoices or reports attached, the rig time of each well with a drilling
302
- record, the collision check, each surveyed well against its plan and the frac-hit check, as JSON computed when the
303
- dossier is signed (a check that cannot run is left out, and says why)."""
303
+ record, the collision check, each surveyed well against its plan, each declared well's pore pressure and torque and
304
+ drag, and the frac-hit check, as JSON computed when the dossier is signed (a check that cannot run is left out, and says why)."""
304
305
  out = []
305
306
  wells = [well_id] if well_id else [w['id'] for w in ws.wells()]
306
307
  try:
@@ -338,6 +339,28 @@ def _results(ws, well_id: Optional[str]) -> List[tuple]:
338
339
  indent=1, sort_keys=True, default=str).encode('utf-8')))
339
340
  except Exception as e:
340
341
  out.append(('results/collision_not_run.txt', f'the collision check did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
342
+ try:
343
+ from . import torque_drag as TND
344
+ declared = (TND._load(ws).get('wells') or {})
345
+ for wid in wells:
346
+ if wid in declared:
347
+ r = TND.page(ws, 'workspace', wid).get('result')
348
+ if r:
349
+ out.append((f'results/torque_drag/{wid}.json', json.dumps({k: v for k, v in r.items() if k != 'fan'}, indent=1, sort_keys=True,
350
+ default=str).encode('utf-8')))
351
+ except Exception as e:
352
+ out.append(('results/torque_drag_not_run.txt', f'the torque-and-drag model did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
353
+ try:
354
+ from . import pore_pressure as PPF
355
+ declared = (PPF._load(ws).get('wells') or {})
356
+ for wid in wells:
357
+ if wid in declared:
358
+ r = PPF.page(ws, 'workspace', wid).get('result')
359
+ if r:
360
+ out.append((f'results/pore_pressure/{wid}.json', json.dumps({k: v for k, v in r.items() if k != 'points'}, indent=1, sort_keys=True,
361
+ default=str).encode('utf-8')))
362
+ except Exception as e:
363
+ out.append(('results/pore_pressure_not_run.txt', f'the pore-pressure reading did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
341
364
  try:
342
365
  from . import frac_hits as FH
343
366
  if FH.stages(ws):