gea-program 0.56.0__tar.gz → 0.58.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 (370) hide show
  1. {gea_program-0.56.0/gea_program.egg-info → gea_program-0.58.0}/PKG-INFO +27 -2
  2. {gea_program-0.56.0 → gea_program-0.58.0}/README.md +26 -1
  3. {gea_program-0.56.0 → gea_program-0.58.0}/gea/__init__.py +1 -1
  4. {gea_program-0.56.0 → gea_program-0.58.0}/gea/__main__.py +67 -0
  5. {gea_program-0.56.0 → gea_program-0.58.0}/gea/acceptance_tests.py +344 -0
  6. {gea_program-0.56.0 → gea_program-0.58.0}/gea/cli.py +14 -1
  7. {gea_program-0.56.0 → gea_program-0.58.0}/gea/dossier.py +12 -1
  8. gea_program-0.58.0/gea/help/IT_SECURITY.md +138 -0
  9. gea_program-0.58.0/gea/help/hydraulics.md +19 -0
  10. {gea_program-0.56.0 → gea_program-0.58.0}/gea/helplib.py +10 -1
  11. gea_program-0.58.0/gea/hydraulics.py +739 -0
  12. {gea_program-0.56.0 → gea_program-0.58.0}/gea/service.py +17 -0
  13. {gea_program-0.56.0 → gea_program-0.58.0}/gea/web/app.html +78 -1
  14. {gea_program-0.56.0 → gea_program-0.58.0/gea_program.egg-info}/PKG-INFO +27 -2
  15. {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/SOURCES.txt +3 -0
  16. {gea_program-0.56.0 → gea_program-0.58.0}/pyproject.toml +1 -1
  17. {gea_program-0.56.0 → gea_program-0.58.0}/LICENSE +0 -0
  18. {gea_program-0.56.0 → gea_program-0.58.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  19. {gea_program-0.56.0 → gea_program-0.58.0}/gea/accuracy_statement.py +0 -0
  20. {gea_program-0.56.0 → gea_program-0.58.0}/gea/alarm_engine.py +0 -0
  21. {gea_program-0.56.0 → gea_program-0.58.0}/gea/alerts.py +0 -0
  22. {gea_program-0.56.0 → gea_program-0.58.0}/gea/backup.py +0 -0
  23. {gea_program-0.56.0 → gea_program-0.58.0}/gea/bench.py +0 -0
  24. {gea_program-0.56.0 → gea_program-0.58.0}/gea/blind_harness.py +0 -0
  25. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  26. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  27. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  28. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  29. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  30. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  31. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  32. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  33. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  34. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  35. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  36. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  37. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  38. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  39. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  40. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  41. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  42. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  43. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  44. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  45. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  46. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  47. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  48. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  49. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  50. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  51. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  52. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  53. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  54. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  55. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  56. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  57. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  58. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  59. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  60. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  61. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  62. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  63. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  64. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  65. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  66. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  67. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  68. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  69. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  70. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  71. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  72. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  73. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  74. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  75. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  76. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  77. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  78. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  79. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  80. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  81. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  82. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  83. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  84. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  85. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  86. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  87. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  88. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  89. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  90. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  91. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  92. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/med_160_sapropels.txt +0 -0
  93. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  94. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  95. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  96. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  97. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  98. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  99. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  100. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  101. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  102. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  103. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  104. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  105. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  106. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  107. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  108. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  109. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  110. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  111. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  112. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  113. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  114. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  115. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  116. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  117. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  118. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  119. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  120. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  121. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  122. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  123. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  124. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  125. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  126. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  127. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  128. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  129. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog_map.py +0 -0
  130. {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog_register.py +0 -0
  131. {gea_program-0.56.0 → gea_program-0.58.0}/gea/certificates.py +0 -0
  132. {gea_program-0.56.0 → gea_program-0.58.0}/gea/client_reports.py +0 -0
  133. {gea_program-0.56.0 → gea_program-0.58.0}/gea/collision.py +0 -0
  134. {gea_program-0.56.0 → gea_program-0.58.0}/gea/config_versioning.py +0 -0
  135. {gea_program-0.56.0 → gea_program-0.58.0}/gea/conformance.py +0 -0
  136. {gea_program-0.56.0 → gea_program-0.58.0}/gea/control_room.py +0 -0
  137. {gea_program-0.56.0 → gea_program-0.58.0}/gea/correlation.py +0 -0
  138. {gea_program-0.56.0 → gea_program-0.58.0}/gea/dashboard.py +0 -0
  139. {gea_program-0.56.0 → gea_program-0.58.0}/gea/declarations.py +0 -0
  140. {gea_program-0.56.0 → gea_program-0.58.0}/gea/decline.py +0 -0
  141. {gea_program-0.56.0 → gea_program-0.58.0}/gea/deviation.py +0 -0
  142. {gea_program-0.56.0 → gea_program-0.58.0}/gea/dlis.py +0 -0
  143. {gea_program-0.56.0 → gea_program-0.58.0}/gea/dnp3.py +0 -0
  144. {gea_program-0.56.0 → gea_program-0.58.0}/gea/doctor.py +0 -0
  145. {gea_program-0.56.0 → gea_program-0.58.0}/gea/dossier_verify.py +0 -0
  146. {gea_program-0.56.0 → gea_program-0.58.0}/gea/downhole_engine.py +0 -0
  147. {gea_program-0.56.0 → gea_program-0.58.0}/gea/drift_monitor.py +0 -0
  148. {gea_program-0.56.0 → gea_program-0.58.0}/gea/drill_string.py +0 -0
  149. {gea_program-0.56.0 → gea_program-0.58.0}/gea/earth_model.py +0 -0
  150. {gea_program-0.56.0 → gea_program-0.58.0}/gea/economics.py +0 -0
  151. {gea_program-0.56.0 → gea_program-0.58.0}/gea/ed25519.py +0 -0
  152. {gea_program-0.56.0 → gea_program-0.58.0}/gea/edr.py +0 -0
  153. {gea_program-0.56.0 → gea_program-0.58.0}/gea/elevation.py +0 -0
  154. {gea_program-0.56.0 → gea_program-0.58.0}/gea/etp.py +0 -0
  155. {gea_program-0.56.0 → gea_program-0.58.0}/gea/etp12_schemas.json +0 -0
  156. {gea_program-0.56.0 → gea_program-0.58.0}/gea/example_register_map.json +0 -0
  157. {gea_program-0.56.0 → gea_program-0.58.0}/gea/fat_sat.py +0 -0
  158. {gea_program-0.56.0 → gea_program-0.58.0}/gea/files.py +0 -0
  159. {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/README.md +0 -0
  160. {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  161. {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  162. {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  163. {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  164. {gea_program-0.56.0 → gea_program-0.58.0}/gea/follower.py +0 -0
  165. {gea_program-0.56.0 → gea_program-0.58.0}/gea/forward_model.py +0 -0
  166. {gea_program-0.56.0 → gea_program-0.58.0}/gea/frac_hits.py +0 -0
  167. {gea_program-0.56.0 → gea_program-0.58.0}/gea/friction_priors.py +0 -0
  168. {gea_program-0.56.0 → gea_program-0.58.0}/gea/gamma.py +0 -0
  169. {gea_program-0.56.0 → gea_program-0.58.0}/gea/gauge_aging.py +0 -0
  170. {gea_program-0.56.0 → gea_program-0.58.0}/gea/gauge_specs.py +0 -0
  171. {gea_program-0.56.0 → gea_program-0.58.0}/gea/geodesy.py +0 -0
  172. {gea_program-0.56.0 → gea_program-0.58.0}/gea/geomag.py +0 -0
  173. {gea_program-0.56.0 → gea_program-0.58.0}/gea/gravity_reference.py +0 -0
  174. {gea_program-0.56.0 → gea_program-0.58.0}/gea/hartip.py +0 -0
  175. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/TESTER_GUIDE.md +0 -0
  176. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/alarms.md +0 -0
  177. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/alerts.md +0 -0
  178. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/audit-update.md +0 -0
  179. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/backup.md +0 -0
  180. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/catalog.md +0 -0
  181. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/charts.md +0 -0
  182. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/collision.md +0 -0
  183. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/company-sign-in.md +0 -0
  184. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/conformance.md +0 -0
  185. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/control-room.md +0 -0
  186. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/data-in.md +0 -0
  187. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/declare.md +0 -0
  188. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/decline.md +0 -0
  189. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/dlis.md +0 -0
  190. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/doctor.md +0 -0
  191. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/dossier.md +0 -0
  192. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/drift.md +0 -0
  193. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/drillstring.md +0 -0
  194. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/economics.md +0 -0
  195. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/edr.md +0 -0
  196. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/etp.md +0 -0
  197. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/field-ports.md +0 -0
  198. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/files.md +0 -0
  199. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/frac-hits.md +0 -0
  200. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/historian.md +0 -0
  201. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/https.md +0 -0
  202. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/instruments.md +0 -0
  203. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/interval.md +0 -0
  204. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/invoice-check.md +0 -0
  205. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/map.md +0 -0
  206. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/month-end.md +0 -0
  207. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/notifications.md +0 -0
  208. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/opseis.md +0 -0
  209. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/osdu.md +0 -0
  210. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/pad-summary.md +0 -0
  211. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/patches.md +0 -0
  212. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/penetrator.md +0 -0
  213. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/plan.md +0 -0
  214. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/pore-pressure.md +0 -0
  215. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/ppdm.md +0 -0
  216. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/quakeml.md +0 -0
  217. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/quality.md +0 -0
  218. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/report-history.md +0 -0
  219. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/rig-time.md +0 -0
  220. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/seedlink.md +0 -0
  221. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/seismic.md +0 -0
  222. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/setup.md +0 -0
  223. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/simulation.md +0 -0
  224. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/site.md +0 -0
  225. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/sites.md +0 -0
  226. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/slip.md +0 -0
  227. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/soak.md +0 -0
  228. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/sra.md +0 -0
  229. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/start.md +0 -0
  230. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/structure.md +0 -0
  231. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/tools.md +0 -0
  232. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/torque-drag.md +0 -0
  233. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/transients.md +0 -0
  234. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/upkeep.md +0 -0
  235. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/vibration.md +0 -0
  236. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/well-report.md +0 -0
  237. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/well-tests.md +0 -0
  238. {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/well3d.md +0 -0
  239. {gea_program-0.56.0 → gea_program-0.58.0}/gea/housekeeping.py +0 -0
  240. {gea_program-0.56.0 → gea_program-0.58.0}/gea/interval.py +0 -0
  241. {gea_program-0.56.0 → gea_program-0.58.0}/gea/inverse_engine.py +0 -0
  242. {gea_program-0.56.0 → gea_program-0.58.0}/gea/invoice_check.py +0 -0
  243. {gea_program-0.56.0 → gea_program-0.58.0}/gea/jobs.py +0 -0
  244. {gea_program-0.56.0 → gea_program-0.58.0}/gea/live_ports.py +0 -0
  245. {gea_program-0.56.0 → gea_program-0.58.0}/gea/loadtest.py +0 -0
  246. {gea_program-0.56.0 → gea_program-0.58.0}/gea/matplotlib_demo.py +0 -0
  247. {gea_program-0.56.0 → gea_program-0.58.0}/gea/modbus.py +0 -0
  248. {gea_program-0.56.0 → gea_program-0.58.0}/gea/modbus_rtu.py +0 -0
  249. {gea_program-0.56.0 → gea_program-0.58.0}/gea/model_card.py +0 -0
  250. {gea_program-0.56.0 → gea_program-0.58.0}/gea/month_end.py +0 -0
  251. {gea_program-0.56.0 → gea_program-0.58.0}/gea/mqtt_port.py +0 -0
  252. {gea_program-0.56.0 → gea_program-0.58.0}/gea/notify.py +0 -0
  253. {gea_program-0.56.0 → gea_program-0.58.0}/gea/oidc.py +0 -0
  254. {gea_program-0.56.0 → gea_program-0.58.0}/gea/oidc_mock.py +0 -0
  255. {gea_program-0.56.0 → gea_program-0.58.0}/gea/op_seismicity.py +0 -0
  256. {gea_program-0.56.0 → gea_program-0.58.0}/gea/opcua_port.py +0 -0
  257. {gea_program-0.56.0 → gea_program-0.58.0}/gea/operator_app.py +0 -0
  258. {gea_program-0.56.0 → gea_program-0.58.0}/gea/osdu.py +0 -0
  259. {gea_program-0.56.0 → gea_program-0.58.0}/gea/pad_summary.py +0 -0
  260. {gea_program-0.56.0 → gea_program-0.58.0}/gea/patches.py +0 -0
  261. {gea_program-0.56.0 → gea_program-0.58.0}/gea/pdf_report.py +0 -0
  262. {gea_program-0.56.0 → gea_program-0.58.0}/gea/penetrator.py +0 -0
  263. {gea_program-0.56.0 → gea_program-0.58.0}/gea/permits.py +0 -0
  264. {gea_program-0.56.0 → gea_program-0.58.0}/gea/piwebapi.py +0 -0
  265. {gea_program-0.56.0 → gea_program-0.58.0}/gea/pore_pressure.py +0 -0
  266. {gea_program-0.56.0 → gea_program-0.58.0}/gea/ports.py +0 -0
  267. {gea_program-0.56.0 → gea_program-0.58.0}/gea/ppdm.py +0 -0
  268. {gea_program-0.56.0 → gea_program-0.58.0}/gea/profile_catalog.py +0 -0
  269. {gea_program-0.56.0 → gea_program-0.58.0}/gea/project.py +0 -0
  270. {gea_program-0.56.0 → gea_program-0.58.0}/gea/qt6_downhole_app.py +0 -0
  271. {gea_program-0.56.0 → gea_program-0.58.0}/gea/quakeml.py +0 -0
  272. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reconciler.py +0 -0
  273. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  274. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  275. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/WMM2025.COF +0 -0
  276. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  277. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/eia_prices.provenance.json +0 -0
  278. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  279. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/iscwsa_rev4_example.json +0 -0
  280. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/iscwsa_rev5_example.json +0 -0
  281. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
  282. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
  283. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  284. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  285. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  286. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  287. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/mahoning_example.json +0 -0
  288. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/mud_motor_specs.json +0 -0
  289. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/owsg_tool_models.json +0 -0
  290. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/penetrator_ratings.json +0 -0
  291. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/seismicity_protocols.json +0 -0
  292. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/tool_sources.json +0 -0
  293. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/us_outline.json +0 -0
  294. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/us_outline.provenance.json +0 -0
  295. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_3d.provenance.json +0 -0
  296. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  297. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  298. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  299. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_surfaces.json.gz +0 -0
  300. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  301. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  302. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  303. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  304. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  305. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  306. {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  307. {gea_program-0.56.0 → gea_program-0.58.0}/gea/report_history.py +0 -0
  308. {gea_program-0.56.0 → gea_program-0.58.0}/gea/rig_state.py +0 -0
  309. {gea_program-0.56.0 → gea_program-0.58.0}/gea/rock_inventory.py +0 -0
  310. {gea_program-0.56.0 → gea_program-0.58.0}/gea/sample_record.py +0 -0
  311. {gea_program-0.56.0 → gea_program-0.58.0}/gea/sample_well_profile.csv +0 -0
  312. {gea_program-0.56.0 → gea_program-0.58.0}/gea/sbom.py +0 -0
  313. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seedlink.py +0 -0
  314. {gea_program-0.56.0 → gea_program-0.58.0}/gea/segy.py +0 -0
  315. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic.py +0 -0
  316. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_array.py +0 -0
  317. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_assoc.py +0 -0
  318. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_detect.py +0 -0
  319. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_film.py +0 -0
  320. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_harmonic.py +0 -0
  321. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_qc.py +0 -0
  322. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_response.py +0 -0
  323. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_signature.py +0 -0
  324. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_track.py +0 -0
  325. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_uncertainty.py +0 -0
  326. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_unlisted.py +0 -0
  327. {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_watch.py +0 -0
  328. {gea_program-0.56.0 → gea_program-0.58.0}/gea/sensor_swap.py +0 -0
  329. {gea_program-0.56.0 → gea_program-0.58.0}/gea/series_cache.py +0 -0
  330. {gea_program-0.56.0 → gea_program-0.58.0}/gea/service_life.py +0 -0
  331. {gea_program-0.56.0 → gea_program-0.58.0}/gea/setup_checklist.py +0 -0
  332. {gea_program-0.56.0 → gea_program-0.58.0}/gea/shapefile.py +0 -0
  333. {gea_program-0.56.0 → gea_program-0.58.0}/gea/shell.py +0 -0
  334. {gea_program-0.56.0 → gea_program-0.58.0}/gea/shut_in.py +0 -0
  335. {gea_program-0.56.0 → gea_program-0.58.0}/gea/simulation.py +0 -0
  336. {gea_program-0.56.0 → gea_program-0.58.0}/gea/site_map.py +0 -0
  337. {gea_program-0.56.0 → gea_program-0.58.0}/gea/sla_report.py +0 -0
  338. {gea_program-0.56.0 → gea_program-0.58.0}/gea/slip.py +0 -0
  339. {gea_program-0.56.0 → gea_program-0.58.0}/gea/soak.py +0 -0
  340. {gea_program-0.56.0 → gea_program-0.58.0}/gea/sra.py +0 -0
  341. {gea_program-0.56.0 → gea_program-0.58.0}/gea/store_forward.py +0 -0
  342. {gea_program-0.56.0 → gea_program-0.58.0}/gea/strata_join.py +0 -0
  343. {gea_program-0.56.0 → gea_program-0.58.0}/gea/structure.py +0 -0
  344. {gea_program-0.56.0 → gea_program-0.58.0}/gea/supervisor.py +0 -0
  345. {gea_program-0.56.0 → gea_program-0.58.0}/gea/survey_cmd.py +0 -0
  346. {gea_program-0.56.0 → gea_program-0.58.0}/gea/survey_view.py +0 -0
  347. {gea_program-0.56.0 → gea_program-0.58.0}/gea/telemetry.py +0 -0
  348. {gea_program-0.56.0 → gea_program-0.58.0}/gea/tls.py +0 -0
  349. {gea_program-0.56.0 → gea_program-0.58.0}/gea/tool_catalog.py +0 -0
  350. {gea_program-0.56.0 → gea_program-0.58.0}/gea/tool_library.py +0 -0
  351. {gea_program-0.56.0 → gea_program-0.58.0}/gea/tool_taxonomy.py +0 -0
  352. {gea_program-0.56.0 → gea_program-0.58.0}/gea/torque_drag.py +0 -0
  353. {gea_program-0.56.0 → gea_program-0.58.0}/gea/transient.py +0 -0
  354. {gea_program-0.56.0 → gea_program-0.58.0}/gea/vibration.py +0 -0
  355. {gea_program-0.56.0 → gea_program-0.58.0}/gea/well3d.py +0 -0
  356. {gea_program-0.56.0 → gea_program-0.58.0}/gea/well_assembler.py +0 -0
  357. {gea_program-0.56.0 → gea_program-0.58.0}/gea/well_report.py +0 -0
  358. {gea_program-0.56.0 → gea_program-0.58.0}/gea/well_test_validation.py +0 -0
  359. {gea_program-0.56.0 → gea_program-0.58.0}/gea/wellplan.py +0 -0
  360. {gea_program-0.56.0 → gea_program-0.58.0}/gea/wellpos.py +0 -0
  361. {gea_program-0.56.0 → gea_program-0.58.0}/gea/wits0.py +0 -0
  362. {gea_program-0.56.0 → gea_program-0.58.0}/gea/witsml.py +0 -0
  363. {gea_program-0.56.0 → gea_program-0.58.0}/gea/workspace.py +0 -0
  364. {gea_program-0.56.0 → gea_program-0.58.0}/gea/xlsx_read.py +0 -0
  365. {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/dependency_links.txt +0 -0
  366. {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/entry_points.txt +0 -0
  367. {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/requires.txt +0 -0
  368. {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/top_level.txt +0 -0
  369. {gea_program-0.56.0 → gea_program-0.58.0}/setup.cfg +0 -0
  370. {gea_program-0.56.0 → gea_program-0.58.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.56.0
3
+ Version: 0.58.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
@@ -659,6 +659,17 @@ EIA) or a declared price, with the net revenue interest, severance tax and
659
659
  operating cost declared. A forecast is not a reserves report, and the page
660
660
  says so. The **Decline curves** tab, `gea decline`, `gea help decline`.
661
661
 
662
+ ### Hydraulics and ECD
663
+
664
+ The pressures the mud makes while it circulates, from the six-speed
665
+ viscometer (the API RP 13D power law: the pipe from 600/300, the annulus
666
+ from 100/3): the standpipe pressure part by part - surface lines, string,
667
+ motor and MWD, bit, annulus - the bit's hydraulics, the ECD at every depth
668
+ against the pore pressure and fracture gradient, surge and swab and the
669
+ fastest safe trip at each bit depth, and the standpipe pressure the rig
670
+ measured against the model. The **Hydraulics & ECD** tab, `gea hydraulics`,
671
+ `gea help hydraulics`.
672
+
662
673
  ### Torque and drag
663
674
 
664
675
  The hook load and torque the drill string should take at each bit depth, by
@@ -1291,6 +1302,19 @@ gea oidc --workspace C:\site --issuer https://login.microsoftonline.com/3f8b2c1d
1291
1302
  Or Administration -> **Company sign-in**. `gea help company-sign-in` has the
1292
1303
  Entra ID app registration step by step.
1293
1304
 
1305
+ ### For an IT department
1306
+
1307
+ `docs/IT_SECURITY.md` (`gea security` prints it from any installation)
1308
+ answers what a reviewer asks before installing: where the data is and every
1309
+ connection the program can make, the one port, HTTPS, company sign-in and
1310
+ roles, the audit log, the bill of materials (`gea sbom`), and how a kit is
1311
+ checked - the build's attestation
1312
+ (`gea-program-0.57.0-win64.zip` checked with
1313
+ `gh attestation verify gea-program-0.57.0-win64.zip -R Daniel8Murphy0007/GEA-Program`)
1314
+ and the file catalog beside the kit's folder, Authenticode-signed once the
1315
+ publisher's certificate is in place. `SECURITY.md` says how to report a
1316
+ vulnerability.
1317
+
1294
1318
  ### Before a site goes live
1295
1319
 
1296
1320
  `deploy/README.md` is the checklist: HTTPS from the service itself (above),
@@ -1422,4 +1446,5 @@ https://pypi.org/project/gea-program/ after the first successful run.
1422
1446
  ## Licence
1423
1447
 
1424
1448
  Mozilla Public License 2.0 (MPL-2.0); see `LICENSE`. Every source file carries the MPL-2.0
1425
- header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy.
1449
+ header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy. The signed kit, its updates and
1450
+ support are a subscription from ENRGYONE (`docs/commercial/COMMERCIAL.md`).
@@ -620,6 +620,17 @@ EIA) or a declared price, with the net revenue interest, severance tax and
620
620
  operating cost declared. A forecast is not a reserves report, and the page
621
621
  says so. The **Decline curves** tab, `gea decline`, `gea help decline`.
622
622
 
623
+ ### Hydraulics and ECD
624
+
625
+ The pressures the mud makes while it circulates, from the six-speed
626
+ viscometer (the API RP 13D power law: the pipe from 600/300, the annulus
627
+ from 100/3): the standpipe pressure part by part - surface lines, string,
628
+ motor and MWD, bit, annulus - the bit's hydraulics, the ECD at every depth
629
+ against the pore pressure and fracture gradient, surge and swab and the
630
+ fastest safe trip at each bit depth, and the standpipe pressure the rig
631
+ measured against the model. The **Hydraulics & ECD** tab, `gea hydraulics`,
632
+ `gea help hydraulics`.
633
+
623
634
  ### Torque and drag
624
635
 
625
636
  The hook load and torque the drill string should take at each bit depth, by
@@ -1252,6 +1263,19 @@ gea oidc --workspace C:\site --issuer https://login.microsoftonline.com/3f8b2c1d
1252
1263
  Or Administration -> **Company sign-in**. `gea help company-sign-in` has the
1253
1264
  Entra ID app registration step by step.
1254
1265
 
1266
+ ### For an IT department
1267
+
1268
+ `docs/IT_SECURITY.md` (`gea security` prints it from any installation)
1269
+ answers what a reviewer asks before installing: where the data is and every
1270
+ connection the program can make, the one port, HTTPS, company sign-in and
1271
+ roles, the audit log, the bill of materials (`gea sbom`), and how a kit is
1272
+ checked - the build's attestation
1273
+ (`gea-program-0.57.0-win64.zip` checked with
1274
+ `gh attestation verify gea-program-0.57.0-win64.zip -R Daniel8Murphy0007/GEA-Program`)
1275
+ and the file catalog beside the kit's folder, Authenticode-signed once the
1276
+ publisher's certificate is in place. `SECURITY.md` says how to report a
1277
+ vulnerability.
1278
+
1255
1279
  ### Before a site goes live
1256
1280
 
1257
1281
  `deploy/README.md` is the checklist: HTTPS from the service itself (above),
@@ -1383,4 +1407,5 @@ https://pypi.org/project/gea-program/ after the first successful run.
1383
1407
  ## Licence
1384
1408
 
1385
1409
  Mozilla Public License 2.0 (MPL-2.0); see `LICENSE`. Every source file carries the MPL-2.0
1386
- header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy.
1410
+ header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy. The signed kit, its updates and
1411
+ support are a subscription from ENRGYONE (`docs/commercial/COMMERCIAL.md`).
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.56.0"
126
+ __version__ = "0.58.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -67,7 +67,16 @@ def _build_config(a):
67
67
  return SimulatorConfig(**kw)
68
68
 
69
69
 
70
+ # the front door's own commands (gea.cli), reached the same way through `python -m gea` - the kit's gea.cmd and
71
+ # gea wrapper run `python -m gea`, and before v0.57 `gea guide`, `gea help` and the rest answered "invalid choice"
72
+ FRONT_ONLY = ('quickstart', 'guide', 'security', 'docs', 'gui', 'help')
73
+
74
+
70
75
  def main(argv=None) -> int:
76
+ _argv = list(sys.argv[1:] if argv is None else argv)
77
+ if _argv and _argv[0] in FRONT_ONLY:
78
+ from .cli import main as _front
79
+ return _front(_argv)
71
80
  ap = argparse.ArgumentParser(prog="gea",
72
81
  description="GEA Downhole Simulator - headless CLI")
73
82
  sub = ap.add_subparsers(dest="cmd", required=True)
@@ -637,6 +646,24 @@ def main(argv=None) -> int:
637
646
  p_dc.add_argument("--actor", type=str, default="cli")
638
647
  p_dc.add_argument("--json", action="store_true")
639
648
 
649
+ p_hy = sub.add_parser("hydraulics", help="the pressures the mud makes while it circulates - the standpipe, the bit, the annulus, the ECD against the pore pressure and fracture gradient, surge and swab - and the standpipe pressure the rig measured")
650
+ p_hy.add_argument("--workspace", type=str, default=None)
651
+ p_hy.add_argument("--well", type=str, default=None)
652
+ p_hy.add_argument("--example", action="store_true", help="the worked example (Volve 15/9-F-10 with an example mud, string and hole)")
653
+ p_hy.add_argument("--string", type=str, default=None, help="sections from the bit up: 'BHA:350:6.75:2.875; drill pipe::5:4.276' (what:length_ft:od_in:id_in)")
654
+ p_hy.add_argument("--hole", type=str, default=None, help="sections from the surface down: '9-5/8 casing:3500:8.681; open hole::8.75' (what:bottom_md_ft:id_in)")
655
+ p_hy.add_argument("--fann", type=str, default=None, help="the viscometer dial: '600=60 300=38 200=30 100=21 6=7 3=6'")
656
+ p_hy.add_argument("--nozzles", type=str, default=None, help="nozzle sizes in 32nds of an inch: '14 14 14 14 14'")
657
+ for k_, h_ in (("mw", "mud weight, ppg"), ("flow", "pump rate, gal/min"), ("bit", "bit diameter, in"), ("surface", "surface lines' pressure loss, psi"),
658
+ ("surface-ref", "the rate (gal/min) the surface loss was declared at"), ("tools", "motor and MWD pressure drop, psi"),
659
+ ("trip", "average stand speed tripping, ft/min"), ("pp", "a pore pressure for the hole when the Pore pressure tab has none, ppg"),
660
+ ("fg", "a fracture gradient for the hole when the Pore pressure tab has none, ppg")):
661
+ p_hy.add_argument("--" + k_, type=str, default=None, help=h_)
662
+ p_hy.add_argument("--source", type=str, default="", help="where the mud, string and hole figures came from")
663
+ p_hy.add_argument("--write", action="store_true")
664
+ p_hy.add_argument("--actor", type=str, default="cli")
665
+ p_hy.add_argument("--json", action="store_true")
666
+
640
667
  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")
641
668
  p_td.add_argument("--workspace", type=str, default=None)
642
669
  p_td.add_argument("--well", type=str, default=None)
@@ -2568,6 +2595,46 @@ def main(argv=None) -> int:
2568
2595
  raise SystemExit(f"decline: {e.args[0] if e.args else e}")
2569
2596
  print(_json.dumps(r, default=str) if a.json else _DCA.text(r))
2570
2597
  return 0
2598
+ elif a.cmd == "hydraulics":
2599
+ import json as _json
2600
+ from . import hydraulics as _HYD
2601
+ from .cli import _safe_stdout
2602
+ from .workspace import Workspace, WorkspaceError
2603
+ _safe_stdout()
2604
+ try:
2605
+ ws = Workspace(a.workspace) if a.workspace else None
2606
+ scene = "example" if (a.example or ws is None) else "workspace"
2607
+ keys_ = {"mw": "mw_ppg", "flow": "flow_gpm", "bit": "bit_in", "surface": "surface_psi", "surface_ref": "surface_ref_gpm", "tools": "tools_psi",
2608
+ "trip": "trip_ft_min", "pp": "pp_ppg", "fg": "fg_ppg"}
2609
+ vals_ = {v: getattr(a, k) for k, v in keys_.items() if getattr(a, k) is not None}
2610
+ if ws is not None and (vals_ or any(x is not None for x in (a.string, a.hole, a.fann, a.nozzles))):
2611
+ if not a.well:
2612
+ raise ValueError("--well is needed")
2613
+ fann_ = None
2614
+ if a.fann is not None:
2615
+ fann_ = {}
2616
+ for p_ in a.fann.replace(",", " ").split():
2617
+ k_, _, v_ = p_.partition("=")
2618
+ if not v_:
2619
+ raise ValueError(f"--fann takes SPEED=DIAL pairs, e.g. 600=60 300=38, not {p_!r}")
2620
+ fann_[k_.strip()] = v_.strip()
2621
+ c_ = _HYD.set_well(ws, a.actor, a.well, a.source, a.string, a.hole, fann_, a.nozzles, **vals_)
2622
+ print(f"{a.well}: mud {c_['mw_ppg']:g} ppg, {c_['flow_gpm']:g} gpm; string {len(c_.get('string') or [])} section(s), hole {len(c_.get('hole') or [])} section(s); "
2623
+ f"viscometer {' '.join(f'{k}={v:g}' for k, v in (c_.get('fann') or {}).items()) or 'not declared'}")
2624
+ return 0
2625
+ if a.write:
2626
+ if ws is None:
2627
+ raise ValueError("--write needs --workspace")
2628
+ r_ = _HYD.write_report(ws, __version__, a.well, a.actor, scene)
2629
+ print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
2630
+ if r_['paths'].get('pdf'):
2631
+ print(f" printable: {r_['paths']['pdf']}")
2632
+ return 0
2633
+ r = _HYD.page(ws, scene, a.well)
2634
+ except (KeyError, ValueError, OSError, WorkspaceError) as e:
2635
+ raise SystemExit(f"hydraulics: {e.args[0] if e.args else e}")
2636
+ print(_json.dumps(r, default=str) if a.json else _HYD.text(r))
2637
+ return 0
2571
2638
  elif a.cmd == "torque-drag":
2572
2639
  import json as _json
2573
2640
  from . import torque_drag as _TND
@@ -12660,6 +12660,348 @@ def section_cy_company_sign_in(tmp: str) -> None:
12660
12660
  "the help page with the Entra ID steps" + (f" - failed: {failed4}" if failed4 else ""))
12661
12661
 
12662
12662
 
12663
+ def section_cz_it_review(tmp: str) -> None:
12664
+ """Section CZ - what an IT department asks: the security overview ships with the program and agrees with the
12665
+ code it describes (the rounds, the sessions, the lock, the port, TLS, every host the program can reach); `gea
12666
+ security`, `gea guide` and `gea help` answer through `python -m gea` as the kit's launcher runs it; in a checkout,
12667
+ the kit tools zip the kit again with its catalog beside it and tell the site how to check it, and the build
12668
+ workflow attests both kits and signs the catalog only when a certificate is configured."""
12669
+ import importlib.util
12670
+ import json as _json
12671
+ import subprocess as _sp
12672
+ import urllib.parse as _up
12673
+ import zipfile as _zf
12674
+ from . import doctor as DR
12675
+ from . import elevation as EL
12676
+ from . import helplib as H
12677
+ from . import seismic_watch as SW
12678
+ from . import service as SVC
12679
+ from . import tls as TLS
12680
+ pkg = Path(__file__).parent
12681
+ checkout = (pkg.parent / "pyproject.toml").exists() and (pkg.parent / "tools" / "build_installer.py").exists()
12682
+
12683
+ # -- CZ1 the overview, and the code it describes -------------------------------------------------------------------
12684
+ text = H.security_text() or ""
12685
+ docs_copy = pkg.parent / "docs" / "IT_SECURITY.md"
12686
+ same = (not docs_copy.exists()) or docs_copy.read_text(encoding="utf-8") == text
12687
+ flat = " ".join(text.split())
12688
+ with open(Path(pkg, "reference", "eia_prices.provenance.json"), encoding="utf-8") as f:
12689
+ eia_hosts = {_up.urlsplit(s["refresh_url"]).hostname for s in _json.load(f)["series"].values() if s.get("refresh_url")}
12690
+ doctor_src = Path(DR.__file__).read_text(encoding="utf-8")
12691
+ hosts = {_up.urlsplit(EL.EPQS_URL).hostname, _up.urlsplit(SW.QUERY_URL).hostname, _up.urlsplit(SW.FEED_URL).hostname, *eia_hosts}
12692
+ hosts |= {"pypi.org"} if "https://pypi.org/pypi/gea-program/json" in doctor_src else {"(the update check moved)"}
12693
+ facts = {
12694
+ "rounds": f"{SVC.PBKDF2_ROUNDS:,}" in flat,
12695
+ "sessions": f"{SVC.SESSION_HOURS} hours" in flat and "256-bit" in flat,
12696
+ "lock": SVC.LOGIN_MAX_FAILURES == 5 and "five failures" in flat and f"{SVC.LOGIN_LOCK_S // 60} minutes" in flat,
12697
+ "port": "TCP 8765" in flat and "8765" in Path(DR.__file__).read_text(encoding="utf-8"),
12698
+ "tls": "TLS 1.2 minimum" in flat and f"last {TLS.EXPIRING_DAYS} days" in flat,
12699
+ "hosts": all(f"`{h}`" in flat for h in hosts),
12700
+ "no_telemetry": "No telemetry" in flat,
12701
+ }
12702
+ # Python 3.14's argparse formats every help string as the parser is built (help % params) and stops on a bare
12703
+ # "%": v0.54.0's first ship stopped at its gate that way while 3.13 passed. The same check, on any Python.
12704
+ import argparse as _ap
12705
+ import contextlib as _cl
12706
+ import io as _io
12707
+ from . import __main__ as _M
12708
+ bad_help = []
12709
+ _orig_add, _orig_sub = _ap.ArgumentParser.add_argument, _ap._SubParsersAction.add_parser
12710
+ _params = {k_: "x" for k_ in ("prog", "default", "dest", "type", "choices", "metavar", "required", "nargs", "const", "option_strings")}
12711
+
12712
+ def _fmt_ok(h):
12713
+ try:
12714
+ h % _params
12715
+ return True
12716
+ except (ValueError, TypeError, KeyError):
12717
+ return False
12718
+
12719
+ def _add(self, *a_, **k_):
12720
+ h_ = k_.get("help")
12721
+ if isinstance(h_, str) and h_ != _ap.SUPPRESS and not _fmt_ok(h_):
12722
+ bad_help.append(f"{self.prog} {'/'.join(a_)}")
12723
+ return _orig_add(self, *a_, **k_)
12724
+
12725
+ def _sub(self, name, **k_):
12726
+ h_ = k_.get("help")
12727
+ if isinstance(h_, str) and not _fmt_ok(h_):
12728
+ bad_help.append(f"gea {name}")
12729
+ return _orig_sub(self, name, **k_)
12730
+ _ap.ArgumentParser.add_argument, _ap._SubParsersAction.add_parser = _add, _sub
12731
+ try:
12732
+ with _cl.redirect_stdout(_io.StringIO()), _cl.redirect_stderr(_io.StringIO()):
12733
+ try:
12734
+ _M.main(["wells"])
12735
+ except SystemExit:
12736
+ pass
12737
+ finally:
12738
+ _ap.ArgumentParser.add_argument, _ap._SubParsersAction.add_parser = _orig_add, _orig_sub
12739
+ facts["help_strings"] = not bad_help
12740
+ r_sec = _sp.run([sys.executable, "-m", "gea", "security"], capture_output=True, text=True, encoding="utf-8", errors="replace")
12741
+ r_guide = _sp.run([sys.executable, "-m", "gea", "guide"], capture_output=True, text=True, encoding="utf-8", errors="replace")
12742
+ r_help = _sp.run([sys.executable, "-m", "gea", "help", "https"], capture_output=True, text=True, encoding="utf-8", errors="replace")
12743
+ bad = [k for k, v in facts.items() if not v]
12744
+ ok(text.startswith("# GEA-Program for an IT department") and same and not bad
12745
+ and r_sec.returncode == 0 and "GEA-Program for an IT department" in r_sec.stdout
12746
+ and r_guide.returncode == 0 and "HOW TO TEST GEA" in r_guide.stdout and r_help.returncode == 0 and "**The command**" in r_help.stdout,
12747
+ "CZ1 the security overview ships in the package (identical to docs/IT_SECURITY.md) and agrees with the code: the password hashing rounds, the "
12748
+ "session length and token size, the sign-in lock, the one port, the TLS floor and the expiry warning, every host the program can reach "
12749
+ "(ComCat, the elevation service, the price series, PyPI); `python -m gea security`, `guide` and `help` answer as the kit's own launcher "
12750
+ "runs them; every option's help text formats the way Python 3.14 checks it as the parser is built"
12751
+ + (f" - disagrees: {bad}" if bad else "") + (f" - help texts: {bad_help}" if bad_help else ""))
12752
+
12753
+ # -- CZ2 the kit is zipped again with its catalog, and says how to check it ----------------------------------------------
12754
+ if checkout:
12755
+ spec = importlib.util.spec_from_file_location("cz_installer", str(pkg.parent / "tools" / "build_installer.py"))
12756
+ bi = importlib.util.module_from_spec(spec); spec.loader.exec_module(bi)
12757
+ kit = Path(tmp, "cz_kit", "gea-program-9.9.9-win64"); (kit / "python").mkdir(parents=True)
12758
+ (kit / "MANIFEST.json").write_text("{}", encoding="utf-8"); (kit / "python" / "python.exe").write_bytes(b"MZ stand-in")
12759
+ (kit / "SHA256SUMS.txt").write_text("x MANIFEST.json\n", encoding="utf-8")
12760
+ cat = Path(tmp, "cz_kit", "gea-program-9.9.9-win64.cat"); cat.write_bytes(b"0\x82 a catalog stand-in")
12761
+ zp = bi.rezip(kit, [cat])
12762
+ with _zf.ZipFile(zp) as z:
12763
+ names = sorted(z.namelist())
12764
+ cat_back = z.read("gea-program-9.9.9-win64.cat")
12765
+ try:
12766
+ bi.rezip(Path(tmp, "cz_kit"), [])
12767
+ not_kit = "accepted"
12768
+ except SystemExit as e:
12769
+ not_kit = str(e)
12770
+ readme = bi.README.format(version="9.9.9", target="Windows x64", pyline="p", install="i", start="s", verify="v", port="8765",
12771
+ service="sv", stop="st", uninstall="u", workspace="w", check=bi.WIN_CHECK.format(version="9.9.9"))
12772
+ build_src = (pkg.parent / "tools" / "build_installer.py").read_text(encoding="utf-8")
12773
+ c2 = {
12774
+ "rezip": names == ["gea-program-9.9.9-win64.cat", "gea-program-9.9.9-win64/MANIFEST.json", "gea-program-9.9.9-win64/SHA256SUMS.txt",
12775
+ "gea-program-9.9.9-win64/python/python.exe"] and cat_back == cat.read_bytes() and "not a kit folder" in not_kit,
12776
+ "readme": all(x in readme for x in ("gh attestation verify gea-program-9.9.9-win64.zip -R Daniel8Murphy0007/GEA-Program",
12777
+ "Test-FileCatalog -CatalogFilePath .\\gea-program-9.9.9-win64.cat -Path .\\gea-program-9.9.9-win64",
12778
+ "Get-AuthenticodeSignature", "IT_SECURITY.md", "gea help https"))
12779
+ and "368 checks" not in readme and "behind the site's TLS proxy" not in readme,
12780
+ "ships_overview": "docs' / 'IT_SECURITY.md'" in build_src and "kit / 'IT_SECURITY.md'" in build_src,
12781
+ }
12782
+ where = "the checkout's kit tools"
12783
+ else:
12784
+ c2 = {"installed": bool(H.security_text())}
12785
+ where = "an installed copy: the kit tools are checked in the checkout"
12786
+ failed2 = [k for k, v in c2.items() if not v]
12787
+ ok(not failed2,
12788
+ f"CZ2 ({where}) the kit is zipped again from its folder with its file catalog beside the folder, every file kept; a folder that is not a kit "
12789
+ "is refused; the kit's README tells the site how to check the build's attestation and every file against the catalog and its signature, "
12790
+ "and carries the IT overview" + (f" - failed: {failed2}" if failed2 else ""))
12791
+
12792
+ # -- CZ3 the build workflow: attested, signed only with a certificate, never signed in name only ----------------------------
12793
+ wf = pkg.parent / ".github" / "workflows" / "build-installer.yml"
12794
+ if wf.exists():
12795
+ y = wf.read_text(encoding="utf-8")
12796
+ win = y[y.index("windows-kit:"):y.index("linux-kit:")]
12797
+ lin = y[y.index("linux-kit:"):]
12798
+ order = [win.index(s) for s in ("New-FileCatalog", "azure/artifact-signing-action", "signtool", "Test-FileCatalog", "--rezip", "call install.cmd",
12799
+ "attest-build-provenance", "attach-kit.sh")]
12800
+ c3 = {
12801
+ "permissions": "id-token: write" in y and "attestations: write" in y and "contents: write" in y,
12802
+ "attested": "actions/attest-build-provenance" in win and "actions/attest-build-provenance" in lin
12803
+ and "subject-path: dist/gea-program-*-win64.zip" in win and "subject-path: dist/gea-program-*-linux-*.zip" in lin,
12804
+ "conditional": "if: env.ARTIFACT_SIGNING_ACCOUNT != ''" in win and "if: env.ARTIFACT_SIGNING_ACCOUNT == '' && env.SIGNING_PFX_BASE64 != ''" in win
12805
+ and "secrets.ARTIFACT_SIGNING_ACCOUNT" in win and "secrets.SIGNING_PFX_BASE64" in win,
12806
+ "checked": "-CatalogVersion 2.0" in win and "throw 'the catalog does not match the kit'" in win
12807
+ and "throw 'a certificate is configured but the catalog is not validly signed'" in win and "ships unsigned" in win
12808
+ and "Remove-Item $pfx" in win and "/tr http://timestamp" in win and "timestamp-rfc3161" in win,
12809
+ "order": order == sorted(order),
12810
+ "policy": (pkg.parent / "SECURITY.md").exists() and "Report a vulnerability" in " ".join((pkg.parent / "SECURITY.md").read_text(encoding="utf-8").split()),
12811
+ }
12812
+ where3 = "the checkout's workflow"
12813
+ else:
12814
+ c3 = {"installed": True}
12815
+ where3 = "an installed copy: the workflow is checked in the checkout"
12816
+ failed3 = [k for k, v in c3.items() if not v]
12817
+ ok(not failed3,
12818
+ f"CZ3 ({where3}) both kits are attested by the build that made them; the Windows kit's file catalog is made, signed by Azure Artifact "
12819
+ "Signing or a PFX only when its secrets are configured (timestamped; the PFX removed), checked against the kit - a mismatch, or a "
12820
+ "configured certificate that did not sign, stops the build - then zipped beside the kit before the kit is installed and its gate run; "
12821
+ "SECURITY.md says how to report a vulnerability" + (f" - failed: {failed3}" if failed3 else ""))
12822
+
12823
+
12824
+ def section_da_hydraulics(tmp: str) -> None:
12825
+ """Section DA - hydraulics and ECD: the power law's losses against their closed forms (Hagen-Poiseuille, the slot,
12826
+ Blasius) and API RP 13D's field-unit forms, the bit against its classic formula; a record whose standpipe pressure is
12827
+ the model's plus a known amount, read back; the worked example's ECD against its window, surge and swab; the API, the
12828
+ CLI, the report, the dossier, the tab and the help."""
12829
+ import http.cookiejar
12830
+ import math as _m
12831
+ import subprocess as _sp
12832
+ import urllib.request
12833
+ import urllib.error
12834
+ import zipfile as _zf
12835
+ from . import __version__ as _ver
12836
+ from . import dossier as DZ
12837
+ from . import helplib as H
12838
+ from . import hydraulics as HY
12839
+ from . import well3d as W
12840
+ from .workspace import Workspace
12841
+ from .service import Service, Users
12842
+ pkg = Path(__file__).parent
12843
+ d = Path(tmp, "hydraulics"); d.mkdir()
12844
+
12845
+ # -- DA1 by hand --------------------------------------------------------------------------------------------------------
12846
+ mu, q = 0.03, 50.0
12847
+ lp = HY.pipe_loss(q, 4.0, 1000.0, 8.33, 1.0, mu)
12848
+ Q, D = q * HY.GPM, 4.0 * HY.IN
12849
+ V = Q / (_m.pi * D * D / 4.0)
12850
+ hp = 32.0 * mu * V * 1000.0 * HY.FT / D ** 2 / HY.PSI
12851
+ la = HY.annulus_loss(q, 8.5, 5.0, 1000.0, 8.33, 1.0, mu)
12852
+ Va = q * HY.GPM / (_m.pi * ((8.5 * HY.IN) ** 2 - (5.0 * HY.IN) ** 2) / 4.0)
12853
+ slot = 48.0 * mu * Va * 1000.0 * HY.FT / (3.5 * HY.IN) ** 2 / HY.PSI
12854
+ f_t = HY.friction(1e5, 1.0, 16.0)[0]
12855
+ rh = HY.rheology({"600": 56, "300": 36, "200": 28, "100": 20, "6": 6, "3": 5})
12856
+ n, K = rh["n_pipe"], rh["K_pipe"]
12857
+ api = HY.pipe_loss(550.0, 4.276, 1000.0, 10.5, n, K)
12858
+ Vf = 24.51 * 550.0 / 4.276 ** 2 # ft/min
12859
+ mu_api = 100.0 * (5.11 * 56 / 1022 ** n) * (1.6 * Vf / 4.276) ** (n - 1) * ((3 * n + 1) / (4 * n)) ** n
12860
+ re_api = 15.467 * Vf * 4.276 * 10.5 / mu_api
12861
+ dp_api = 1.076 * 10.5 * Vf ** 2 * api["f"] / 1e5 / 4.276 * 1000.0
12862
+ bt = HY.bit(550.0, 10.5, [13] * 6, 8.5)
12863
+ refused = []
12864
+ for bad in ({"600": 56}, {"600": 30, "300": 36}, {"600": 56, "300": 36, "450": 40}, {"600": 400, "300": 36}):
12865
+ try:
12866
+ HY.rheology(bad); refused.append(False)
12867
+ except ValueError:
12868
+ refused.append(True)
12869
+ for fn, arg in ((HY.check_string, [{"what": "x", "length_ft": 100, "od_in": 5, "id_in": 6}]), (HY.check_hole, [{"what": "a", "bottom_md_ft": 1000, "id_in": 8},
12870
+ {"what": "b", "bottom_md_ft": 500, "id_in": 8}, {"what": "c", "id_in": 8}]),
12871
+ (HY.parse_sections, "BHA:300:6.75"), (HY.annulus_loss, None)):
12872
+ try:
12873
+ fn(arg, "string") if fn is HY.parse_sections else (HY.annulus_loss(500, 5.0, 6.0, 100, 10, 0.5, 0.5) if fn is HY.annulus_loss else fn(arg))
12874
+ refused.append(False)
12875
+ except ValueError:
12876
+ refused.append(True)
12877
+ c1 = {
12878
+ "newtonian": abs(lp["psi"] / hp - 1) < 1e-4 and lp["regime"] == "laminar" and abs(la["psi"] / slot - 1) < 1e-9 and abs(f_t / (0.0791 / 1e5 ** 0.25) - 1) < 0.01,
12879
+ "rheology": rh["pv_cp"] == 20 and rh["yp_lbf_100ft2"] == 16 and abs(rh["n_pipe"] - _m.log2(56 / 36)) < 1e-4 and abs(rh["n_ann"] - _m.log(20 / 5) / _m.log(170.2 / 5.11)) < 1e-4
12880
+ and rh["tau0_lbf_100ft2"] == 4 and rh["annulus_from"] == "100 and 3 rpm",
12881
+ "api_forms": abs(api["v_ft_min"] / Vf - 1) < 1e-3 and abs(api["mu_cp"] / mu_api - 1) < 1e-3 and abs(api["re"] / re_api - 1) < 2e-3 and abs(api["psi"] / dp_api - 1) < 1e-3,
12882
+ "bit": abs(bt["psi"] / (156.5 * 10.5 * 550.0 ** 2 / (6 * 13 ** 2) ** 2) - 1) < 1e-3 and abs(bt["tfa_in2"] - 6 * _m.pi / 4 * (13 / 32) ** 2) < 1e-3
12883
+ and abs(bt["hhp"] - bt["psi"] * 550 / 1714) < 0.1,
12884
+ "refused": all(refused) and len(refused) == 8,
12885
+ }
12886
+ failed1 = [k for k, v in c1.items() if not v]
12887
+ ok(not failed1,
12888
+ "DA1 the losses by hand: a Newtonian fluid's laminar loss in a pipe is Hagen-Poiseuille's and in the annulus the slot's, exactly; its turbulent "
12889
+ "friction factor Blasius's to 1 %; the viscometer's PV, YP, the pipe's n from 600/300 and the annulus's from 100/3; the velocity, effective "
12890
+ "viscosity, Reynolds number and loss equal to API RP 13D's field-unit forms; the bit's loss the classic 156.5 rho Q^2 / (sum d^2)^2; readings "
12891
+ "missing, falling or out of range, a bore wider than the pipe, a hole out of order, a short section and a pipe wider than its hole refused"
12892
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
12893
+
12894
+ # -- DA2 a record made by the model, read back ---------------------------------------------------------------------------
12895
+ f = HY.selftest_files(str(d))
12896
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
12897
+ wid = ws.add_well_file(f["record"], display="HY-1", actor="tester")["id"]
12898
+ ws.set_well(wid, surface={"lat": 31.42, "lon": -104.10, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
12899
+ no_survey = HY.page(ws, "workspace", wid)["detail"]
12900
+ W.attach_trajectory(ws, wid, "s.csv", Path(f["survey"]).read_bytes(), "tester", "true", "ft", None, None, None, "test survey")
12901
+ no_decl = HY.page(ws, "workspace", wid)["detail"]
12902
+ c = f["config"]
12903
+ HY.set_well(ws, "tester", wid, "mud report (test)", string=c["string"], hole=c["hole"], fann=c["fann"], nozzles_32=c["nozzles_32"], mw_ppg=c["mw_ppg"],
12904
+ flow_gpm=500, bit_in=8.75, tools_psi=400, pp_ppg=9.5, fg_ppg=14.5)
12905
+ r = HY.page(ws, "workspace", wid)["result"]
12906
+ rc = r["record"]
12907
+ cc = r["circulating"]
12908
+ c2 = {
12909
+ "needs": "survey" in no_survey and "string" in no_decl and "viscometer" in no_decl,
12910
+ "record": rc and rc["bins"] >= 20 and abs(rc["median_difference_psi"] - f["offset_psi"]) <= 10.0 and rc["spread_psi"] < 30.0,
12911
+ "parts": abs(sum(cc["parts"].values()) - cc["spp_psi"]) < 1.0 and cc["parts"]["tools"] == 400.0 and cc["parts"]["surface"] == 0.0,
12912
+ "ecd": cc["ecd_bit_ppg"] > c["mw_ppg"] and all(e_ >= c["mw_ppg"] for e_ in r["ecd"]["ecd_ppg"])
12913
+ and abs(cc["ecd_bit_ppg"] - (c["mw_ppg"] + cc["parts"]["annulus"] / (0.052 * cc["tvd_bit_ft"]))) < 0.002,
12914
+ "window": r["window_how"].startswith("declared") and r["min_fg_margin"]["ppg"] == min(x for x in r["ecd"]["margin_fg_ppg"] if x is not None),
12915
+ }
12916
+ failed2 = [k for k, v in c2.items() if not v]
12917
+ ok(not failed2,
12918
+ "DA2 a drilling record made by the model - drilling from 4,000 to 10,000 ft at 500 gpm and circulating at 350 gpm, its standpipe pressure the "
12919
+ "model's plus 120 psi and noise - read back while pumping: the median difference 120 psi to within 10 over 20 or more depth bins; the standpipe "
12920
+ "pressure the sum of its parts; the ECD at the bit the mud weight plus the annular loss over 0.052 TVD; a well without a survey or its "
12921
+ "declarations says what it needs" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
12922
+
12923
+ # -- DA3 the worked example ----------------------------------------------------------------------------------------------
12924
+ ex = HY.example()
12925
+ t = ex["trips"]
12926
+ s_hi = HY.surge_swab(ex["config"], ex["rheology"], ex["td_ft"], 180.0, lambda z: ex["tvd_td_ft"] * z / ex["td_ft"])
12927
+ s_lo = HY.surge_swab(ex["config"], ex["rheology"], ex["td_ft"], 90.0, lambda z: ex["tvd_td_ft"] * z / ex["td_ft"])
12928
+ rates = ex["by_rate"]
12929
+ c3 = {
12930
+ "example": ex["well"] == "15/9-F-10" and abs(ex["td_ft"] - 5300.07 / 0.3048) < 5.0 and "not from Volve" in ex["config"]["source"]
12931
+ and 2000 < ex["circulating"]["spp_psi"] < 5000 and ex["circulating"]["ecd_bit_ppg"] > ex["config"]["mw_ppg"],
12932
+ "rates": all(b["spp_psi"] > a["spp_psi"] and b["ecd_bit_ppg"] >= a["ecd_bit_ppg"] for a, b in zip(rates, rates[1:])),
12933
+ "surge": all(su > ex["config"]["mw_ppg"] > sw for su, sw in zip(t["surge_ppg"], t["swab_ppg"])) and s_hi["psi"] > s_lo["psi"] > 0,
12934
+ "speeds": all(v is not None and v > 0 for v in t["run_max_ft_min"] + t["pull_max_ft_min"])
12935
+ and HY.surge_swab(ex["config"], ex["rheology"], ex["td_ft"], t["run_max_ft_min"][-1], lambda z: ex["tvd_td_ft"] * z / ex["td_ft"])["surge_ppg"] <= 14.0 + 0.02,
12936
+ }
12937
+ failed3 = [k for k, v in c3.items() if not v]
12938
+ ok(not failed3,
12939
+ "DA3 the worked example, Volve 15/9-F-10's survey to its 5,300 m TD with a mud, string and hole declared for the example: the standpipe pressure "
12940
+ "and the ECD rising with the rate; surge above and swab below the mud weight at every bit depth, more at a faster trip; the fastest run keeping "
12941
+ "the surge within the declared fracture gradient" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
12942
+
12943
+ # -- DA4 the API, the CLI, the report, the dossier, the tab and the help -------------------------------------------------
12944
+ Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
12945
+ svc = Service(wsp, port=0, scheduler=False).start()
12946
+ base_url = svc.url.rstrip("/")
12947
+
12948
+ def opener(name, pw_):
12949
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
12950
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
12951
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
12952
+ return o
12953
+
12954
+ def call(o, method, path, body=None):
12955
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
12956
+ if method == "POST":
12957
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
12958
+ try:
12959
+ with o.open(rq, timeout=120) as rr_:
12960
+ return rr_.status, json.loads(rr_.read())
12961
+ except urllib.error.HTTPError as e:
12962
+ return e.code, json.loads(e.read())
12963
+ try:
12964
+ V_, O_ = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
12965
+ s_vg, vg = call(V_, "GET", "/api/hydraulics?well=" + wid)
12966
+ s_ve, ve = call(V_, "GET", "/api/hydraulics?scene=example")
12967
+ s_vs, _ = call(V_, "POST", "/api/hydraulics/settings", {"well_id": wid, "mw_ppg": 11.2, "source": "x"})
12968
+ s_os, os_ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "mw_ppg": "11.2", "r600": "62", "r300": "40", "r100": "22", "r3": "6",
12969
+ "nozzles_32": "13 13 13 13 13", "hole": "9-5/8 casing:3500:8.681; open hole::8.75", "source": "mud report (test)"})
12970
+ s_bf, _ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "r600": "30", "r300": "40", "source": "x"})
12971
+ s_bh, _ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "flow_gpm": 9000, "source": "x"})
12972
+ s_ns, _ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "mw_ppg": 12})
12973
+ s_vw, _ = call(V_, "POST", "/api/hydraulics/write", {"well_id": wid})
12974
+ s_ow, ow = call(O_, "POST", "/api/hydraulics/write", {"well_id": wid})
12975
+ finally:
12976
+ svc.stop()
12977
+ cli_x = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--example"], capture_output=True, text=True, cwd=str(pkg.parent))
12978
+ cli_s = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--workspace", wsp, "--well", wid, "--flow", "450", "--fann", "600=60 300=38 100=21 3=6",
12979
+ "--source", "mud report (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
12980
+ cli_b = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--workspace", wsp, "--well", wid, "--fann", "600:60", "--source", "x"], capture_output=True, text=True, cwd=str(pkg.parent))
12981
+ cli_t = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--workspace", wsp, "--well", wid], capture_output=True, text=True, cwd=str(pkg.parent))
12982
+ dz = DZ.build(ws, "tester", _ver, None)
12983
+ names = _zf.ZipFile(Path(ws.reports_dir, dz["path"])).namelist()
12984
+ audit = Path(ws.audit_path).read_text(encoding="utf-8")
12985
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
12986
+ c4 = {
12987
+ "api": s_vg == 200 and vg["result"]["record"]["bins"] >= 20 and s_ve == 200 and ve["result"]["well"] == "15/9-F-10" and s_vs == 403 and s_os == 200
12988
+ and os_["fann"] == {"600": 62.0, "300": 40.0, "100": 22.0, "3": 6.0} and os_["nozzles_32"] == [13.0] * 5 and os_["mw_ppg"] == 11.2
12989
+ and s_bf == 400 and s_bh == 400 and s_ns == 400 and s_vw == 403 and s_ow == 200 and ow["report_id"].startswith("HYD-"),
12990
+ "cli": cli_x.returncode == 0 and "standpipe" in cli_x.stdout and "ECD at the bit" in cli_x.stdout and cli_s.returncode == 0 and "450 gpm" in cli_s.stdout
12991
+ and cli_b.returncode != 0 and "SPEED=DIAL" in (cli_b.stderr + cli_b.stdout) and cli_t.returncode == 0 and "against the record" in cli_t.stdout,
12992
+ "report": PR_read_ok(Path(ws.reports_dir, "wells", wid, "hydraulics.pdf")) and all(x in audit for x in ("hydraulics.declare", "report.hydraulics")),
12993
+ "dossier": "declarations/drillstring/hydraulics.json" in names and f"results/hydraulics/{wid}.json" in names,
12994
+ "page": all(x in page_src for x in ("VIEWS.hydraulics", "['#/hydraulics', 'Hydraulics & ECD']", "/api/hydraulics/settings", "hyChart", "hySppChart")),
12995
+ "help": not H.check("hydraulics") and H.VIEW_TOPIC.get("hydraulics") == "hydraulics",
12996
+ }
12997
+ failed4 = [k for k, v in c4.items() if not v]
12998
+ ok(not failed4,
12999
+ "DA4 the tab's data for a viewer (the well, the worked example); the declarations by an operator only, with a source, readings and rates in "
13000
+ "range; `gea hydraulics` prints the example and a well and declares the viscometer as SPEED=DIAL; the report with its PDF, audited; the "
13001
+ "signed dossier carries the declarations and the result; the tab and the help page"
13002
+ + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
13003
+
13004
+
12663
13005
  def PR_read_ok(path) -> bool:
12664
13006
  from . import pdf_report as PR
12665
13007
  try:
@@ -12774,6 +13116,8 @@ def main() -> int:
12774
13116
  section_cw_decline(tmp)
12775
13117
  section_cx_https(tmp)
12776
13118
  section_cy_company_sign_in(tmp)
13119
+ section_cz_it_review(tmp)
13120
+ section_da_hydraulics(tmp)
12777
13121
  if _FAILS:
12778
13122
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
12779
13123
  for f in _FAILS:
@@ -10,6 +10,7 @@
10
10
  gea model-cards ... gea store-forward ... gea sla-report ... gea fat-sat ...
11
11
  gea help [topic] the help library, by the job (gea help drift); same text as the dashboard's panels
12
12
  gea guide the click-by-click tester guide
13
+ gea security what an IT department asks before installing, answered (docs/IT_SECURITY.md)
13
14
  gea docs where the catalogue and the reports live on this machine
14
15
  gea gui the desktop window (desktop extra)
15
16
  gea accept the product gate
@@ -23,7 +24,7 @@ import os
23
24
  import sys
24
25
  from pathlib import Path
25
26
 
26
- FRONT = {'quickstart', 'survey', 'guide', 'docs', 'gui', 'help', '-h', '--help'}
27
+ FRONT = {'quickstart', 'survey', 'guide', 'security', 'docs', 'gui', 'help', '-h', '--help'}
27
28
 
28
29
 
29
30
  def _pkg_root() -> Path:
@@ -62,6 +63,16 @@ def cmd_guide(_argv) -> int:
62
63
  return 0
63
64
 
64
65
 
66
+ def cmd_security(_argv) -> int:
67
+ from .helplib import security_text
68
+ text = security_text() # ships inside the package (gea/help/IT_SECURITY.md)
69
+ if text is None:
70
+ print('the security overview is missing from this installation; reinstall: python -m pip install --upgrade --force-reinstall "gea-program[live]"')
71
+ return 2
72
+ print(text)
73
+ return 0
74
+
75
+
65
76
  def cmd_help(argv) -> int:
66
77
  from .helplib import page, index_text, topics
67
78
  if not argv:
@@ -141,6 +152,8 @@ def main(argv=None) -> int:
141
152
  return cmd_survey(rest)
142
153
  if cmd == 'guide':
143
154
  return cmd_guide(rest)
155
+ if cmd == 'security':
156
+ return cmd_security(rest)
144
157
  if cmd == 'docs':
145
158
  return cmd_docs(rest)
146
159
  if cmd == 'gui':
@@ -278,7 +278,7 @@ def _gather(ws, well_id: Optional[str]) -> Dict[str, List[tuple]]:
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
280
  ('completion', 'frac', 'stages.json'), ('completion', 'frac', 'settings.json'), ('geomech', 'pore_pressure.json'),
281
- ('drillstring', 'torque_drag.json'), ('economics', 'production', 'production.json')):
281
+ ('drillstring', 'torque_drag.json'), ('economics', 'production', 'production.json'), ('drillstring', 'hydraulics.json')):
282
282
  p = os.path.join(ws.path, *rel)
283
283
  if os.path.isfile(p):
284
284
  out_list.append((p, 'declarations/' + '/'.join(rel)))
@@ -351,6 +351,17 @@ def _results(ws, well_id: Optional[str]) -> List[tuple]:
351
351
  indent=1, sort_keys=True, default=str).encode('utf-8')))
352
352
  except Exception as e:
353
353
  out.append(('results/decline_not_run.txt', f'the decline curves did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
354
+ try:
355
+ from . import hydraulics as HYD
356
+ declared = (HYD._load(ws).get('wells') or {})
357
+ for wid in wells:
358
+ if wid in declared:
359
+ r = HYD.page(ws, 'workspace', wid).get('result')
360
+ if r:
361
+ out.append((f'results/hydraulics/{wid}.json', json.dumps({k: v for k, v in r.items() if k != 'measured'}, indent=1, sort_keys=True,
362
+ default=str).encode('utf-8')))
363
+ except Exception as e:
364
+ out.append(('results/hydraulics_not_run.txt', f'the hydraulics model did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
354
365
  try:
355
366
  from . import torque_drag as TND
356
367
  declared = (TND._load(ws).get('wells') or {})