gea-program 0.25.0__tar.gz → 0.27.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 (271) hide show
  1. {gea_program-0.25.0/gea_program.egg-info → gea_program-0.27.0}/PKG-INFO +27 -2
  2. {gea_program-0.25.0 → gea_program-0.27.0}/README.md +26 -1
  3. {gea_program-0.25.0 → gea_program-0.27.0}/gea/__init__.py +1 -1
  4. {gea_program-0.25.0 → gea_program-0.27.0}/gea/__main__.py +110 -2
  5. {gea_program-0.25.0 → gea_program-0.27.0}/gea/acceptance_tests.py +251 -1
  6. {gea_program-0.25.0 → gea_program-0.27.0}/gea/client_reports.py +27 -0
  7. gea_program-0.27.0/gea/fixtures/README.md +10 -0
  8. gea_program-0.27.0/gea/fixtures/comcat_delaware_basin_2026-09.txt +29 -0
  9. gea_program-0.27.0/gea/fixtures/comcat_youngstown_2011-2012.txt +10 -0
  10. gea_program-0.27.0/gea/help/slip.md +11 -0
  11. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/sra.md +1 -1
  12. {gea_program-0.25.0 → gea_program-0.27.0}/gea/helplib.py +1 -0
  13. {gea_program-0.25.0 → gea_program-0.27.0}/gea/service.py +47 -2
  14. {gea_program-0.25.0 → gea_program-0.27.0}/gea/shapefile.py +25 -5
  15. gea_program-0.27.0/gea/slip.py +276 -0
  16. {gea_program-0.25.0 → gea_program-0.27.0}/gea/sra.py +43 -0
  17. {gea_program-0.25.0 → gea_program-0.27.0}/gea/structure.py +1 -1
  18. {gea_program-0.25.0 → gea_program-0.27.0}/gea/web/app.html +44 -5
  19. {gea_program-0.25.0 → gea_program-0.27.0}/gea/workspace.py +157 -3
  20. {gea_program-0.25.0 → gea_program-0.27.0/gea_program.egg-info}/PKG-INFO +27 -2
  21. {gea_program-0.25.0 → gea_program-0.27.0}/gea_program.egg-info/SOURCES.txt +5 -0
  22. {gea_program-0.25.0 → gea_program-0.27.0}/pyproject.toml +2 -2
  23. {gea_program-0.25.0 → gea_program-0.27.0}/LICENSE +0 -0
  24. {gea_program-0.25.0 → gea_program-0.27.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  25. {gea_program-0.25.0 → gea_program-0.27.0}/gea/accuracy_statement.py +0 -0
  26. {gea_program-0.25.0 → gea_program-0.27.0}/gea/alarm_engine.py +0 -0
  27. {gea_program-0.25.0 → gea_program-0.27.0}/gea/backup.py +0 -0
  28. {gea_program-0.25.0 → gea_program-0.27.0}/gea/bench.py +0 -0
  29. {gea_program-0.25.0 → gea_program-0.27.0}/gea/blind_harness.py +0 -0
  30. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  31. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  32. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  33. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  34. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  35. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  36. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  37. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  38. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  39. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  40. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  41. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  42. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  43. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  44. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  45. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  46. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  47. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  48. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  49. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  50. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  51. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  52. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  53. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  54. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  55. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  56. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  57. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  58. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  59. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  60. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  61. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  62. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  63. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  64. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  65. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  66. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  67. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  68. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  69. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  70. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  71. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  72. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  73. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  74. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  75. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  76. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  77. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  78. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  79. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  80. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  81. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  82. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  83. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  84. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  85. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  86. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  87. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  88. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  89. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  90. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  91. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  92. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  93. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  94. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  95. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  96. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  97. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/med_160_sapropels.txt +0 -0
  98. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  99. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  100. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  101. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  102. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  103. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  104. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  105. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  106. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  107. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  108. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  109. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  110. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  111. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  112. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  113. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  114. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  115. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  116. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  117. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  118. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  119. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  120. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  121. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  122. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  123. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  124. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  125. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  126. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  127. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  128. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  129. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  130. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  131. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  132. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  133. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  134. {gea_program-0.25.0 → gea_program-0.27.0}/gea/catalog_register.py +0 -0
  135. {gea_program-0.25.0 → gea_program-0.27.0}/gea/certificates.py +0 -0
  136. {gea_program-0.25.0 → gea_program-0.27.0}/gea/cli.py +0 -0
  137. {gea_program-0.25.0 → gea_program-0.27.0}/gea/config_versioning.py +0 -0
  138. {gea_program-0.25.0 → gea_program-0.27.0}/gea/conformance.py +0 -0
  139. {gea_program-0.25.0 → gea_program-0.27.0}/gea/correlation.py +0 -0
  140. {gea_program-0.25.0 → gea_program-0.27.0}/gea/dashboard.py +0 -0
  141. {gea_program-0.25.0 → gea_program-0.27.0}/gea/deviation.py +0 -0
  142. {gea_program-0.25.0 → gea_program-0.27.0}/gea/dlis.py +0 -0
  143. {gea_program-0.25.0 → gea_program-0.27.0}/gea/dnp3.py +0 -0
  144. {gea_program-0.25.0 → gea_program-0.27.0}/gea/doctor.py +0 -0
  145. {gea_program-0.25.0 → gea_program-0.27.0}/gea/downhole_engine.py +0 -0
  146. {gea_program-0.25.0 → gea_program-0.27.0}/gea/drift_monitor.py +0 -0
  147. {gea_program-0.25.0 → gea_program-0.27.0}/gea/earth_model.py +0 -0
  148. {gea_program-0.25.0 → gea_program-0.27.0}/gea/elevation.py +0 -0
  149. {gea_program-0.25.0 → gea_program-0.27.0}/gea/etp.py +0 -0
  150. {gea_program-0.25.0 → gea_program-0.27.0}/gea/etp12_schemas.json +0 -0
  151. {gea_program-0.25.0 → gea_program-0.27.0}/gea/example_register_map.json +0 -0
  152. {gea_program-0.25.0 → gea_program-0.27.0}/gea/fat_sat.py +0 -0
  153. {gea_program-0.25.0 → gea_program-0.27.0}/gea/files.py +0 -0
  154. {gea_program-0.25.0 → gea_program-0.27.0}/gea/follower.py +0 -0
  155. {gea_program-0.25.0 → gea_program-0.27.0}/gea/forward_model.py +0 -0
  156. {gea_program-0.25.0 → gea_program-0.27.0}/gea/friction_priors.py +0 -0
  157. {gea_program-0.25.0 → gea_program-0.27.0}/gea/gamma.py +0 -0
  158. {gea_program-0.25.0 → gea_program-0.27.0}/gea/gauge_aging.py +0 -0
  159. {gea_program-0.25.0 → gea_program-0.27.0}/gea/gauge_specs.py +0 -0
  160. {gea_program-0.25.0 → gea_program-0.27.0}/gea/geodesy.py +0 -0
  161. {gea_program-0.25.0 → gea_program-0.27.0}/gea/gravity_reference.py +0 -0
  162. {gea_program-0.25.0 → gea_program-0.27.0}/gea/hartip.py +0 -0
  163. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/TESTER_GUIDE.md +0 -0
  164. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/alarms.md +0 -0
  165. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/audit-update.md +0 -0
  166. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/backup.md +0 -0
  167. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/catalog.md +0 -0
  168. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/conformance.md +0 -0
  169. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/data-in.md +0 -0
  170. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/dlis.md +0 -0
  171. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/doctor.md +0 -0
  172. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/drift.md +0 -0
  173. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/etp.md +0 -0
  174. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/field-ports.md +0 -0
  175. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/files.md +0 -0
  176. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/historian.md +0 -0
  177. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/instruments.md +0 -0
  178. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/interval.md +0 -0
  179. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/month-end.md +0 -0
  180. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/notifications.md +0 -0
  181. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/osdu.md +0 -0
  182. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/patches.md +0 -0
  183. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/ppdm.md +0 -0
  184. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/quakeml.md +0 -0
  185. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/quality.md +0 -0
  186. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/seedlink.md +0 -0
  187. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/seismic.md +0 -0
  188. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/site.md +0 -0
  189. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/sites.md +0 -0
  190. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/soak.md +0 -0
  191. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/start.md +0 -0
  192. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/structure.md +0 -0
  193. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/transients.md +0 -0
  194. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/upkeep.md +0 -0
  195. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/vibration.md +0 -0
  196. {gea_program-0.25.0 → gea_program-0.27.0}/gea/help/well-tests.md +0 -0
  197. {gea_program-0.25.0 → gea_program-0.27.0}/gea/housekeeping.py +0 -0
  198. {gea_program-0.25.0 → gea_program-0.27.0}/gea/interval.py +0 -0
  199. {gea_program-0.25.0 → gea_program-0.27.0}/gea/inverse_engine.py +0 -0
  200. {gea_program-0.25.0 → gea_program-0.27.0}/gea/jobs.py +0 -0
  201. {gea_program-0.25.0 → gea_program-0.27.0}/gea/live_ports.py +0 -0
  202. {gea_program-0.25.0 → gea_program-0.27.0}/gea/loadtest.py +0 -0
  203. {gea_program-0.25.0 → gea_program-0.27.0}/gea/matplotlib_demo.py +0 -0
  204. {gea_program-0.25.0 → gea_program-0.27.0}/gea/modbus.py +0 -0
  205. {gea_program-0.25.0 → gea_program-0.27.0}/gea/modbus_rtu.py +0 -0
  206. {gea_program-0.25.0 → gea_program-0.27.0}/gea/model_card.py +0 -0
  207. {gea_program-0.25.0 → gea_program-0.27.0}/gea/month_end.py +0 -0
  208. {gea_program-0.25.0 → gea_program-0.27.0}/gea/mqtt_port.py +0 -0
  209. {gea_program-0.25.0 → gea_program-0.27.0}/gea/notify.py +0 -0
  210. {gea_program-0.25.0 → gea_program-0.27.0}/gea/opcua_port.py +0 -0
  211. {gea_program-0.25.0 → gea_program-0.27.0}/gea/operator_app.py +0 -0
  212. {gea_program-0.25.0 → gea_program-0.27.0}/gea/osdu.py +0 -0
  213. {gea_program-0.25.0 → gea_program-0.27.0}/gea/patches.py +0 -0
  214. {gea_program-0.25.0 → gea_program-0.27.0}/gea/permits.py +0 -0
  215. {gea_program-0.25.0 → gea_program-0.27.0}/gea/piwebapi.py +0 -0
  216. {gea_program-0.25.0 → gea_program-0.27.0}/gea/ports.py +0 -0
  217. {gea_program-0.25.0 → gea_program-0.27.0}/gea/ppdm.py +0 -0
  218. {gea_program-0.25.0 → gea_program-0.27.0}/gea/profile_catalog.py +0 -0
  219. {gea_program-0.25.0 → gea_program-0.27.0}/gea/project.py +0 -0
  220. {gea_program-0.25.0 → gea_program-0.27.0}/gea/qt6_downhole_app.py +0 -0
  221. {gea_program-0.25.0 → gea_program-0.27.0}/gea/quakeml.py +0 -0
  222. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reconciler.py +0 -0
  223. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  224. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  225. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  226. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  227. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  228. {gea_program-0.25.0 → gea_program-0.27.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  229. {gea_program-0.25.0 → gea_program-0.27.0}/gea/rock_inventory.py +0 -0
  230. {gea_program-0.25.0 → gea_program-0.27.0}/gea/sample_record.py +0 -0
  231. {gea_program-0.25.0 → gea_program-0.27.0}/gea/sample_well_profile.csv +0 -0
  232. {gea_program-0.25.0 → gea_program-0.27.0}/gea/sbom.py +0 -0
  233. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seedlink.py +0 -0
  234. {gea_program-0.25.0 → gea_program-0.27.0}/gea/segy.py +0 -0
  235. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic.py +0 -0
  236. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_array.py +0 -0
  237. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_assoc.py +0 -0
  238. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_detect.py +0 -0
  239. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_film.py +0 -0
  240. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_harmonic.py +0 -0
  241. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_qc.py +0 -0
  242. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_response.py +0 -0
  243. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_signature.py +0 -0
  244. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_track.py +0 -0
  245. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_uncertainty.py +0 -0
  246. {gea_program-0.25.0 → gea_program-0.27.0}/gea/seismic_unlisted.py +0 -0
  247. {gea_program-0.25.0 → gea_program-0.27.0}/gea/sensor_swap.py +0 -0
  248. {gea_program-0.25.0 → gea_program-0.27.0}/gea/service_life.py +0 -0
  249. {gea_program-0.25.0 → gea_program-0.27.0}/gea/shell.py +0 -0
  250. {gea_program-0.25.0 → gea_program-0.27.0}/gea/shut_in.py +0 -0
  251. {gea_program-0.25.0 → gea_program-0.27.0}/gea/sla_report.py +0 -0
  252. {gea_program-0.25.0 → gea_program-0.27.0}/gea/soak.py +0 -0
  253. {gea_program-0.25.0 → gea_program-0.27.0}/gea/store_forward.py +0 -0
  254. {gea_program-0.25.0 → gea_program-0.27.0}/gea/strata_join.py +0 -0
  255. {gea_program-0.25.0 → gea_program-0.27.0}/gea/supervisor.py +0 -0
  256. {gea_program-0.25.0 → gea_program-0.27.0}/gea/survey_cmd.py +0 -0
  257. {gea_program-0.25.0 → gea_program-0.27.0}/gea/survey_view.py +0 -0
  258. {gea_program-0.25.0 → gea_program-0.27.0}/gea/telemetry.py +0 -0
  259. {gea_program-0.25.0 → gea_program-0.27.0}/gea/tool_library.py +0 -0
  260. {gea_program-0.25.0 → gea_program-0.27.0}/gea/transient.py +0 -0
  261. {gea_program-0.25.0 → gea_program-0.27.0}/gea/vibration.py +0 -0
  262. {gea_program-0.25.0 → gea_program-0.27.0}/gea/well_assembler.py +0 -0
  263. {gea_program-0.25.0 → gea_program-0.27.0}/gea/well_test_validation.py +0 -0
  264. {gea_program-0.25.0 → gea_program-0.27.0}/gea/wits0.py +0 -0
  265. {gea_program-0.25.0 → gea_program-0.27.0}/gea/witsml.py +0 -0
  266. {gea_program-0.25.0 → gea_program-0.27.0}/gea_program.egg-info/dependency_links.txt +0 -0
  267. {gea_program-0.25.0 → gea_program-0.27.0}/gea_program.egg-info/entry_points.txt +0 -0
  268. {gea_program-0.25.0 → gea_program-0.27.0}/gea_program.egg-info/requires.txt +0 -0
  269. {gea_program-0.25.0 → gea_program-0.27.0}/gea_program.egg-info/top_level.txt +0 -0
  270. {gea_program-0.25.0 → gea_program-0.27.0}/setup.cfg +0 -0
  271. {gea_program-0.25.0 → gea_program-0.27.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.25.0
3
+ Version: 0.27.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
@@ -125,7 +125,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
125
125
  gea client-report --report accuracy --out client_report
126
126
  gea model-cards --out model_cards
127
127
  gea sbom --out sbom
128
- gea accept # the product gate (362 checks)
128
+ gea accept # the product gate (366 checks)
129
129
  gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
130
130
  gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
131
131
  gea gui # the desktop window (pip install "gea-program[desktop]")
@@ -648,6 +648,31 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
648
648
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
649
649
  `rtserve.iris.washington.edu:18000`, network `TX`.
650
650
 
651
+ ### ComCat
652
+
653
+ The seismicity response plan reads a TexNet export; it reads the FDSN text
654
+ export of USGS ComCat - or any FDSN event service - the same way, with the
655
+ contributor and magnitude type on every event. `gea comcat --lat --lon
656
+ --radius-km --start --end` prints the query to open; `gea comcat --file`
657
+ reads the answer; `sra-report --catalog` takes it. Proven on two verbatim
658
+ public-domain extracts (the Delaware Basin, September 2026; the 2011
659
+ Youngstown sequence with its M 4.0), kept as fixtures with their provenance.
660
+
661
+ ### Slip tendency on the mapped faults
662
+
663
+ The stress state the site declares - the three gradients and the ambient
664
+ pore pressure with their source, the SHmax azimuth with its source or from
665
+ the World Stress Map records near the well (`add-stress-map wsm2016.csv`,
666
+ `wsm --lat --lon`) - resolved on every mapped fault trace within the radius
667
+ of a well at the depth of its declared interval: the slip tendency at the
668
+ ambient and the increased pore pressure, the pressure to slip, the margin
669
+ left after the increase, worst and best declared case. The dip comes from
670
+ the layer where it states one (the Hennings 2021 BEG/TexNet layers do, and
671
+ publish their own pressure-to-slip, printed beside this arithmetic); UTM and
672
+ state-plane layers are unprojected from their .prj parameters. `set-stress`,
673
+ `slip --well`, `gea slip`; the SRA packet's section 2e; `gea help slip`. A
674
+ statement on declared inputs, not a probability and not a verdict.
675
+
651
676
  ### The catalogue accounted for
652
677
 
653
678
  `gea catalog --list` is the register of the 52 shipped public datasets: for
@@ -86,7 +86,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
86
86
  gea client-report --report accuracy --out client_report
87
87
  gea model-cards --out model_cards
88
88
  gea sbom --out sbom
89
- gea accept # the product gate (362 checks)
89
+ gea accept # the product gate (366 checks)
90
90
  gea help drift # the help library, by the job (16 pages; the same text is on every dashboard page)
91
91
  gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
92
92
  gea gui # the desktop window (pip install "gea-program[desktop]")
@@ -609,6 +609,31 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
609
609
  itself; `gea help seedlink`. A TexNet station is served by EarthScope at
610
610
  `rtserve.iris.washington.edu:18000`, network `TX`.
611
611
 
612
+ ### ComCat
613
+
614
+ The seismicity response plan reads a TexNet export; it reads the FDSN text
615
+ export of USGS ComCat - or any FDSN event service - the same way, with the
616
+ contributor and magnitude type on every event. `gea comcat --lat --lon
617
+ --radius-km --start --end` prints the query to open; `gea comcat --file`
618
+ reads the answer; `sra-report --catalog` takes it. Proven on two verbatim
619
+ public-domain extracts (the Delaware Basin, September 2026; the 2011
620
+ Youngstown sequence with its M 4.0), kept as fixtures with their provenance.
621
+
622
+ ### Slip tendency on the mapped faults
623
+
624
+ The stress state the site declares - the three gradients and the ambient
625
+ pore pressure with their source, the SHmax azimuth with its source or from
626
+ the World Stress Map records near the well (`add-stress-map wsm2016.csv`,
627
+ `wsm --lat --lon`) - resolved on every mapped fault trace within the radius
628
+ of a well at the depth of its declared interval: the slip tendency at the
629
+ ambient and the increased pore pressure, the pressure to slip, the margin
630
+ left after the increase, worst and best declared case. The dip comes from
631
+ the layer where it states one (the Hennings 2021 BEG/TexNet layers do, and
632
+ publish their own pressure-to-slip, printed beside this arithmetic); UTM and
633
+ state-plane layers are unprojected from their .prj parameters. `set-stress`,
634
+ `slip --well`, `gea slip`; the SRA packet's section 2e; `gea help slip`. A
635
+ statement on declared inputs, not a probability and not a verdict.
636
+
612
637
  ### The catalogue accounted for
613
638
 
614
639
  `gea catalog --list` is the register of the 52 shipped public datasets: for
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.25.0"
126
+ __version__ = "0.27.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -278,6 +278,19 @@ def main(argv=None) -> int:
278
278
  p_lp.add_argument("--stream-csv", type=str, default=None, help="also write a historian-style CSV the other commands ingest")
279
279
  p_lp.add_argument("--selftest", action="store_true", help="etp: the codec round-trip and a session against the simulated store on the loopback")
280
280
 
281
+ p_cc = sub.add_parser("comcat", help="USGS ComCat / any FDSN event service: --lat --lon --radius-km --start --end prints the text-format query to open; --file reads the export it returns")
282
+ p_cc.add_argument("--lat", type=float, default=None); p_cc.add_argument("--lon", type=float, default=None); p_cc.add_argument("--radius-km", type=float, default=25.0)
283
+ p_cc.add_argument("--start", type=str, default=None); p_cc.add_argument("--end", type=str, default=None); p_cc.add_argument("--min-mag", type=float, default=1.0)
284
+ p_cc.add_argument("--file", type=str, default=None, help="an FDSN text export to read and summarise")
285
+ p_cc.add_argument("--json", action="store_true")
286
+ p_sl = sub.add_parser("slip", help="slip tendency on the mapped faults of a layer at a position, from declared gradients: --layer --lat --lon --depth-ft --sv --shmin --shmax (--shmax-azimuth or --wsm)")
287
+ p_sl.add_argument("--layer", type=str, required=True); p_sl.add_argument("--member", type=str, default=None); p_sl.add_argument("--feature-kind", type=str, choices=["fault", "fold"], default=None)
288
+ p_sl.add_argument("--lat", type=float, required=True); p_sl.add_argument("--lon", type=float, required=True); p_sl.add_argument("--radius-km", type=float, default=25.0)
289
+ p_sl.add_argument("--depth-ft", type=float, required=True)
290
+ p_sl.add_argument("--sv", type=float, required=True); p_sl.add_argument("--shmin", type=float, required=True); p_sl.add_argument("--shmax", type=float, required=True)
291
+ p_sl.add_argument("--pp", type=float, default=0.433); p_sl.add_argument("--shmax-azimuth", type=float, default=None); p_sl.add_argument("--wsm", type=str, default=None, help="the WSM database CSV: the azimuth from its records within 100 km")
292
+ p_sl.add_argument("--dips", type=float, nargs="+", default=[60.0, 75.0, 90.0]); p_sl.add_argument("--mu", type=float, nargs="+", default=[0.6]); p_sl.add_argument("--delta-pp", type=float, default=0.0)
293
+ p_sl.add_argument("--json", action="store_true")
281
294
  p_cat = sub.add_parser("catalog", help="the shipped catalogue: --list every entry with what reads it; --unused the carried ones; --check reads every entry; --friction the measured friction envelope; --core the core check; --entry X --info | --stream-csv")
282
295
  p_cat.add_argument("--list", action="store_true"); p_cat.add_argument("--unused", action="store_true"); p_cat.add_argument("--check", action="store_true")
283
296
  p_cat.add_argument("--friction", action="store_true"); p_cat.add_argument("--core", action="store_true")
@@ -319,7 +332,8 @@ def main(argv=None) -> int:
319
332
  "add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
320
333
  "seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
321
334
  "set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export", "well-identity", "vibration-report",
322
- "add-live-station", "live-fold", "live-run", "month-end", "add-layer", "layers", "remove-layer", "structure", "interval"])
335
+ "add-live-station", "live-fold", "live-run", "month-end", "add-layer", "layers", "remove-layer", "structure", "interval",
336
+ "set-stress", "add-stress-map", "wsm", "slip"])
323
337
  p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
324
338
  p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
325
339
  p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
@@ -399,6 +413,18 @@ def main(argv=None) -> int:
399
413
  p_ws.add_argument("--formation-name", type=str, default=None, help="add-layer: the formation a contours layer maps (Wolfcamp)")
400
414
  p_ws.add_argument("--role", type=str, choices=["top", "isopach", "base"], default=None, help="add-layer: what the contours are of that formation: its top elevation, its thickness, or its base")
401
415
  p_ws.add_argument("--base-interval-ft", type=float, default=None, help="set-well: base of the disposal interval, ft")
416
+ p_ws.add_argument("--feature-kind", type=str, choices=["fault", "fold"], default=None, help="add-layer: what every feature of a structure layer is when its attributes do not say")
417
+ p_ws.add_argument("--sv", type=float, default=None, help="set-stress: vertical stress gradient, psi/ft")
418
+ p_ws.add_argument("--shmin", type=float, default=None, help="set-stress: minimum horizontal stress gradient, psi/ft")
419
+ p_ws.add_argument("--shmax", type=float, default=None, help="set-stress: maximum horizontal stress gradient, psi/ft")
420
+ p_ws.add_argument("--pp", type=float, default=None, help="set-stress: ambient pore-pressure gradient, psi/ft (hydrostatic 0.433 unless declared)")
421
+ p_ws.add_argument("--shmax-azimuth", type=float, default=None, help="set-stress: SHmax azimuth, degrees from north (or leave it to the stress map)")
422
+ p_ws.add_argument("--azimuth-source", type=str, default=None, help="set-stress: where the azimuth came from")
423
+ p_ws.add_argument("--gradients-source", type=str, default=None, help="set-stress: where the gradients came from")
424
+ p_ws.add_argument("--dips", type=float, nargs="+", default=None, help="set-stress: the dips to run when the layer states none (default 60 75 90)")
425
+ p_ws.add_argument("--mu", type=float, nargs="+", default=None, help="set-stress: the friction coefficients to run (default 0.6)")
426
+ p_ws.add_argument("--delta-pp", type=float, default=None, help="set-stress / slip: the pore-pressure increase to judge at, psi")
427
+ p_ws.add_argument("--depth-ft", type=float, default=None, help="slip: the depth to judge at (default the midpoint of the declared interval)")
402
428
  p_ws.add_argument("--depth-ref", type=str, choices=["TVD", "MD"], default=None, help="set-well: the interval depths are true vertical (TVD) or measured (MD) depth")
403
429
  p_ws.add_argument("--ref-elev-ft", type=float, default=None, help="set-well: the reference elevation the depths are measured from, ft above sea level")
404
430
  p_ws.add_argument("--ref-elev-datum", type=str, choices=["KB", "GL", "DF"], default=None, help="set-well: what that elevation is: kelly bushing, ground level, derrick floor")
@@ -2095,7 +2121,7 @@ def main(argv=None) -> int:
2095
2121
  elif a.action == "add-layer":
2096
2122
  if not a.layer or not a.name:
2097
2123
  raise SystemExit("add-layer needs --name <display> and --layer <shapefile zip | folder | .shp>")
2098
- L = ws.add_layer(a.layer, a.name, actor=a.actor, citation=a.citation, member=a.member, formation=a.formation_name, role=a.role)
2124
+ L = ws.add_layer(a.layer, a.name, actor=a.actor, citation=a.citation, member=a.member, formation=a.formation_name, role=a.role, feature_kind=a.feature_kind)
2099
2125
  tc = ", ".join(f"{k} {v}" for k, v in list(L["type_counts"].items())[:6])
2100
2126
  print(f"layer {L['id']} ({L['kind']}): {L['n_features']} {L['shape_type']} features, {L['crs']['kind']} {L['crs'].get('datum') or ''}, extent lon {L['bbox'][0]:.3f}..{L['bbox'][2]:.3f} lat {L['bbox'][1]:.3f}..{L['bbox'][3]:.3f}"
2101
2127
  + (f"; contour value {L['value_attr']}" if L.get('value_attr') else ''))
@@ -2124,6 +2150,32 @@ def main(argv=None) -> int:
2124
2150
  for line in L["lines"]:
2125
2151
  print(" " + line)
2126
2152
  return 0 if r["status"] == "OK" else 1
2153
+ elif a.action == "set-stress":
2154
+ st = ws.set_stress({"sv_psi_ft": a.sv, "shmin_psi_ft": a.shmin, "shmax_psi_ft": a.shmax, "pp_psi_ft": a.pp, "shmax_azimuth_deg": a.shmax_azimuth,
2155
+ "azimuth_source": a.azimuth_source, "gradients_source": a.gradients_source, "dips_deg": a.dips, "mus": a.mu, "delta_pp_psi": a.delta_pp, "note": a.note or ""}, actor=a.actor)
2156
+ print(f"stress: Sv {st['sv_psi_ft']} Shmin {st['shmin_psi_ft']} SHmax {st['shmax_psi_ft']} Pp {st['pp_psi_ft']} psi/ft ({st['gradients_source']}); "
2157
+ f"SHmax azimuth {st.get('shmax_azimuth_deg', 'from the stress map')} ({st.get('azimuth_source', '')}); dips {st['dips_deg']}; mu {st['mus']}; delta Pp {st['delta_pp_psi']} psi"); return 0
2158
+ elif a.action == "add-stress-map":
2159
+ if not a.file:
2160
+ raise SystemExit("add-stress-map needs --file <wsm.csv>")
2161
+ r = ws.add_stress_map(a.file, actor=a.actor, citation=a.citation)
2162
+ print(f"stress map: {r['n_records']} records kept as {r['file']} (sha256 {r['sha256'][:16]})"); return 0
2163
+ elif a.action == "wsm":
2164
+ if a.lat is None or a.lon is None:
2165
+ raise SystemExit("wsm needs --lat --lon [--radius-km]")
2166
+ r = ws.wsm_at(a.lat, a.lon, a.radius_km if a.radius_km is not None else 100.0)
2167
+ print(r.get("statement") or f"{r['status']}: {r.get('note', '')}")
2168
+ for rec in r.get("records", [])[:10]:
2169
+ print(f" {rec['distance_km']:7.1f} km {rec['azimuth']:5.0f} deg {rec['quality']} {rec['regime'] or '?':3s} {rec['type']:4s} {rec['locality'][:40]}")
2170
+ return 0 if r.get("n") else 1
2171
+ elif a.action == "slip":
2172
+ if not a.well:
2173
+ raise SystemExit("slip needs --well <id>")
2174
+ r = ws.well_slip(a.well, a.radius_km if a.radius_km is not None else 25.0, delta_pp_psi=a.delta_pp, depth_ft=a.depth_ft, actor=a.actor)
2175
+ print(f"slip {a.well}: {r['status']}")
2176
+ for line in r["lines"]:
2177
+ print(" " + line)
2178
+ return 0 if r["status"] == "OK" else 1
2127
2179
  elif a.action == "interval":
2128
2180
  if not a.well:
2129
2181
  raise SystemExit("interval needs --well <id>")
@@ -2304,6 +2356,62 @@ def main(argv=None) -> int:
2304
2356
  stop.set()
2305
2357
  print("etp-sim: stopped")
2306
2358
  return 0
2359
+ elif a.cmd == "comcat":
2360
+ import json as _json
2361
+ from . import sra as _SR
2362
+ from . import geodesy as _GD
2363
+ if a.file:
2364
+ c = _SR.read_catalog(a.file)
2365
+ if c.get("format") != "fdsn-text":
2366
+ print(f"comcat: {a.file} is not an FDSN text export (it read as a {c.get('basis', '')[:60]}...)"); return 1
2367
+ ev = c["events"]
2368
+ if a.lat is not None and a.lon is not None:
2369
+ for e in ev:
2370
+ dm = _GD.geodesic_m(a.lat, a.lon, e["lat"], e["lon"])["distance_m"]
2371
+ e["distance_km"] = round(dm / 1000.0, 2) if dm is not None else None
2372
+ if a.json:
2373
+ print(_json.dumps(c, indent=1, default=str)); return 0
2374
+ big = max(ev, key=lambda e: e["magnitude"]) if ev else None
2375
+ ts = sorted(e["time"] for e in ev)
2376
+ print(f"comcat: {c['n_events']} event(s) ({c['n_skipped']} skipped) from {ts[0][:10] if ts else '-'} to {ts[-1][:10] if ts else '-'}; "
2377
+ f"contributors {', '.join(c['contributors'])}; magnitude types {', '.join(c['magnitude_types'])}")
2378
+ if big:
2379
+ print(f" largest: M {big['magnitude']:.1f} {big['mag_type']} {big['time']} at {big['lat']:.4f}, {big['lon']:.4f}, {big['depth_km']} km - {big['place']} ({big['event_id']})")
2380
+ for e in sorted(ev, key=lambda x: x["time"])[:40]:
2381
+ print(f" {e['time'][:19]} M {e['magnitude']:>4.1f} {e['mag_type']:<6s} {e['lat']:9.4f} {e['lon']:10.4f} {str(e['depth_km']):>7s} km " + (f"{e['distance_km']:7.2f} km " if e.get("distance_km") is not None else "") + f"{e['place']}")
2382
+ return 0
2383
+ if a.lat is None or a.lon is None or not a.start or not a.end:
2384
+ raise SystemExit("comcat needs --lat --lon --start --end (--radius-km, --min-mag), or --file <export>")
2385
+ print(_SR.comcat_url(a.lat, a.lon, a.radius_km, a.start, a.end, a.min_mag))
2386
+ print(" open it in a browser (this machine may not reach the service) and save the text it returns; `gea comcat --file <saved>` reads it, and sra-report --catalog takes it.")
2387
+ return 0
2388
+ elif a.cmd == "slip":
2389
+ import json as _json
2390
+ from . import shapefile as _SF, slip as _SL, friction_priors as _FP
2391
+ try:
2392
+ L = _SF.read(a.layer, member=a.member)
2393
+ except (ValueError, OSError) as e:
2394
+ raise SystemExit(f"slip: {e}")
2395
+ if a.feature_kind:
2396
+ L.declared_kind = a.feature_kind
2397
+ az, az_src = a.shmax_azimuth, "declared"
2398
+ if az is None and a.wsm:
2399
+ nb = _SL.wsm_nearby(_SL.read_wsm(a.wsm), a.lat, a.lon, 100.0)
2400
+ if not nb["n"]:
2401
+ raise SystemExit("slip: no World Stress Map record of quality A/B/C within 100 km; declare --shmax-azimuth with its source")
2402
+ az, az_src = nb["azimuth_mean"], nb["statement"]
2403
+ if az is None:
2404
+ raise SystemExit("slip: give --shmax-azimuth or --wsm <wsm.csv>")
2405
+ faults = _SL.faults_in_radius(L, a.lat, a.lon, a.radius_km)
2406
+ r = _SL.statement(faults, a.depth_ft, {"sv_psi_ft": a.sv, "shmin_psi_ft": a.shmin, "shmax_psi_ft": a.shmax, "pp_psi_ft": a.pp}, az, tuple(a.dips), tuple(a.mu), a.delta_pp,
2407
+ sources={"shmax_azimuth": az_src, "gradients": "the command line"})
2408
+ r["friction_envelope"] = _FP.envelope()
2409
+ if a.json:
2410
+ print(_json.dumps(r, indent=1, default=str)); return 0
2411
+ for line in r["lines"]:
2412
+ print(line)
2413
+ print(r["friction_envelope"].get("statement", ""))
2414
+ return 0
2307
2415
  elif a.cmd == "catalog":
2308
2416
  import json as _json
2309
2417
  from . import catalog_register as _CR
@@ -6179,7 +6179,7 @@ def section_bo_structure(tmp: str) -> None:
6179
6179
  "counts_and_lines": r["counts_within"] == {"fault": 1, "fold": 1, "other": 0} and f"{g_fault:.2f} km" in ST.lines(r)[0] and "N (" in ST.lines(r)[0] and "North Fault" in ST.lines(r)[0],
6180
6180
  "outside_extent": out_ext["status"] == "OUTSIDE LAYER EXTENT" and "extent" in out_ext["note"] and ST.lines(out_ext)[0].startswith("Layer") and "OUTSIDE LAYER EXTENT" in ST.lines(out_ext)[0],
6181
6181
  "projected_unprojected": Lp.crs["kind"] == "projected" and Lp.crs["zone"] == "TX_CENTRAL" and abs(plat - (lat0 + dlat)) < 1e-6 and abs(plon - lon0) < 1e-6,
6182
- "refused_by_name": utm_err and "UTM" in utm_err and "Texas State Plane" in utm_err and noprj_err and "no .prj" in noprj_err,
6182
+ "refused_by_name": utm_err and "UTM" in utm_err and "from its parameters" in utm_err and noprj_err and "no .prj" in noprj_err,
6183
6183
  "shx_honoured": Lbad.n == 3 and seq_err and "content length" in seq_err,
6184
6184
  }
6185
6185
  failed1 = [k for k, v in c1.items() if not v]
@@ -7072,6 +7072,254 @@ def section_bs_catalog(tmp: str) -> None:
7072
7072
  + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
7073
7073
 
7074
7074
 
7075
+ def section_bt_slip(tmp: str) -> None:
7076
+ """Section BT - slip tendency on the mapped faults: the stress resolution against its identities, the regime, Ts and
7077
+ the pressure to slip, the World Stress Map reader and the nearby azimuth; a UTM layer unprojected from its .prj
7078
+ parameters with the fault's own dip and a published pressure-to-slip read beside this arithmetic; the workspace's
7079
+ stress state, stress map and statement through the SRA packet, the API, the page, the CLI; the help."""
7080
+ import http.cookiejar
7081
+ import math as _math
7082
+ import subprocess as _sp
7083
+ import urllib.request
7084
+ import urllib.error
7085
+ import zipfile
7086
+ from . import shapefile as SF
7087
+ from . import slip as SL
7088
+ from . import geodesy as GD
7089
+ from . import helplib as H
7090
+ from .telemetry import TelemetryRecorder, TelemetryConfig
7091
+ from .downhole_engine import DownholeEngine, SimulatorConfig
7092
+ from .workspace import Workspace, WorkspaceError
7093
+ from .service import Service, Users
7094
+ pkg = Path(__file__).parent
7095
+ d = Path(tmp, "bt"); d.mkdir()
7096
+
7097
+ # -- BT1: the arithmetic and the stress map -----------------------------------------------------------------------
7098
+ st = SL.stress_at(8000.0, 1.05, 0.65, 0.95)
7099
+ par = SL.resolve(80.0, 90.0, 80.0, st["Sv"], st["Shmin"], st["SHmax"]) # a vertical plane along SHmax: Shmin, no shear
7100
+ per = SL.resolve(170.0, 90.0, 80.0, st["Sv"], st["Shmin"], st["SHmax"]) # across SHmax: SHmax, no shear
7101
+ d45 = SL.resolve(35.0, 90.0, 80.0, st["Sv"], st["Shmin"], st["SHmax"]) # 45 deg: the mean and the half-difference
7102
+ hor = SL.resolve(0.0, 0.0, 80.0, st["Sv"], st["Shmin"], st["SHmax"]) # a horizontal plane: Sv
7103
+ ts = SL.slip_tendency(35.0, 90.0, st, 80.0, 0.6, 500.0)
7104
+ ts_past = SL.slip_tendency(35.0, 90.0, st, 80.0, 0.6, 2000.0)
7105
+ hdr = "ID,SITE,LAT,LON,AZI,TYPE,DEPTH,QUALITY,REGIME,LOCALITY,COUNTRY\n"
7106
+ rows = "".join(f"w{i},S,{31.3 + 0.05 * i},{-103.6 + 0.04 * i},{(75 + 3 * (i % 5)) % 180},BO,2.5,{'ABC'[i % 3]},{'NF' if i % 2 else 'SS'},Delaware,USA\n" for i in range(12))
7107
+ rows += "far,S,35.0,-100.0,10,FMS,3,A,TF,away,USA\nbad,S,31.4,-103.5,999,BO,2,A,NF,x,USA\nlow,S,31.42,-103.5,120,BO,2,E,NF,x,USA\n"
7108
+ wsm_path = str(d / "wsm_like.csv")
7109
+ Path(wsm_path).write_text(hdr + rows, encoding="utf-8")
7110
+ recs = SL.read_wsm(wsm_path)
7111
+ nb = SL.wsm_nearby(recs, 31.42, -103.50, 100.0)
7112
+ nb_none = SL.wsm_nearby(recs, 45.0, -70.0, 50.0)
7113
+ bad_wsm = None
7114
+ try:
7115
+ SL.read_wsm(io_bytes := b"A,B,C\n1,2,3\n")
7116
+ except ValueError as e:
7117
+ bad_wsm = str(e)
7118
+ c1 = {
7119
+ "identities": abs(par[0] - st["Shmin"]) < 1e-3 and abs(par[1]) < 1e-3 and abs(per[0] - st["SHmax"]) < 1e-3 and abs(per[1]) < 1e-3
7120
+ and abs(d45[0] - (st["SHmax"] + st["Shmin"]) / 2) < 1e-3 and abs(d45[1] - (st["SHmax"] - st["Shmin"]) / 2) < 1e-3 and abs(hor[0] - st["Sv"]) < 1e-3 and abs(hor[1]) < 1e-3,
7121
+ "regime": SL.regime_of(8400, 5200, 7600) == "normal" and SL.regime_of(8000, 5200, 9000) == "strike-slip" and SL.regime_of(5000, 6000, 9000) == "reverse",
7122
+ "tendency": ts["Ts_ambient"] == round(1200.0 / (6400.0 - 3464.0), 3) and ts["pressure_to_slip_psi"] == round(6400.0 - 3464.0 - 1200.0 / 0.6, 1) and ts["margin_after_injection_psi"] == round(ts["pressure_to_slip_psi"] - 500.0, 1)
7123
+ and ts["status"] == "BELOW THE FRICTION LINE" and ts_past["status"] == "PAST THE FRICTION LINE" and ts_past["Ts_injection"] > ts["Ts_injection"],
7124
+ "wsm": len(recs) == 14 and nb["n"] == 12 and abs(nb["azimuth_mean"] - 80.25) < 0.5 and nb["by_quality"] == {"A": 4, "B": 4, "C": 4} and nb["by_regime"] == {"NF": 6, "SS": 6}
7125
+ and nb["spread_deg_median"] <= 6 and nb_none["n"] == 0 and bad_wsm and "columns" in bad_wsm,
7126
+ }
7127
+ failed1 = [k for k, v in c1.items() if not v]
7128
+ ok(not failed1,
7129
+ "BT1 the stress resolution against its identities (a vertical plane along SHmax feels Shmin and no shear, across it SHmax, at 45 degrees the mean and the "
7130
+ "half-difference, a horizontal plane Sv), Anderson's regime from the ordering, the slip tendency and the pressure to slip by the published arithmetic, "
7131
+ "past the friction line when the increase exceeds it; the World Stress Map CSV read by column name (a 999 azimuth and a D/E quality left out), the "
7132
+ "records within the radius with the axial mean azimuth, spread, counts by quality and regime; no records, and a file without the columns, named"
7133
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
7134
+
7135
+ # -- BT2: a UTM layer from its .prj parameters, with dip and a published pressure to slip -----------------------------
7136
+ lat1, lon1 = 31.42, -103.50
7137
+ utm13 = GD.utm(13, True, "WGS84")
7138
+ wkt = ('PROJCS["WGS_1984_UTM_Zone_13N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],'
7139
+ 'UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],'
7140
+ 'PARAMETER["Central_Meridian",-105.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]')
7141
+
7142
+ def en(lat, lon):
7143
+ p_ = GD.project(lat, lon, utm13)
7144
+ return (p_["easting_m"], p_["northing_m"])
7145
+ kmlat = 1.0 / 111.195
7146
+ kmlon = 1.0 / (111.195 * _math.cos(_math.radians(lat1)))
7147
+ # three faults: one along SHmax (92 deg) 3 km north with dip 62 and a published value; one across it; one 40 km away (outside the radius)
7148
+ feats = [([[en(lat1 + 3 * kmlat, lon1 - 0.05), en(lat1 + 3 * kmlat - 0.1 * kmlat * 0.035 * 0, lon1 + 0.05)]], {"dip": 62.0, "note": "HIGH CONFIDENCE", "Pp2S_MPa": 5.0, "BEARING": 92.0}),
7149
+ ([[en(lat1 - 0.05, lon1 + 6 * kmlon), en(lat1 + 0.05, lon1 + 6 * kmlon)]], {"dip": 85.0, "note": "LOW CONFIDENCE", "Pp2S_MPa": 20.0, "BEARING": 0.0}),
7150
+ ([[en(lat1 + 0.36, lon1 - 0.02), en(lat1 + 0.36, lon1 + 0.02)]], {"dip": 70.0, "note": "FAR", "Pp2S_MPa": 9.0, "BEARING": 90.0})]
7151
+ SF.write(str(d / "utm_faults"), "PolyLine", feats, [("dip", "F", 6, 1), ("note", "C", 20, 0), ("Pp2S_MPa", "F", 10, 4), ("BEARING", "F", 8, 2)], wkt)
7152
+ with zipfile.ZipFile(str(d / "utm_faults.zip"), "w") as z:
7153
+ for ext in (".shp", ".shx", ".dbf", ".prj"):
7154
+ z.write(str(d / ("utm_faults" + ext)), "utm_faults" + ext)
7155
+ L = SF.read(str(d / "utm_faults.zip"))
7156
+ L.declared_kind = "fault"
7157
+ f1 = L.features[0].parts[0][0]
7158
+ faults = SL.faults_in_radius(L, lat1, lon1, 25.0)
7159
+ res = SL.statement(faults, 9000.0, {"sv_psi_ft": 1.05, "shmin_psi_ft": 0.65, "shmax_psi_ft": 0.95, "pp_psi_ft": 0.45}, 92.0, (60.0, 75.0, 90.0), (0.6,), 300.0,
7160
+ sources={"shmax_azimuth": "the gate", "gradients": "the gate"})
7161
+ along = next(r for r in res["faults"] if r["attrs"].get("note") == "HIGH CONFIDENCE")
7162
+ across = next(r for r in res["faults"] if r["attrs"].get("note") == "LOW CONFIDENCE")
7163
+ lcc_wkt = ('PROJCS["NAD_1983_Texas_Centric_Mapping_System_Lambert",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],'
7164
+ 'PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",1500000.0],PARAMETER["False_Northing",5000000.0],'
7165
+ 'PARAMETER["Central_Meridian",-100.0],PARAMETER["Standard_Parallel_1",27.5],PARAMETER["Standard_Parallel_2",35.0],PARAMETER["Latitude_Of_Origin",18.0],UNIT["Meter",1.0]]')
7166
+ lcc = SF.parse_prj(lcc_wkt)
7167
+ unknown = SF.parse_prj('PROJCS["X",GEOGCS["G",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]]],PROJECTION["Mercator"],PARAMETER["Central_Meridian",0.0],UNIT["Meter",1.0]]')
7168
+ c2 = {
7169
+ "unprojected": L.crs["kind"] == "projected" and L.crs["usable"] and "Transverse Mercator on WGS84" in L.crs["how"] and abs(f1[1] - (lat1 + 3 * kmlat)) < 2e-6 and abs(f1[0] - (lon1 - 0.05)) < 2e-6
7170
+ and lcc["usable"] and lcc["zone_def"]["kind"] == "lcc" and lcc["zone_def"]["lat1"] == 27.5 and not unknown["usable"] and "MERCATOR" in unknown["note"],
7171
+ "faults_found": len(faults) == 2 and along["attrs"]["dip_deg"] == 62.0 and along["attrs"]["published_pressure_to_slip_psi"] == round(5.0 * 145.037738, 1)
7172
+ and abs(along["distance_km"] - 3.0) < 0.05 and abs(along["strike_deg"] - 90.0) < 1.0 and along["dip_source"] == "the layer" and len(along["cases"]) == 1,
7173
+ "statement": along["worst"]["dip_deg"] == 62.0 and across["worst"]["dip_deg"] == 85.0 and along["angle_to_shmax_deg"] <= 3 and across["angle_to_shmax_deg"] > 85
7174
+ and along["worst"]["Ts_injection"] > across["worst"]["Ts_injection"] and res["regime"] == "normal"
7175
+ and along["published"]["pressure_to_slip_psi"] == round(5.0 * 145.037738, 1) and along["published"]["difference_psi"] == round(along["cases"][0]["pressure_to_slip_psi"] - along["published"]["pressure_to_slip_psi"], 1)
7176
+ and any("The layer publishes 725 psi to slip (Pp2S_MPa)" in ln for ln in res["lines"]) and res["faults"][0] is along,
7177
+ }
7178
+ failed2 = [k for k, v in c2.items() if not v]
7179
+ ok(not failed2,
7180
+ "BT2 a UTM zone 13N layer unprojected from its .prj parameters (Transverse Mercator on WGS84; a Lambert Conformal Conic parsed the same way; a "
7181
+ "Mercator refused by name); the faults within the radius with the layer's own dip and its published pressure-to-slip read as attributes; the trace "
7182
+ "along SHmax ranked first with the higher tendency, its dip from the layer and one case, the published value printed beside this arithmetic with the difference"
7183
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
7184
+
7185
+ # -- BT3: the workspace, the SRA packet, the API, the page, the CLI, the help -------------------------------------------
7186
+ hist = str(d / "hist.csv")
7187
+ TelemetryRecorder(engine=DownholeEngine(SimulatorConfig()), config=TelemetryConfig(duration_hours=1.0, seed=5)).run().export_csv(hist)
7188
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
7189
+ w = ws.add_well_file(hist, display="SWD 1", actor="tester")
7190
+ s0 = ws.well_slip(w["id"], actor="tester")
7191
+ ws.set_well(w["id"], surface={"lat": lat1, "lon": lon1, "datum": "NAD83", "elevation": {"ft": 2950.0, "datum": "KB", "source": "completion report"}}, actor="tester")
7192
+ ws.set_well(w["id"], disposal={"top_interval_ft": 8700.0, "base_interval_ft": 9300.0, "depth_reference": "TVD", "depth_tier": "deep", "channel_pressure": "P_surf_psi",
7193
+ "channel_rate": "Q_bpm", "rate_unit": "bbl/min", "bhp_method": "probe", "channel_bhp": "P_bh_psi"}, actor="tester")
7194
+ s1 = ws.well_slip(w["id"], actor="tester")
7195
+ no_src = None
7196
+ try:
7197
+ ws.set_stress({"sv_psi_ft": 1.05, "shmin_psi_ft": 0.65, "shmax_psi_ft": 0.95}, actor="tester")
7198
+ except WorkspaceError as e:
7199
+ no_src = str(e)
7200
+ stx = ws.set_stress({"sv_psi_ft": 1.05, "shmin_psi_ft": 0.65, "shmax_psi_ft": 0.95, "pp_psi_ft": 0.45, "gradients_source": "the gate's declaration", "delta_pp_psi": 300.0}, actor="tester")
7201
+ s2 = ws.well_slip(w["id"], actor="tester")
7202
+ lay = ws.add_layer(str(d / "utm_faults.zip"), "Gate UTM faults", actor="tester", feature_kind="fault", citation="the gate's own layer")
7203
+ bad_fk = None
7204
+ try:
7205
+ ws.add_layer(str(d / "utm_faults.zip"), "Again", actor="tester", feature_kind="contour")
7206
+ except WorkspaceError as e:
7207
+ bad_fk = str(e)
7208
+ s3 = ws.well_slip(w["id"], actor="tester")
7209
+ sm = ws.add_stress_map(wsm_path, actor="tester", citation="WSM-shaped, the gate")
7210
+ s4 = ws.well_slip(w["id"], actor="tester")
7211
+ wsm_q = ws.wsm_at(lat1, lon1, 100.0)
7212
+ site = ws.add_site("Pad 3 site", wells=[w["id"]], actor="tester")
7213
+ ws.sra_define(site["id"], actor="tester", name="Pad 3 SRA", lat=lat1, lon=lon1, radius_km=9.08, plan_date="2026-01-15")
7214
+ pk = ws.write_sra_packet(site["id"], actor="tester")
7215
+ sra_md = open(pk["paths"]["markdown"], encoding="utf-8").read()
7216
+ audit = [a["action"] for a in ws.audit_log()]
7217
+ users = Users(str(Path(wsp, "users.json"))); users.add("op", "operator-pass-1", "operator")
7218
+ svc = Service(wsp, port=0, scheduler=False).start()
7219
+ base_url = svc.url.rstrip("/")
7220
+ opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
7221
+
7222
+ def call(method, path, body=None):
7223
+ req = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
7224
+ req.add_header("Content-Type", "application/json"); req.add_header("X-GEA-Action", "1")
7225
+ try:
7226
+ with opener.open(req, timeout=120) as rr_:
7227
+ return rr_.status, json.loads(rr_.read())
7228
+ except urllib.error.HTTPError as e:
7229
+ return e.code, json.loads(e.read())
7230
+ try:
7231
+ call("POST", "/api/login", {"name": "op", "password": "operator-pass-1"})
7232
+ st_g, got = call("GET", f"/api/wells/{w['id']}/slip")
7233
+ st_s, stt = call("GET", "/api/stress")
7234
+ st_w, wq = call("GET", f"/api/stress/wsm?lat={lat1}&lon={lon1}")
7235
+ st_d, dec = call("POST", "/api/stress", {"sv_psi_ft": 1.05, "shmin_psi_ft": 0.65, "shmax_psi_ft": 0.95, "gradients_source": "api", "shmax_azimuth_deg": 95.0, "azimuth_source": "api"})
7236
+ st_bad = call("POST", "/api/stress", {"sv_psi_ft": 1.0, "shmin_psi_ft": 0.9, "shmax_psi_ft": 0.5, "gradients_source": "x"})[0]
7237
+ st_p, wrote = call("POST", f"/api/wells/{w['id']}/slip", {"delta_pp_psi": 100.0})
7238
+ import base64 as _b64
7239
+ st_m, mp = call("POST", "/api/stress/map", {"citation": "again", "file": {"filename": "wsm.csv", "content_b64": _b64.b64encode(open(wsm_path, "rb").read()).decode()}})
7240
+ finally:
7241
+ svc.stop()
7242
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
7243
+ help_md = (pkg / "help" / "slip.md").read_text(encoding="utf-8")
7244
+ r_cli = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "slip", "--well", w["id"], "--delta-pp", "300"], capture_output=True, text=True)
7245
+ r_wsm = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "wsm", "--lat", str(lat1), "--lon", str(lon1)], capture_output=True, text=True)
7246
+ r_set = _sp.run([sys.executable, "-m", "gea", "workspace", "--path", wsp, "--action", "set-stress", "--sv", "1.05", "--shmin", "0.65", "--shmax", "0.95", "--gradients-source", "cli", "--shmax-azimuth", "92", "--azimuth-source", "cli"], capture_output=True, text=True)
7247
+ r_sa = _sp.run([sys.executable, "-m", "gea", "slip", "--layer", str(d / "utm_faults.zip"), "--feature-kind", "fault", "--lat", str(lat1), "--lon", str(lon1), "--depth-ft", "9000", "--sv", "1.05", "--shmin", "0.65", "--shmax", "0.95",
7248
+ "--wsm", wsm_path, "--delta-pp", "300"], capture_output=True, text=True)
7249
+ sec2e = sra_md.split("Slip tendency on the mapped faults, from the declared stress state")[1].split("## 2d")[0] if "Slip tendency on the mapped faults" in sra_md else ""
7250
+ c3 = {
7251
+ "refusals_in_order": s0["status"] == "NO SURFACE POSITION" and s1["status"] == "NO STRESS STATE" and no_src and "source" in no_src and s2["status"] == "NO FAULT LAYER"
7252
+ and bad_fk and "'fault' or 'fold'" in bad_fk and s3["status"] == "NO SHMAX AZIMUTH" and "add-stress-map" in s3["note"],
7253
+ "from_the_map": s4["status"] == "OK" and abs(s4["shmax_azimuth_deg"] - 80.25) < 0.5 and "World Stress Map records within 100 km (12" in s4["azimuth_source"] and s4["depth_ft"] == 9000.0
7254
+ and s4["depth_how"].startswith("the midpoint") and s4["delta_pp_psi"] == 300.0 and len(s4["layers"]) == 1 and len(s4["layers"][0]["faults"]) == 2
7255
+ and s4["layers"][0]["faults"][0]["attrs"]["note"] == "HIGH CONFIDENCE" and s4["friction_envelope"]["min"] == 0.21 and sm["n_records"] == 14
7256
+ and wsm_q["n"] == 12 and wsm_q["citation"] == "WSM-shaped, the gate" and os.path.isfile(Path(wsp, "reports", "wells", w["id"], "slip.json")),
7257
+ "sra_section": "## 2e" in sra_md and "| SWD-1 | Gate UTM faults |" in sec2e and "62 (layer)" in sec2e and "published 725 psi (Pp2S_MPa)" in sec2e and "none from the Permian Basin" in sec2e,
7258
+ "audited": "stress.declare" in audit and "stress_map.add" in audit and audit.count("well.slip") >= 4,
7259
+ "api": st_g == 200 and got["status"] == "OK" and st_s == 200 and stt["stress"]["gradients_source"] == "the gate's declaration" and stt["stress_map"]["n_records"] == 14
7260
+ and st_w == 200 and wq["n"] == 12 and st_d == 200 and dec["shmax_azimuth_deg"] == 95.0 and st_bad == 400 and st_p == 200 and wrote["delta_pp_psi"] == 100.0
7261
+ and wrote["shmax_azimuth_deg"] == 95.0 and st_m == 200 and mp["citation"] == "again",
7262
+ "page": "slipCardHtml" in page_src and "stressCardHtml" in page_src and "/api/stress/map" in page_src and "lyFK" in page_src and "'/slip'" in page_src,
7263
+ "cli": r_cli.returncode == 0 and "Slip tendency at 9,000 ft" in r_cli.stdout and "PAST THE FRICTION LINE" in r_cli.stdout or "BELOW THE FRICTION LINE" in r_cli.stdout
7264
+ and r_wsm.returncode == 0 and "12 World Stress Map record(s)" in r_wsm.stdout and r_set.returncode == 0 and "SHmax azimuth 92.0" in r_set.stdout
7265
+ and r_sa.returncode == 0 and "The layer publishes 725 psi to slip" in r_sa.stdout and "run from 0.21 to 0.9" in r_sa.stdout,
7266
+ "help": any(t["topic"] == "slip" for t in H.topics()) and not H.check("slip") and "does not say the fault exists at depth" in help_md.split("will not call a measurement")[1],
7267
+ }
7268
+ failed3 = [k for k, v in c3.items() if not v]
7269
+ ok(not failed3,
7270
+ "BT3 the workspace: NO SURFACE POSITION, NO STRESS STATE (gradients without a source refused), NO FAULT LAYER (a feature kind that is not fault or fold "
7271
+ "refused), NO SHMAX AZIMUTH naming the stress map, then OK with the azimuth from the map's records near the well and the depth from the declared interval; "
7272
+ "the SRA packet's section 2e with the layer's dip and the published value; audited; the API declares the state, takes the map, answers the records near a "
7273
+ "position and writes the statement; the page cards; `--action slip`, `wsm`, `set-stress` and `gea slip --wsm`; the help"
7274
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
7275
+
7276
+
7277
+ def section_bu_comcat(tmp: str) -> None:
7278
+ """Section BU - the USGS ComCat / FDSN text export read as a catalogue, proven on two verbatim public-domain
7279
+ extracts kept as fixtures (the Delaware Basin, September 2026; the Youngstown 2011 sequence); the SRA seismicity
7280
+ on the real Youngstown sequence; the query builder; the CLI."""
7281
+ import subprocess as _sp
7282
+ from . import sra as SR
7283
+ from . import geodesy as GD
7284
+ pkg = Path(__file__).parent
7285
+ d = Path(tmp, "bu"); d.mkdir()
7286
+ fx = pkg / "fixtures"
7287
+ de = SR.read_catalog(str(fx / "comcat_delaware_basin_2026-09.txt"))
7288
+ yo = SR.read_catalog(str(fx / "comcat_youngstown_2011-2012.txt"))
7289
+ big = max(yo["events"], key=lambda e: e["magnitude"])
7290
+ url = SR.comcat_url(41.1, -80.68, 25.0, "2011-03-01", "2012-03-01", 1.5)
7291
+ # the Youngstown sequence against an SRA-shaped area around the Northstar 1 site: 10 km, M 2.0 threshold, plan date 2011-12-01
7292
+ sra = SR.define("Youngstown", 41.12, -80.69, 10.0, "2011-12-01", threshold_m=2.0, formation_boundary="base of the Knox")
7293
+ from datetime import datetime as _dt, timezone as _tz
7294
+ se = SR.seismicity(sra, yo, as_of=_dt(2012, 2, 1, tzinfo=_tz.utc))
7295
+ far = next(e for e in yo["events"] if e["event_id"] == "ld60004308")
7296
+ dist_far = GD.geodesic_m(41.12, -80.69, far["lat"], far["lon"])["distance_m"] / 1000.0
7297
+ r_file = _sp.run([sys.executable, "-m", "gea", "comcat", "--file", str(fx / "comcat_youngstown_2011-2012.txt"), "--lat", "41.1", "--lon", "-80.68"], capture_output=True, text=True)
7298
+ r_url = _sp.run([sys.executable, "-m", "gea", "comcat", "--lat", "31.42", "--lon", "-103.5", "--radius-km", "30", "--start", "2026-09-01", "--end", "2026-10-01", "--min-mag", "2.5"], capture_output=True, text=True)
7299
+ r_bad = _sp.run([sys.executable, "-m", "gea", "comcat", "--file", str(pkg / "catalog" / "collingwood_1_28_ks_complete.las")], capture_output=True, text=True)
7300
+ help_md = (pkg / "help" / "sra.md").read_text(encoding="utf-8")
7301
+ c1 = {
7302
+ "delaware": de["format"] == "fdsn-text" and de["n_events"] == 28 and de["n_skipped"] == 0 and de["contributors"] == ["tx", "us"] and de["magnitude_types"] == ["mb_lg", "ml"]
7303
+ and max(e["magnitude"] for e in de["events"]) == 3.4 and de["events"][0]["event_id"] == "tx2026tgiogl" and de["events"][0]["time"] == "2026-09-30T13:35:58Z"
7304
+ and de["events"][0]["depth_km"] == 6.2024 and "mixes scales" in de["basis"] and sum(1 for e in de["events"] if e["contributor"] == "us") == 4,
7305
+ "youngstown": yo["n_events"] == 9 and big["event_id"] == "ld60029101" and big["magnitude"] == 4.0 and big["time"] == "2011-12-31T20:05:01Z" and big["lat"] == 41.1215 and big["lon"] == -80.6843333
7306
+ and big["place"] == "3 km NW of Youngstown, Ohio" and yo["contributors"] == ["ld"],
7307
+ "seismicity": se["n_inside"] == 9 and se["largest"]["event_id"] == "ld60029101" and se["largest"]["magnitude"] == 4.0 and se["n_at_or_above"] == 6 and dist_far < 10.0
7308
+ and se["trigger"]["status"] == "TRIGGERED" and se["trigger"]["respond_by"] == "2012-01-02T20:05:01Z" and se["goal"]["clock_started"] == "2011-12-31",
7309
+ "url": url == "https://earthquake.usgs.gov/fdsnws/event/1/query?format=text&starttime=2011-03-01&endtime=2012-03-01&latitude=41.1000&longitude=-80.6800&maxradiuskm=25&minmagnitude=1.5&orderby=time-asc",
7310
+ "cli": r_file.returncode == 0 and "largest: M 4.0 ml 2011-12-31T20:05:01Z at 41.1215, -80.6843" in r_file.stdout and "1.68 km" in r_file.stdout
7311
+ and r_url.returncode == 0 and "maxradiuskm=30&minmagnitude=2.5" in r_url.stdout and r_bad.returncode == 1 and "not an FDSN text export" in r_bad.stdout,
7312
+ "help_and_fixtures": "gea comcat" in help_md and (fx / "README.md").read_text(encoding="utf-8").count("earthquake.usgs.gov/fdsnws/event/1/query") == 2,
7313
+ }
7314
+ failed1 = [k for k, v in c1.items() if not v]
7315
+ ok(not failed1,
7316
+ "BU1 the FDSN text export read as a catalogue with contributors and magnitude types: the Delaware Basin window of September 2026 (28 events, TexNet "
7317
+ "and NEIC, ML and mb_Lg, largest M 3.4) and the Youngstown 2011 sequence (9 events, the M 4.0 of 2011-12-31 3 km NW of Youngstown); the SRA seismicity "
7318
+ "on the real sequence inside a 10 km area (six at or above M 2.0, the M 4.0 TRIGGERED, the goal clock from 2011-12-31); the query builder; `gea comcat --file` with distances and `--lat --lon --start --end` printing the query; "
7319
+ "a LAS refused; the help and the fixtures' provenance"
7320
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
7321
+
7322
+
7075
7323
  def main() -> int:
7076
7324
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
7077
7325
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -7146,6 +7394,8 @@ def main() -> int:
7146
7394
  section_bq_field_ports(tmp)
7147
7395
  section_br_logs_historian(tmp)
7148
7396
  section_bs_catalog(tmp)
7397
+ section_bt_slip(tmp)
7398
+ section_bu_comcat(tmp)
7149
7399
  if _FAILS:
7150
7400
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
7151
7401
  for f in _FAILS:
@@ -1680,6 +1680,33 @@ def sra_packet_report(pk: dict, evaluated_at: Optional[datetime] = None, program
1680
1680
  'arithmetic shown, and the declared depth tier (shallow or deep by the base the plan names) is CONSISTENT with the map, INCONSISTENT, or '
1681
1681
  'NOT RESOLVED. A contour map is read, not interpolated; the completion report is the record of the interval.'],
1682
1682
  [Table(['Well', 'Status', 'Declared interval', 'Against the mapped tops', 'Depth tier against the map'], irows, 'Interval against the tops')]))
1683
+ slrows = []
1684
+ sl_paras = []
1685
+ for wid, sl in (pk.get('slip') or {}).items():
1686
+ if sl.get('status') != 'OK':
1687
+ slrows.append([wid, '-', '-', sl.get('status'), '-', '-', '-', (sl.get('note') or '')[:160]])
1688
+ continue
1689
+ if not sl_paras:
1690
+ sl_paras.append(f"Stress state: {sl['lines'][0]}" if sl.get('lines') else '')
1691
+ for lay in sl.get('layers', []):
1692
+ for fr in lay.get('faults', [])[:12]:
1693
+ w_, b_ = fr['worst'], fr['best']
1694
+ pub = fr.get('published') or {}
1695
+ slrows.append([wid, lay.get('layer_display') or lay.get('layer_id'), fr.get('name') or fr.get('type') or f"trace {fr['index']}", f"{fr['distance_km']:.2f} km / strike {fr['strike_deg']:g}",
1696
+ f"{w_['dip_deg']:g}" + (' (layer)' if fr.get('dip_source') == 'the layer' else ' (declared)'),
1697
+ f"{b_['Ts_injection']:.2f}-{w_['Ts_injection']:.2f}", f"{w_['margin_after_injection_psi']:,.0f} to {b_['margin_after_injection_psi']:,.0f}",
1698
+ (w_['status'] + (f"; published {pub['pressure_to_slip_psi']:,.0f} psi ({pub['field']})" if pub else ''))[:160]])
1699
+ if slrows:
1700
+ env = next((sl.get('friction_envelope') for sl in pk['slip'].values() if sl.get('friction_envelope')), None)
1701
+ secs.append(Section('2e', 'Slip tendency on the mapped faults, from the declared stress state', [
1702
+ 'For each well inside the area, the mapped fault traces within the radius against the stress state the site declared - the three gradients and the '
1703
+ 'ambient pore pressure, the SHmax azimuth from the declaration or from the World Stress Map records near the well - at the depth of the declared '
1704
+ 'interval and the pore-pressure increase the plan is judged at: the shear and effective normal stress on each plane, the slip tendency, and the '
1705
+ 'pore-pressure increase that would bring the plane to the friction line (negative: past it under these inputs). The strike comes from the map; '
1706
+ 'the dip from the layer when it states one, otherwise the declared range; the friction coefficient as declared. Where a layer publishes its own '
1707
+ 'pressure-to-slip (BEG/TexNet Hennings et al. 2021), that number is printed beside this arithmetic. A statement on declared inputs, not a probability '
1708
+ 'and not a verdict.'] + sl_paras + ([env['statement']] if env else []),
1709
+ [Table(['Well', 'Layer', 'Fault', 'Distance / strike', 'Dip', 'Ts (after)', 'Pressure to slip after (psi)', 'Worst case'], slrows[:120], 'Slip tendency')]))
1683
1710
  ef = pk.get('events_faults') or []
1684
1711
  if ef:
1685
1712
  paras = ['The catalogue events inside the area, each against the mapped fault traces of every structure layer: the nearest trace to the epicentre by '
@@ -0,0 +1,10 @@
1
+ # fixtures
2
+
3
+ Small, verbatim, public-domain extracts kept so the gate can prove a reader on real records without a network.
4
+
5
+ - `comcat_delaware_basin_2026-09.txt` - USGS ComCat, FDSN event web service text output, fetched 2026-10-12:
6
+ `https://earthquake.usgs.gov/fdsnws/event/1/query?format=text&starttime=2026-09-01&endtime=2026-10-01&minlatitude=30.5&maxlatitude=32.5&minlongitude=-104.5&maxlongitude=-102.0&minmagnitude=2.5&orderby=time`
7
+ (28 events M >= 2.5, Delaware and Midland basins; contributors tx = TexNet, us = NEIC). US Government work, public domain.
8
+ - `comcat_youngstown_2011-2012.txt` - the same service, fetched 2026-10-12:
9
+ `https://earthquake.usgs.gov/fdsnws/event/1/query?format=text&starttime=2011-03-01&endtime=2012-03-01&latitude=41.1&longitude=-80.68&maxradiuskm=25&minmagnitude=1.5&orderby=time-asc`
10
+ (9 events within 25 km of Youngstown, Ohio: the 2011 sequence that ended with the M 4.0 of 2011-12-31; contributor ld = Lamont-Doherty). Public domain.