gea-program 0.41.0__tar.gz → 0.42.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 (325) hide show
  1. {gea_program-0.41.0 → gea_program-0.42.0}/PKG-INFO +15 -1
  2. {gea_program-0.41.0 → gea_program-0.42.0}/README.md +14 -0
  3. {gea_program-0.41.0 → gea_program-0.42.0}/gea/__init__.py +1 -1
  4. {gea_program-0.41.0 → gea_program-0.42.0}/gea/__main__.py +82 -0
  5. {gea_program-0.41.0 → gea_program-0.42.0}/gea/acceptance_tests.py +209 -1
  6. {gea_program-0.41.0 → gea_program-0.42.0}/gea/declarations.py +52 -2
  7. {gea_program-0.41.0 → gea_program-0.42.0}/gea/fixtures/README.md +6 -0
  8. gea_program-0.42.0/gea/fixtures/comcat_mahoning_1990-2026.txt +22 -0
  9. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/declare.md +3 -3
  10. gea_program-0.42.0/gea/help/opseis.md +13 -0
  11. {gea_program-0.41.0 → gea_program-0.42.0}/gea/helplib.py +2 -1
  12. gea_program-0.42.0/gea/op_seismicity.py +625 -0
  13. gea_program-0.42.0/gea/reference/mahoning_example.json +48 -0
  14. gea_program-0.42.0/gea/reference/seismicity_protocols.json +95 -0
  15. {gea_program-0.41.0 → gea_program-0.42.0}/gea/service.py +43 -0
  16. {gea_program-0.41.0 → gea_program-0.42.0}/gea/web/app.html +91 -1
  17. {gea_program-0.41.0 → gea_program-0.42.0}/gea_program.egg-info/PKG-INFO +15 -1
  18. {gea_program-0.41.0 → gea_program-0.42.0}/gea_program.egg-info/SOURCES.txt +5 -0
  19. {gea_program-0.41.0 → gea_program-0.42.0}/pyproject.toml +1 -1
  20. {gea_program-0.41.0 → gea_program-0.42.0}/LICENSE +0 -0
  21. {gea_program-0.41.0 → gea_program-0.42.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  22. {gea_program-0.41.0 → gea_program-0.42.0}/gea/accuracy_statement.py +0 -0
  23. {gea_program-0.41.0 → gea_program-0.42.0}/gea/alarm_engine.py +0 -0
  24. {gea_program-0.41.0 → gea_program-0.42.0}/gea/backup.py +0 -0
  25. {gea_program-0.41.0 → gea_program-0.42.0}/gea/bench.py +0 -0
  26. {gea_program-0.41.0 → gea_program-0.42.0}/gea/blind_harness.py +0 -0
  27. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  28. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  29. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  30. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  31. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  32. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  33. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  34. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  35. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  36. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  37. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  38. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  39. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  40. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  41. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  42. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  43. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  44. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  45. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  46. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  47. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  48. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  49. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  50. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  51. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  52. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  53. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  54. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  55. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  56. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  57. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  58. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  59. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  60. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  61. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  62. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  63. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  64. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  65. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  66. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  67. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  68. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  69. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  70. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  71. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  72. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  73. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  74. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  75. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  76. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  77. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  78. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  79. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  80. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  81. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  82. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  83. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  84. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  85. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  86. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  87. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  88. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  89. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  90. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  91. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  92. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  93. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  94. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/med_160_sapropels.txt +0 -0
  95. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  96. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  97. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  98. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  99. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  100. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  101. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  102. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  103. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  104. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  105. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  106. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  107. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  108. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  109. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  110. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  111. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  112. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  113. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  114. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  115. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  116. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  117. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  118. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  119. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  120. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  121. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  122. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  123. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  124. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  125. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  126. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  127. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  128. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  129. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  130. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  131. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog_map.py +0 -0
  132. {gea_program-0.41.0 → gea_program-0.42.0}/gea/catalog_register.py +0 -0
  133. {gea_program-0.41.0 → gea_program-0.42.0}/gea/certificates.py +0 -0
  134. {gea_program-0.41.0 → gea_program-0.42.0}/gea/cli.py +0 -0
  135. {gea_program-0.41.0 → gea_program-0.42.0}/gea/client_reports.py +0 -0
  136. {gea_program-0.41.0 → gea_program-0.42.0}/gea/config_versioning.py +0 -0
  137. {gea_program-0.41.0 → gea_program-0.42.0}/gea/conformance.py +0 -0
  138. {gea_program-0.41.0 → gea_program-0.42.0}/gea/control_room.py +0 -0
  139. {gea_program-0.41.0 → gea_program-0.42.0}/gea/correlation.py +0 -0
  140. {gea_program-0.41.0 → gea_program-0.42.0}/gea/dashboard.py +0 -0
  141. {gea_program-0.41.0 → gea_program-0.42.0}/gea/deviation.py +0 -0
  142. {gea_program-0.41.0 → gea_program-0.42.0}/gea/dlis.py +0 -0
  143. {gea_program-0.41.0 → gea_program-0.42.0}/gea/dnp3.py +0 -0
  144. {gea_program-0.41.0 → gea_program-0.42.0}/gea/doctor.py +0 -0
  145. {gea_program-0.41.0 → gea_program-0.42.0}/gea/downhole_engine.py +0 -0
  146. {gea_program-0.41.0 → gea_program-0.42.0}/gea/drift_monitor.py +0 -0
  147. {gea_program-0.41.0 → gea_program-0.42.0}/gea/drill_string.py +0 -0
  148. {gea_program-0.41.0 → gea_program-0.42.0}/gea/earth_model.py +0 -0
  149. {gea_program-0.41.0 → gea_program-0.42.0}/gea/economics.py +0 -0
  150. {gea_program-0.41.0 → gea_program-0.42.0}/gea/edr.py +0 -0
  151. {gea_program-0.41.0 → gea_program-0.42.0}/gea/elevation.py +0 -0
  152. {gea_program-0.41.0 → gea_program-0.42.0}/gea/etp.py +0 -0
  153. {gea_program-0.41.0 → gea_program-0.42.0}/gea/etp12_schemas.json +0 -0
  154. {gea_program-0.41.0 → gea_program-0.42.0}/gea/example_register_map.json +0 -0
  155. {gea_program-0.41.0 → gea_program-0.42.0}/gea/fat_sat.py +0 -0
  156. {gea_program-0.41.0 → gea_program-0.42.0}/gea/files.py +0 -0
  157. {gea_program-0.41.0 → gea_program-0.42.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  158. {gea_program-0.41.0 → gea_program-0.42.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  159. {gea_program-0.41.0 → gea_program-0.42.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  160. {gea_program-0.41.0 → gea_program-0.42.0}/gea/follower.py +0 -0
  161. {gea_program-0.41.0 → gea_program-0.42.0}/gea/forward_model.py +0 -0
  162. {gea_program-0.41.0 → gea_program-0.42.0}/gea/friction_priors.py +0 -0
  163. {gea_program-0.41.0 → gea_program-0.42.0}/gea/gamma.py +0 -0
  164. {gea_program-0.41.0 → gea_program-0.42.0}/gea/gauge_aging.py +0 -0
  165. {gea_program-0.41.0 → gea_program-0.42.0}/gea/gauge_specs.py +0 -0
  166. {gea_program-0.41.0 → gea_program-0.42.0}/gea/geodesy.py +0 -0
  167. {gea_program-0.41.0 → gea_program-0.42.0}/gea/gravity_reference.py +0 -0
  168. {gea_program-0.41.0 → gea_program-0.42.0}/gea/hartip.py +0 -0
  169. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/TESTER_GUIDE.md +0 -0
  170. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/alarms.md +0 -0
  171. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/audit-update.md +0 -0
  172. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/backup.md +0 -0
  173. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/catalog.md +0 -0
  174. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/charts.md +0 -0
  175. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/conformance.md +0 -0
  176. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/control-room.md +0 -0
  177. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/data-in.md +0 -0
  178. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/dlis.md +0 -0
  179. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/doctor.md +0 -0
  180. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/drift.md +0 -0
  181. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/drillstring.md +0 -0
  182. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/economics.md +0 -0
  183. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/edr.md +0 -0
  184. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/etp.md +0 -0
  185. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/field-ports.md +0 -0
  186. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/files.md +0 -0
  187. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/historian.md +0 -0
  188. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/instruments.md +0 -0
  189. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/interval.md +0 -0
  190. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/map.md +0 -0
  191. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/month-end.md +0 -0
  192. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/notifications.md +0 -0
  193. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/osdu.md +0 -0
  194. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/patches.md +0 -0
  195. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/penetrator.md +0 -0
  196. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/ppdm.md +0 -0
  197. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/quakeml.md +0 -0
  198. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/quality.md +0 -0
  199. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/seedlink.md +0 -0
  200. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/seismic.md +0 -0
  201. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/setup.md +0 -0
  202. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/simulation.md +0 -0
  203. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/site.md +0 -0
  204. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/sites.md +0 -0
  205. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/slip.md +0 -0
  206. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/soak.md +0 -0
  207. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/sra.md +0 -0
  208. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/start.md +0 -0
  209. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/structure.md +0 -0
  210. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/tools.md +0 -0
  211. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/transients.md +0 -0
  212. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/upkeep.md +0 -0
  213. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/vibration.md +0 -0
  214. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/well-report.md +0 -0
  215. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/well-tests.md +0 -0
  216. {gea_program-0.41.0 → gea_program-0.42.0}/gea/help/well3d.md +0 -0
  217. {gea_program-0.41.0 → gea_program-0.42.0}/gea/housekeeping.py +0 -0
  218. {gea_program-0.41.0 → gea_program-0.42.0}/gea/interval.py +0 -0
  219. {gea_program-0.41.0 → gea_program-0.42.0}/gea/inverse_engine.py +0 -0
  220. {gea_program-0.41.0 → gea_program-0.42.0}/gea/jobs.py +0 -0
  221. {gea_program-0.41.0 → gea_program-0.42.0}/gea/live_ports.py +0 -0
  222. {gea_program-0.41.0 → gea_program-0.42.0}/gea/loadtest.py +0 -0
  223. {gea_program-0.41.0 → gea_program-0.42.0}/gea/matplotlib_demo.py +0 -0
  224. {gea_program-0.41.0 → gea_program-0.42.0}/gea/modbus.py +0 -0
  225. {gea_program-0.41.0 → gea_program-0.42.0}/gea/modbus_rtu.py +0 -0
  226. {gea_program-0.41.0 → gea_program-0.42.0}/gea/model_card.py +0 -0
  227. {gea_program-0.41.0 → gea_program-0.42.0}/gea/month_end.py +0 -0
  228. {gea_program-0.41.0 → gea_program-0.42.0}/gea/mqtt_port.py +0 -0
  229. {gea_program-0.41.0 → gea_program-0.42.0}/gea/notify.py +0 -0
  230. {gea_program-0.41.0 → gea_program-0.42.0}/gea/opcua_port.py +0 -0
  231. {gea_program-0.41.0 → gea_program-0.42.0}/gea/operator_app.py +0 -0
  232. {gea_program-0.41.0 → gea_program-0.42.0}/gea/osdu.py +0 -0
  233. {gea_program-0.41.0 → gea_program-0.42.0}/gea/patches.py +0 -0
  234. {gea_program-0.41.0 → gea_program-0.42.0}/gea/penetrator.py +0 -0
  235. {gea_program-0.41.0 → gea_program-0.42.0}/gea/permits.py +0 -0
  236. {gea_program-0.41.0 → gea_program-0.42.0}/gea/piwebapi.py +0 -0
  237. {gea_program-0.41.0 → gea_program-0.42.0}/gea/ports.py +0 -0
  238. {gea_program-0.41.0 → gea_program-0.42.0}/gea/ppdm.py +0 -0
  239. {gea_program-0.41.0 → gea_program-0.42.0}/gea/profile_catalog.py +0 -0
  240. {gea_program-0.41.0 → gea_program-0.42.0}/gea/project.py +0 -0
  241. {gea_program-0.41.0 → gea_program-0.42.0}/gea/qt6_downhole_app.py +0 -0
  242. {gea_program-0.41.0 → gea_program-0.42.0}/gea/quakeml.py +0 -0
  243. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reconciler.py +0 -0
  244. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  245. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  246. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  247. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/eia_prices.provenance.json +0 -0
  248. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  249. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  250. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  251. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  252. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  253. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/mud_motor_specs.json +0 -0
  254. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/penetrator_ratings.json +0 -0
  255. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/tool_sources.json +0 -0
  256. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/us_outline.json +0 -0
  257. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/us_outline.provenance.json +0 -0
  258. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_3d.provenance.json +0 -0
  259. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  260. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  261. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  262. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_surfaces.json.gz +0 -0
  263. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  264. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  265. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  266. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  267. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  268. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  269. {gea_program-0.41.0 → gea_program-0.42.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  270. {gea_program-0.41.0 → gea_program-0.42.0}/gea/rock_inventory.py +0 -0
  271. {gea_program-0.41.0 → gea_program-0.42.0}/gea/sample_record.py +0 -0
  272. {gea_program-0.41.0 → gea_program-0.42.0}/gea/sample_well_profile.csv +0 -0
  273. {gea_program-0.41.0 → gea_program-0.42.0}/gea/sbom.py +0 -0
  274. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seedlink.py +0 -0
  275. {gea_program-0.41.0 → gea_program-0.42.0}/gea/segy.py +0 -0
  276. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic.py +0 -0
  277. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_array.py +0 -0
  278. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_assoc.py +0 -0
  279. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_detect.py +0 -0
  280. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_film.py +0 -0
  281. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_harmonic.py +0 -0
  282. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_qc.py +0 -0
  283. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_response.py +0 -0
  284. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_signature.py +0 -0
  285. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_track.py +0 -0
  286. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_uncertainty.py +0 -0
  287. {gea_program-0.41.0 → gea_program-0.42.0}/gea/seismic_unlisted.py +0 -0
  288. {gea_program-0.41.0 → gea_program-0.42.0}/gea/sensor_swap.py +0 -0
  289. {gea_program-0.41.0 → gea_program-0.42.0}/gea/series_cache.py +0 -0
  290. {gea_program-0.41.0 → gea_program-0.42.0}/gea/service_life.py +0 -0
  291. {gea_program-0.41.0 → gea_program-0.42.0}/gea/setup_checklist.py +0 -0
  292. {gea_program-0.41.0 → gea_program-0.42.0}/gea/shapefile.py +0 -0
  293. {gea_program-0.41.0 → gea_program-0.42.0}/gea/shell.py +0 -0
  294. {gea_program-0.41.0 → gea_program-0.42.0}/gea/shut_in.py +0 -0
  295. {gea_program-0.41.0 → gea_program-0.42.0}/gea/simulation.py +0 -0
  296. {gea_program-0.41.0 → gea_program-0.42.0}/gea/site_map.py +0 -0
  297. {gea_program-0.41.0 → gea_program-0.42.0}/gea/sla_report.py +0 -0
  298. {gea_program-0.41.0 → gea_program-0.42.0}/gea/slip.py +0 -0
  299. {gea_program-0.41.0 → gea_program-0.42.0}/gea/soak.py +0 -0
  300. {gea_program-0.41.0 → gea_program-0.42.0}/gea/sra.py +0 -0
  301. {gea_program-0.41.0 → gea_program-0.42.0}/gea/store_forward.py +0 -0
  302. {gea_program-0.41.0 → gea_program-0.42.0}/gea/strata_join.py +0 -0
  303. {gea_program-0.41.0 → gea_program-0.42.0}/gea/structure.py +0 -0
  304. {gea_program-0.41.0 → gea_program-0.42.0}/gea/supervisor.py +0 -0
  305. {gea_program-0.41.0 → gea_program-0.42.0}/gea/survey_cmd.py +0 -0
  306. {gea_program-0.41.0 → gea_program-0.42.0}/gea/survey_view.py +0 -0
  307. {gea_program-0.41.0 → gea_program-0.42.0}/gea/telemetry.py +0 -0
  308. {gea_program-0.41.0 → gea_program-0.42.0}/gea/tool_catalog.py +0 -0
  309. {gea_program-0.41.0 → gea_program-0.42.0}/gea/tool_library.py +0 -0
  310. {gea_program-0.41.0 → gea_program-0.42.0}/gea/tool_taxonomy.py +0 -0
  311. {gea_program-0.41.0 → gea_program-0.42.0}/gea/transient.py +0 -0
  312. {gea_program-0.41.0 → gea_program-0.42.0}/gea/vibration.py +0 -0
  313. {gea_program-0.41.0 → gea_program-0.42.0}/gea/well3d.py +0 -0
  314. {gea_program-0.41.0 → gea_program-0.42.0}/gea/well_assembler.py +0 -0
  315. {gea_program-0.41.0 → gea_program-0.42.0}/gea/well_report.py +0 -0
  316. {gea_program-0.41.0 → gea_program-0.42.0}/gea/well_test_validation.py +0 -0
  317. {gea_program-0.41.0 → gea_program-0.42.0}/gea/wits0.py +0 -0
  318. {gea_program-0.41.0 → gea_program-0.42.0}/gea/witsml.py +0 -0
  319. {gea_program-0.41.0 → gea_program-0.42.0}/gea/workspace.py +0 -0
  320. {gea_program-0.41.0 → gea_program-0.42.0}/gea_program.egg-info/dependency_links.txt +0 -0
  321. {gea_program-0.41.0 → gea_program-0.42.0}/gea_program.egg-info/entry_points.txt +0 -0
  322. {gea_program-0.41.0 → gea_program-0.42.0}/gea_program.egg-info/requires.txt +0 -0
  323. {gea_program-0.41.0 → gea_program-0.42.0}/gea_program.egg-info/top_level.txt +0 -0
  324. {gea_program-0.41.0 → gea_program-0.42.0}/setup.cfg +0 -0
  325. {gea_program-0.41.0 → gea_program-0.42.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.41.0
3
+ Version: 0.42.0
4
4
  Summary: GEA-Program: downhole gauge monitoring - canonical measurement records, quality rules, drift evaluation with a re-fit change log, well-test validation, alarm management, model cards, store-and-forward, monthly SLA measurement, FAT/SAT protocols and a dashboard, generated from live objects in the client's reporting outline.
5
5
  Author: Daniel T. Murphy
6
6
  License: MPL-2.0
@@ -648,6 +648,20 @@ 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
+ ### Operations and seismicity
652
+
653
+ The Operations & Seismicity tab sets each well's drilling, completion and
654
+ disposal windows against the earthquake catalogue around it: every event
655
+ within the screening distance by its distance and time, whether it fell
656
+ during an operation or in the months after, what the rig was doing when a
657
+ drilling record covers it, the level a regulator's protocol would reach
658
+ (British Columbia, Alberta, Oklahoma, Ohio, Texas, each with its
659
+ document), and the exact test of whether the rate near the well is higher
660
+ while it works than in the rest of the record. The Mahoning Valley, Ohio -
661
+ the 2011 Youngstown sequence against the Northstar 1 injection year, the
662
+ 2014 Poland Township events against a stimulation - ships as a worked
663
+ example from the public record. `gea seismicity`, `gea help opseis`.
664
+
651
665
  ### The declarations sheet
652
666
 
653
667
  Everything the operator declares about a pad comes in from one spreadsheet:
@@ -609,6 +609,20 @@ 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
+ ### Operations and seismicity
613
+
614
+ The Operations & Seismicity tab sets each well's drilling, completion and
615
+ disposal windows against the earthquake catalogue around it: every event
616
+ within the screening distance by its distance and time, whether it fell
617
+ during an operation or in the months after, what the rig was doing when a
618
+ drilling record covers it, the level a regulator's protocol would reach
619
+ (British Columbia, Alberta, Oklahoma, Ohio, Texas, each with its
620
+ document), and the exact test of whether the rate near the well is higher
621
+ while it works than in the rest of the record. The Mahoning Valley, Ohio -
622
+ the 2011 Youngstown sequence against the Northstar 1 injection year, the
623
+ 2014 Poland Township events against a stimulation - ships as a worked
624
+ example from the public record. `gea seismicity`, `gea help opseis`.
625
+
612
626
  ### The declarations sheet
613
627
 
614
628
  Everything the operator declares about a pad comes in from one spreadsheet:
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.41.0"
126
+ __version__ = "0.42.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -539,6 +539,33 @@ def main(argv=None) -> int:
539
539
  p_pn.add_argument("--actor", type=str, default="cli")
540
540
  p_pn.add_argument("--json", action="store_true")
541
541
 
542
+ p_os = sub.add_parser("seismicity", help="operations and seismicity: each well's drilling, completion and disposal windows against the earthquake catalogue around it - the events during and after each, the levels a regulator's protocol would reach, and the rate during operations against the rest of the record; the Mahoning Valley worked example")
543
+ p_os.add_argument("--workspace", type=str, default=None)
544
+ p_os.add_argument("--scene", type=str, default=None, choices=("workspace", "mahoning"), help="your wells (the default with --workspace) or the public worked example")
545
+ p_os.add_argument("--protocol", type=str, default=None, help="a protocol id (--protocols lists them)")
546
+ p_os.add_argument("--radius-km", type=float, default=None, help="the association radius (default 5 km)")
547
+ p_os.add_argument("--screen-km", type=float, default=None, help="the screening distance (default 20 km)")
548
+ p_os.add_argument("--post-days", type=float, default=None, help="the association window after an operation ends (default 90 days)")
549
+ p_os.add_argument("--mc", type=float, default=None, help="the completeness magnitude counted in the rate (default the catalogue's minimum)")
550
+ p_os.add_argument("--set", action="store_true", help="keep --protocol, --radius-km, --screen-km, --post-days and --mc as this workspace's settings")
551
+ p_os.add_argument("--protocols", action="store_true", help="list the protocols and their levels")
552
+ p_os.add_argument("--query", action="store_true", help="print the ComCat query for the workspace's wells (run it where the catalogue service is reachable)")
553
+ p_os.add_argument("--attach", type=str, default=None, help="a catalogue export to attach (with --start --end --min-magnitude --lat --lon --catalog-radius-km --source)")
554
+ p_os.add_argument("--start", type=str, default=None)
555
+ p_os.add_argument("--end", type=str, default=None)
556
+ p_os.add_argument("--min-magnitude", type=float, default=None)
557
+ p_os.add_argument("--lat", type=float, default=None)
558
+ p_os.add_argument("--lon", type=float, default=None)
559
+ p_os.add_argument("--catalog-radius-km", type=float, default=None)
560
+ p_os.add_argument("--source", type=str, default="")
561
+ p_os.add_argument("--declare-op", action="store_true", help="declare an operation window (with --well --kind --start [--end] --source)")
562
+ p_os.add_argument("--well", type=str, default=None)
563
+ p_os.add_argument("--kind", type=str, default=None, choices=("drilling", "completion", "disposal", "injection", "other"))
564
+ p_os.add_argument("--remove-op", type=str, default=None)
565
+ p_os.add_argument("--remove-catalog", type=str, default=None)
566
+ p_os.add_argument("--actor", type=str, default="cli")
567
+ p_os.add_argument("--json", action="store_true")
568
+
542
569
  p_sm = sub.add_parser("simulation", help="the simulation in words: the scenes (Volve 15/9-F-4 with its logs and interpretation, Volve F-5 and F-10 on the survey clock, every drilling-recorder well), and what the record says at a depth (the Simulation tab replays it)")
543
570
  p_sm.add_argument("--workspace", type=str, default=None)
544
571
  p_sm.add_argument("--scene", type=str, default=None, help="e.g. volve-f4, or well:Well-30636033")
@@ -2144,6 +2171,61 @@ def main(argv=None) -> int:
2144
2171
  raise SystemExit(f"penetrator: {e.args[0] if e.args else e}")
2145
2172
  print(_json.dumps(p, default=str) if a.json else _PN.text(p))
2146
2173
  return 0
2174
+ elif a.cmd == "seismicity":
2175
+ import json as _json
2176
+ from . import op_seismicity as _OS
2177
+ from .cli import _safe_stdout
2178
+ from .workspace import Workspace, WorkspaceError
2179
+ _safe_stdout()
2180
+ if a.protocols:
2181
+ for p_ in _OS.protocols()["protocols"]:
2182
+ print(f"{p_['id']}: {p_['name']} - within {p_['radius_km'] if p_['radius_km'] is not None else 'the permit distance'} km of {p_['radius_from']}; covers {', '.join(p_['applies_to'])}")
2183
+ for lv in p_["levels"]:
2184
+ print(f" M {lv['m']:g}{' (' + lv['condition'] + ')' if lv.get('condition') else ''}: {lv['action']}")
2185
+ print(f" {p_['document']} - {p_['url']}{' (a secondary summary)' if p_['source_kind'] == 'secondary' else ''}")
2186
+ return 0
2187
+ ws = None
2188
+ if a.workspace:
2189
+ try:
2190
+ ws = Workspace(a.workspace)
2191
+ except WorkspaceError as e:
2192
+ raise SystemExit(f"seismicity: {e}")
2193
+ try:
2194
+ if a.attach:
2195
+ if ws is None:
2196
+ raise SystemExit("seismicity: --attach needs --workspace")
2197
+ with open(a.attach, "rb") as f:
2198
+ r = _OS.attach_catalog(ws, a.actor, os.path.basename(a.attach), f.read(), a.start, a.end, a.min_magnitude, a.lat, a.lon, a.catalog_radius_km, a.source)
2199
+ print(f"attached {r['file']}: {r['n_events']} event(s), {r['start']} to {r['end']}, M >= {r['min_magnitude']:g}")
2200
+ return 0
2201
+ if a.declare_op:
2202
+ if ws is None or not a.well or not a.kind:
2203
+ raise SystemExit("seismicity: --declare-op needs --workspace, --well and --kind")
2204
+ r = _OS.save_operation(ws, a.actor, a.well, a.kind, a.start, a.end, a.source)
2205
+ print(f"declared {r['id']}: {r['kind']} on {r['well_id']}, {r['start']} to {r['end'] or 'open'}")
2206
+ return 0
2207
+ if a.remove_op:
2208
+ _OS.remove_operation(ws, a.actor, a.remove_op); print(f"removed {a.remove_op}"); return 0
2209
+ if a.remove_catalog:
2210
+ _OS.remove_catalog(ws, a.actor, a.remove_catalog); print(f"removed {a.remove_catalog}"); return 0
2211
+ over = {k: v for k, v in (("protocol", a.protocol), ("radius_km", a.radius_km), ("screen_km", a.screen_km), ("post_days", a.post_days), ("mc", a.mc)) if v is not None}
2212
+ if a.set:
2213
+ if ws is None:
2214
+ raise SystemExit("seismicity: --set needs --workspace")
2215
+ s_ = _OS.set_settings(ws, a.actor, over.get("protocol"), over.get("radius_km"), over.get("screen_km"), over.get("post_days"), over.get("mc"))
2216
+ print("settings: " + ", ".join(f"{k} {v}" for k, v in s_.items()))
2217
+ return 0
2218
+ if over.get("protocol"):
2219
+ _OS.protocol(over["protocol"])
2220
+ r = _OS.page(ws, a.scene or ("workspace" if ws is not None else "mahoning"), over or None)
2221
+ except (ValueError, KeyError, OSError) as e:
2222
+ raise SystemExit(f"seismicity: {e.args[0] if e.args else e}")
2223
+ if a.query:
2224
+ q = r.get("query")
2225
+ print(q["url"] if q else "no well of the workspace has a surface position yet: declare one first")
2226
+ return 0
2227
+ print(_json.dumps(r, default=str) if a.json else _OS.text(r))
2228
+ return 0
2147
2229
  elif a.cmd == "simulation":
2148
2230
  import json as _json
2149
2231
  from . import simulation as _SIM
@@ -7310,7 +7310,7 @@ def section_bu_comcat(tmp: str) -> None:
7310
7310
  "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",
7311
7311
  "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
7312
7312
  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,
7313
- "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
+ "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") == 3,
7314
7314
  }
7315
7315
  failed1 = [k for k, v in c1.items() if not v]
7316
7316
  ok(not failed1,
@@ -9703,6 +9703,213 @@ def section_cj_declarations(tmp: str) -> None:
9703
9703
  + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
9704
9704
 
9705
9705
 
9706
+ def section_ck_operations_seismicity(tmp: str) -> None:
9707
+ """Section CK - operations and seismicity: the Mahoning Valley worked example from the public record (the 2011
9708
+ Youngstown sequence against Northstar 1's injection year, the 2014 Poland Township events against the Poland-CLL2
9709
+ stimulation, the ComCat catalogue 1990-2026), the protocols each event reaches by the catalogue's magnitude, the
9710
+ exact conditional rate test against its hand value; a labelled synthetic pad; a workspace with a drilling record,
9711
+ an attached catalogue and declared operations; the sheet, the API, the CLI, the tab and the help page."""
9712
+ import http.cookiejar
9713
+ import subprocess as _sp
9714
+ import urllib.request
9715
+ import urllib.error
9716
+ import base64 as _b64
9717
+ from math import comb
9718
+ from . import declarations as DC
9719
+ from . import drill_string as DS
9720
+ from . import helplib as H
9721
+ from . import op_seismicity as OS
9722
+ from .workspace import Workspace
9723
+ from .service import Service, Users
9724
+ pkg = Path(__file__).parent
9725
+ d = Path(tmp, "ck"); d.mkdir()
9726
+
9727
+ # -- CK1 the worked example ------------------------------------------------------------------------------------
9728
+ ex = OS.page(None, "mahoning")
9729
+ W = {w["id"]: w for w in ex["wells"]}
9730
+ ns, pl = W["northstar-1"], W["poland-cll2"]
9731
+ ev_ns = {e["event_id"]: e for e in ns["events"]}
9732
+ ev_pl = {e["event_id"]: e for e in pl["events"]}
9733
+ share = 457.0 / (457.0 + 12982.0)
9734
+ by_prot = {}
9735
+ for pid in ("ohio-odnr-2014", "oklahoma-occ-2016", "alberta-aer-so2-2015", "texas-rrc-2022", "bc-ksmma-2021"):
9736
+ r_ = OS.page(None, "mahoning", {"protocol": pid})
9737
+ by_prot[pid] = sorted((e["event_id"], e["protocol"]["highest"]["m"]) for w in r_["wells"] for e in w["protocol_events"])
9738
+ c1 = {
9739
+ "catalogue": ex["n_events"] == 21 and ex["catalog"]["start"] == "1990-01-01" and ex["catalog"]["min_magnitude"] == 1.0 and (pkg / "fixtures" / "comcat_mahoning_1990-2026.txt").is_file()
9740
+ and "comcat_mahoning_1990-2026.txt" in (pkg / "fixtures" / "README.md").read_text(encoding="utf-8"),
9741
+ "northstar": ns["rate"]["n_operations"] == 8 and ns["rate"]["n_background"] == 0 and ns["rate"]["days_operations"] == 457.0 and ns["rate"]["status"] == "HIGHER DURING OPERATIONS"
9742
+ and abs(ns["rate"]["p_value"] - share ** 8) / share ** 8 < 1e-4 and ns["rate"]["ratio"] is None and ns["rate"]["background_upper_95_per_year"] == round(3 / 12982.0 * 365.25, 4)
9743
+ and ev_ns["ld60029101"]["relation"] == "AFTER" and ev_ns["ld60029101"]["days_after_end"] == 0.8 and ev_ns["ld60029101"]["distance_km"] < 1.0
9744
+ and ev_ns["ld60004308"]["relation"] == "DURING" and not ev_ns["ld60004308"]["within_radius"] and ev_ns["ld60014118"]["relation"] == "AFTER",
9745
+ "poland": pl["rate"]["n_operations"] == 5 and pl["rate"]["n_background"] == 1 and sum(e["relation"] == "DURING" for e in pl["associated"]) == 4
9746
+ and ev_pl["ld60034503"]["relation"] == "AFTER" and ev_pl["ld60047703"]["relation"] == "OUTSIDE" and pl["rate"]["p_value"] < 1e-9
9747
+ and abs(pl["rate"]["p_value"] / sum(comb(6, k) * (96 / 13439) ** k * (1 - 96 / 13439) ** (6 - k) for k in (5, 6)) - 1) < 1e-4,
9748
+ "coverage": ns["coverage"]["inside"] and pl["coverage"]["inside"],
9749
+ "protocols": by_prot["ohio-odnr-2014"] == sorted((x, 1.0) for x in ("ld60034308", "ld60034403", "ld60034408", "ld60065316", "ld60034503"))
9750
+ and by_prot["oklahoma-occ-2016"] == [] and by_prot["alberta-aer-so2-2015"] == sorted((x, 2.0) for x in ("ld60034308", "ld60034408", "ld60065316"))
9751
+ and by_prot["texas-rrc-2022"] == [("ld60029101", 3.5)] and len(by_prot["bc-ksmma-2021"]) == 5,
9752
+ "record_magnitude": any("2.44" in n and "3.0" in n for n in ex["notes"]) and max(e["magnitude"] for e in pl["associated"]) == 2.44,
9753
+ "sources": all(p["url"].startswith("https://") and p["document"] for p in ex["protocols"]) and len(ex["protocols"]) == 5
9754
+ and [p["source_kind"] for p in ex["protocols"] if p["id"] == "ohio-odnr-2014"] == ["secondary"] and "Atkinson" in ex["sources"]["screen"]
9755
+ and "Kim" in ns["operations"][0]["source"] and "Skoumal" in pl["operations"][0]["source"],
9756
+ }
9757
+ failed1 = [k for k, v in c1.items() if not v]
9758
+ ok(not failed1,
9759
+ "CK1 the worked example from the public record: ComCat 1990-2026 within 30 km of the Mahoning Valley, 21 events; Northstar 1's eight "
9760
+ "events within 5 km in its injection year and the 90 days after against none in the rest, the probability equal to the share of time to "
9761
+ "the eighth power, the M 4.0 0.8 days after shut-in; Poland-CLL2's four during its stimulation, one the next day, one in 2016 outside; "
9762
+ "Ohio's 1.0 reached by all five, Oklahoma's 2.5 by none at the catalogue's ML 2.44 (the regulator's M 3.0 named), Alberta's 2.0 by "
9763
+ "three, Texas's 3.5 by the M 4.0 after disposal; every protocol with its document, Ohio's marked secondary"
9764
+ + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
9765
+
9766
+ # -- CK2 the arithmetic -----------------------------------------------------------------------------------------
9767
+ syn = OS.analyse(OS.synthetic_scene())
9768
+ sp = {w["id"]: w for w in syn["wells"]}["pad-1"]
9769
+ ev = {e["event_id"]: e for e in sp["events"]}
9770
+ quiet = OS.synthetic_scene()
9771
+ quiet["events"] = [e for e in quiet["events"] if e["event_id"].startswith("bg")]
9772
+ qr = {w["id"]: w for w in OS.analyse(quiet)["wells"]}["pad-1"]["rate"]
9773
+ one = OS.synthetic_scene()
9774
+ one["events"] = [e for e in one["events"] if e["event_id"].startswith("bg")] + [e for e in one["events"] if e["event_id"] == "op0"]
9775
+ onr = {w["id"]: w for w in OS.analyse(one)["wells"]}["pad-1"]["rate"]
9776
+ two = OS.synthetic_scene()
9777
+ two["wells"].append({"id": "pad-2", "display": "SYNTHETIC pad 2", "lat": 32.005, "lon": -102.0, "operations": [{"kind": "completion", "start": "2024-05-03", "end": "2024-05-06", "source": "s"}]})
9778
+ tw = {w["id"]: w for w in OS.analyse(two)["wells"]}
9779
+ c2 = {
9780
+ "binomial": abs(OS.binom_tail(3, 10, 0.2) - sum(comb(10, k) * 0.2 ** k * 0.8 ** (10 - k) for k in range(3, 11))) < 1e-12 and OS.binom_tail(0, 5, 0.3) == 1.0
9781
+ and OS.binom_tail(6, 5, 0.3) == 0.0 and OS.binom_tail(2, 4, 0.0) == 0.0,
9782
+ "synthetic": sp["rate"]["n_operations"] == 12 and sp["rate"]["status"] == "HIGHER DURING OPERATIONS" and "far" not in ev and ev["op5"]["protocol"]["highest"]["m"] == 3.0
9783
+ and ev["op4"]["protocol"]["highest"]["m"] == 2.0 and [c["m"] for c in ev["op4"]["protocol"]["conditional"]] == [2.2]
9784
+ and sp["cluster"] and len(sp["cluster"][0]["events"]) >= 5 and all(e["relation"] == "DURING" and e["operation"]["kind"] == "completion" for e in sp["associated"]),
9785
+ "unplaced": {w["id"]: w for w in syn["wells"]}["unplaced"]["placed"] is False and "surface position" in {w["id"]: w for w in syn["wells"]}["unplaced"]["notes"][0],
9786
+ "not_distinguished": qr["n_operations"] == 0 and qr["status"] == "NOT DISTINGUISHED" and onr["status"] == "NOT DISTINGUISHED" and onr["n_operations"] == 1,
9787
+ "also": any(x["well"] == "SYNTHETIC pad 2" for e in tw["pad-1"]["events"] for x in e["also"]) and any(x["well"] == "SYNTHETIC pad 1" for e in tw["pad-2"]["events"] for x in e["also"]),
9788
+ "radius": {w["id"]: w for w in OS.analyse({**OS.synthetic_scene(), "settings": {**OS.DEFAULTS, "radius_km": 1.0}})["wells"]}["pad-1"]["rate"]["n_operations"] == 6,
9789
+ }
9790
+ failed2 = [k for k, v in c2.items() if not v]
9791
+ ok(not failed2,
9792
+ "CK2 the arithmetic: the binomial tail against the sum by hand; over a labelled synthetic pad twelve events in its completion found "
9793
+ "higher than a twenty-year background, an event 30 km away left out, British Columbia's levels with the after-restart ones only "
9794
+ "conditional, its five-in-72-hours cluster found; a background alone, and one event in the window, NOT DISTINGUISHED; an event near "
9795
+ "two wells' operations named on both; a 1 km radius counting six; a well without a position named"
9796
+ + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
9797
+
9798
+ # -- CK3 a workspace -------------------------------------------------------------------------------------------
9799
+ synr = DS.synthetic_motor_record(str(d))
9800
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad CK", actor="tester")
9801
+ we = ws.add_well_file(synr["path"], display="SYNTHETIC EDR well", actor="tester")
9802
+ wf = ws.add_well_file(str(pkg / "sample_well_profile.csv"), display="SYNTHETIC frac well", actor="tester")
9803
+ ws.set_well(we["id"], surface={"lat": 32.0, "lon": -102.0, "datum": "NAD83"}, actor="tester")
9804
+ lines = ["#EventID|Time|Latitude|Longitude|Depth/km|Author|Catalog|Contributor|ContributorID|MagType|Magnitude|MagAuthor|EventLocationName",
9805
+ "tx1|2026-08-01T05:00:00|32.005|-102.004|4|tx|tx|tx|tx1|ml|1.9|tx|syn",
9806
+ "tx2|2026-08-04T05:00:00|32.012|-102.012|4|tx|tx|tx|tx2|ml|2.6|tx|syn",
9807
+ "tx3|2026-08-05T05:00:00|32.013|-102.011|4|tx|tx|tx|tx3|ml|3.1|tx|syn",
9808
+ "tx4|2020-02-01T05:00:00|32.10|-102.10|4|tx|tx|tx|tx4|ml|2.0|tx|syn"]
9809
+ (d / "cat.txt").write_text("\n".join(lines) + "\n", encoding="utf-8")
9810
+ (d / "notcat.txt").write_text("hello\nworld\n", encoding="utf-8")
9811
+ bad = {}
9812
+ for k, fn in (("kind", lambda: OS.save_operation(ws, "t", wf["id"], "fracking", "2026-08-03", None, "s")),
9813
+ ("order", lambda: OS.save_operation(ws, "t", wf["id"], "completion", "2026-08-03", "2026-08-01", "s")),
9814
+ ("source", lambda: OS.save_operation(ws, "t", wf["id"], "completion", "2026-08-03", None, "")),
9815
+ ("start", lambda: OS.save_operation(ws, "t", wf["id"], "completion", "", None, "s")),
9816
+ ("notcat", lambda: OS.attach_catalog(ws, "t", "notcat.txt", (d / "notcat.txt").read_bytes(), "2016-01-01", "2026-10-18", 1, 32, -102, 40, "s")),
9817
+ ("circle", lambda: OS.attach_catalog(ws, "t", "cat.txt", (d / "cat.txt").read_bytes(), "2016-01-01", "2026-10-18", 1, None, -102, 40, "s")),
9818
+ ("window", lambda: OS.attach_catalog(ws, "t", "cat.txt", (d / "cat.txt").read_bytes(), "2026-10-18", "2016-01-01", 1, 32, -102, 40, "s")),
9819
+ ("settings", lambda: OS.set_settings(ws, "t", radius_km=30, screen_km=10)),
9820
+ ("protocol", lambda: OS.set_settings(ws, "t", protocol_id="nope"))):
9821
+ try:
9822
+ fn(); bad[k] = False
9823
+ except (ValueError, KeyError):
9824
+ bad[k] = True
9825
+ sheet = ("[wells],well,lat,lon,datum\n,SYNTHETIC frac well,32.01,-102.01,NAD83\n"
9826
+ "[operations],well,kind,start,end,source\n,SYNTHETIC frac well,completion,2026-08-03,2026-08-08T12:00,stage log\n"
9827
+ "[catalogs],file,start,end,min_magnitude,lat,lon,radius_km,source\n,cat.txt,2016-01-01,2026-10-18,1.0,32.0,-102.0,40,SYNTHETIC catalogue\n")
9828
+ p1 = DC.apply(ws, sheet, "importer", str(d), write=True)
9829
+ p2 = DC.plan(ws, sheet, str(d))
9830
+ tp = DC.plan(ws, DC.template(ws), str(d))
9831
+ OS.set_settings(ws, "tester", protocol_id="bc-ksmma-2021", post_days=30)
9832
+ pg = OS.page(ws)
9833
+ WW = {w["id"]: w for w in pg["wells"]}
9834
+ ee = {e["event_id"]: e for e in WW[we["id"]]["events"]}
9835
+ ef = {e["event_id"]: e for e in WW[wf["id"]]["events"]}
9836
+ c3 = {
9837
+ "refused": all(bad.values()),
9838
+ "sheet": p1["applied"] and p1["counts"] == {"NEW": 3, "CHANGE": 0, "SAME": 0} and p2["counts"] == {"NEW": 0, "CHANGE": 0, "SAME": 3}
9839
+ and not tp["errors"] and tp["counts"]["NEW"] == 0 and tp["counts"]["CHANGE"] == 0 and len(OS.operations(ws)) == 1 and OS.catalogs(ws)[0]["n_events"] == 4,
9840
+ "rig": ee["tx1"]["relation"] == "DURING" and ee["tx1"]["operation"]["kind"] == "drilling" and ee["tx1"]["rig"]["state"] == "drilling"
9841
+ and abs(ee["tx1"]["rig"]["bottom_ft"] - 8300.0) < 1.0 and WW[we["id"]]["operations"][0]["source"].startswith("the drilling record"),
9842
+ "frac": ef["tx3"]["relation"] == "DURING" and ef["tx3"]["protocol"]["highest"]["m"] == 3.0 and ef["tx1"]["relation"] == "OUTSIDE"
9843
+ and any(x["kind"] == "drilling" for x in ef["tx1"]["also"]) and pg["settings"]["post_days"] == 30,
9844
+ "query": pg["query"]["url"].startswith("https://earthquake.usgs.gov/fdsnws/event/1/query?format=text") and pg["query"]["start"] == "1990-01-01",
9845
+ "audit": all(a in [x["action"] for x in ws.audit_log(500)] for a in ("seismicity.operation.declare", "seismicity.catalog.attach", "seismicity.settings")),
9846
+ }
9847
+ Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
9848
+ svc = Service(wsp, port=0, scheduler=False).start()
9849
+ base_url = svc.url.rstrip("/")
9850
+
9851
+ def opener(name, pw_):
9852
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
9853
+ rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
9854
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
9855
+ return o
9856
+
9857
+ def call(o, method, path, body=None):
9858
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
9859
+ if method == "POST":
9860
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
9861
+ try:
9862
+ with o.open(rq, timeout=120) as rr_:
9863
+ return rr_.status, json.loads(rr_.read())
9864
+ except urllib.error.HTTPError as e:
9865
+ return e.code, json.loads(e.read())
9866
+ try:
9867
+ V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
9868
+ s_w, wj = call(V, "GET", "/api/opseis")
9869
+ s_m, mj = call(V, "GET", "/api/opseis?scene=mahoning&protocol=texas-rrc-2022&radius_km=10")
9870
+ s_x, _ = call(V, "GET", "/api/opseis?scene=nope")
9871
+ s_n, _ = call(V, "GET", "/api/opseis?radius_km=abc")
9872
+ s_vo, _ = call(V, "POST", "/api/opseis/operation", {"well_id": wf["id"], "kind": "disposal", "start": "2026-09-01", "source": "s"})
9873
+ s_oo, oo = call(O, "POST", "/api/opseis/operation", {"well_id": wf["id"], "kind": "disposal", "start": "2026-09-01", "source": "s"})
9874
+ s_ob, _ = call(O, "POST", "/api/opseis/operation", {"well_id": "nope", "kind": "disposal", "start": "2026-09-01", "source": "s"})
9875
+ s_or, _ = call(O, "POST", "/api/opseis/operation/remove", {"op_id": oo.get("id", "")})
9876
+ s_or2, _ = call(O, "POST", "/api/opseis/operation/remove", {"op_id": oo.get("id", "")})
9877
+ s_oc, _ = call(O, "POST", "/api/opseis/catalog", {"filename": "cat2.txt", "content_b64": _b64.b64encode((d / "cat.txt").read_bytes()).decode(), "start": "2016-01-01",
9878
+ "end": "2026-10-18", "min_magnitude": 1, "lat": 32, "lon": -102, "radius_km": 40, "source": "s"})
9879
+ s_ocb, _ = call(O, "POST", "/api/opseis/catalog", {"filename": "x.txt", "content_b64": "!!", "start": "2016-01-01"})
9880
+ s_rc, _ = call(O, "POST", "/api/opseis/catalog/remove", {"file": "cat2.txt"})
9881
+ s_st, _ = call(O, "POST", "/api/opseis/settings", {"radius_km": 4})
9882
+ s_ac, _ = call(O, "POST", "/api/opseis/nope", {})
9883
+ finally:
9884
+ svc.stop()
9885
+ cli_p = _sp.run([sys.executable, "-m", "gea", "seismicity", "--protocols"], capture_output=True, text=True, cwd=str(pkg.parent))
9886
+ cli_m = _sp.run([sys.executable, "-m", "gea", "seismicity", "--scene", "mahoning"], capture_output=True, text=True, cwd=str(pkg.parent))
9887
+ cli_q = _sp.run([sys.executable, "-m", "gea", "seismicity", "--workspace", wsp, "--query"], capture_output=True, text=True, cwd=str(pkg.parent))
9888
+ cli_w = _sp.run([sys.executable, "-m", "gea", "seismicity", "--workspace", wsp], capture_output=True, text=True, cwd=str(pkg.parent))
9889
+ cli_b = _sp.run([sys.executable, "-m", "gea", "seismicity", "--workspace", wsp, "--declare-op", "--well", wf["id"], "--kind", "completion", "--start", "2026-09-09"],
9890
+ capture_output=True, text=True, cwd=str(pkg.parent))
9891
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
9892
+ c3.update({
9893
+ "api": s_w == 200 and len(wj["wells"]) == 2 and wj["scene"] == "workspace" and s_m == 200 and mj["protocol"]["id"] == "texas-rrc-2022" and mj["settings"]["radius_km"] == 10
9894
+ and s_x == 404 and s_n == 400 and s_vo == 403 and s_oo == 200 and s_ob == 404 and s_or == 200 and s_or2 == 404 and s_oc == 200 and s_ocb == 400
9895
+ and s_rc == 200 and s_st == 200 and OS.settings(ws)["radius_km"] == 4 and s_ac == 404,
9896
+ "cli": cli_p.returncode == 0 and "bc-ksmma-2021" in cli_p.stdout and "(after a restart)" in cli_p.stdout and cli_m.returncode == 0 and "Northstar 1" in cli_m.stdout
9897
+ and "probability 1.8e-12" in cli_m.stdout and cli_q.returncode == 0 and cli_q.stdout.startswith("https://earthquake.usgs.gov/")
9898
+ and cli_w.returncode == 0 and "rig: drilling at 8300 ft" in cli_w.stdout and cli_b.returncode != 0 and "source" in (cli_b.stderr + cli_b.stdout),
9899
+ "page": all(x in page_src for x in ("VIEWS.opseis", "['#/opseis', 'Operations & seismicity']", "osChart", "/api/opseis/catalog", "/api/opseis/operation",
9900
+ "What this page will not call a measurement", "km from the well", "the whole catalogue")),
9901
+ "help": any(t[0] == "opseis" for t in H.INDEX) and not H.check("opseis") and H.VIEW_TOPIC.get("opseis") == "opseis",
9902
+ })
9903
+ failed3 = [k for k, v in c3.items() if not v]
9904
+ ok(not failed3,
9905
+ "CK3 a workspace: an unknown kind, an end before the start, no source, no start, a file that is no catalogue, a catalogue without its "
9906
+ "circle or with its window backwards, a screen narrower than the radius and an unknown protocol refused; the sheet's operations and "
9907
+ "catalogues written and then SAME, the template round-tripping; the drilling record's span a drilling window and the rig drilling at "
9908
+ "8,300 ft when an event struck; a completion's M 3.1 at British Columbia's 3.0; the ComCat query drawn around the wells; the API, the "
9909
+ "CLI, the tab and the help page"
9910
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
9911
+
9912
+
9706
9913
  def main() -> int:
9707
9914
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
9708
9915
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -9794,6 +10001,7 @@ def main() -> int:
9794
10001
  section_ch_simulation(tmp)
9795
10002
  section_ci_drill_string(tmp)
9796
10003
  section_cj_declarations(tmp)
10004
+ section_ck_operations_seismicity(tmp)
9797
10005
  if _FAILS:
9798
10006
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
9799
10007
  for f in _FAILS:
@@ -53,6 +53,11 @@ TABLES: Dict[str, dict] = {
53
53
  'service_basis', 'service_source', 'fleet_id', 'note') + MOTOR_COLS,
54
54
  'need': ('well', 'run', 'class_id'),
55
55
  'about': "each run's components from the bit up, in order: the class, maker, model, serial, OD, length, the motor's catalogue id (`gea tools --action motors`) or - for a motor not in the catalogue - its own figures (motor_*) with their source, the service interval with its basis and source"},
56
+ 'operations': {'cols': ('well', 'kind', 'start', 'end', 'source', 'note'), 'need': ('well', 'kind', 'start', 'source'),
57
+ 'about': "operation windows for Operations & Seismicity: drilling, completion, disposal, injection or other, the start and end in UTC (blank end: still going), and the record it comes from"},
58
+ 'catalogs': {'cols': ('file', 'start', 'end', 'min_magnitude', 'lat', 'lon', 'radius_km', 'source'),
59
+ 'need': ('file', 'start', 'end', 'min_magnitude', 'lat', 'lon', 'radius_km', 'source'),
60
+ 'about': "earthquake catalogue exports (USGS ComCat text or a TexNet CSV, a path beside this sheet) with the window, minimum magnitude and circle they were queried for"},
56
61
  'penetrators': {'cols': ('well', 'penetrator', 'installed', 'installer', 'certified', 'certificate', 'motor', 'gauge_distance_in', 'ir_a_megohm',
57
62
  'ir_b_megohm', 'ir_c_megohm', 'ir_test_volts', 'ir_min_megohm', 'ir_min_source', 'void_test_psi', 'void_test_minutes',
58
63
  'void_held', 'note'),
@@ -63,8 +68,8 @@ TABLES: Dict[str, dict] = {
63
68
  'need': ('well', 'source'),
64
69
  'about': "the ESP drive's output limits and the motor nameplate, the wellhead's hazardous-area classification (e.g. Class I, Division 1), gas group, temperature class and the site's ambient range"},
65
70
  }
66
- ORDER = ('wells', 'fleet', 'rates', 'items', 'afe', 'surveys', 'runs', 'components', 'penetrators', 'esp_drives')
67
- APPLY_ORDER = ('wells', 'fleet', 'rates', 'items', 'afe', 'surveys', 'runs', 'penetrators', 'esp_drives')
71
+ ORDER = ('wells', 'fleet', 'rates', 'items', 'afe', 'surveys', 'runs', 'components', 'penetrators', 'esp_drives', 'operations', 'catalogs')
72
+ APPLY_ORDER = ('wells', 'fleet', 'rates', 'items', 'afe', 'surveys', 'runs', 'penetrators', 'esp_drives', 'operations', 'catalogs')
68
73
 
69
74
 
70
75
  class SheetError(ValueError):
@@ -482,6 +487,44 @@ def plan(ws, text: str, base_dir: str = '') -> dict:
482
487
  lambda actor, wid=wid, kw=kw: PN.save_drive(ws, actor, wid, **kw))
483
488
  guard('esp_drives', rec, b)
484
489
 
490
+ from . import op_seismicity as OS
491
+ ops_have = OS.operations(ws)
492
+ seen_ops = set()
493
+ for rec in tables.get('operations', []):
494
+ def b(rec=rec):
495
+ wid = _well_id(ws, rec['well'], wells)
496
+ new = OS.operation_record(ws, wid, rec.get('kind', ''), rec.get('start'), rec.get('end'), rec.get('source', ''), rec.get('note', ''))
497
+ key = (wid, new['kind'], new['start'])
498
+ if key in seen_ops:
499
+ raise SheetError(f"the {new['kind']} operation starting {new['start']} on {wid} is on the sheet twice")
500
+ seen_ops.add(key)
501
+ old = next((o for o in ops_have if (o['well_id'], o['kind'], o['start']) == key), None)
502
+ if old and (old['end'], old['source'], old['note']) == (new['end'], new['source'], new['note']):
503
+ add('operations', rec['_row'], wid, 'SAME', f"{new['kind']} from {new['start']} already declared")
504
+ return
505
+ add('operations', rec['_row'], wid, 'CHANGE' if old else 'NEW', f"{new['kind']} {new['start']} to {new['end'] or 'open'} ({new['source']})",
506
+ lambda actor, wid=wid, rec=rec, oid=(old or {}).get('id'): OS.save_operation(ws, actor, wid, rec.get('kind', ''), rec.get('start'), rec.get('end'),
507
+ rec.get('source', ''), rec.get('note', ''), oid))
508
+ guard('operations', rec, b)
509
+ cats_have = {c['file']: c for c in OS.catalogs(ws)}
510
+ for rec in tables.get('catalogs', []):
511
+ def b(rec=rec):
512
+ p = rec['file'].strip()
513
+ path = p if os.path.isabs(p) else os.path.join(base_dir or '.', p)
514
+ if not os.path.isfile(path):
515
+ raise SheetError(f'no catalogue file {p!r} beside the sheet' + ('' if base_dir else ' (a sheet sent through the browser carries no files: attach catalogues on the Operations & Seismicity tab or from the command line)'))
516
+ with open(path, 'rb') as f:
517
+ content = f.read()
518
+ new = OS.catalog_record(os.path.basename(p), content, rec['start'], rec['end'], rec['min_magnitude'], rec['lat'], rec['lon'], rec['radius_km'], rec['source'])
519
+ old = cats_have.get(new['file'])
520
+ if old and all(old.get(k) == new[k] for k in ('sha256', 'start', 'end', 'min_magnitude', 'circle', 'source')):
521
+ add('catalogs', rec['_row'], None, 'SAME', f"{new['file']} already attached")
522
+ return
523
+ add('catalogs', rec['_row'], None, 'CHANGE' if old else 'NEW', f"{new['file']}: {new['n_events']} event(s), {new['start']} to {new['end']}, M >= {new['min_magnitude']:g}",
524
+ lambda actor, p=p, content=content, rec=rec: OS.attach_catalog(ws, actor, os.path.basename(p), content, rec['start'], rec['end'], rec['min_magnitude'],
525
+ rec['lat'], rec['lon'], rec['radius_km'], rec['source']))
526
+ guard('catalogs', rec, b)
527
+
485
528
  steps.sort(key=lambda s: (APPLY_ORDER.index(s['table']), s['row']))
486
529
  counts = {k: sum(1 for s in steps if s['action'] == k) for k in ('NEW', 'CHANGE', 'SAME')}
487
530
  return {'steps': steps, 'errors': sorted(errs, key=lambda e: int(re.match(r'row (\d+)', e).group(1)) if re.match(r'row (\d+)', e) else 0),
@@ -619,4 +662,11 @@ def template(ws) -> str:
619
662
  d = rec.get('drive')
620
663
  if d:
621
664
  w.writerow(['', wid] + [_g(d.get(k)) if d.get(k) is not None else '' for k in TABLES['esp_drives']['cols'][1:]])
665
+ from . import op_seismicity as OS
666
+ head('operations')
667
+ for o in OS.operations(ws):
668
+ w.writerow(['', o['well_id'], o['kind'], o['start'], o['end'] or '', o['source'], o.get('note', '')])
669
+ head('catalogs')
670
+ for c in OS.catalogs(ws):
671
+ w.writerow(['# ' + c['file'] + ' is attached (' + str(c['n_events']) + ' events); list it here only to replace it'])
622
672
  return buf.getvalue()
@@ -8,6 +8,12 @@ Small, verbatim, public-domain extracts kept so the gate can prove a reader on r
8
8
  - `comcat_youngstown_2011-2012.txt` - the same service, fetched 2026-10-12:
9
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
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.
11
+ - `comcat_mahoning_1990-2026.txt` - the same service, read 2026-10-18 through the web reader as text and again as CSV, the two
12
+ readings compared event by event and agreeing on every id, time, position, depth and magnitude (no byte-for-byte fetch, so no hash):
13
+ `https://earthquake.usgs.gov/fdsnws/event/1/query?format=text&starttime=1990-01-01&endtime=2026-10-18&latitude=41.065&longitude=-80.605&maxradiuskm=30&minmagnitude=1&orderby=time-asc`
14
+ (21 events M >= 1.0 within 30 km of the Mahoning Valley, Ohio, 1990 to 2026: none before 2011; the 2011 Youngstown sequence; the
15
+ March 2014 Poland Township sequence; the April 2016 events by Bessemer, Pennsylvania; one in 2024). The Operations & Seismicity
16
+ worked example. Public domain.
11
17
  - `ne_110m_coastline.json` - Natural Earth 1:110m coastline (`ne_110m_coastline`), fetched 2026-10-13 from
12
18
  `https://github.com/nvkelso/natural-earth-vector` (geojson/ne_110m_coastline.geojson, md5 0b8f91e9d9c4fb607e61e121b5ac48fa):
13
19
  134 lines, 5,127 points after rounding to 0.01 degree and dropping consecutive duplicates. Public domain (Natural Earth).
@@ -0,0 +1,22 @@
1
+ #EventID|Time|Latitude|Longitude|Depth/km|Author|Catalog|Contributor|ContributorID|MagType|Magnitude|MagAuthor|EventLocationName
2
+ ld60004308|2011-03-17T10:53:11.670|41.126|-80.6056667|3.54|ld|ld|ld|ld60004308|ml|2.1|ld|2 km WSW of Maplewood Park, Ohio
3
+ ld60007303|2011-08-22T08:00:32.640|41.1303333|-80.7081667|5|ld|ld|ld|ld60007303|ml|1.87|ld|2 km SSW of Girard, Ohio
4
+ ld60009603|2011-09-26T01:06:10.840|41.115|-80.6823333|5|ld|ld|ld|ld60009603|ml|2.12|ld|3 km WNW of Youngstown, Ohio
5
+ ld60009803|2011-09-30T00:52:38.710|41.1346667|-80.6845|5|ld|ld|ld|ld60009803|ml|2.44|ld|2 km SSE of Girard, Ohio
6
+ ld60010908|2011-10-20T22:41:10.820|41.1253333|-80.704|6.39|ld|ld|ld|ld60010908|ml|2.04|ld|3 km S of Girard, Ohio
7
+ ld60012603|2011-11-25T06:47:28.250|41.124|-80.6771667|5|ld|ld|ld|ld60012603|ml|1.79|ld|3 km NW of Youngstown, Ohio
8
+ ld60013703|2011-12-24T06:24:58.810|41.1225|-80.7046667|5|ld|ld|ld|ld60013703|ml|2.37|ld|3 km S of Girard, Ohio
9
+ ld60029101|2011-12-31T20:05:01.060|41.1215|-80.6843333|5|ld|ld|ld|ld60029101|ml|4|ld|3 km NW of Youngstown, Ohio
10
+ ld60014118|2012-01-13T22:29:35.120|41.1245|-80.6923333|5.79|ld|ld|ld|ld60014118|ml|1.74|ld|3 km SSE of Girard, Ohio
11
+ ld60034308|2014-03-10T06:26:45.900|41.0103333|-80.5433333|2.54|ld|ld|ld|ld60034308|ml|2.44|ld|2 km SSW of Lowellville, Ohio
12
+ ld60034403|2014-03-10T06:42:44.280|41.0088333|-80.555|5|ld|ld|ld|ld60034403|ml|1.97|ld|3 km SSW of Lowellville, Ohio
13
+ ld60034408|2014-03-10T15:03:47.610|41.0096667|-80.53|5|ld|ld|ld|ld60034408|ml|2.16|ld|2 km S of Lowellville, Ohio
14
+ ld60065316|2014-03-10T15:44:06.780|41.009|-80.5316667|5|ld|ld|ld|ld60065316|ml|2.42|ld|2 km S of Lowellville, Ohio
15
+ ld60034503|2014-03-11T07:01:13.340|41.0025|-80.5336667|5.22|ld|ld|ld|ld60034503|ml|1.86|ld|3 km S of Lowellville, Ohio
16
+ ld60078511|2014-08-31T21:34:10.940|41.196|-80.7765|2.56|ld|ld|ld|ld60078511|ml|1.98|ld|Ohio
17
+ ld60112616|2016-04-25T04:05:25.280|41|-80.46|3|ld|ld|ld|ld60112616|ml|2.04|ld|3 km NE of Bessemer, Pennsylvania
18
+ ld60047603|2016-04-25T08:16:55.120|40.9888333|-80.3863333|3|ld|ld|ld|ld60047603|ml|1.76|ld|1 km W of Oakland, Pennsylvania
19
+ ld60047703|2016-04-25T09:03:02.690|41.0258333|-80.512|3|ld|ld|ld|ld60047703|ml|1.85|ld|2 km ESE of Lowellville, Ohio
20
+ ld60112636|2016-04-25T14:54:26.660|41.0031667|-80.4505|3|ld|ld|ld|ld60112636|ml|2.12|ld|4 km NE of Bessemer, Pennsylvania
21
+ ld60112721|2016-04-26T02:10:23.290|41.0008333|-80.4646667|3|ld|ld|ld|ld60112721|ml|1.86|ld|3 km NE of Bessemer, Pennsylvania
22
+ us6000p7j0|2024-11-25T05:36:04.376|40.937|-80.6673|5.159|us|us|us|us6000p7j0|ml|1.8|us|5 km NNE of Columbiana, Ohio
@@ -1,10 +1,10 @@
1
1
  # The declarations sheet
2
2
 
3
- > Everything the operator declares about a pad, in one spreadsheet: who each well is and where, its reference elevation, its directional survey, the day rates, the one-off costs and the AFE, the fleet, the drill-string runs, and the ESP penetrator and drive. Every row is checked against the workspace before anything is written, and the same sheet can be imported again and again.
3
+ > Everything the operator declares about a pad, in one spreadsheet: who each well is and where, its reference elevation, its directional survey, the day rates, the one-off costs and the AFE, the fleet, the drill-string runs, the ESP penetrator and drive, and the operation windows and earthquake catalogues for Operations & Seismicity. Every row is checked against the workspace before anything is written, and the same sheet can be imported again and again.
4
4
 
5
- **The command**: on the Setup tab, **Download the sheet** gives this workspace's sheet - every table with its header and a line saying what it takes, already filled with what is declared - and **Check the sheet** reads a sheet saved as CSV and plans each row: NEW, CHANGE (it replaces what is declared) or SAME (already declared exactly so). One error and nothing is written; with no error, **Write** (the operator role) writes the NEW and CHANGE rows. From a terminal: `gea declare --workspace C:\site --template pad3.csv` writes the sheet; `gea declare --workspace C:\site --file pad3.csv` checks it; add `--apply` to write. A directional survey is named by its file beside the sheet, so surveys are imported from the terminal (or attached on the well page).
5
+ **The command**: on the Setup tab, **Download the sheet** gives this workspace's sheet - every table with its header and a line saying what it takes, already filled with what is declared - and **Check the sheet** reads a sheet saved as CSV and plans each row: NEW, CHANGE (it replaces what is declared) or SAME (already declared exactly so). One error and nothing is written; with no error, **Write** (the operator role) writes the NEW and CHANGE rows. From a terminal: `gea declare --workspace C:\site --template pad3.csv` writes the sheet; `gea declare --workspace C:\site --file pad3.csv` checks it; add `--apply` to write. A directional survey or an earthquake catalogue is named by its file beside the sheet, so those are imported from the terminal (or attached on the well page and the Operations & Seismicity tab).
6
6
 
7
- **The layout**: one table after another. A table starts with a row whose first cell is its name in brackets - `[wells]`, `[fleet]`, `[rates]`, `[items]`, `[afe]`, `[surveys]`, `[runs]`, `[components]`, `[penetrators]`, `[esp_drives]` - and whose other cells name its columns in any order; its rows leave the first cell empty. A row starting with `#` is a comment. A well is named by its id or its name. A run's components are listed in `[components]` under the run's name, from the bit up; a motor names its catalogue id, or gives its own figures (`motor_rev_per_gal` ... `motor_source`). Fleet ratings are `key=value` pairs separated by `;`, lists with `/`.
7
+ **The layout**: one table after another. A table starts with a row whose first cell is its name in brackets - `[wells]`, `[fleet]`, `[rates]`, `[items]`, `[afe]`, `[surveys]`, `[runs]`, `[components]`, `[penetrators]`, `[esp_drives]`, `[operations]`, `[catalogs]` - and whose other cells name its columns in any order; its rows leave the first cell empty. A row starting with `#` is a comment. A well is named by its id or its name. A run's components are listed in `[components]` under the run's name, from the bit up; a motor names its catalogue id, or gives its own figures (`motor_rev_per_gal` ... `motor_source`). Fleet ratings are `key=value` pairs separated by `;`, lists with `/`.
8
8
 
9
9
  **What it writes**: through the same functions the forms and commands use, so each declaration is audited the same way under the importing user, and one `declarations.import` line records the import. It adds and replaces; it never removes: a rate that overlaps one already declared, or a fleet tool already there with another datasheet, is an error to resolve where it lives first.
10
10
 
@@ -0,0 +1,13 @@
1
+ # Operations and seismicity
2
+
3
+ > Each well's operations - drilling, completion, disposal, injection - set against the earthquake catalogue around it: what happened near the well while it worked and in the months after, what each event would reach under a regulator's protocol, and whether the earthquake rate near the well differs while it works from the rest of the record. The Mahoning Valley, Ohio, ships as a worked example from the public record.
4
+
5
+ **The command**: the Operations & Seismicity tab. Choose the scene - your wells, or the worked example - and the protocol (British Columbia's Kiskatinaw order, Alberta's Subsurface Order No. 2, Oklahoma's completion notice, Ohio's 2014 permit conditions, Texas's response plan), the association radius (5 km by default), the days after an operation that still count (90) and the completeness magnitude. Each well shows its operations as bands on a time line, every event within the screening distance (20 km) as a circle by its distance from the well and sized by magnitude, the events table with the rig's state at an event during a drilling record, the protocol level each reaches, and the rate comparison. The operator declares operation windows (or the declarations sheet's `[operations]`) and attaches catalogue exports with the window, minimum magnitude and circle they were queried for (or `[catalogs]`); the program cannot reach a catalogue service itself, so the tab gives the ComCat query to open in a browser. From a terminal: `gea seismicity --workspace C:\site`; `--scene mahoning` for the example; `--protocols`; `--query`; `--attach FILE --start --end --min-magnitude --lat --lon --catalog-radius-km --source`; `--declare-op --well W --kind completion --start --end --source`; `--set` keeps the settings.
6
+
7
+ **What it writes**: `seismicity/operations.json`, `seismicity/catalogs.json` with each catalogue file kept verbatim beside it (its SHA-256 recorded), and `seismicity/settings.json` in the workspace; each declaration, attachment, removal and change of settings a line in the audit log. The protocols ship with the program (`reference/seismicity_protocols.json`), each with its document; the worked example's catalogue (`fixtures/comcat_mahoning_1990-2026.txt`) and its wells and dates (`reference/mahoning_example.json`), each with its source.
8
+
9
+ **The arithmetic**: an event's distance is the great-circle distance from the well's declared surface position (converted to WGS84). It is DURING an operation inside its window, AFTER one within the association days that follow, and OUTSIDE otherwise; an event near two wells' operations is named on both, each with the other beside it. The rate comparison counts the events at or above the completeness within the radius over the catalogue's window: n inside the operation windows (with their association days) and m outside. If one constant rate held, n would be binomial in n + m with the share of time inside; the probability of n or more is the exact conditional test for two Poisson rates. HIGHER DURING OPERATIONS means that probability is under 1 % and the rate inside is the higher; otherwise NOT DISTINGUISHED. With no event outside, the background is given as its 95 % upper bound, 3 events over its time.
10
+
11
+ **The number to check**: a distance against a map; for the worked example, Northstar 1's eight events within 5 km during its injection year and the 90 days after against none in the rest of 1990 to 2026; Poland-CLL2's four events during its stimulation and one the next day; the Poland events' ML 2.44 in ComCat against the M 3.0 the regulator and the published study give - a protocol is read against its own magnitude of record.
12
+
13
+ **What this page will not call a measurement**: a cause - a coincidence in time and place is what the record shows; the catalogue's completeness - declared (the query's minimum magnitude by default), not measured, and a sparse network misses small events, so a quiet background is a floor; independence - the test counts every event, so an aftershock sequence weighs as many; the distance to the lateral - it is from the surface position; a protocol's verdict - the level an event reaches by the catalogue's magnitude, while the regulator's catalogue and the permit's own conditions govern.
@@ -49,6 +49,7 @@ INDEX: List[tuple] = [
49
49
  ('drillstring', 'Drill string', "each run's string from the bit up, every component's usage against its service interval, and the downhole motor's statistics and stability"),
50
50
  ('declare', 'The declarations sheet', 'everything the operator declares about a pad in one spreadsheet - identity, position, elevation, surveys, rates, AFE, fleet, runs, penetrators and drives - checked row by row before anything is written'),
51
51
  ('penetrator', 'Completion - the ESP penetrator', "the wellhead penetrator's ratings from its maker's documents, each well's installation record, and the ESP drive, motor and site checked against them; the permanent magnet motor safety note"),
52
+ ('opseis', 'Operations and seismicity', "each well's drilling, completion and disposal windows against the earthquake catalogue around it: the events during and after each, the levels a regulator's protocol would reach, the rate during operations against the rest of the record; the Mahoning Valley worked example"),
52
53
  ('economics', 'Economics', 'each well\'s days, footage and cost by the operator\'s own declared rates, against the EIA oil and gas spot prices while it was drilled, and any series the site imports'),
53
54
  ('well3d', 'The well in 3D', 'each well\'s path from its directional survey, set among the mapped formation surfaces and contours with its picks, coloured by what was measured along it, and the drill string replayed from the record; the Volve field shipped as the worked example'),
54
55
  ('charts', 'The charts', 'a well\'s channels stacked on one axis with a linked cursor, zoom and pan from every sample, time or depth for a drilling recorder, the depth events marked, and the drilling progress of every well'),
@@ -79,7 +80,7 @@ REQUIRED_LINES = ('**The command**', '**What it writes**', '**The number to chec
79
80
  VIEW_TOPIC: Dict[str, str] = {
80
81
  'home': 'start', 'wells': 'data-in', 'well': 'drift', 'live': 'patches', 'patch': 'patches', 'files': 'files', 'alarms': 'alarms',
81
82
  'approvals': 'well-tests', 'config': 'site', 'reports': 'drift', 'verify': 'start', 'survey': 'data-in', 'jobs': 'site',
82
- 'admin': 'site', 'prefs': 'site', 'search': 'start', 'welcome': 'start', 'seismic': 'seismic', 'seismic_station': 'seismic', 'track': 'seismic', 'sar': 'seismic', 'sites': 'sites', 'setup': 'setup', 'map': 'map', '3d': 'well3d', 'simulation': 'simulation', 'drillstring': 'drillstring', 'completion': 'penetrator', 'economics': 'economics', 'control': 'control-room', 'wall': 'control-room', 'audit': 'audit-update', 'tools': 'tools', 'catalog': 'catalog',
83
+ 'admin': 'site', 'prefs': 'site', 'search': 'start', 'welcome': 'start', 'seismic': 'seismic', 'seismic_station': 'seismic', 'track': 'seismic', 'sar': 'seismic', 'sites': 'sites', 'setup': 'setup', 'map': 'map', '3d': 'well3d', 'simulation': 'simulation', 'drillstring': 'drillstring', 'completion': 'penetrator', 'opseis': 'opseis', 'economics': 'economics', 'control': 'control-room', 'wall': 'control-room', 'audit': 'audit-update', 'tools': 'tools', 'catalog': 'catalog',
83
84
  }
84
85
 
85
86