gea-program 0.90.0__tar.gz → 0.91.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 (448) hide show
  1. {gea_program-0.90.0 → gea_program-0.91.0}/PKG-INFO +44 -4
  2. {gea_program-0.90.0 → gea_program-0.91.0}/README.md +43 -3
  3. {gea_program-0.90.0 → gea_program-0.91.0}/gea/__init__.py +1 -1
  4. {gea_program-0.90.0 → gea_program-0.91.0}/gea/__main__.py +57 -0
  5. {gea_program-0.90.0 → gea_program-0.91.0}/gea/acceptance_tests.py +274 -6
  6. gea_program-0.91.0/gea/frac_treatment.py +1224 -0
  7. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/driller.md +1 -1
  8. gea_program-0.91.0/gea/help/frac-treatment.md +13 -0
  9. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/install.md +2 -2
  10. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/mcp.md +1 -1
  11. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/public-data.md +2 -0
  12. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/reader.md +1 -1
  13. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/recorder.md +1 -1
  14. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/01-install.md +2 -2
  15. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/14-production-pages.md +12 -11
  16. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/19-dossier-reader.md +1 -1
  17. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/22-updates.md +1 -1
  18. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/23-vpn.md +1 -1
  19. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/25-install-recorder.md +2 -2
  20. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/29-driller-rig.md +1 -1
  21. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial.md +2 -2
  22. {gea_program-0.90.0 → gea_program-0.91.0}/gea/helplib.py +2 -1
  23. {gea_program-0.90.0 → gea_program-0.91.0}/gea/mcp_server.py +14 -0
  24. {gea_program-0.90.0 → gea_program-0.91.0}/gea/public_pad.py +163 -5
  25. {gea_program-0.90.0 → gea_program-0.91.0}/gea/service.py +22 -0
  26. {gea_program-0.90.0 → gea_program-0.91.0}/gea/web/app.html +128 -2
  27. {gea_program-0.90.0 → gea_program-0.91.0}/gea_program.egg-info/PKG-INFO +44 -4
  28. {gea_program-0.90.0 → gea_program-0.91.0}/gea_program.egg-info/SOURCES.txt +2 -0
  29. {gea_program-0.90.0 → gea_program-0.91.0}/pyproject.toml +1 -1
  30. {gea_program-0.90.0 → gea_program-0.91.0}/LICENSE +0 -0
  31. {gea_program-0.90.0 → gea_program-0.91.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  32. {gea_program-0.90.0 → gea_program-0.91.0}/gea/accuracy_statement.py +0 -0
  33. {gea_program-0.90.0 → gea_program-0.91.0}/gea/alarm_engine.py +0 -0
  34. {gea_program-0.90.0 → gea_program-0.91.0}/gea/alerts.py +0 -0
  35. {gea_program-0.90.0 → gea_program-0.91.0}/gea/backup.py +0 -0
  36. {gea_program-0.90.0 → gea_program-0.91.0}/gea/bench.py +0 -0
  37. {gea_program-0.90.0 → gea_program-0.91.0}/gea/benchmark.py +0 -0
  38. {gea_program-0.90.0 → gea_program-0.91.0}/gea/bit_seismic.py +0 -0
  39. {gea_program-0.90.0 → gea_program-0.91.0}/gea/blind_harness.py +0 -0
  40. {gea_program-0.90.0 → gea_program-0.91.0}/gea/calibration.py +0 -0
  41. {gea_program-0.90.0 → gea_program-0.91.0}/gea/casing_design.py +0 -0
  42. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  43. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  44. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  45. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  46. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  47. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  48. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  49. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  50. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  51. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  52. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  53. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  54. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  55. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  56. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  57. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  58. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  59. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  60. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  61. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  62. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  63. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  64. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  65. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  66. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  67. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  68. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  69. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  70. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  71. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  72. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  73. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  74. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  75. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  76. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  77. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  78. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  79. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  80. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  81. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  82. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  83. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  84. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  85. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  86. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  87. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  88. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  89. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  90. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  91. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  92. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  93. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  94. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  95. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  96. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  97. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  98. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  99. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  100. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  101. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  102. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  103. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  104. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  105. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  106. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  107. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  108. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  109. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/med_160_sapropels.txt +0 -0
  110. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  111. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  112. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  113. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  114. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  115. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  116. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  117. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  118. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  119. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  120. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  121. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  122. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  123. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  124. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  125. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  126. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  127. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  128. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  129. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  130. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  131. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  132. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  133. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  134. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  135. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  136. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  137. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  138. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  139. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  140. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  141. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  142. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  143. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  144. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  145. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  146. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog_map.py +0 -0
  147. {gea_program-0.90.0 → gea_program-0.91.0}/gea/catalog_register.py +0 -0
  148. {gea_program-0.90.0 → gea_program-0.91.0}/gea/certificates.py +0 -0
  149. {gea_program-0.90.0 → gea_program-0.91.0}/gea/cli.py +0 -0
  150. {gea_program-0.90.0 → gea_program-0.91.0}/gea/client_reports.py +0 -0
  151. {gea_program-0.90.0 → gea_program-0.91.0}/gea/collision.py +0 -0
  152. {gea_program-0.90.0 → gea_program-0.91.0}/gea/config_versioning.py +0 -0
  153. {gea_program-0.90.0 → gea_program-0.91.0}/gea/conformance.py +0 -0
  154. {gea_program-0.90.0 → gea_program-0.91.0}/gea/control_room.py +0 -0
  155. {gea_program-0.90.0 → gea_program-0.91.0}/gea/correlation.py +0 -0
  156. {gea_program-0.90.0 → gea_program-0.91.0}/gea/dashboard.py +0 -0
  157. {gea_program-0.90.0 → gea_program-0.91.0}/gea/declarations.py +0 -0
  158. {gea_program-0.90.0 → gea_program-0.91.0}/gea/decline.py +0 -0
  159. {gea_program-0.90.0 → gea_program-0.91.0}/gea/deviation.py +0 -0
  160. {gea_program-0.90.0 → gea_program-0.91.0}/gea/dlis.py +0 -0
  161. {gea_program-0.90.0 → gea_program-0.91.0}/gea/dnp3.py +0 -0
  162. {gea_program-0.90.0 → gea_program-0.91.0}/gea/doctor.py +0 -0
  163. {gea_program-0.90.0 → gea_program-0.91.0}/gea/dossier.py +0 -0
  164. {gea_program-0.90.0 → gea_program-0.91.0}/gea/dossier_verify.py +0 -0
  165. {gea_program-0.90.0 → gea_program-0.91.0}/gea/downhole_engine.py +0 -0
  166. {gea_program-0.90.0 → gea_program-0.91.0}/gea/drift_monitor.py +0 -0
  167. {gea_program-0.90.0 → gea_program-0.91.0}/gea/drill_string.py +0 -0
  168. {gea_program-0.90.0 → gea_program-0.91.0}/gea/driller_link.py +0 -0
  169. {gea_program-0.90.0 → gea_program-0.91.0}/gea/dysfunction.py +0 -0
  170. {gea_program-0.90.0 → gea_program-0.91.0}/gea/earth_model.py +0 -0
  171. {gea_program-0.90.0 → gea_program-0.91.0}/gea/economics.py +0 -0
  172. {gea_program-0.90.0 → gea_program-0.91.0}/gea/ed25519.py +0 -0
  173. {gea_program-0.90.0 → gea_program-0.91.0}/gea/edr.py +0 -0
  174. {gea_program-0.90.0 → gea_program-0.91.0}/gea/elevation.py +0 -0
  175. {gea_program-0.90.0 → gea_program-0.91.0}/gea/etp.py +0 -0
  176. {gea_program-0.90.0 → gea_program-0.91.0}/gea/etp12_schemas.json +0 -0
  177. {gea_program-0.90.0 → gea_program-0.91.0}/gea/example_register_map.json +0 -0
  178. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fat_sat.py +0 -0
  179. {gea_program-0.90.0 → gea_program-0.91.0}/gea/field_pull.py +0 -0
  180. {gea_program-0.90.0 → gea_program-0.91.0}/gea/files.py +0 -0
  181. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fixtures/README.md +0 -0
  182. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  183. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  184. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  185. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  186. {gea_program-0.90.0 → gea_program-0.91.0}/gea/fluid.py +0 -0
  187. {gea_program-0.90.0 → gea_program-0.91.0}/gea/follower.py +0 -0
  188. {gea_program-0.90.0 → gea_program-0.91.0}/gea/forward_model.py +0 -0
  189. {gea_program-0.90.0 → gea_program-0.91.0}/gea/frac_hits.py +0 -0
  190. {gea_program-0.90.0 → gea_program-0.91.0}/gea/friction_priors.py +0 -0
  191. {gea_program-0.90.0 → gea_program-0.91.0}/gea/front.py +0 -0
  192. {gea_program-0.90.0 → gea_program-0.91.0}/gea/gamma.py +0 -0
  193. {gea_program-0.90.0 → gea_program-0.91.0}/gea/gauge_aging.py +0 -0
  194. {gea_program-0.90.0 → gea_program-0.91.0}/gea/gauge_specs.py +0 -0
  195. {gea_program-0.90.0 → gea_program-0.91.0}/gea/geodesy.py +0 -0
  196. {gea_program-0.90.0 → gea_program-0.91.0}/gea/geomag.py +0 -0
  197. {gea_program-0.90.0 → gea_program-0.91.0}/gea/gravity_reference.py +0 -0
  198. {gea_program-0.90.0 → gea_program-0.91.0}/gea/hartip.py +0 -0
  199. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/IT_SECURITY.md +0 -0
  200. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/TESTER_GUIDE.md +0 -0
  201. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/alarms.md +0 -0
  202. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/alerts.md +0 -0
  203. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/audit-update.md +0 -0
  204. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/backup.md +0 -0
  205. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/benchmark.md +0 -0
  206. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/bit-image.md +0 -0
  207. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/calibration.md +0 -0
  208. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/casing.md +0 -0
  209. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/catalog.md +0 -0
  210. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/charts.md +0 -0
  211. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/collision.md +0 -0
  212. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/company-sign-in.md +0 -0
  213. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/conformance.md +0 -0
  214. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/control-room.md +0 -0
  215. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/data-in.md +0 -0
  216. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/declare.md +0 -0
  217. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/decline.md +0 -0
  218. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/dlis.md +0 -0
  219. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/doctor.md +0 -0
  220. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/dossier.md +0 -0
  221. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/drift.md +0 -0
  222. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/drillstring.md +0 -0
  223. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/dysfunction.md +0 -0
  224. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/economics.md +0 -0
  225. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/edr.md +0 -0
  226. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/etp.md +0 -0
  227. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/field-ports.md +0 -0
  228. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/files.md +0 -0
  229. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/fluid.md +0 -0
  230. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/frac-hits.md +0 -0
  231. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/front.md +0 -0
  232. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/historian.md +0 -0
  233. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/https.md +0 -0
  234. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/hydraulics.md +0 -0
  235. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/instruments.md +0 -0
  236. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/interval.md +0 -0
  237. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/invoice-check.md +0 -0
  238. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/map.md +0 -0
  239. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/month-end.md +0 -0
  240. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/notifications.md +0 -0
  241. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/opseis.md +0 -0
  242. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/osdu.md +0 -0
  243. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/pad-summary.md +0 -0
  244. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/patches.md +0 -0
  245. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/penetrator.md +0 -0
  246. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/plan.md +0 -0
  247. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/plume.md +0 -0
  248. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/pore-pressure.md +0 -0
  249. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/ppdm.md +0 -0
  250. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/quakeml.md +0 -0
  251. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/quality.md +0 -0
  252. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/records.md +0 -0
  253. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/report-history.md +0 -0
  254. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/rig-time.md +0 -0
  255. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/roadmap.md +0 -0
  256. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/rocks.md +0 -0
  257. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/seedlink.md +0 -0
  258. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/seismic.md +0 -0
  259. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/setup.md +0 -0
  260. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/simulation.md +0 -0
  261. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/site.md +0 -0
  262. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/sites.md +0 -0
  263. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/slip.md +0 -0
  264. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/soak.md +0 -0
  265. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/sra.md +0 -0
  266. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/start.md +0 -0
  267. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/structure.md +0 -0
  268. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tools.md +0 -0
  269. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/torque-drag.md +0 -0
  270. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/transients.md +0 -0
  271. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/02-sign-in.md +0 -0
  272. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/03-site.md +0 -0
  273. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/04-add-well.md +0 -0
  274. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/05-records.md +0 -0
  275. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/06-well-page.md +0 -0
  276. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/07-declare.md +0 -0
  277. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/08-patches.md +0 -0
  278. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/09-alarms.md +0 -0
  279. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/10-control-room.md +0 -0
  280. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/11-summary-month.md +0 -0
  281. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/12-where-the-well-is.md +0 -0
  282. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/13-drilling-pages.md +0 -0
  283. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/15-seismic.md +0 -0
  284. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/16-seismicity.md +0 -0
  285. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/17-recorder-office.md +0 -0
  286. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/18-reports.md +0 -0
  287. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/20-admin.md +0 -0
  288. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/21-upkeep.md +0 -0
  289. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/24-the-box.md +0 -0
  290. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/26-monitor.md +0 -0
  291. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/27-mirror-vpn.md +0 -0
  292. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/tutorial/28-driller-office.md +0 -0
  293. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/upkeep.md +0 -0
  294. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/vibration.md +0 -0
  295. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/vpn.md +0 -0
  296. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/well-report.md +0 -0
  297. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/well-tests.md +0 -0
  298. {gea_program-0.90.0 → gea_program-0.91.0}/gea/help/well3d.md +0 -0
  299. {gea_program-0.90.0 → gea_program-0.91.0}/gea/housekeeping.py +0 -0
  300. {gea_program-0.90.0 → gea_program-0.91.0}/gea/hydraulics.py +0 -0
  301. {gea_program-0.90.0 → gea_program-0.91.0}/gea/icon.py +0 -0
  302. {gea_program-0.90.0 → gea_program-0.91.0}/gea/interval.py +0 -0
  303. {gea_program-0.90.0 → gea_program-0.91.0}/gea/inverse_engine.py +0 -0
  304. {gea_program-0.90.0 → gea_program-0.91.0}/gea/invoice_check.py +0 -0
  305. {gea_program-0.90.0 → gea_program-0.91.0}/gea/jobs.py +0 -0
  306. {gea_program-0.90.0 → gea_program-0.91.0}/gea/launcher.py +0 -0
  307. {gea_program-0.90.0 → gea_program-0.91.0}/gea/live_ports.py +0 -0
  308. {gea_program-0.90.0 → gea_program-0.91.0}/gea/loadtest.py +0 -0
  309. {gea_program-0.90.0 → gea_program-0.91.0}/gea/matplotlib_demo.py +0 -0
  310. {gea_program-0.90.0 → gea_program-0.91.0}/gea/modbus.py +0 -0
  311. {gea_program-0.90.0 → gea_program-0.91.0}/gea/modbus_rtu.py +0 -0
  312. {gea_program-0.90.0 → gea_program-0.91.0}/gea/model_card.py +0 -0
  313. {gea_program-0.90.0 → gea_program-0.91.0}/gea/month_end.py +0 -0
  314. {gea_program-0.90.0 → gea_program-0.91.0}/gea/mqtt_port.py +0 -0
  315. {gea_program-0.90.0 → gea_program-0.91.0}/gea/notify.py +0 -0
  316. {gea_program-0.90.0 → gea_program-0.91.0}/gea/oidc.py +0 -0
  317. {gea_program-0.90.0 → gea_program-0.91.0}/gea/oidc_mock.py +0 -0
  318. {gea_program-0.90.0 → gea_program-0.91.0}/gea/op_seismicity.py +0 -0
  319. {gea_program-0.90.0 → gea_program-0.91.0}/gea/opcua_port.py +0 -0
  320. {gea_program-0.90.0 → gea_program-0.91.0}/gea/operator_app.py +0 -0
  321. {gea_program-0.90.0 → gea_program-0.91.0}/gea/osdu.py +0 -0
  322. {gea_program-0.90.0 → gea_program-0.91.0}/gea/pad_summary.py +0 -0
  323. {gea_program-0.90.0 → gea_program-0.91.0}/gea/patches.py +0 -0
  324. {gea_program-0.90.0 → gea_program-0.91.0}/gea/pdf_report.py +0 -0
  325. {gea_program-0.90.0 → gea_program-0.91.0}/gea/penetrator.py +0 -0
  326. {gea_program-0.90.0 → gea_program-0.91.0}/gea/permits.py +0 -0
  327. {gea_program-0.90.0 → gea_program-0.91.0}/gea/piwebapi.py +0 -0
  328. {gea_program-0.90.0 → gea_program-0.91.0}/gea/plume.py +0 -0
  329. {gea_program-0.90.0 → gea_program-0.91.0}/gea/pore_pressure.py +0 -0
  330. {gea_program-0.90.0 → gea_program-0.91.0}/gea/ports.py +0 -0
  331. {gea_program-0.90.0 → gea_program-0.91.0}/gea/ppdm.py +0 -0
  332. {gea_program-0.90.0 → gea_program-0.91.0}/gea/profile_catalog.py +0 -0
  333. {gea_program-0.90.0 → gea_program-0.91.0}/gea/project.py +0 -0
  334. {gea_program-0.90.0 → gea_program-0.91.0}/gea/qt6_downhole_app.py +0 -0
  335. {gea_program-0.90.0 → gea_program-0.91.0}/gea/quakeml.py +0 -0
  336. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reader.py +0 -0
  337. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reader_app.py +0 -0
  338. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reconciler.py +0 -0
  339. {gea_program-0.90.0 → gea_program-0.91.0}/gea/recorder_link.py +0 -0
  340. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  341. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  342. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/WMM2025.COF +0 -0
  343. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  344. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/eia_prices.provenance.json +0 -0
  345. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  346. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/iscwsa_rev4_example.json +0 -0
  347. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/iscwsa_rev5_example.json +0 -0
  348. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
  349. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
  350. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  351. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  352. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  353. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  354. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/mahoning_example.json +0 -0
  355. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/mud_motor_specs.json +0 -0
  356. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/owsg_tool_models.json +0 -0
  357. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/penetrator_ratings.json +0 -0
  358. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/seismicity_protocols.json +0 -0
  359. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/tool_sources.json +0 -0
  360. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/us_outline.json +0 -0
  361. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/us_outline.provenance.json +0 -0
  362. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_3d.provenance.json +0 -0
  363. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  364. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  365. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  366. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_surfaces.json.gz +0 -0
  367. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  368. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  369. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  370. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  371. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  372. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  373. {gea_program-0.90.0 → gea_program-0.91.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  374. {gea_program-0.90.0 → gea_program-0.91.0}/gea/report_history.py +0 -0
  375. {gea_program-0.90.0 → gea_program-0.91.0}/gea/rig_state.py +0 -0
  376. {gea_program-0.90.0 → gea_program-0.91.0}/gea/roadmap.py +0 -0
  377. {gea_program-0.90.0 → gea_program-0.91.0}/gea/rock_inventory.py +0 -0
  378. {gea_program-0.90.0 → gea_program-0.91.0}/gea/sample_record.py +0 -0
  379. {gea_program-0.90.0 → gea_program-0.91.0}/gea/sample_well_profile.csv +0 -0
  380. {gea_program-0.90.0 → gea_program-0.91.0}/gea/sbom.py +0 -0
  381. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seedlink.py +0 -0
  382. {gea_program-0.90.0 → gea_program-0.91.0}/gea/segy.py +0 -0
  383. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic.py +0 -0
  384. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_array.py +0 -0
  385. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_assoc.py +0 -0
  386. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_detect.py +0 -0
  387. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_film.py +0 -0
  388. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_harmonic.py +0 -0
  389. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_qc.py +0 -0
  390. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_response.py +0 -0
  391. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_signature.py +0 -0
  392. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_track.py +0 -0
  393. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_uncertainty.py +0 -0
  394. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_unlisted.py +0 -0
  395. {gea_program-0.90.0 → gea_program-0.91.0}/gea/seismic_watch.py +0 -0
  396. {gea_program-0.90.0 → gea_program-0.91.0}/gea/sensor_swap.py +0 -0
  397. {gea_program-0.90.0 → gea_program-0.91.0}/gea/series_cache.py +0 -0
  398. {gea_program-0.90.0 → gea_program-0.91.0}/gea/service_life.py +0 -0
  399. {gea_program-0.90.0 → gea_program-0.91.0}/gea/setup_checklist.py +0 -0
  400. {gea_program-0.90.0 → gea_program-0.91.0}/gea/shapefile.py +0 -0
  401. {gea_program-0.90.0 → gea_program-0.91.0}/gea/shell.py +0 -0
  402. {gea_program-0.90.0 → gea_program-0.91.0}/gea/shut_in.py +0 -0
  403. {gea_program-0.90.0 → gea_program-0.91.0}/gea/simulation.py +0 -0
  404. {gea_program-0.90.0 → gea_program-0.91.0}/gea/site_map.py +0 -0
  405. {gea_program-0.90.0 → gea_program-0.91.0}/gea/sla_report.py +0 -0
  406. {gea_program-0.90.0 → gea_program-0.91.0}/gea/slip.py +0 -0
  407. {gea_program-0.90.0 → gea_program-0.91.0}/gea/soak.py +0 -0
  408. {gea_program-0.90.0 → gea_program-0.91.0}/gea/sra.py +0 -0
  409. {gea_program-0.90.0 → gea_program-0.91.0}/gea/store_forward.py +0 -0
  410. {gea_program-0.90.0 → gea_program-0.91.0}/gea/strata_join.py +0 -0
  411. {gea_program-0.90.0 → gea_program-0.91.0}/gea/structure.py +0 -0
  412. {gea_program-0.90.0 → gea_program-0.91.0}/gea/supervisor.py +0 -0
  413. {gea_program-0.90.0 → gea_program-0.91.0}/gea/survey_cmd.py +0 -0
  414. {gea_program-0.90.0 → gea_program-0.91.0}/gea/survey_view.py +0 -0
  415. {gea_program-0.90.0 → gea_program-0.91.0}/gea/telemetry.py +0 -0
  416. {gea_program-0.90.0 → gea_program-0.91.0}/gea/timestamp.py +0 -0
  417. {gea_program-0.90.0 → gea_program-0.91.0}/gea/tls.py +0 -0
  418. {gea_program-0.90.0 → gea_program-0.91.0}/gea/tool_catalog.py +0 -0
  419. {gea_program-0.90.0 → gea_program-0.91.0}/gea/tool_library.py +0 -0
  420. {gea_program-0.90.0 → gea_program-0.91.0}/gea/tool_taxonomy.py +0 -0
  421. {gea_program-0.90.0 → gea_program-0.91.0}/gea/torque_drag.py +0 -0
  422. {gea_program-0.90.0 → gea_program-0.91.0}/gea/transient.py +0 -0
  423. {gea_program-0.90.0 → gea_program-0.91.0}/gea/tray.py +0 -0
  424. {gea_program-0.90.0 → gea_program-0.91.0}/gea/tutorial.py +0 -0
  425. {gea_program-0.90.0 → gea_program-0.91.0}/gea/updates.py +0 -0
  426. {gea_program-0.90.0 → gea_program-0.91.0}/gea/vibration.py +0 -0
  427. {gea_program-0.90.0 → gea_program-0.91.0}/gea/vpn.py +0 -0
  428. {gea_program-0.90.0 → gea_program-0.91.0}/gea/web/reader.html +0 -0
  429. {gea_program-0.90.0 → gea_program-0.91.0}/gea/well3d.py +0 -0
  430. {gea_program-0.90.0 → gea_program-0.91.0}/gea/well_assembler.py +0 -0
  431. {gea_program-0.90.0 → gea_program-0.91.0}/gea/well_records.py +0 -0
  432. {gea_program-0.90.0 → gea_program-0.91.0}/gea/well_report.py +0 -0
  433. {gea_program-0.90.0 → gea_program-0.91.0}/gea/well_test_validation.py +0 -0
  434. {gea_program-0.90.0 → gea_program-0.91.0}/gea/wellplan.py +0 -0
  435. {gea_program-0.90.0 → gea_program-0.91.0}/gea/wellpos.py +0 -0
  436. {gea_program-0.90.0 → gea_program-0.91.0}/gea/winident.py +0 -0
  437. {gea_program-0.90.0 → gea_program-0.91.0}/gea/wits0.py +0 -0
  438. {gea_program-0.90.0 → gea_program-0.91.0}/gea/witsml.py +0 -0
  439. {gea_program-0.90.0 → gea_program-0.91.0}/gea/workspace.py +0 -0
  440. {gea_program-0.90.0 → gea_program-0.91.0}/gea/workspaces.py +0 -0
  441. {gea_program-0.90.0 → gea_program-0.91.0}/gea/x25519.py +0 -0
  442. {gea_program-0.90.0 → gea_program-0.91.0}/gea/xlsx_read.py +0 -0
  443. {gea_program-0.90.0 → gea_program-0.91.0}/gea_program.egg-info/dependency_links.txt +0 -0
  444. {gea_program-0.90.0 → gea_program-0.91.0}/gea_program.egg-info/entry_points.txt +0 -0
  445. {gea_program-0.90.0 → gea_program-0.91.0}/gea_program.egg-info/requires.txt +0 -0
  446. {gea_program-0.90.0 → gea_program-0.91.0}/gea_program.egg-info/top_level.txt +0 -0
  447. {gea_program-0.90.0 → gea_program-0.91.0}/setup.cfg +0 -0
  448. {gea_program-0.90.0 → gea_program-0.91.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.90.0
3
+ Version: 0.91.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
@@ -847,7 +847,7 @@ unsigned, the key hand-overs - and shows the reports as they were signed. A
847
847
  reader made from the workspace knows the site's key fingerprints, so it
848
848
  says whether a dossier is the site's; it allows no network request, and it
849
849
  leaves the trusted time-stamp to `verify_dossier.py`. For a client who wants
850
- it installed, every release also carries `GEA-Reader-Setup-0.90.0.exe`: GEA
850
+ it installed, every release also carries `GEA-Reader-Setup-0.91.0.exe`: GEA
851
851
  Reader as a Windows program of its own - its own icon and taskbar button,
852
852
  Check with GEA Reader on a dossier's right-click menu, fingerprints the client
853
853
  saves kept on that computer, nothing of GEA-Program. `gea help reader`.
@@ -934,6 +934,26 @@ the window. The Dutch offshore well L07-01 (NLOG open data) is the worked
934
934
  example. The **Pore pressure** tab, `gea pore-pressure`,
935
935
  `gea help pore-pressure`.
936
936
 
937
+ ### Frac treatment
938
+
939
+ Each fracturing stage from its own pumping record - the treating pressure,
940
+ the slurry rate and the proppant concentration, second by second, as the
941
+ frac service company gave them (a spreadsheet with its stacked header, a
942
+ frac van's data-acquisition export, or a plain CSV). With nothing fitted to
943
+ a model of the fracture, the page reads what was pumped beside the record's
944
+ own totalisers; the ISIP - a straight line fitted to the pressure after the
945
+ ringing has died down and read back to the moment the rate reached zero,
946
+ over three windows, with the spread between them as its range; the friction
947
+ at the last rate held, and where the rate was stepped down its two parts,
948
+ one with the square of the rate (the pipe and the perforations) and one
949
+ with its root (the path just outside the wellbore); the pressure at 5, 10
950
+ and 15 minutes and against Nolte's G, with a closure candidate offered only
951
+ where the record supports one; and a step-rate test where there was one.
952
+ With the depth of the perforations declared, the bottomhole ISIP and its
953
+ gradient. It hands the Frac hits tab each stage's times. The **Frac
954
+ treatment** tab, `gea frac-treatment`, `gea help frac-treatment`; an
955
+ assistant reads it as the `frac_treatment` tool.
956
+
937
957
  ### Frac hits
938
958
 
939
959
  What each fracturing stage did to the pressure in the wells around it. A
@@ -1125,6 +1145,26 @@ that one well's parameters are not the next well's roadmap. The drilling
1125
1145
  alerts, replayed, find 27 motor stalls on the three wells whose records
1126
1146
  can show one, three falls in off-bottom pressure on 78B-32 that no trip followed
1127
1147
  and one on 58-32 that a trip out of the hole followed within twelve hours.
1148
+
1149
+ The same site takes the published pumping records of the two deviated
1150
+ wells' stimulation - three stages on 16A(78)-32 in April 2022, nine more
1151
+ and four on 16B(78)-32 in April 2024 - when those files are in the folder
1152
+ too, and the Frac treatment tab reads all sixteen: 132,132 bbl of slurry
1153
+ and 2,969,800 lbm of proppant by the records' own rates, fifteen of the
1154
+ sixteen volumes within 0.6 % of the record's own totaliser (the sixteenth
1155
+ 3.6 % under it: that totaliser also counted the lines being primed); an
1156
+ ISIP at every stage, 3,289 to 5,664 psi at the surface; thirteen stages
1157
+ stepped down to the shut-in. The service company's reports are published with the
1158
+ 2022 stages, so the page can be checked against them: for stage 1 the
1159
+ company gives a final ISIP of 4,019 psi and 3,715, 3,582 and 3,501 psi
1160
+ after 5, 10 and 15 minutes, and this page reads 3,985 psi (3,975 to 3,997)
1161
+ and 3,720, 3,585 and 3,486; for stage 2, 3,918 psi and 3,664, 3,565 and
1162
+ 3,506 against 3,859 (3,852 to 3,862) and 3,665, 3,572 and 3,508. The page's
1163
+ ISIP is the lower because it is read back along the decline, not taken at
1164
+ the top of the ringing. No
1165
+ stage's fall-off shows a closure: the records stop a quarter of an hour
1166
+ after the pumps, and the page says so and offers none.
1167
+
1128
1168
  Data: Utah FORGE, Energy & Geoscience Institute at the University of Utah,
1129
1169
  funded by the U.S. Department of Energy; Geothermal Data Repository,
1130
1170
  gdr.openei.org; CC BY 4.0. The **Public data** tab, `gea public-pad`,
@@ -1794,7 +1834,7 @@ attaches the zips to the GitHub release of that tag.
1794
1834
 
1795
1835
  ### The Windows installer
1796
1836
 
1797
- Every release also carries `GEA-Program-Setup-0.90.0.exe` (for 0.90.0): the
1837
+ Every release also carries `GEA-Program-Setup-0.91.0.exe` (for 0.91.0): the
1798
1838
  same Windows kit installed like any Windows program, for the signed-in user
1799
1839
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1800
1840
  It asks for the site workspace (an existing one such as `C:\site` is used as
@@ -1813,7 +1853,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1813
1853
  software deployment:
1814
1854
 
1815
1855
  ```
1816
- GEA-Program-Setup-0.90.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1856
+ GEA-Program-Setup-0.91.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1817
1857
  ```
1818
1858
 
1819
1859
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -808,7 +808,7 @@ unsigned, the key hand-overs - and shows the reports as they were signed. A
808
808
  reader made from the workspace knows the site's key fingerprints, so it
809
809
  says whether a dossier is the site's; it allows no network request, and it
810
810
  leaves the trusted time-stamp to `verify_dossier.py`. For a client who wants
811
- it installed, every release also carries `GEA-Reader-Setup-0.90.0.exe`: GEA
811
+ it installed, every release also carries `GEA-Reader-Setup-0.91.0.exe`: GEA
812
812
  Reader as a Windows program of its own - its own icon and taskbar button,
813
813
  Check with GEA Reader on a dossier's right-click menu, fingerprints the client
814
814
  saves kept on that computer, nothing of GEA-Program. `gea help reader`.
@@ -895,6 +895,26 @@ the window. The Dutch offshore well L07-01 (NLOG open data) is the worked
895
895
  example. The **Pore pressure** tab, `gea pore-pressure`,
896
896
  `gea help pore-pressure`.
897
897
 
898
+ ### Frac treatment
899
+
900
+ Each fracturing stage from its own pumping record - the treating pressure,
901
+ the slurry rate and the proppant concentration, second by second, as the
902
+ frac service company gave them (a spreadsheet with its stacked header, a
903
+ frac van's data-acquisition export, or a plain CSV). With nothing fitted to
904
+ a model of the fracture, the page reads what was pumped beside the record's
905
+ own totalisers; the ISIP - a straight line fitted to the pressure after the
906
+ ringing has died down and read back to the moment the rate reached zero,
907
+ over three windows, with the spread between them as its range; the friction
908
+ at the last rate held, and where the rate was stepped down its two parts,
909
+ one with the square of the rate (the pipe and the perforations) and one
910
+ with its root (the path just outside the wellbore); the pressure at 5, 10
911
+ and 15 minutes and against Nolte's G, with a closure candidate offered only
912
+ where the record supports one; and a step-rate test where there was one.
913
+ With the depth of the perforations declared, the bottomhole ISIP and its
914
+ gradient. It hands the Frac hits tab each stage's times. The **Frac
915
+ treatment** tab, `gea frac-treatment`, `gea help frac-treatment`; an
916
+ assistant reads it as the `frac_treatment` tool.
917
+
898
918
  ### Frac hits
899
919
 
900
920
  What each fracturing stage did to the pressure in the wells around it. A
@@ -1086,6 +1106,26 @@ that one well's parameters are not the next well's roadmap. The drilling
1086
1106
  alerts, replayed, find 27 motor stalls on the three wells whose records
1087
1107
  can show one, three falls in off-bottom pressure on 78B-32 that no trip followed
1088
1108
  and one on 58-32 that a trip out of the hole followed within twelve hours.
1109
+
1110
+ The same site takes the published pumping records of the two deviated
1111
+ wells' stimulation - three stages on 16A(78)-32 in April 2022, nine more
1112
+ and four on 16B(78)-32 in April 2024 - when those files are in the folder
1113
+ too, and the Frac treatment tab reads all sixteen: 132,132 bbl of slurry
1114
+ and 2,969,800 lbm of proppant by the records' own rates, fifteen of the
1115
+ sixteen volumes within 0.6 % of the record's own totaliser (the sixteenth
1116
+ 3.6 % under it: that totaliser also counted the lines being primed); an
1117
+ ISIP at every stage, 3,289 to 5,664 psi at the surface; thirteen stages
1118
+ stepped down to the shut-in. The service company's reports are published with the
1119
+ 2022 stages, so the page can be checked against them: for stage 1 the
1120
+ company gives a final ISIP of 4,019 psi and 3,715, 3,582 and 3,501 psi
1121
+ after 5, 10 and 15 minutes, and this page reads 3,985 psi (3,975 to 3,997)
1122
+ and 3,720, 3,585 and 3,486; for stage 2, 3,918 psi and 3,664, 3,565 and
1123
+ 3,506 against 3,859 (3,852 to 3,862) and 3,665, 3,572 and 3,508. The page's
1124
+ ISIP is the lower because it is read back along the decline, not taken at
1125
+ the top of the ringing. No
1126
+ stage's fall-off shows a closure: the records stop a quarter of an hour
1127
+ after the pumps, and the page says so and offers none.
1128
+
1089
1129
  Data: Utah FORGE, Energy & Geoscience Institute at the University of Utah,
1090
1130
  funded by the U.S. Department of Energy; Geothermal Data Repository,
1091
1131
  gdr.openei.org; CC BY 4.0. The **Public data** tab, `gea public-pad`,
@@ -1755,7 +1795,7 @@ attaches the zips to the GitHub release of that tag.
1755
1795
 
1756
1796
  ### The Windows installer
1757
1797
 
1758
- Every release also carries `GEA-Program-Setup-0.90.0.exe` (for 0.90.0): the
1798
+ Every release also carries `GEA-Program-Setup-0.91.0.exe` (for 0.91.0): the
1759
1799
  same Windows kit installed like any Windows program, for the signed-in user
1760
1800
  alone and with no administrator rights (`%LOCALAPPDATA%\Programs\GEA-Program`).
1761
1801
  It asks for the site workspace (an existing one such as `C:\site` is used as
@@ -1774,7 +1814,7 @@ it, removes the program and leaves the workspace. Silent, for a site's
1774
1814
  software deployment:
1775
1815
 
1776
1816
  ```
1777
- GEA-Program-Setup-0.90.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1817
+ GEA-Program-Setup-0.91.0.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /WORKSPACE="C:\site" /SITENAME="Pad 3" /TASKS="desktopicon"
1778
1818
  ```
1779
1819
 
1780
1820
  The setup is built by Inno Setup 6 from `tools/installer.iss` in the same
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.90.0"
126
+ __version__ = "0.91.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -682,6 +682,19 @@ def main(argv=None) -> int:
682
682
  p_bm.add_argument("--write", action="store_true", help="write reports/benchmark/benchmark.json, .md, .csv and stretches.csv")
683
683
  p_bm.add_argument("--actor", type=str, default="cli")
684
684
  p_bm.add_argument("--json", action="store_true")
685
+ p_ft = sub.add_parser("frac-treatment", help="each fracturing stage from its own pumping record: what was pumped, the ISIP read back through the ringing with its range, the friction and its two parts from a step-down, the fall-off against G, a step-rate test - nothing fitted to a model of the fracture")
686
+ p_ft.add_argument("--workspace", type=str, default=None, help="the workspace folder; without one the self-test runs (stages built with every answer known)")
687
+ p_ft.add_argument("--well", type=str, default=None, help="a well, by its name or id: with --stage, that stage in full")
688
+ p_ft.add_argument("--stage", type=str, default=None, help="the stage (1, 3R, 6A)")
689
+ p_ft.add_argument("--attach", type=str, default=None, help="a stage's treatment record to keep (CSV or Excel: the pressure, the rate and the proppant, second by second); needs --well, --stage and --source")
690
+ p_ft.add_argument("--start", type=str, default=None, help="--attach: the record's first moment, for a file that only has elapsed time (2026-08-03T06:00:00)")
691
+ p_ft.add_argument("--tvd", type=float, default=None, help="declare the true vertical depth of the perforations, ft: for --stage, or every stage of --well; needs --source")
692
+ p_ft.add_argument("--fluid", type=float, default=None, help="with --tvd: the gradient of the fluid in the well at the shut-in, psi/ft (fresh water, 0.433, when not given)")
693
+ p_ft.add_argument("--source", type=str, default="", help="where the record, or the depth, came from")
694
+ p_ft.add_argument("--to-frac-hits", action="store_true", help="give the Frac hits page --well's stage times, read from these records")
695
+ p_ft.add_argument("--write", action="store_true", help="write reports/frac_treatment/frac_treatment.json, .md, stages.csv and a stage table a well")
696
+ p_ft.add_argument("--actor", type=str, default="cli")
697
+ p_ft.add_argument("--json", action="store_true")
685
698
  p_pp = sub.add_parser("public-pad", help="a real pad from public data: the Utah FORGE wells' published rig recorders (CC BY 4.0) made into a site of their own - the files to get and where, checked against their sizes and hashes, converted and attached; the program fetches nothing")
686
699
  p_pp.add_argument("--folder", type=str, default=None, help="the folder the downloaded files are in; with nothing else, lists each file and whether it is there. Without a folder the self-test runs (stand-ins in each published layout)")
687
700
  p_pp.add_argument("--verify", action="store_true", help="hash each file that is there against the SHA-256 this release was checked with")
@@ -3609,6 +3622,48 @@ def main(argv=None) -> int:
3609
3622
  raise SystemExit(f"benchmark: {e.args[0] if e.args else e}")
3610
3623
  print(_json.dumps(p, default=str) if a.json else _BM.text(p))
3611
3624
  return 0
3625
+ elif a.cmd == "frac-treatment":
3626
+ import json as _json
3627
+ from . import frac_treatment as _FT
3628
+ from .cli import _safe_stdout
3629
+ from .workspace import Workspace, WorkspaceError
3630
+ _safe_stdout()
3631
+ if not a.workspace:
3632
+ r = _FT.selftest()
3633
+ print("\n".join(f"{'PASS' if c['pass'] else 'FAIL'} {c['check']}: {c['value']}" for c in r["checks"]) + f"\n{'OK' if r['ok'] else 'FAILED'} - {r['label']}")
3634
+ return 0 if r["ok"] else 1
3635
+ try:
3636
+ ws = Workspace(a.workspace)
3637
+ wid = None
3638
+ if a.well:
3639
+ wid = next((w["id"] for w in ws.wells() if a.well in (w["id"], w.get("display"))), None)
3640
+ if wid is None:
3641
+ raise SystemExit(f"frac-treatment: no well {a.well!r} in this workspace")
3642
+ if a.attach:
3643
+ if not (wid and a.stage):
3644
+ raise SystemExit("frac-treatment: --attach needs --well and --stage")
3645
+ with open(a.attach, "rb") as f_:
3646
+ m_ = _FT.attach(ws, wid, a.stage, a.attach, f_.read(), a.actor, a.source, a.start)
3647
+ print(f"kept: stage {m_['stage']}, {m_['samples']:,} samples from {m_['start'] or 'its first sample'} ({m_['layout']}); read as " + ", ".join(f"{k}: {v}" for k, v in m_["read_as"].items()))
3648
+ if a.tvd is not None:
3649
+ if not wid:
3650
+ raise SystemExit("frac-treatment: --tvd needs --well")
3651
+ d_ = _FT.declare(ws, wid, a.actor, a.source, a.tvd, a.fluid, a.stage)
3652
+ print(f"declared for stage {', '.join(d_['stages'])}: perforations at {d_['tvd_ft']:,.0f} ft TVD, fluid {d_['fluid_psi_ft']:g} psi/ft")
3653
+ if a.to_frac_hits:
3654
+ if not wid:
3655
+ raise SystemExit("frac-treatment: --to-frac-hits needs --well")
3656
+ h_ = _FT.to_frac_hits(ws, wid, a.actor)
3657
+ print(f"the Frac hits page has {h_['stages']} stage(s) for this well, {h_['first'][:16]} to {h_['last'][:16]}")
3658
+ if a.write:
3659
+ from . import __version__ as _ver91
3660
+ r = _FT.write_report(ws, _ver91, a.actor)
3661
+ print(f"written: {', '.join(r['files'])} under {r['folder']}")
3662
+ p = _FT.page(ws, wid, a.stage)
3663
+ except (_FT.TreatmentError, WorkspaceError, KeyError, ValueError, OSError) as e:
3664
+ raise SystemExit(f"frac-treatment: {e.args[0] if e.args else e}")
3665
+ print(_json.dumps(p, default=str) if a.json else _FT.text(p))
3666
+ return 0
3612
3667
  elif a.cmd == "public-pad":
3613
3668
  import json as _json
3614
3669
  from . import public_pad as _PP
@@ -3631,6 +3686,8 @@ def main(argv=None) -> int:
3631
3686
  print(f" {d_.get('n', '')}/{d_.get('of', '')} {t_}", flush=True)
3632
3687
  r = _PP.build(a.folder, a.site, a.actor, verify=True, progress=say_)
3633
3688
  print(f"{r['name']} at {r['path']}: " + (f"added {', '.join(r['added'])}" if r["added"] else "nothing to add") + f"; it has {', '.join(r['wells'])}")
3689
+ if r.get("stages"):
3690
+ print(f" and the pumping record of {len(r['stages'])} stimulation stage(s): {', '.join(r['stages'])}")
3634
3691
  for e_ in r["record"]["wells"]:
3635
3692
  for n_ in e_["notes"]:
3636
3693
  print(f" {e_['well']}: {n_}")
@@ -18763,7 +18763,7 @@ def section_eh_mcp(tmp: str) -> None:
18763
18763
  plain = MC._plain({"a": np.float64(1.5), "_work": [1, 2], "b": [np.int64(3), float("nan")], "c": np.arange(4), "d": (1, "x")})
18764
18764
  c1 = {
18765
18765
  "selftest": st["ok"] and len(st["checks"]) >= 7,
18766
- "tools": len(defs) == 24 and defs[0]["name"] == "site" and all(d_["annotations"]["readOnlyHint"] is True and d_["annotations"]["destructiveHint"] is False and d_["annotations"]["openWorldHint"] is False
18766
+ "tools": len(defs) == 25 and defs[0]["name"] == "site" and all(d_["annotations"]["readOnlyHint"] is True and d_["annotations"]["destructiveHint"] is False and d_["annotations"]["openWorldHint"] is False
18767
18767
  and d_["inputSchema"]["type"] == "object" and d_["inputSchema"]["additionalProperties"] is False and d_["title"] for d_ in defs)
18768
18768
  and "well" in defs[1]["inputSchema"]["properties"] and "json" in defs[2]["inputSchema"]["properties"] and "json" not in MC.tool_definition("help")["inputSchema"]["properties"]
18769
18769
  and not [n_ for n_ in MC.NAMES if any(w_ in n_ for w_ in ("write", "set", "declare", "attach", "remove", "delete", "run"))],
@@ -18775,7 +18775,7 @@ def section_eh_mcp(tmp: str) -> None:
18775
18775
  failed1 = [k_ for k_, v in c1.items() if not v]
18776
18776
  ok(not failed1,
18777
18777
  "EH1 the MCP server: the self-test (a client of an earlier revision through its handshake and a client of revision 2026-07-28 statelessly, each listing and calling the tools; a line that is "
18778
- "not JSON, a tool, a well, an argument and a method that do not exist, a batch; a withheld tool; the access log; an assistant's configuration file added to and restored); twenty-four tools, "
18778
+ "not JSON, a tool, a well, an argument and a method that do not exist, a batch; a withheld tool; the access log; an assistant's configuration file added to and restored); twenty-five tools, "
18779
18779
  "each read-only by its own declaration with its arguments closed, none named for a change; the revisions spoken; the command a client starts - the console interpreter, never the windowless "
18780
18780
  "one; a page as plain JSON with the working arrays left out" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
18781
18781
 
@@ -18853,12 +18853,12 @@ def section_eh_mcp(tmp: str) -> None:
18853
18853
  and "SIMULATION" in answers["pressure_plume"]["text"].splitlines()[0] and "RIG-1" in answers["site"]["text"] and _ver in answers["site"]["text"],
18854
18854
  "json": all(isinstance(v, (dict, list)) for n_, v in parsed.items() if n_ != "well") and parsed["well"] is None and as_json["well"]["is_error"] and parsed["roadmap"]["status"] == "OK" and not [k_ for k_ in parsed["rig_time"] if str(k_).startswith("_")]
18855
18855
  and len(parsed["drilling_alerts"]["wells"]) == 2,
18856
- "child_legacy": rc_o == 0 and len(out_o) == 4 and out_o[0]["result"]["protocolVersion"] == "2025-11-25" and len(out_o[1]["result"]["tools"]) == 24 and "resultType" not in out_o[1]["result"]
18856
+ "child_legacy": rc_o == 0 and len(out_o) == 4 and out_o[0]["result"]["protocolVersion"] == "2025-11-25" and len(out_o[1]["result"]["tools"]) == 25 and "resultType" not in out_o[1]["result"]
18857
18857
  and out_o[2]["result"]["content"][0]["text"] == answers["rig_time"]["text"] and "motor stall" in out_o[3]["result"]["content"][0]["text"] and "Traceback" not in err_o,
18858
18858
  "child_modern": rc_m == 0 and len(out_m) == 4 and out_m[0]["result"]["supportedVersions"] == list(MC.SUPPORTED) and out_m[1]["result"]["resultType"] == "complete"
18859
18859
  and out_m[1]["result"]["cacheScope"] == "private" and out_m[2]["result"]["_meta"][MC.K_SERVER] == {"name": "gea-program", "title": "GEA-Program", "version": _ver}
18860
18860
  and out_m[2]["result"]["isError"] is False and out_m[3]["error"]["code"] == -32022 and out_m[3]["error"]["data"]["requested"] == "2099-01-01" and "Traceback" not in err_m,
18861
- "withheld": kept["tools"] is not None and "audit" not in kept["tools"] and rc_w == 0 and "audit" not in [t_["name"] for t_ in out_w[0]["result"]["tools"]] and len(out_w[0]["result"]["tools"]) == 23
18861
+ "withheld": kept["tools"] is not None and "audit" not in kept["tools"] and rc_w == 0 and "audit" not in [t_["name"] for t_ in out_w[0]["result"]["tools"]] and len(out_w[0]["result"]["tools"]) == 24
18862
18862
  and out_w[1]["error"]["code"] == -32602 and "[cut at 3,000 of" in out_w[2]["result"]["content"][0]["text"],
18863
18863
  "narrowed": rc_a == 0 and [t_["name"] for t_ in out_a[0]["result"]["tools"]] == ["site"] and out_a[1]["error"]["code"] == -32602,
18864
18864
  "no_workspace": rc_x.returncode == 2 and rc_x.stdout == b"" and b"not a workspace" in rc_x.stderr,
@@ -18926,7 +18926,7 @@ def section_eh_mcp(tmp: str) -> None:
18926
18926
  its = (pkg / "help" / "IT_SECURITY.md").read_text(encoding="utf-8")
18927
18927
  backups = sorted(p_.name for p_ in cfgp.parent.iterdir() if ".before-gea-" in p_.name)
18928
18928
  c3 = {
18929
- "api": s_vg == 200 and len(vg["tools"]) == 24 and all(t_["offered"] for t_ in vg["tools"]) and vg["config"]["mcpServers"]["gea"]["args"][-1] == os.path.abspath(wsp) and len(vg["log"]) >= 6
18929
+ "api": s_vg == 200 and len(vg["tools"]) == 25 and all(t_["offered"] for t_ in vg["tools"]) and vg["config"]["mcpServers"]["gea"]["args"][-1] == os.path.abspath(wsp) and len(vg["log"]) >= 6
18930
18930
  and vg["claude_desktop"]["found"] and not vg["claude_desktop"]["installed"] and s_vs == 403 and s_os == 200 and os_["offered"] == ["site", "roadmap", "help"] and os_["max_chars"] == 20000
18931
18931
  and s_bs == 400 and s_oa == 200,
18932
18932
  "claude_desktop": s_oc == 403 and s_ac == 200 and len(ac["done"]) == 1 and cfg_added["globalShortcut"] == "Ctrl+Space" and cfg_added["mcpServers"]["files"] == {"command": "npx", "args": ["-y", "x"]}
@@ -19067,7 +19067,7 @@ def section_ei_public_pad_benchmark(tmp: str) -> None:
19067
19067
  and bm["pad_best_connection_min"] is not None and len(bm["stretches"]) >= 3 and all(s_["best_well"] in bm["names"] for s_ in bm["stretches"] if s_["best_rop_ft_h"]),
19068
19068
  "report": set(wr["files"]) == {"benchmark.json", "benchmark.md", "benchmark.csv", "stretches.csv"} and len(rows_b) == 6 and rows_b[0].startswith("display,drilled_ft,days,feet_per_day") and len(rows_s) == len(bm["stretches"]) + 1
19069
19069
  and "benchmark.report" in audit,
19070
- "tool": not bm_txt["is_error"] and bm_txt["text"] == BM.text(BM.page(ws)) and "benchmark" in MC.NAMES and len(MC.NAMES) == 24,
19070
+ "tool": not bm_txt["is_error"] and bm_txt["text"] == BM.text(BM.page(ws)) and "benchmark" in MC.NAMES and len(MC.NAMES) == 25,
19071
19071
  }
19072
19072
  failed2 = [k_ for k_, v in c2.items() if not v]
19073
19073
  ok(not failed2,
@@ -19160,6 +19160,273 @@ def section_ei_public_pad_benchmark(tmp: str) -> None:
19160
19160
  "Public data tabs, the help pages, the README and lesson 13" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
19161
19161
 
19162
19162
 
19163
+ def section_ej_frac_treatment(tmp: str) -> None:
19164
+ """Section EJ - the frac treatment: each stage from its own pumping record. The arithmetic on stages built with
19165
+ every answer known (the ISIP through the ringing, the friction and its two parts, the closure, the extension
19166
+ pressure, the volumes), in other units and other layouts; what is turned away and what is said not to be read;
19167
+ the records kept in a workspace with their hashes, the depth declared, the report, the stage times handed to the
19168
+ Frac hits tab; the published stimulation layouts on the public pad; the assistant's tool; the API, the CLI, the
19169
+ tab, the help, the README and the lessons."""
19170
+ import base64 as _b64
19171
+ import csv as _csv
19172
+ import http.cookiejar
19173
+ import math
19174
+ import subprocess as _sp
19175
+ import urllib.error
19176
+ import urllib.request
19177
+ from datetime import datetime, timedelta
19178
+ from . import __version__ as _ver
19179
+ from . import frac_hits as FH
19180
+ from . import frac_treatment as FT
19181
+ from . import helplib as H
19182
+ from . import mcp_server as MC
19183
+ from . import public_pad as PP
19184
+ from . import tutorial as TU
19185
+ from . import xlsx_read as XR
19186
+ from .service import Service, Users
19187
+ from .workspace import Workspace
19188
+ pkg = Path(__file__).parent
19189
+ d = Path(tmp, "ej"); d.mkdir()
19190
+ os.environ["GEA_WORKSPACES_FILE"] = str(d / "workspaces.json")
19191
+ # -- EJ1 the arithmetic --------------------------------------------------------------------------------------------------
19192
+ st = FT.selftest()
19193
+ rec = FT.synthetic(isip=5200.0, a=0.2, b=80.0, rate=40.0, pump_min=25.0, steps=(30.0, 20.0, 10.0, 5.0), closure_g=0.7, leak_psi=400.0, falloff_min=70.0, step_up=False, seed=11)
19194
+ e = rec["expected"]
19195
+ r = FT.analyse(rec)
19196
+ f = r["final"]; sd = f["step_down"]; pk = r["falloff"].get("pick") or {}
19197
+ # the same stage written in kPa, cubic metres a minute and kilograms a cubic metre
19198
+ si = str(d / "metric.csv")
19199
+ with open(si, "w", encoding="utf-8", newline="") as fh:
19200
+ w = _csv.writer(fh)
19201
+ w.writerow(["Timestamp", "Wellhead Pressure (kPa)", "Slurry Rate (m3/min)", "Proppant Concentration (kg/m3)"])
19202
+ t0 = datetime(2026, 8, 3, 6, 0, 0)
19203
+ for i in range(len(rec["t"])):
19204
+ w.writerow([(t0 + timedelta(seconds=float(rec["t"][i]))).strftime("%Y-%m-%d %H:%M:%S"), f"{rec['pressure'][i] / 0.1450377:.2f}", f"{rec['rate'][i] / 6.289811:.4f}", f"{rec['conc'][i] * 119.8264:.1f}"])
19205
+ rm = FT.read_treatment(Path(si).read_bytes(), "metric.csv")
19206
+ am = FT.analyse(rm)
19207
+ # the pumps back on half a minute after they stopped; a line test at 20 psi; the well opened during the fall-off
19208
+ brief = FT.synthetic(pump_min=10.0, falloff_min=10.0, step_up=False, steps=())
19209
+ k = int(brief["expected"]["shut_in_s"])
19210
+ brief["rate"][k + 30:k + 300] = 20.0
19211
+ brief["pressure"][k + 30:k + 300] = 5000.0
19212
+ ab = FT.analyse(brief)
19213
+ prime = FT.synthetic(pump_min=10.0, falloff_min=10.0, step_up=False)
19214
+ lead = 300
19215
+ tt = np.arange(len(prime["t"]) + lead, dtype=float)
19216
+ pr = {**prime, "t": tt, "pressure": np.concatenate([np.full(lead, 20.0), prime["pressure"]]), "rate": np.concatenate([np.r_[np.full(120, 8.0), np.zeros(lead - 120)], prime["rate"]]),
19217
+ "conc": None, "clean_rate": None, "slurry_total": None}
19218
+ ap = FT.analyse(pr)
19219
+ bled = FT.synthetic(pump_min=10.0, falloff_min=20.0, step_up=False, closure_g=None)
19220
+ kb = int(bled["expected"]["shut_in_s"]) + 660
19221
+ bled["pressure"][kb:] = 15.0
19222
+ abl = FT.analyse(bled)
19223
+ spike = FT.synthetic(pump_min=10.0, falloff_min=6.0, step_up=False)
19224
+ spike["rate"][200] = 400.0; spike["conc"][300:303] = 25.0
19225
+ asp = FT.analyse(spike)
19226
+ errs = {}
19227
+ for name, body in (("empty", b""), ("no_header", b"1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n"), ("no_rate", ("Timestamp,Treating Pressure (psi)\n" + "".join(f"2026-08-03 06:00:{i:02d},{4000 + i}\n" for i in range(30))).encode()),
19228
+ ("no_time", ("Treating Pressure (psi),Slurry Rate (bpm)\n" + "".join(f"{4000 + i},{10}\n" for i in range(30))).encode())):
19229
+ try:
19230
+ FT.read_treatment(body, name + ".csv"); errs[name] = ""
19231
+ except FT.TreatmentError as ex:
19232
+ errs[name] = str(ex)
19233
+ el = FT.read_treatment(("Elapsed Time (min),Treating Pressure (psi),Slurry Rate (bpm)\n" + "".join(f"{i / 60:.4f},{4000 + i},{10}\n" for i in range(120))).encode(), "elapsed.csv", start="2026-08-03T06:00:00")
19234
+ c1 = {
19235
+ "selftest": st["ok"] and len(st["checks"]) >= 12,
19236
+ "isip": abs(f["isip_psi"] - e["isip"]) <= 0.01 * e["isip"] and not f["wide"] and len(f["windows"]) == 3 and f["isip_low_psi"] <= f["isip_psi"] <= f["isip_high_psi"],
19237
+ "friction": abs(f["friction_psi"] - e["friction"]) <= 0.04 * e["friction"] and f["last_rate_bpm"] == 40.0 and len(sd["points"]) == 5
19238
+ and abs(sd["square_psi"] - e["square"]) <= 0.15 * e["square"] and abs(sd["root_psi"] - e["root"]) <= 0.15 * e["root"] and "both parts" in sd["reads"],
19239
+ "closure": bool(pk) and abs(pk["g"] - 0.7) <= 0.06 and abs(pk["pressure_psi"] - e["closure_psi"]) <= 40.0 and "closure candidate" in r["falloff"]["reads"],
19240
+ "volumes": abs(r["pumping"]["slurry_bbl"] - e["slurry_bbl"]) <= 2 and abs(r["pumping"]["slurry_bbl"] - r["pumping"]["slurry_totaliser_bbl"]) <= 2 and r["pumping"]["proppant_lbm"] > 30000
19241
+ and r["pumping"]["pressure_max_psi"] >= r["pumping"]["pressure_avg_psi"] > e["isip"] and abs(r["pumping"]["rate_avg_bpm"] - 40.0) < 1.5,
19242
+ "metric": rm["read_as"]["pressure"] == "Wellhead Pressure (kpa)" and abs(am["final"]["isip_psi"] - f["isip_psi"]) <= 3 and abs(am["pumping"]["slurry_bbl"] - r["pumping"]["slurry_bbl"]) <= 3
19243
+ and abs(am["final"]["friction_psi"] - f["friction_psi"]) <= 5 and am["start"] == "2026-08-03T06:00:00"
19244
+ and any("no clean rate" in n_ for n_ in am["notes"]) and am["pumping"]["proppant_lbm"] > r["pumping"]["proppant_lbm"],
19245
+ "too_short": ab["shut_ins"][0]["status"] == "TOO SHORT" and "came back on 30 s after" in ab["shut_ins"][0]["why"] and ab["shut_ins"][-1]["status"] == "OK",
19246
+ "surface": ap["not_injection"]["surface"] == 1 and len(ap["shut_ins"]) == 1 and abs(ap["final"]["isip_psi"] - 4200) <= 45 and abs(ap["pumping"]["slurry_bbl"] - prime["expected"]["slurry_bbl"]) <= 2,
19247
+ "bled_off": abs(abl["falloff"]["bled_off_min"] - 11.0) <= 0.2 and 10.0 < abl["falloff"]["minutes"] < 11.1 and abl["falloff"].get("pick") is None and "10" in abl["falloff"]["at"] and "15" not in abl["falloff"]["at"],
19248
+ "spikes": asp["pumping"]["rate_max_bpm"] <= 61.0 and any("3 s of proppant concentration above 12" in n_ for n_ in asp["notes"]) and asp["pumping"]["conc_max_ppg"] == 1.5,
19249
+ "turned_away": "empty" in errs["empty"] and "no header" in errs["no_header"] and "treating pressure and a slurry rate" in errs["no_rate"] and "Treating Pressure" in errs["no_rate"] and "no time column" in errs["no_time"],
19250
+ "elapsed": el["start"] == "2026-08-03T06:00:00" and abs(el["cadence_s"] - 1.0) < 0.01 and el["samples"] == 120,
19251
+ "g": abs(float(FT.g_function(np.array([0.0]), 100.0)[0])) < 1e-12 and abs(float(FT.g_function(np.array([100.0]), 100.0)[0]) - 4 / math.pi * (4 / 3 * (2 ** 1.5 - 1) - 4 / 3)) < 1e-9,
19252
+ }
19253
+ failed1 = [k_ for k_, v in c1.items() if not v]
19254
+ ok(not failed1,
19255
+ "EJ1 a fracturing stage read from its own pumping record, on stages built with the answer known: the ISIP read back through the ringing over three windows to within 1 %; the friction at the "
19256
+ "last rate held and, from a four-step step-down, its part with the square of the rate and its part with the root; the closure where the pressure leaves its line in G; the volume against the "
19257
+ "totaliser; the same stage in kPa, cubic metres a minute and kilograms a cubic metre giving the same figures; pumps back on after half a minute said to leave too little to read; a line test "
19258
+ "at 20 psi not counted as injection; a well opened during the fall-off cutting it there; a rate spike and a flushed densitometer left out and said so; a file with no pressure, no rate, no "
19259
+ "time or no header turned away by name; an elapsed-time file given its start; Nolte's G against its formula" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
19260
+
19261
+ # -- EJ2 the workspace, the public pad, the assistant ------------------------------------------------------------------
19262
+ gz = FH.selftest_files(str(d / "gauge"))
19263
+ ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
19264
+ tw = ws.add_well_file(gz["gauge"], display="Treat 1H", actor="tester")["id"]
19265
+ ws.add_well_file(gz["gauge"], display="Monitor 2H", actor="tester")
19266
+ stages_ = {}
19267
+ for n_, (name, isip) in enumerate((("3R", 4100.0), ("10", 4500.0), ("4", 4300.0))):
19268
+ s_ = FT.synthetic(isip=isip, pump_min=9.0, falloff_min=8.0, step_up=False, seed=30 + n_)
19269
+ s_["start"] = f"2026-08-0{3 + n_}T06:00:00"
19270
+ stages_[name] = (s_, isip)
19271
+ FT.write_csv(str(d / "s3r.csv"), stages_["3R"][0]); FT.write_dataacq(str(d / "s10.txt"), stages_["10"][0])
19272
+ rows4 = [["", "Time of", "Treating", "Slurry", "Clean", "Prop", "Slurry Stage"], ["Date", "Day", "Pressure", "Rate", "Rate", "Conc", "Total"], ["(mm:dd:yyyy)", "(hh:mm:ss)", "(psi)", "(bpm)", "(bpm)", "(ppg)", "(bbl)"]]
19273
+ s4 = stages_["4"][0]; t4 = datetime(2026, 8, 5, 6, 0, 0)
19274
+ for i in range(len(s4["t"])):
19275
+ ts = t4 + timedelta(seconds=float(s4["t"][i]))
19276
+ rows4.append([ts.strftime("%Y-%m-%d"), ts.strftime("%H:%M:%S"), round(float(s4["pressure"][i]), 1), round(float(s4["rate"][i]), 2), round(float(s4["clean_rate"][i]), 3), round(float(s4["conc"][i]), 2), round(float(s4["slurry_total"][i]), 2)])
19277
+ XR.write_simple(str(d / "s4.xlsx"), rows4)
19278
+ m3 = FT.attach(ws, tw, "3R", "s3r.csv", (d / "s3r.csv").read_bytes(), "tester", "post-job data (test)")
19279
+ m10 = FT.attach(ws, tw, "10", "s10.txt", (d / "s10.txt").read_bytes(), "tester", "post-job data (test)")
19280
+ m4 = FT.attach(ws, tw, "4", "s4.xlsx", (d / "s4.xlsx").read_bytes(), "tester", "post-job data (test)")
19281
+ refused = {}
19282
+ for name, fn in (("no_source", lambda: FT.attach(ws, tw, "5", "s3r.csv", (d / "s3r.csv").read_bytes(), "tester", "")), ("no_stage", lambda: FT.attach(ws, tw, " ", "s3r.csv", (d / "s3r.csv").read_bytes(), "tester", "x")),
19283
+ ("no_well", lambda: FT.attach(ws, "nowhere", "5", "s3r.csv", (d / "s3r.csv").read_bytes(), "tester", "x")), ("bad_tvd", lambda: FT.declare(ws, tw, "tester", "x", 50)),
19284
+ ("bad_grad", lambda: FT.declare(ws, tw, "tester", "x", 8000, 2.0)), ("dec_source", lambda: FT.declare(ws, tw, "tester", "", 8000)), ("dec_stage", lambda: FT.declare(ws, tw, "tester", "x", 8000, None, "99")),
19285
+ ("no_tvd", lambda: FT.declare(ws, tw, "tester", "x", None))):
19286
+ try:
19287
+ fn(); refused[name] = ""
19288
+ except (ValueError, KeyError) as ex:
19289
+ refused[name] = str(ex.args[0]) if ex.args else "x"
19290
+ p0 = FT.page(ws)
19291
+ FT.declare(ws, tw, "tester", "perforation record (test); fresh water", 8000)
19292
+ FT.declare(ws, tw, "tester", "perforation record (test); brine", 8200, 0.465, "10")
19293
+ p1 = FT.page(ws, tw, "10")
19294
+ by = {r_["stage"]: r_ for r_ in p1["stages"]}
19295
+ det = p1["detail"]
19296
+ wr = FT.write_report(ws, _ver, "tester")
19297
+ csv_rows = (Path(wr["folder"]) / "stages.csv").read_text(encoding="utf-8").strip().splitlines()
19298
+ table = (Path(wr["folder"]) / [x for x in wr["files"] if x.startswith("stage_table_")][0]).read_text(encoding="utf-8").strip().splitlines()
19299
+ fh = FT.to_frac_hits(ws, tw, "tester")
19300
+ fh_rows = FH.stage_rows(ws, tw)
19301
+ kept = Path(ws.path, "completion", "frac", "treatment")
19302
+ FT.remove(ws, tw, "4", "tester")
19303
+ p2 = FT.page(ws)
19304
+ audit = Path(ws.audit_path).read_text(encoding="utf-8")
19305
+ tool = MC.call_tool(ws, "frac_treatment", {"well": "Treat 1H", "stage": "10"}); tool_j = MC.call_tool(ws, "frac_treatment", {"well": "Treat 1H", "stage": "10", "json": True})
19306
+ # the published layouts on the public pad
19307
+ folder, site = str(d / "files"), str(d / "forge-site")
19308
+ PP.standins(folder, stands=13)
19309
+ made_t = PP.treatment_standins(folder)
19310
+ stt = PP.status(folder)
19311
+ seen = []
19312
+ br = PP.build(folder, site, "tester", verify=False, progress=seen.append)
19313
+ fs = Workspace(site); fp = FT.page(fs)
19314
+ off = [abs((r_.get("isip_psi") or 0) - made_t[f"{r_['display']}|{r_['stage']}"]) for r_ in fp["stages"]]
19315
+ prec = PP.record(fs)
19316
+ c2 = {
19317
+ "attached": m3["layout"] == "table" and m10["layout"] == "data-acquisition export" and m4["layout"] == "table with a stacked header" and m4["start"] == "2026-08-05T06:00:00" and len(m3["sha256"]) == 64
19318
+ and m3["read_as"]["rate"] == "Slurry Rate (bpm)" and any("names no units" in n_ for n_ in m10["notes"]) and (kept / tw / "3R" / "s3r.csv").read_bytes() == (d / "s3r.csv").read_bytes()
19319
+ and (kept / tw / "10" / "channels.npz").is_file(),
19320
+ "refused": "where the record came from" in refused["no_source"] and "name the stage" in refused["no_stage"] and "no well" in refused["no_well"] and "between 100 and 40,000" in refused["bad_tvd"]
19321
+ and "between 0.3 and 1.2" in refused["bad_grad"] and "where the depth" in refused["dec_source"] and "no treatment record for stage 99" in refused["dec_stage"] and "give the true vertical depth" in refused["no_tvd"],
19322
+ "page": p0["status"] == "OK" and [r_["stage"] for r_ in p0["stages"]] == ["3R", "4", "10"] and all(abs(by[k_]["isip_psi"] - v[1]) <= 45 for k_, v in stages_.items())
19323
+ and all(r_["gradient_psi_ft"] is None for r_ in p0["stages"]) and "No perforation depth is declared" in " ".join(p0["lines"]) and p0["totals"]["step_downs"] == 3,
19324
+ "declared": abs(by["3R"]["gradient_psi_ft"] - (by["3R"]["isip_psi"] + 0.433 * 8000) / 8000) < 2e-3 and abs(by["10"]["gradient_psi_ft"] - (by["10"]["isip_psi"] + 0.465 * 8200) / 8200) < 2e-3
19325
+ and det["bottomhole"]["source"].endswith("brine") and p1["stage"] == "10" and "ISIP gradient is" in " ".join(p1["lines"]),
19326
+ "traces": set(det["traces"]) >= {"pressure", "rate", "conc", "shut_in"} and len(det["traces"]["pressure"]["t"]) == len(det["traces"]["pressure"]["v"]) and det["traces"]["shut_in"]["t"][0] == -180.0
19327
+ and len(det["falloff"]["g"]) == 120 and det["meta"]["file"] == "s10.txt",
19328
+ "report": set(wr["files"]) >= {"frac_treatment.json", "frac_treatment.md", "stages.csv"} and len(csv_rows) == 4 and csv_rows[0].startswith("display,stage,start,status") and len(table) == 4
19329
+ and table[0] == "Stage,Start Time,End Time,Clean Volume (bbl),Proppant (lb)" and table[1].startswith("3R,2026-08-03 06:0") and "frac_treatment.report" in audit,
19330
+ "to_frac_hits": fh["stages"] == 3 and [x["stage"] for x in fh_rows] == ["3R", "10", "4"] and fh_rows[0]["start"].startswith("2026-08-03T06:0") and "frac.stages" in audit and "the stages' own treatment records" in fh["source"],
19331
+ "removed": [r_["stage"] for r_ in p2["stages"]] == ["3R", "10"] and "frac_treatment.removed" in audit and "frac_treatment.attach" in audit and "frac_treatment.declared" in audit,
19332
+ "tool": not tool["is_error"] and tool["text"] == FT.text(FT.page(ws, tw, "10")) and "traces" not in json.loads(tool_j["text"]).get("detail", {}) and "frac_treatment" in MC.NAMES and len(MC.NAMES) == 25,
19333
+ "public": stt["treatments_present"] == 5 and len(stt["treatments"]) == 5 and stt["treatment_bytes"] > 6e7 and len(br["stages"]) == 16 and {s_.get("step") for s_ in seen} >= {"treatment", "done"} and seen[-1]["stages"] == br["stages"]
19334
+ and [r_["stage"] for r_ in fp["stages"] if r_["display"] == "16A(78)-32"] == ["1", "2", "3", "3R", "4", "5", "6A", "6B", "7", "8", "9", "10"]
19335
+ and [r_["stage"] for r_ in fp["stages"] if r_["display"] == "16B(78)-32"] == ["1", "2", "3", "4"] and max(off) <= 40.0 and fp["lines"][-1].startswith("Data: Utah FORGE")
19336
+ and fp["public_source"]["name"] == "Utah FORGE" and len(prec["treatments"]) == 5 and "gdr.openei.org/submissions/1611" in FT._index(fs)[next(w_["id"] for w_ in fs.wells() if w_["display"] == "16B(78)-32")]["1"]["source"]
19337
+ and PP.build(folder, site, "tester", verify=False)["stages"] == [] and "public_source" not in p2,
19338
+ }
19339
+ failed2 = [k_ for k_, v in c2.items() if not v]
19340
+ ok(not failed2,
19341
+ "EJ2 three stages kept in a workspace from a plain CSV, a spreadsheet with a stacked header and a frac van's export - each file verbatim with its hash, the channels beside it; a record with no "
19342
+ "source, no stage or no such well, and a depth out of range or without its source, each stopped; the stages in the order a crew counts them (3R, 4, 10), each ISIP its own; the depth of the "
19343
+ "perforations declared for the well and again for one stage, and the gradient the surface ISIP plus that column over that depth; the charts' traces; the report with a row a stage and the "
19344
+ "stage table; that table given to the Frac hits tab and read there as three stages; a stage removed; every step in the audit log; the assistant's tool the page's own text, without the "
19345
+ "charts' points. On the public pad: the five stimulation files found in the folder's stimulation folder, sixteen stages attached to the two wells in both published layouts, each ISIP read, "
19346
+ "the credit on the page, and built again nothing added" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
19347
+
19348
+ # -- EJ3 the routes, the commands, the tab, the help --------------------------------------------------------------------
19349
+ Users(str(Path(ws.path, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(ws.path, "users.json"))).add("op", "op-pass-123", "operator")
19350
+ svc = Service(ws.path, port=0, scheduler=False).start()
19351
+ base_url = svc.url.rstrip("/")
19352
+
19353
+ def opener(name, pw_):
19354
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
19355
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
19356
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
19357
+ return o
19358
+
19359
+ def call(o, method, path, body=None):
19360
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
19361
+ if method == "POST":
19362
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
19363
+ try:
19364
+ with o.open(rq, timeout=300) as rr_:
19365
+ return rr_.status, json.loads(rr_.read())
19366
+ except urllib.error.HTTPError as ex:
19367
+ return ex.code, json.loads(ex.read())
19368
+ from urllib.parse import quote as _q
19369
+ b64 = _b64.b64encode((d / "s4.xlsx").read_bytes()).decode("ascii")
19370
+ try:
19371
+ V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
19372
+ s_v, pv = call(V, "GET", "/api/frac-treatment")
19373
+ s_vs, pvs = call(V, "GET", f"/api/frac-treatment?well={_q(tw)}&stage=3R")
19374
+ s_va, _ = call(V, "POST", "/api/frac-treatment/attach", {"well_id": tw, "stage": "4", "filename": "s4.xlsx", "content_b64": b64, "source": "x"})
19375
+ s_oa, oa = call(O, "POST", "/api/frac-treatment/attach", {"well_id": tw, "stage": "4", "filename": "s4.xlsx", "content_b64": b64, "source": "post-job data (API)"})
19376
+ s_ob, ob = call(O, "POST", "/api/frac-treatment/attach", {"well_id": tw, "stage": "5", "filename": "bad.csv", "content": "Depth,Gamma\n1,2\n", "source": "x"})
19377
+ s_on, _ = call(O, "POST", "/api/frac-treatment/attach", {"well_id": "nowhere", "stage": "5", "filename": "s4.xlsx", "content_b64": b64, "source": "x"})
19378
+ s_od, od = call(O, "POST", "/api/frac-treatment/declare", {"well_id": tw, "stage": "4", "tvd_ft": "8100", "fluid_psi_ft": "", "source": "perforation record (API)"})
19379
+ s_oe, oe = call(O, "POST", "/api/frac-treatment/declare", {"well_id": tw, "tvd_ft": "", "source": "x"})
19380
+ s_of, of_ = call(O, "POST", "/api/frac-treatment/to-frac-hits", {"well_id": tw})
19381
+ s_ow, ow = call(O, "POST", "/api/frac-treatment/write", {})
19382
+ s_vw, _ = call(V, "POST", "/api/frac-treatment/write", {})
19383
+ s_or, _ = call(O, "POST", "/api/frac-treatment/remove", {"well_id": tw, "stage": "4"})
19384
+ s_o2, _ = call(O, "POST", "/api/frac-treatment/remove", {"well_id": tw, "stage": "4"})
19385
+ s_p2, pp2 = call(V, "GET", "/api/public-pad")
19386
+ finally:
19387
+ svc.stop()
19388
+ env = {**os.environ, "PYTHONPATH": str(pkg.parent) + os.pathsep + os.environ.get("PYTHONPATH", "")}
19389
+ run = lambda *a_: _sp.run([sys.executable, "-m", "gea", *a_], capture_output=True, text=True, env=env, cwd=str(d), timeout=600)
19390
+ cli_s, cli_t, cli_j = run("frac-treatment"), run("frac-treatment", "--workspace", ws.path), run("frac-treatment", "--workspace", ws.path, "--well", "Treat 1H", "--stage", "3R", "--json")
19391
+ cli_a = run("frac-treatment", "--workspace", ws.path, "--well", "Treat 1H", "--stage", "4", "--attach", str(d / "s4.xlsx"), "--source", "post-job data (CLI)")
19392
+ cli_d = run("frac-treatment", "--workspace", ws.path, "--well", "Treat 1H", "--stage", "4", "--tvd", "8050", "--fluid", "0.44", "--source", "perforation record (CLI)")
19393
+ cli_h = run("frac-treatment", "--workspace", ws.path, "--well", "Treat 1H", "--to-frac-hits", "--write")
19394
+ cli_x = run("frac-treatment", "--workspace", ws.path, "--well", "Nobody 9H")
19395
+ cli_n = run("frac-treatment", "--workspace", ws.path, "--stage", "4", "--attach", str(d / "s4.xlsx"), "--source", "x")
19396
+ cli_w = run("frac-treatment", "--workspace", str(d / "nowhere"))
19397
+ cli_pl = run("public-pad", "--list")
19398
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
19399
+ readme = (pkg.parent / "README.md").read_text(encoding="utf-8") if (pkg.parent / "README.md").is_file() else ""
19400
+ lesson = (pkg / "help" / "tutorial" / "14-production-pages.md").read_text(encoding="utf-8")
19401
+ hp = H.page("frac-treatment") or ""
19402
+ c3 = {
19403
+ "api_read": s_v == 200 and pv["status"] == "OK" and [r_["stage"] for r_ in pv["stages"]] == ["3R", "10"] and len(pv["not_a_measurement"]) >= 6 and s_vs == 200 and pvs["stage"] == "3R"
19404
+ and pvs["detail"]["final"]["isip_psi"] == pv["stages"][0]["isip_psi"] and "traces" in pvs["detail"],
19405
+ "api_write": s_va == 403 and s_oa == 200 and oa["stage"] == "4" and oa["samples"] == len(s4["t"]) and s_ob == 400 and s_on == 404 and s_od == 200 and od["fluid_psi_ft"] == 0.433 and od["stages"] == ["4"]
19406
+ and s_oe == 400 and "true vertical depth" in oe.get("error", "") and s_of == 200 and of_["stages"] == 3 and s_ow == 200 and "stages.csv" in ow["files"] and s_vw == 403 and s_or == 200 and s_o2 == 404
19407
+ and s_p2 == 200 and len(pp2["treatments"]) == 5 and pp2["treatments"][3]["stages"][0] == "3R",
19408
+ "cli": cli_s.returncode == 0 and cli_s.stdout.strip().splitlines()[-1].startswith("OK - SIMULATION") and cli_t.returncode == 0 and "Frac treatment: each stage from its own pumping record" in cli_t.stdout
19409
+ and cli_j.returncode == 0 and json.loads(cli_j.stdout)["stage"] == "3R" and cli_a.returncode == 0 and "kept: stage 4" in cli_a.stdout and "table with a stacked header" in cli_a.stdout
19410
+ and cli_d.returncode == 0 and "declared for stage 4: perforations at 8,050 ft TVD, fluid 0.44 psi/ft" in cli_d.stdout and cli_h.returncode == 0 and "the Frac hits page has 3 stage(s)" in cli_h.stdout
19411
+ and "written: frac_treatment.json" in cli_h.stdout and cli_x.returncode != 0 and "no well 'Nobody 9H'" in cli_x.stderr and cli_n.returncode != 0 and "--attach needs --well and --stage" in cli_n.stderr
19412
+ and cli_w.returncode != 0 and cli_w.stdout == "" and cli_pl.returncode == 0 and "16B Frac Data.zip" in cli_pl.stdout and "doi:10.15121/2371032" in cli_pl.stdout,
19413
+ "page": all(x in page_src for x in ("VIEWS.treatment", "['#/treatment', 'Frac treatment']", "ftPanel", "ftCharts", "/api/frac-treatment/attach", "/api/frac-treatment/declare", "/api/frac-treatment/to-frac-hits",
19414
+ "help('treatment')", "never two scales on one plot", "The stimulation records", "What this page will not call a measurement")),
19415
+ "help": not H.check("frac-treatment") and not H.check("public-data") and not H.check("mcp") and H.VIEW_TOPIC.get("treatment") == "frac-treatment"
19416
+ and all(x in hp for x in ("gea frac-treatment --workspace C:\\site", "15 to 60 s, 20 to 90 s and 30 to 120 s", "4,019 psi", "3,985 psi", "the pipe and the perforations together", "Nolte"))
19417
+ and "`frac_treatment`" in (H.page("mcp") or "") and "16B Frac Data.zip" in (H.page("public-data") or "") and "stimulation" in (H.page("public-data") or ""),
19418
+ "readme_lesson": (not readme or ("### Frac treatment" in readme and "`gea frac-treatment`" in readme and "2,969,800 lbm" in readme and readme.index("### Frac treatment") < readme.index("### Frac hits")))
19419
+ and "**Frac treatment**" in lesson and "frac-treatment" in lesson.split("Help:")[-1] and not TU.check("production-pages"),
19420
+ }
19421
+ failed3 = [k_ for k_, v in c3.items() if not v]
19422
+ ok(not failed3,
19423
+ "EJ3 on the API (anyone reads the stages and one stage in full with its traces; only an operator attaches a record - a spreadsheet sent whole - declares the depth, hands the stage times to "
19424
+ "the Frac hits tab, writes the report or removes a stage; a file that is no treatment record 400, a well that is not there 404, a stage already removed 404; the public files list carries "
19425
+ "the stimulation files), the commands (the self-test, the table, one stage as JSON, a record attached, the depth declared, the stage times handed over and the report written; a well that is "
19426
+ "not there, an attach with no well and a folder that is no workspace each stopped; the public list naming the stimulation files), the Frac treatment tab with a panel a measure, the help "
19427
+ "page with the published figures it was checked against, the README and lesson 14" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
19428
+
19429
+
19163
19430
  def main() -> int:
19164
19431
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
19165
19432
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -19302,6 +19569,7 @@ def main() -> int:
19302
19569
  section_eg_dysfunction(tmp)
19303
19570
  section_eh_mcp(tmp)
19304
19571
  section_ei_public_pad_benchmark(tmp)
19572
+ section_ej_frac_treatment(tmp)
19305
19573
  if _FAILS:
19306
19574
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
19307
19575
  for f in _FAILS: