gea-program 0.25.0__tar.gz → 0.26.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 (268) hide show
  1. {gea_program-0.25.0/gea_program.egg-info → gea_program-0.26.0}/PKG-INFO +17 -2
  2. {gea_program-0.25.0 → gea_program-0.26.0}/README.md +16 -1
  3. {gea_program-0.25.0 → gea_program-0.26.0}/gea/__init__.py +1 -1
  4. {gea_program-0.25.0 → gea_program-0.26.0}/gea/__main__.py +76 -2
  5. {gea_program-0.25.0 → gea_program-0.26.0}/gea/acceptance_tests.py +204 -1
  6. {gea_program-0.25.0 → gea_program-0.26.0}/gea/client_reports.py +27 -0
  7. gea_program-0.26.0/gea/help/slip.md +11 -0
  8. {gea_program-0.25.0 → gea_program-0.26.0}/gea/helplib.py +1 -0
  9. {gea_program-0.25.0 → gea_program-0.26.0}/gea/service.py +47 -2
  10. {gea_program-0.25.0 → gea_program-0.26.0}/gea/shapefile.py +25 -5
  11. gea_program-0.26.0/gea/slip.py +276 -0
  12. {gea_program-0.25.0 → gea_program-0.26.0}/gea/structure.py +1 -1
  13. {gea_program-0.25.0 → gea_program-0.26.0}/gea/web/app.html +44 -5
  14. {gea_program-0.25.0 → gea_program-0.26.0}/gea/workspace.py +157 -3
  15. {gea_program-0.25.0 → gea_program-0.26.0/gea_program.egg-info}/PKG-INFO +17 -2
  16. {gea_program-0.25.0 → gea_program-0.26.0}/gea_program.egg-info/SOURCES.txt +2 -0
  17. {gea_program-0.25.0 → gea_program-0.26.0}/pyproject.toml +1 -1
  18. {gea_program-0.25.0 → gea_program-0.26.0}/LICENSE +0 -0
  19. {gea_program-0.25.0 → gea_program-0.26.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  20. {gea_program-0.25.0 → gea_program-0.26.0}/gea/accuracy_statement.py +0 -0
  21. {gea_program-0.25.0 → gea_program-0.26.0}/gea/alarm_engine.py +0 -0
  22. {gea_program-0.25.0 → gea_program-0.26.0}/gea/backup.py +0 -0
  23. {gea_program-0.25.0 → gea_program-0.26.0}/gea/bench.py +0 -0
  24. {gea_program-0.25.0 → gea_program-0.26.0}/gea/blind_harness.py +0 -0
  25. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  26. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  27. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  28. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  29. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  30. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  31. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  32. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  33. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  34. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  35. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  36. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  37. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  38. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  39. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  40. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  41. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  42. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  43. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  44. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  45. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  46. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  47. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  48. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  49. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  50. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  51. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  52. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  53. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  54. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  55. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  56. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  57. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  58. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  59. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  60. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  61. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  62. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  63. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  64. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  65. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  66. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  67. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  68. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  69. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  70. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  71. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  72. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  73. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  74. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  75. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  76. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  77. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  78. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  79. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  80. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  81. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  82. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  83. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  84. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  85. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  86. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  87. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  88. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  89. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  90. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  91. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  92. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/med_160_sapropels.txt +0 -0
  93. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  94. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  95. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  96. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  97. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  98. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  99. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  100. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  101. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  102. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  103. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  104. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  105. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  106. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  107. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  108. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  109. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  110. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  111. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  112. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  113. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  114. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  115. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  116. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  117. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  118. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  119. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  120. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  121. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  122. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  123. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  124. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  125. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  126. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  127. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  128. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  129. {gea_program-0.25.0 → gea_program-0.26.0}/gea/catalog_register.py +0 -0
  130. {gea_program-0.25.0 → gea_program-0.26.0}/gea/certificates.py +0 -0
  131. {gea_program-0.25.0 → gea_program-0.26.0}/gea/cli.py +0 -0
  132. {gea_program-0.25.0 → gea_program-0.26.0}/gea/config_versioning.py +0 -0
  133. {gea_program-0.25.0 → gea_program-0.26.0}/gea/conformance.py +0 -0
  134. {gea_program-0.25.0 → gea_program-0.26.0}/gea/correlation.py +0 -0
  135. {gea_program-0.25.0 → gea_program-0.26.0}/gea/dashboard.py +0 -0
  136. {gea_program-0.25.0 → gea_program-0.26.0}/gea/deviation.py +0 -0
  137. {gea_program-0.25.0 → gea_program-0.26.0}/gea/dlis.py +0 -0
  138. {gea_program-0.25.0 → gea_program-0.26.0}/gea/dnp3.py +0 -0
  139. {gea_program-0.25.0 → gea_program-0.26.0}/gea/doctor.py +0 -0
  140. {gea_program-0.25.0 → gea_program-0.26.0}/gea/downhole_engine.py +0 -0
  141. {gea_program-0.25.0 → gea_program-0.26.0}/gea/drift_monitor.py +0 -0
  142. {gea_program-0.25.0 → gea_program-0.26.0}/gea/earth_model.py +0 -0
  143. {gea_program-0.25.0 → gea_program-0.26.0}/gea/elevation.py +0 -0
  144. {gea_program-0.25.0 → gea_program-0.26.0}/gea/etp.py +0 -0
  145. {gea_program-0.25.0 → gea_program-0.26.0}/gea/etp12_schemas.json +0 -0
  146. {gea_program-0.25.0 → gea_program-0.26.0}/gea/example_register_map.json +0 -0
  147. {gea_program-0.25.0 → gea_program-0.26.0}/gea/fat_sat.py +0 -0
  148. {gea_program-0.25.0 → gea_program-0.26.0}/gea/files.py +0 -0
  149. {gea_program-0.25.0 → gea_program-0.26.0}/gea/follower.py +0 -0
  150. {gea_program-0.25.0 → gea_program-0.26.0}/gea/forward_model.py +0 -0
  151. {gea_program-0.25.0 → gea_program-0.26.0}/gea/friction_priors.py +0 -0
  152. {gea_program-0.25.0 → gea_program-0.26.0}/gea/gamma.py +0 -0
  153. {gea_program-0.25.0 → gea_program-0.26.0}/gea/gauge_aging.py +0 -0
  154. {gea_program-0.25.0 → gea_program-0.26.0}/gea/gauge_specs.py +0 -0
  155. {gea_program-0.25.0 → gea_program-0.26.0}/gea/geodesy.py +0 -0
  156. {gea_program-0.25.0 → gea_program-0.26.0}/gea/gravity_reference.py +0 -0
  157. {gea_program-0.25.0 → gea_program-0.26.0}/gea/hartip.py +0 -0
  158. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/TESTER_GUIDE.md +0 -0
  159. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/alarms.md +0 -0
  160. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/audit-update.md +0 -0
  161. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/backup.md +0 -0
  162. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/catalog.md +0 -0
  163. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/conformance.md +0 -0
  164. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/data-in.md +0 -0
  165. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/dlis.md +0 -0
  166. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/doctor.md +0 -0
  167. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/drift.md +0 -0
  168. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/etp.md +0 -0
  169. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/field-ports.md +0 -0
  170. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/files.md +0 -0
  171. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/historian.md +0 -0
  172. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/instruments.md +0 -0
  173. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/interval.md +0 -0
  174. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/month-end.md +0 -0
  175. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/notifications.md +0 -0
  176. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/osdu.md +0 -0
  177. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/patches.md +0 -0
  178. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/ppdm.md +0 -0
  179. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/quakeml.md +0 -0
  180. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/quality.md +0 -0
  181. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/seedlink.md +0 -0
  182. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/seismic.md +0 -0
  183. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/site.md +0 -0
  184. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/sites.md +0 -0
  185. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/soak.md +0 -0
  186. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/sra.md +0 -0
  187. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/start.md +0 -0
  188. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/structure.md +0 -0
  189. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/transients.md +0 -0
  190. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/upkeep.md +0 -0
  191. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/vibration.md +0 -0
  192. {gea_program-0.25.0 → gea_program-0.26.0}/gea/help/well-tests.md +0 -0
  193. {gea_program-0.25.0 → gea_program-0.26.0}/gea/housekeeping.py +0 -0
  194. {gea_program-0.25.0 → gea_program-0.26.0}/gea/interval.py +0 -0
  195. {gea_program-0.25.0 → gea_program-0.26.0}/gea/inverse_engine.py +0 -0
  196. {gea_program-0.25.0 → gea_program-0.26.0}/gea/jobs.py +0 -0
  197. {gea_program-0.25.0 → gea_program-0.26.0}/gea/live_ports.py +0 -0
  198. {gea_program-0.25.0 → gea_program-0.26.0}/gea/loadtest.py +0 -0
  199. {gea_program-0.25.0 → gea_program-0.26.0}/gea/matplotlib_demo.py +0 -0
  200. {gea_program-0.25.0 → gea_program-0.26.0}/gea/modbus.py +0 -0
  201. {gea_program-0.25.0 → gea_program-0.26.0}/gea/modbus_rtu.py +0 -0
  202. {gea_program-0.25.0 → gea_program-0.26.0}/gea/model_card.py +0 -0
  203. {gea_program-0.25.0 → gea_program-0.26.0}/gea/month_end.py +0 -0
  204. {gea_program-0.25.0 → gea_program-0.26.0}/gea/mqtt_port.py +0 -0
  205. {gea_program-0.25.0 → gea_program-0.26.0}/gea/notify.py +0 -0
  206. {gea_program-0.25.0 → gea_program-0.26.0}/gea/opcua_port.py +0 -0
  207. {gea_program-0.25.0 → gea_program-0.26.0}/gea/operator_app.py +0 -0
  208. {gea_program-0.25.0 → gea_program-0.26.0}/gea/osdu.py +0 -0
  209. {gea_program-0.25.0 → gea_program-0.26.0}/gea/patches.py +0 -0
  210. {gea_program-0.25.0 → gea_program-0.26.0}/gea/permits.py +0 -0
  211. {gea_program-0.25.0 → gea_program-0.26.0}/gea/piwebapi.py +0 -0
  212. {gea_program-0.25.0 → gea_program-0.26.0}/gea/ports.py +0 -0
  213. {gea_program-0.25.0 → gea_program-0.26.0}/gea/ppdm.py +0 -0
  214. {gea_program-0.25.0 → gea_program-0.26.0}/gea/profile_catalog.py +0 -0
  215. {gea_program-0.25.0 → gea_program-0.26.0}/gea/project.py +0 -0
  216. {gea_program-0.25.0 → gea_program-0.26.0}/gea/qt6_downhole_app.py +0 -0
  217. {gea_program-0.25.0 → gea_program-0.26.0}/gea/quakeml.py +0 -0
  218. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reconciler.py +0 -0
  219. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  220. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  221. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  222. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  223. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  224. {gea_program-0.25.0 → gea_program-0.26.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  225. {gea_program-0.25.0 → gea_program-0.26.0}/gea/rock_inventory.py +0 -0
  226. {gea_program-0.25.0 → gea_program-0.26.0}/gea/sample_record.py +0 -0
  227. {gea_program-0.25.0 → gea_program-0.26.0}/gea/sample_well_profile.csv +0 -0
  228. {gea_program-0.25.0 → gea_program-0.26.0}/gea/sbom.py +0 -0
  229. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seedlink.py +0 -0
  230. {gea_program-0.25.0 → gea_program-0.26.0}/gea/segy.py +0 -0
  231. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic.py +0 -0
  232. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_array.py +0 -0
  233. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_assoc.py +0 -0
  234. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_detect.py +0 -0
  235. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_film.py +0 -0
  236. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_harmonic.py +0 -0
  237. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_qc.py +0 -0
  238. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_response.py +0 -0
  239. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_signature.py +0 -0
  240. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_track.py +0 -0
  241. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_uncertainty.py +0 -0
  242. {gea_program-0.25.0 → gea_program-0.26.0}/gea/seismic_unlisted.py +0 -0
  243. {gea_program-0.25.0 → gea_program-0.26.0}/gea/sensor_swap.py +0 -0
  244. {gea_program-0.25.0 → gea_program-0.26.0}/gea/service_life.py +0 -0
  245. {gea_program-0.25.0 → gea_program-0.26.0}/gea/shell.py +0 -0
  246. {gea_program-0.25.0 → gea_program-0.26.0}/gea/shut_in.py +0 -0
  247. {gea_program-0.25.0 → gea_program-0.26.0}/gea/sla_report.py +0 -0
  248. {gea_program-0.25.0 → gea_program-0.26.0}/gea/soak.py +0 -0
  249. {gea_program-0.25.0 → gea_program-0.26.0}/gea/sra.py +0 -0
  250. {gea_program-0.25.0 → gea_program-0.26.0}/gea/store_forward.py +0 -0
  251. {gea_program-0.25.0 → gea_program-0.26.0}/gea/strata_join.py +0 -0
  252. {gea_program-0.25.0 → gea_program-0.26.0}/gea/supervisor.py +0 -0
  253. {gea_program-0.25.0 → gea_program-0.26.0}/gea/survey_cmd.py +0 -0
  254. {gea_program-0.25.0 → gea_program-0.26.0}/gea/survey_view.py +0 -0
  255. {gea_program-0.25.0 → gea_program-0.26.0}/gea/telemetry.py +0 -0
  256. {gea_program-0.25.0 → gea_program-0.26.0}/gea/tool_library.py +0 -0
  257. {gea_program-0.25.0 → gea_program-0.26.0}/gea/transient.py +0 -0
  258. {gea_program-0.25.0 → gea_program-0.26.0}/gea/vibration.py +0 -0
  259. {gea_program-0.25.0 → gea_program-0.26.0}/gea/well_assembler.py +0 -0
  260. {gea_program-0.25.0 → gea_program-0.26.0}/gea/well_test_validation.py +0 -0
  261. {gea_program-0.25.0 → gea_program-0.26.0}/gea/wits0.py +0 -0
  262. {gea_program-0.25.0 → gea_program-0.26.0}/gea/witsml.py +0 -0
  263. {gea_program-0.25.0 → gea_program-0.26.0}/gea_program.egg-info/dependency_links.txt +0 -0
  264. {gea_program-0.25.0 → gea_program-0.26.0}/gea_program.egg-info/entry_points.txt +0 -0
  265. {gea_program-0.25.0 → gea_program-0.26.0}/gea_program.egg-info/requires.txt +0 -0
  266. {gea_program-0.25.0 → gea_program-0.26.0}/gea_program.egg-info/top_level.txt +0 -0
  267. {gea_program-0.25.0 → gea_program-0.26.0}/setup.cfg +0 -0
  268. {gea_program-0.25.0 → gea_program-0.26.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.26.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 (365 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,21 @@ 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
+ ### Slip tendency on the mapped faults
652
+
653
+ The stress state the site declares - the three gradients and the ambient
654
+ pore pressure with their source, the SHmax azimuth with its source or from
655
+ the World Stress Map records near the well (`add-stress-map wsm2016.csv`,
656
+ `wsm --lat --lon`) - resolved on every mapped fault trace within the radius
657
+ of a well at the depth of its declared interval: the slip tendency at the
658
+ ambient and the increased pore pressure, the pressure to slip, the margin
659
+ left after the increase, worst and best declared case. The dip comes from
660
+ the layer where it states one (the Hennings 2021 BEG/TexNet layers do, and
661
+ publish their own pressure-to-slip, printed beside this arithmetic); UTM and
662
+ state-plane layers are unprojected from their .prj parameters. `set-stress`,
663
+ `slip --well`, `gea slip`; the SRA packet's section 2e; `gea help slip`. A
664
+ statement on declared inputs, not a probability and not a verdict.
665
+
651
666
  ### The catalogue accounted for
652
667
 
653
668
  `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 (365 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,21 @@ 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
+ ### Slip tendency on the mapped faults
613
+
614
+ The stress state the site declares - the three gradients and the ambient
615
+ pore pressure with their source, the SHmax azimuth with its source or from
616
+ the World Stress Map records near the well (`add-stress-map wsm2016.csv`,
617
+ `wsm --lat --lon`) - resolved on every mapped fault trace within the radius
618
+ of a well at the depth of its declared interval: the slip tendency at the
619
+ ambient and the increased pore pressure, the pressure to slip, the margin
620
+ left after the increase, worst and best declared case. The dip comes from
621
+ the layer where it states one (the Hennings 2021 BEG/TexNet layers do, and
622
+ publish their own pressure-to-slip, printed beside this arithmetic); UTM and
623
+ state-plane layers are unprojected from their .prj parameters. `set-stress`,
624
+ `slip --well`, `gea slip`; the SRA packet's section 2e; `gea help slip`. A
625
+ statement on declared inputs, not a probability and not a verdict.
626
+
612
627
  ### The catalogue accounted for
613
628
 
614
629
  `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.26.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -278,6 +278,14 @@ 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_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)")
282
+ 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)
283
+ 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)
284
+ p_sl.add_argument("--depth-ft", type=float, required=True)
285
+ 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)
286
+ 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")
287
+ 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)
288
+ p_sl.add_argument("--json", action="store_true")
281
289
  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
290
  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
291
  p_cat.add_argument("--friction", action="store_true"); p_cat.add_argument("--core", action="store_true")
@@ -319,7 +327,8 @@ def main(argv=None) -> int:
319
327
  "add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
320
328
  "seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
321
329
  "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"])
330
+ "add-live-station", "live-fold", "live-run", "month-end", "add-layer", "layers", "remove-layer", "structure", "interval",
331
+ "set-stress", "add-stress-map", "wsm", "slip"])
323
332
  p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
324
333
  p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
325
334
  p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
@@ -399,6 +408,18 @@ def main(argv=None) -> int:
399
408
  p_ws.add_argument("--formation-name", type=str, default=None, help="add-layer: the formation a contours layer maps (Wolfcamp)")
400
409
  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
410
  p_ws.add_argument("--base-interval-ft", type=float, default=None, help="set-well: base of the disposal interval, ft")
411
+ 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")
412
+ p_ws.add_argument("--sv", type=float, default=None, help="set-stress: vertical stress gradient, psi/ft")
413
+ p_ws.add_argument("--shmin", type=float, default=None, help="set-stress: minimum horizontal stress gradient, psi/ft")
414
+ p_ws.add_argument("--shmax", type=float, default=None, help="set-stress: maximum horizontal stress gradient, psi/ft")
415
+ p_ws.add_argument("--pp", type=float, default=None, help="set-stress: ambient pore-pressure gradient, psi/ft (hydrostatic 0.433 unless declared)")
416
+ 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)")
417
+ p_ws.add_argument("--azimuth-source", type=str, default=None, help="set-stress: where the azimuth came from")
418
+ p_ws.add_argument("--gradients-source", type=str, default=None, help="set-stress: where the gradients came from")
419
+ 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)")
420
+ p_ws.add_argument("--mu", type=float, nargs="+", default=None, help="set-stress: the friction coefficients to run (default 0.6)")
421
+ p_ws.add_argument("--delta-pp", type=float, default=None, help="set-stress / slip: the pore-pressure increase to judge at, psi")
422
+ 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
423
  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
424
  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
425
  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 +2116,7 @@ def main(argv=None) -> int:
2095
2116
  elif a.action == "add-layer":
2096
2117
  if not a.layer or not a.name:
2097
2118
  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)
2119
+ 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
2120
  tc = ", ".join(f"{k} {v}" for k, v in list(L["type_counts"].items())[:6])
2100
2121
  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
2122
  + (f"; contour value {L['value_attr']}" if L.get('value_attr') else ''))
@@ -2124,6 +2145,32 @@ def main(argv=None) -> int:
2124
2145
  for line in L["lines"]:
2125
2146
  print(" " + line)
2126
2147
  return 0 if r["status"] == "OK" else 1
2148
+ elif a.action == "set-stress":
2149
+ 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,
2150
+ "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)
2151
+ 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']}); "
2152
+ 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
2153
+ elif a.action == "add-stress-map":
2154
+ if not a.file:
2155
+ raise SystemExit("add-stress-map needs --file <wsm.csv>")
2156
+ r = ws.add_stress_map(a.file, actor=a.actor, citation=a.citation)
2157
+ print(f"stress map: {r['n_records']} records kept as {r['file']} (sha256 {r['sha256'][:16]})"); return 0
2158
+ elif a.action == "wsm":
2159
+ if a.lat is None or a.lon is None:
2160
+ raise SystemExit("wsm needs --lat --lon [--radius-km]")
2161
+ r = ws.wsm_at(a.lat, a.lon, a.radius_km if a.radius_km is not None else 100.0)
2162
+ print(r.get("statement") or f"{r['status']}: {r.get('note', '')}")
2163
+ for rec in r.get("records", [])[:10]:
2164
+ 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]}")
2165
+ return 0 if r.get("n") else 1
2166
+ elif a.action == "slip":
2167
+ if not a.well:
2168
+ raise SystemExit("slip needs --well <id>")
2169
+ 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)
2170
+ print(f"slip {a.well}: {r['status']}")
2171
+ for line in r["lines"]:
2172
+ print(" " + line)
2173
+ return 0 if r["status"] == "OK" else 1
2127
2174
  elif a.action == "interval":
2128
2175
  if not a.well:
2129
2176
  raise SystemExit("interval needs --well <id>")
@@ -2304,6 +2351,33 @@ def main(argv=None) -> int:
2304
2351
  stop.set()
2305
2352
  print("etp-sim: stopped")
2306
2353
  return 0
2354
+ elif a.cmd == "slip":
2355
+ import json as _json
2356
+ from . import shapefile as _SF, slip as _SL, friction_priors as _FP
2357
+ try:
2358
+ L = _SF.read(a.layer, member=a.member)
2359
+ except (ValueError, OSError) as e:
2360
+ raise SystemExit(f"slip: {e}")
2361
+ if a.feature_kind:
2362
+ L.declared_kind = a.feature_kind
2363
+ az, az_src = a.shmax_azimuth, "declared"
2364
+ if az is None and a.wsm:
2365
+ nb = _SL.wsm_nearby(_SL.read_wsm(a.wsm), a.lat, a.lon, 100.0)
2366
+ if not nb["n"]:
2367
+ raise SystemExit("slip: no World Stress Map record of quality A/B/C within 100 km; declare --shmax-azimuth with its source")
2368
+ az, az_src = nb["azimuth_mean"], nb["statement"]
2369
+ if az is None:
2370
+ raise SystemExit("slip: give --shmax-azimuth or --wsm <wsm.csv>")
2371
+ faults = _SL.faults_in_radius(L, a.lat, a.lon, a.radius_km)
2372
+ 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,
2373
+ sources={"shmax_azimuth": az_src, "gradients": "the command line"})
2374
+ r["friction_envelope"] = _FP.envelope()
2375
+ if a.json:
2376
+ print(_json.dumps(r, indent=1, default=str)); return 0
2377
+ for line in r["lines"]:
2378
+ print(line)
2379
+ print(r["friction_envelope"].get("statement", ""))
2380
+ return 0
2307
2381
  elif a.cmd == "catalog":
2308
2382
  import json as _json
2309
2383
  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,208 @@ 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
+
7075
7277
  def main() -> int:
7076
7278
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
7077
7279
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -7146,6 +7348,7 @@ def main() -> int:
7146
7348
  section_bq_field_ports(tmp)
7147
7349
  section_br_logs_historian(tmp)
7148
7350
  section_bs_catalog(tmp)
7351
+ section_bt_slip(tmp)
7149
7352
  if _FAILS:
7150
7353
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
7151
7354
  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,11 @@
1
+ # Slip tendency on the mapped faults
2
+
3
+ > After "how far is the nearest mapped fault" comes "how close to slipping is it, under this injection". The open method (Morris slip tendency; the fault slip potential arithmetic of Walsh and Zoback) is a stress state resolved on a fault plane: the vertical and the two horizontal stresses on the plane, the pore pressure taken off, the shear stress against the friction times the effective normal stress. This program does that arithmetic on inputs the site declares with their sources, on the strikes the map gives and the dips the layer states or the site declares, and prints beside every number what it rests on.
4
+
5
+ **The command**: `gea workspace --path C:\site --action set-stress --sv 1.05 --shmin 0.65 --shmax 0.95 --pp 0.45 --gradients-source "Lund Snee & Zoback 2018, Delaware Basin" --shmax-azimuth 92 --azimuth-source "WSM 2016 records within 100 km" --dips 60 75 90 --mu 0.6 --delta-pp 300` declares the stress state (psi/ft gradients; the sources required). `--action add-stress-map --file wsm2016.csv --citation "World Stress Map 2016, Heidbach et al., CC BY 4.0"` keeps the WSM database so `--action wsm --lat --lon --radius-km` gives the SHmax azimuth from the records near a position (the axial mean of the A/B/C-quality records with its spread and count) - and a well with no declared azimuth takes it from there, stated. `--action add-layer --name "Hennings 2021 basement faults" --layer Hennings_2021_BSMT_Pp2Slip.zip --feature-kind fault --citation "..."` brings a faults layer whose attributes do not say "fault" (every feature is declared one); a UTM or state-plane layer is unprojected from its .prj parameters. `--action slip --well <id> --radius-km 25 [--delta-pp 300] [--depth-ft 9000]` writes the statement; the well page's "Slip tendency" card and the SRA packet's section 2e carry it. `gea slip --layer faults.zip --lat --lon --depth-ft --sv --shmin --shmax --shmax-azimuth [--wsm wsm2016.csv] --delta-pp` answers for any position without a workspace.
6
+
7
+ **What it writes**: the stress state at the depth (the midpoint of the declared interval, or the depth given) and the regime its ordering implies; for every mapped fault trace within the radius: its distance, its length-weighted strike and the angle to SHmax, its dip (from the layer's `dip` attribute when present, else each declared dip), the normal and shear stress on the plane, the effective normal stress at the ambient and the increased pore pressure, the slip tendency at both, the pore-pressure increase that brings the plane to the friction line and the margin left after the declared increase - BELOW or PAST THE FRICTION LINE, for the worst and the best declared case; where the layer publishes its own pressure-to-slip (the BEG/TexNet `Pp2S_MPa` of Hennings et al. 2021, computed under their calibrated stress model), that value beside this arithmetic with the difference. The measured friction envelope from the catalogue (`gea catalog --friction`) printed last, so the declared coefficient is seen against measurements. Written to `reports/wells/<id>/slip.json`, audited as `well.slip`; the stress state as `stress.declare`, the map as `stress_map.add`.
8
+
9
+ **The number to check**: the pressure to slip after the increase on the nearest traces, and the count past the friction line in the worst case against the best - a count that flips between the two is a statement about the declared dip and friction, not about the fault. Where the layer publishes its own value, the difference column: a large one means the declared gradients are not the published model's, and the published model is the one with a paper behind it.
10
+
11
+ **What this page will not call a measurement**: a stress state - every gradient and the SHmax azimuth are declared with their source, or taken from the World Stress Map with the count and spread printed; nothing is measured here. A dip - a map carries none; the layer's own attribute or the declared range is used and named on the line. A friction coefficient for the fault - Byerlee's 0.6 unless declared, printed against the catalogue's measured 0.21-0.90. A probability of an earthquake - the method gives a pressure margin on a plane under stated assumptions; it does not say the fault exists at depth, is connected, or will slip. An attribution - an event near a trace is a distance, as the structure page says.
@@ -43,6 +43,7 @@ INDEX: List[tuple] = [
43
43
  ('dlis', 'The DLIS log', 'a well log as it is delivered (API RP66 V1), read as the standard lays it out: the frames, the channels, what was skipped; into the strata survey'),
44
44
  ('historian', 'The historian pull', 'an operator\'s gauge history out of the PI System through PI Web API, read-only, every value with its archive time and flags'),
45
45
  ('field-ports', 'The field ports', 'Modbus RTU on the serial line, DNP3 from the pad RTU, HART-IP from the transmitter itself - read-only, polled, with the stand-ins to rehearse against'),
46
+ ('slip', 'Slip tendency on the mapped faults', 'the declared stress state resolved on each mapped fault around a well: the slip tendency, the pressure to slip, the World Stress Map as the SHmax source, the published Hennings values beside'),
46
47
  ('interval', 'The interval against the mapped tops', 'the declared disposal interval, subsea, beside each formation-top bracket; the depth tier against the mapped base; the catalogue events against the mapped faults'),
47
48
  ('sra', 'The Seismicity Response Area', 'the packet an operator inside an SRA puts in front of the Commission'),
48
49
  ('osdu', 'The OSDU-shaped export', 'the site as the manifest an operator\'s data platform loads'),
@@ -892,7 +892,8 @@ class App:
892
892
  try:
893
893
  return self.ws.add_layer(p, str(body.get('display') or os.path.splitext(name)[0]), actor=user['name'],
894
894
  note=str(body.get('note', '')), citation=str(body.get('citation', '')), member=(str(body['member']) if body.get('member') else None),
895
- formation=(str(body['formation']) if body.get('formation') else None), role=(str(body['role']) if body.get('role') else None))
895
+ formation=(str(body['formation']) if body.get('formation') else None), role=(str(body['role']) if body.get('role') else None),
896
+ feature_kind=(str(body['feature_kind']) if body.get('feature_kind') else None))
896
897
  except (WorkspaceError, ValueError) as e:
897
898
  raise ApiError(409, str(e))
898
899
  finally:
@@ -1602,7 +1603,7 @@ def make_handler(app: App):
1602
1603
  **({'live_state': app.live.state(st['id'])} if st.get('live') else {})} for st in app.ws.seismic_stations()],
1603
1604
  'site_reports': site})
1604
1605
  if path == '/api/layers':
1605
- return self._json(200, {'layers': app.ws.layers()})
1606
+ return self._json(200, {'layers': app.ws.layers(), 'stress': app.ws.stress(), 'stress_map': app.ws.stress_map()})
1606
1607
  if path == '/api/layers/members':
1607
1608
  raise ApiError(405, 'POST the zip to list its shapefiles')
1608
1609
  if path.startswith('/api/layers/') and path.endswith('/at'):
@@ -1628,6 +1629,20 @@ def make_handler(app: App):
1628
1629
  return self._json(200, app.ws.well_interval(wid, float(qs.get('radius_km', ['50'])[0]), actor='page', write=False))
1629
1630
  except WorkspaceError as e:
1630
1631
  raise ApiError(404, str(e))
1632
+ if path.startswith('/api/wells/') and path.endswith('/slip'):
1633
+ wid = path[len('/api/wells/'):-len('/slip')]
1634
+ try:
1635
+ return self._json(200, app.ws.well_slip(wid, float(qs.get('radius_km', ['25'])[0]), actor='page', write=False))
1636
+ except WorkspaceError as e:
1637
+ raise ApiError(404, str(e))
1638
+ if path == '/api/stress':
1639
+ return self._json(200, {'stress': app.ws.stress(), 'stress_map': app.ws.stress_map()})
1640
+ if path == '/api/stress/wsm':
1641
+ try:
1642
+ lat, lon = float(qs['lat'][0]), float(qs['lon'][0]); rad = float(qs.get('radius_km', ['100'])[0])
1643
+ except (KeyError, ValueError):
1644
+ raise ApiError(400, 'lat and lon are needed')
1645
+ return self._json(200, app.ws.wsm_at(lat, lon, rad))
1631
1646
  if path == '/api/seismic/live':
1632
1647
  from .seedlink import EXAMPLE_CONFIG as _SLX
1633
1648
  return self._json(200, {'stations': app.live.states(), 'example_config': _SLX})
@@ -1815,6 +1830,36 @@ def make_handler(app: App):
1815
1830
  return self._json(200, app.ws.well_interval(wid, float((body or {}).get('radius_km') or 50.0), actor=user['name']))
1816
1831
  except WorkspaceError as e:
1817
1832
  raise ApiError(404, str(e))
1833
+ if path.startswith('/api/wells/') and path.endswith('/slip'):
1834
+ App.require(user, 'operator')
1835
+ wid = path[len('/api/wells/'):-len('/slip')]
1836
+ b = body or {}
1837
+ try:
1838
+ return self._json(200, app.ws.well_slip(wid, float(b.get('radius_km') or 25.0), delta_pp_psi=(float(b['delta_pp_psi']) if b.get('delta_pp_psi') is not None else None),
1839
+ depth_ft=(float(b['depth_ft']) if b.get('depth_ft') is not None else None), actor=user['name']))
1840
+ except WorkspaceError as e:
1841
+ raise ApiError(404, str(e))
1842
+ if path == '/api/stress':
1843
+ App.require(user, 'operator')
1844
+ try:
1845
+ return self._json(200, app.ws.set_stress(body or {}, actor=user['name']))
1846
+ except WorkspaceError as e:
1847
+ raise ApiError(400, str(e))
1848
+ if path == '/api/stress/map':
1849
+ App.require(user, 'operator')
1850
+ f = (body or {}).get('file') or {}
1851
+ tmp_dir = os.path.join(app.ws.path, 'jobs', '_uploads', secrets.token_hex(4))
1852
+ os.makedirs(tmp_dir, exist_ok=True)
1853
+ try:
1854
+ p_ = os.path.join(tmp_dir, 'wsm.csv')
1855
+ with open(p_, 'wb') as fh:
1856
+ fh.write(base64.b64decode(f.get('content_b64') or ''))
1857
+ try:
1858
+ return self._json(200, app.ws.add_stress_map(p_, actor=user['name'], citation=str((body or {}).get('citation', ''))))
1859
+ except WorkspaceError as e:
1860
+ raise ApiError(400, str(e))
1861
+ finally:
1862
+ shutil.rmtree(tmp_dir, ignore_errors=True)
1818
1863
  if path.startswith('/api/wells/') and path.endswith('/declare'):
1819
1864
  App.require(user, 'operator')
1820
1865
  wid = path[len('/api/wells/'):-len('/declare')]