gea-program 0.8.1__tar.gz → 0.10.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 (228) hide show
  1. {gea_program-0.8.1 → gea_program-0.10.0}/PKG-INFO +25 -2
  2. {gea_program-0.8.1 → gea_program-0.10.0}/README.md +24 -1
  3. {gea_program-0.8.1 → gea_program-0.10.0}/gea/__init__.py +1 -1
  4. {gea_program-0.8.1 → gea_program-0.10.0}/gea/__main__.py +95 -2
  5. {gea_program-0.8.1 → gea_program-0.10.0}/gea/acceptance_tests.py +244 -2
  6. {gea_program-0.8.1 → gea_program-0.10.0}/gea/client_reports.py +102 -0
  7. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/seismic.md +1 -1
  8. gea_program-0.10.0/gea/help/sra.md +15 -0
  9. {gea_program-0.8.1 → gea_program-0.10.0}/gea/helplib.py +1 -0
  10. {gea_program-0.8.1 → gea_program-0.10.0}/gea/jobs.py +15 -6
  11. gea_program-0.10.0/gea/seismic_assoc.py +421 -0
  12. {gea_program-0.8.1 → gea_program-0.10.0}/gea/service.py +13 -1
  13. gea_program-0.10.0/gea/sra.py +623 -0
  14. {gea_program-0.8.1 → gea_program-0.10.0}/gea/web/app.html +3 -3
  15. {gea_program-0.8.1 → gea_program-0.10.0}/gea/workspace.py +179 -0
  16. {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/PKG-INFO +25 -2
  17. {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/SOURCES.txt +3 -0
  18. {gea_program-0.8.1 → gea_program-0.10.0}/pyproject.toml +1 -1
  19. {gea_program-0.8.1 → gea_program-0.10.0}/LICENSE +0 -0
  20. {gea_program-0.8.1 → gea_program-0.10.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  21. {gea_program-0.8.1 → gea_program-0.10.0}/gea/accuracy_statement.py +0 -0
  22. {gea_program-0.8.1 → gea_program-0.10.0}/gea/alarm_engine.py +0 -0
  23. {gea_program-0.8.1 → gea_program-0.10.0}/gea/bench.py +0 -0
  24. {gea_program-0.8.1 → gea_program-0.10.0}/gea/blind_harness.py +0 -0
  25. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  26. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  27. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  28. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  29. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  30. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  31. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  32. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  33. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  34. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  35. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  36. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  37. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  38. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  39. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  40. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  41. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  42. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  43. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  44. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  45. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  46. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  47. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  48. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  49. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  50. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  51. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  52. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  53. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  54. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  55. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  56. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  57. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  58. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  59. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  60. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  61. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  62. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  63. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  64. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  65. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  66. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  67. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  68. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  69. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  70. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  71. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  72. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  73. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  74. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  75. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  76. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  77. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  78. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  79. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  80. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  81. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  82. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  83. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  84. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  85. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  86. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  87. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  88. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  89. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  90. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  91. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  92. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/med_160_sapropels.txt +0 -0
  93. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  94. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  95. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  96. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  97. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  98. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  99. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  100. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  101. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  102. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  103. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  104. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  105. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  106. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  107. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  108. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  109. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  110. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  111. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  112. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  113. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  114. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  115. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  116. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  117. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  118. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  119. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  120. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  121. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  122. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  123. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  124. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  125. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  126. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  127. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  128. {gea_program-0.8.1 → gea_program-0.10.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  129. {gea_program-0.8.1 → gea_program-0.10.0}/gea/certificates.py +0 -0
  130. {gea_program-0.8.1 → gea_program-0.10.0}/gea/cli.py +0 -0
  131. {gea_program-0.8.1 → gea_program-0.10.0}/gea/config_versioning.py +0 -0
  132. {gea_program-0.8.1 → gea_program-0.10.0}/gea/correlation.py +0 -0
  133. {gea_program-0.8.1 → gea_program-0.10.0}/gea/dashboard.py +0 -0
  134. {gea_program-0.8.1 → gea_program-0.10.0}/gea/deviation.py +0 -0
  135. {gea_program-0.8.1 → gea_program-0.10.0}/gea/doctor.py +0 -0
  136. {gea_program-0.8.1 → gea_program-0.10.0}/gea/downhole_engine.py +0 -0
  137. {gea_program-0.8.1 → gea_program-0.10.0}/gea/drift_monitor.py +0 -0
  138. {gea_program-0.8.1 → gea_program-0.10.0}/gea/earth_model.py +0 -0
  139. {gea_program-0.8.1 → gea_program-0.10.0}/gea/example_register_map.json +0 -0
  140. {gea_program-0.8.1 → gea_program-0.10.0}/gea/fat_sat.py +0 -0
  141. {gea_program-0.8.1 → gea_program-0.10.0}/gea/files.py +0 -0
  142. {gea_program-0.8.1 → gea_program-0.10.0}/gea/follower.py +0 -0
  143. {gea_program-0.8.1 → gea_program-0.10.0}/gea/forward_model.py +0 -0
  144. {gea_program-0.8.1 → gea_program-0.10.0}/gea/gamma.py +0 -0
  145. {gea_program-0.8.1 → gea_program-0.10.0}/gea/gauge_aging.py +0 -0
  146. {gea_program-0.8.1 → gea_program-0.10.0}/gea/gauge_specs.py +0 -0
  147. {gea_program-0.8.1 → gea_program-0.10.0}/gea/geodesy.py +0 -0
  148. {gea_program-0.8.1 → gea_program-0.10.0}/gea/gravity_reference.py +0 -0
  149. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/TESTER_GUIDE.md +0 -0
  150. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/alarms.md +0 -0
  151. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/audit-update.md +0 -0
  152. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/data-in.md +0 -0
  153. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/doctor.md +0 -0
  154. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/drift.md +0 -0
  155. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/files.md +0 -0
  156. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/instruments.md +0 -0
  157. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/notifications.md +0 -0
  158. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/patches.md +0 -0
  159. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/quality.md +0 -0
  160. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/site.md +0 -0
  161. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/sites.md +0 -0
  162. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/start.md +0 -0
  163. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/transients.md +0 -0
  164. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/upkeep.md +0 -0
  165. {gea_program-0.8.1 → gea_program-0.10.0}/gea/help/well-tests.md +0 -0
  166. {gea_program-0.8.1 → gea_program-0.10.0}/gea/housekeeping.py +0 -0
  167. {gea_program-0.8.1 → gea_program-0.10.0}/gea/inverse_engine.py +0 -0
  168. {gea_program-0.8.1 → gea_program-0.10.0}/gea/live_ports.py +0 -0
  169. {gea_program-0.8.1 → gea_program-0.10.0}/gea/loadtest.py +0 -0
  170. {gea_program-0.8.1 → gea_program-0.10.0}/gea/matplotlib_demo.py +0 -0
  171. {gea_program-0.8.1 → gea_program-0.10.0}/gea/modbus.py +0 -0
  172. {gea_program-0.8.1 → gea_program-0.10.0}/gea/model_card.py +0 -0
  173. {gea_program-0.8.1 → gea_program-0.10.0}/gea/mqtt_port.py +0 -0
  174. {gea_program-0.8.1 → gea_program-0.10.0}/gea/notify.py +0 -0
  175. {gea_program-0.8.1 → gea_program-0.10.0}/gea/opcua_port.py +0 -0
  176. {gea_program-0.8.1 → gea_program-0.10.0}/gea/operator_app.py +0 -0
  177. {gea_program-0.8.1 → gea_program-0.10.0}/gea/patches.py +0 -0
  178. {gea_program-0.8.1 → gea_program-0.10.0}/gea/permits.py +0 -0
  179. {gea_program-0.8.1 → gea_program-0.10.0}/gea/ports.py +0 -0
  180. {gea_program-0.8.1 → gea_program-0.10.0}/gea/profile_catalog.py +0 -0
  181. {gea_program-0.8.1 → gea_program-0.10.0}/gea/project.py +0 -0
  182. {gea_program-0.8.1 → gea_program-0.10.0}/gea/qt6_downhole_app.py +0 -0
  183. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reconciler.py +0 -0
  184. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  185. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  186. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  187. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  188. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  189. {gea_program-0.8.1 → gea_program-0.10.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  190. {gea_program-0.8.1 → gea_program-0.10.0}/gea/rock_inventory.py +0 -0
  191. {gea_program-0.8.1 → gea_program-0.10.0}/gea/sample_record.py +0 -0
  192. {gea_program-0.8.1 → gea_program-0.10.0}/gea/sample_well_profile.csv +0 -0
  193. {gea_program-0.8.1 → gea_program-0.10.0}/gea/sbom.py +0 -0
  194. {gea_program-0.8.1 → gea_program-0.10.0}/gea/segy.py +0 -0
  195. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic.py +0 -0
  196. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_array.py +0 -0
  197. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_detect.py +0 -0
  198. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_film.py +0 -0
  199. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_harmonic.py +0 -0
  200. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_qc.py +0 -0
  201. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_response.py +0 -0
  202. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_signature.py +0 -0
  203. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_track.py +0 -0
  204. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_uncertainty.py +0 -0
  205. {gea_program-0.8.1 → gea_program-0.10.0}/gea/seismic_unlisted.py +0 -0
  206. {gea_program-0.8.1 → gea_program-0.10.0}/gea/sensor_swap.py +0 -0
  207. {gea_program-0.8.1 → gea_program-0.10.0}/gea/service_life.py +0 -0
  208. {gea_program-0.8.1 → gea_program-0.10.0}/gea/shell.py +0 -0
  209. {gea_program-0.8.1 → gea_program-0.10.0}/gea/shut_in.py +0 -0
  210. {gea_program-0.8.1 → gea_program-0.10.0}/gea/sla_report.py +0 -0
  211. {gea_program-0.8.1 → gea_program-0.10.0}/gea/store_forward.py +0 -0
  212. {gea_program-0.8.1 → gea_program-0.10.0}/gea/strata_join.py +0 -0
  213. {gea_program-0.8.1 → gea_program-0.10.0}/gea/supervisor.py +0 -0
  214. {gea_program-0.8.1 → gea_program-0.10.0}/gea/survey_cmd.py +0 -0
  215. {gea_program-0.8.1 → gea_program-0.10.0}/gea/survey_view.py +0 -0
  216. {gea_program-0.8.1 → gea_program-0.10.0}/gea/telemetry.py +0 -0
  217. {gea_program-0.8.1 → gea_program-0.10.0}/gea/tool_library.py +0 -0
  218. {gea_program-0.8.1 → gea_program-0.10.0}/gea/transient.py +0 -0
  219. {gea_program-0.8.1 → gea_program-0.10.0}/gea/well_assembler.py +0 -0
  220. {gea_program-0.8.1 → gea_program-0.10.0}/gea/well_test_validation.py +0 -0
  221. {gea_program-0.8.1 → gea_program-0.10.0}/gea/wits0.py +0 -0
  222. {gea_program-0.8.1 → gea_program-0.10.0}/gea/witsml.py +0 -0
  223. {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/dependency_links.txt +0 -0
  224. {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/entry_points.txt +0 -0
  225. {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/requires.txt +0 -0
  226. {gea_program-0.8.1 → gea_program-0.10.0}/gea_program.egg-info/top_level.txt +0 -0
  227. {gea_program-0.8.1 → gea_program-0.10.0}/setup.cfg +0 -0
  228. {gea_program-0.8.1 → gea_program-0.10.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.8.1
3
+ Version: 0.10.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 (302 checks)
128
+ gea accept # the product gate (314 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]")
@@ -528,6 +528,29 @@ so one geophone can still say when each rig was working - and the Seismic
528
528
  Station Report and the station page carry the section, with the tracks drawn
529
529
  against time.
530
530
 
531
+ ### The Seismicity Response Area packet
532
+
533
+ The Railroad Commission of Texas declares a Seismicity Response Area around a
534
+ cluster of felt earthquakes and expects the operators inside it to run a
535
+ response plan. The plans on file have one shape - a circle of about 9 km, a
536
+ plan written against M 3.5, an 18-month goal, a response group meeting
537
+ within 48 hours of a threshold event, quarterly checkpoints with Commission
538
+ staff, disposal wells in two tiers by the base of the Wolfcamp - and the
539
+ Commission's December 2023 Notice to Operators names the data: four daily
540
+ parameters (maximum and average surface injection pressure, injection
541
+ volume, maximum injection rate) and three bottomhole-pressure methods, one
542
+ of which is a permanent downhole probe. `gea/sra.py` builds the packet to
543
+ that shape from what the site holds. The operator declares what the program
544
+ cannot measure (`set-well`: surface position and datum, API and UIC, depth
545
+ tier, which channels are the pressure, the rate and the downhole gauge, the
546
+ BHP method); `sra-define` puts the area on the site; `sra-report --catalog
547
+ texnet.csv` writes the packet - membership by geodesic distance, the daily
548
+ record per well rolled up by month, the catalogue's events against the plan
549
+ with the trigger and the goal clock, the schedule, and every gap by name -
550
+ and a daily export under the Notice's own parameter names. It will not
551
+ invent a volume for a well with no rate channel, a tier from depth alone, or
552
+ an aftershock: those are declarations, and the packet says so.
553
+
531
554
  ### Audit / Update
532
555
 
533
556
  The Audit / Update tab holds three things. The whole audit log
@@ -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 (302 checks)
89
+ gea accept # the product gate (314 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]")
@@ -489,6 +489,29 @@ so one geophone can still say when each rig was working - and the Seismic
489
489
  Station Report and the station page carry the section, with the tracks drawn
490
490
  against time.
491
491
 
492
+ ### The Seismicity Response Area packet
493
+
494
+ The Railroad Commission of Texas declares a Seismicity Response Area around a
495
+ cluster of felt earthquakes and expects the operators inside it to run a
496
+ response plan. The plans on file have one shape - a circle of about 9 km, a
497
+ plan written against M 3.5, an 18-month goal, a response group meeting
498
+ within 48 hours of a threshold event, quarterly checkpoints with Commission
499
+ staff, disposal wells in two tiers by the base of the Wolfcamp - and the
500
+ Commission's December 2023 Notice to Operators names the data: four daily
501
+ parameters (maximum and average surface injection pressure, injection
502
+ volume, maximum injection rate) and three bottomhole-pressure methods, one
503
+ of which is a permanent downhole probe. `gea/sra.py` builds the packet to
504
+ that shape from what the site holds. The operator declares what the program
505
+ cannot measure (`set-well`: surface position and datum, API and UIC, depth
506
+ tier, which channels are the pressure, the rate and the downhole gauge, the
507
+ BHP method); `sra-define` puts the area on the site; `sra-report --catalog
508
+ texnet.csv` writes the packet - membership by geodesic distance, the daily
509
+ record per well rolled up by month, the catalogue's events against the plan
510
+ with the trigger and the goal clock, the schedule, and every gap by name -
511
+ and a daily export under the Notice's own parameter names. It will not
512
+ invent a volume for a well with no rate channel, a tier from depth alone, or
513
+ an aftershock: those are declarations, and the packet says so.
514
+
492
515
  ### Audit / Update
493
516
 
494
517
  The Audit / Update tab holds three things. The whole audit log
@@ -122,7 +122,7 @@ from .profile_catalog import (
122
122
  read_pangaea_txt,
123
123
  )
124
124
 
125
- __version__ = "0.8.1"
125
+ __version__ = "0.10.0"
126
126
  __all__ = [
127
127
  "aging_rate", "rate_psi_yr", "accuracy_psi",
128
128
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -281,9 +281,36 @@ def main(argv=None) -> int:
281
281
  p_ws.add_argument("--action", type=str, default="list",
282
282
  choices=["init", "add-file", "add-catalog", "add-live", "remove", "list", "migrate", "refresh", "audit",
283
283
  "add-seismic", "refresh-seismic", "remove-seismic", "sar-film", "refresh-all", "add-track", "refresh-track", "remove-track",
284
- "seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename"])
284
+ "seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
285
+ "set-well", "sra-define", "sra-report", "associate"])
285
286
  p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
286
287
  p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
288
+ p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
289
+ p_ws.add_argument("--uic", type=str, default=None, help="set-well: the UIC permit number")
290
+ p_ws.add_argument("--depth-tier", type=str, choices=["shallow", "deep"], default=None, help="set-well: above or below the named formation base")
291
+ p_ws.add_argument("--formation", type=str, default=None, help="set-well: the basis of the tier, e.g. 'completed below the base of the Wolfcamp'")
292
+ p_ws.add_argument("--channel-pressure", type=str, default=None, help="set-well: the channel that is the surface injection pressure (psi)")
293
+ p_ws.add_argument("--channel-rate", type=str, default=None, help="set-well: the channel that is the injection rate")
294
+ p_ws.add_argument("--rate-unit", type=str, default=None, help="set-well: the rate channel's unit: bbl/min or bbl/d")
295
+ p_ws.add_argument("--bhp-method", type=str, choices=["calculated", "dip_in", "probe"], default=None, help="set-well: how bottomhole pressure is known")
296
+ p_ws.add_argument("--channel-bhp", type=str, default=None, help="set-well: the downhole gauge channel (probe method)")
297
+ p_ws.add_argument("--top-interval-ft", type=float, default=None, help="set-well: top of the disposal interval, ft (calculated method)")
298
+ p_ws.add_argument("--gradient-psi-ft", type=float, default=None, help="set-well: fluid gradient, psi/ft (calculated method)")
299
+ p_ws.add_argument("--radius-km", type=float, default=None, help="sra-define: the area's radius in km (Gardendale: 9.08)")
300
+ p_ws.add_argument("--plan-date", type=str, default=None, help="sra-define: the plan date, YYYY-MM-DD")
301
+ p_ws.add_argument("--threshold-m", type=float, default=3.5, help="sra-define: the magnitude the plan is written against (3.5)")
302
+ p_ws.add_argument("--goal-months", type=int, default=18, help="sra-define: months without a threshold event that meet the goal (18)")
303
+ p_ws.add_argument("--response-hours", type=int, default=48, help="sra-define: hours within which the response group meets (48)")
304
+ p_ws.add_argument("--checkpoint-months", type=int, default=3, help="sra-define: months between checkpoints with Commission staff (3)")
305
+ p_ws.add_argument("--formation-boundary", type=str, default="base of the Wolfcamp", help="sra-define: the stratigraphic boundary between the two tiers")
306
+ p_ws.add_argument("--triggering-event", type=str, default="", help="sra-define: the catalogue id of the event the plan answers")
307
+ p_ws.add_argument("--catalog", type=str, default=None, help="sra-report: a TexNet earthquake-catalogue export (CSV)")
308
+ p_ws.add_argument("--aftershocks", type=str, nargs="*", default=None, help="sra-report: catalogue ids the operator declares as aftershocks (exempt)")
309
+ p_ws.add_argument("--start", type=str, default=None, help="sra-report: first day of the record, YYYY-MM-DD")
310
+ p_ws.add_argument("--end", type=str, default=None, help="sra-report: last day of the record, YYYY-MM-DD")
311
+ p_ws.add_argument("--vp", type=float, default=5.8, help="associate: the P velocity of the model, km/s (declared)")
312
+ p_ws.add_argument("--depth-km", type=float, default=6.0, help="associate: the hypocentral depth the location is made under, km (declared)")
313
+ p_ws.add_argument("--rms-tol", type=float, default=0.15, help="associate: the RMS residual an event must fit within, s")
287
314
  p_ws.add_argument("--wells", type=str, nargs="+", default=None, help="add-site: well ids of this workspace")
288
315
  p_ws.add_argument("--seismic", type=str, nargs="+", default=None, help="add-site: seismic station ids of this workspace")
289
316
  p_ws.add_argument("--tracks", type=str, nargs="+", default=None, help="add-site: track ids of this workspace")
@@ -362,6 +389,9 @@ def main(argv=None) -> int:
362
389
  p_w0.add_argument("--seed", type=int, default=1)
363
390
  p_w0.add_argument("--connect", type=str, default=None, help="host:port - instead of listening, connect to a listening tap and push frames")
364
391
 
392
+ p_sra = sub.add_parser("sra", help="the Seismicity Response Area packet: the self-test on a labelled scene")
393
+ p_sra.add_argument("--action", choices=["selftest"], default="selftest")
394
+ p_sra.add_argument("--json", action="store_true")
365
395
  p_dr = sub.add_parser("doctor", help="which code is running and can it serve: Python, the package, duplicates, the page, PyPI; with --workspace also the site folder and the port")
366
396
  p_dr.add_argument("--workspace", type=str, default=None)
367
397
  p_dr.add_argument("--host", type=str, default="127.0.0.1")
@@ -395,7 +425,8 @@ def main(argv=None) -> int:
395
425
  p_se.add_argument("--action", choices=["info", "spectrum", "lines", "fetch", "stations", "detect", "selftest", "convert", "response", "remove-response",
396
426
  "beam", "array-detect", "locate", "array-selftest", "sar-film", "bearings", "track", "track-selftest",
397
427
  "signatures", "multi-beam", "multi-bearings", "multi-track", "signature-selftest", "field-selftest",
398
- "harmonics", "harmonic-selftest", "datum", "geodesy-selftest", "array-qc", "qc-selftest", "unlisted", "unlisted-selftest", "bearing-sigma", "coverage", "uncertainty-selftest"], default="info")
428
+ "harmonics", "harmonic-selftest", "datum", "geodesy-selftest", "array-qc", "qc-selftest", "unlisted", "unlisted-selftest", "bearing-sigma", "coverage", "uncertainty-selftest",
429
+ "assoc-selftest"], default="info")
399
430
  p_se.add_argument("--signatures", type=str, default=None, help="multi-beam/multi-bearings: the signatures JSON from --action signatures")
400
431
  p_se.add_argument("--histories", type=str, nargs="+", default=None, help="track: two or more bearing-history JSONs (from --action bearings), one per array")
401
432
  p_se.add_argument("--truth", type=str, default=None, help="track: ground truth CSV (point_id, lat, lon, utc[, note]) - the lateral's points in time")
@@ -851,6 +882,18 @@ def main(argv=None) -> int:
851
882
  + (f"; verdict {v['verdict']} ({v['hit']} of {v['of']} inside their ellipse)" if v else "")
852
883
  + (f" - {row.get('detail', '')}" if row.get("detail") else ""))
853
884
  return 0 if r["ok"] else 1
885
+ if a.action == "assoc-selftest":
886
+ from . import seismic_assoc as _SA
887
+ r = _SA.selftest()
888
+ print(f"association selftest [{r['label']}]: {r['status']} - {r['n_picks']} picks, {len(r['events'])} events, errors {r['errors_km']} km")
889
+ for k, v in r["checks"].items():
890
+ print(f" {'ok ' if v else 'BAD'} {k}")
891
+ for e in r["events"]:
892
+ print(f" {e['id']}: {e['origin_utc']} {e['lat']:.4f}, {e['lon']:.4f} {e['n_stations']} stations RMS {e['rms_s']} s "
893
+ f"misfit region {e['region_km']['extent_east_km']} x {e['region_km']['extent_north_km']} km magnitude: not estimated")
894
+ for x in r["catalogue"]["rows"]:
895
+ print(f" vs catalogue: {x['event']} {x['standing']}")
896
+ return 0 if r["status"] == "OK" else 1
854
897
  if a.action == "uncertainty-selftest":
855
898
  from . import seismic_uncertainty as UQ
856
899
  r = UQ.selftest()
@@ -1443,6 +1486,18 @@ def main(argv=None) -> int:
1443
1486
  return 0
1444
1487
  print(f"notifications: {'configured' if n.cfg else 'not configured'}" + (f" - {len(n.cfg['channels'])} channel(s), {len(n.cfg['rules'])} rule(s), quiet {n.cfg['quiet_s']} s" if n.cfg else " (gea notify --example)"))
1445
1488
  return 0
1489
+ elif a.cmd == "sra":
1490
+ from . import sra as _SRA
1491
+ r = _SRA.selftest()
1492
+ if a.json:
1493
+ print(json.dumps({k: v for k, v in r.items() if k != "packet"}, indent=1))
1494
+ else:
1495
+ print(f"sra selftest [{r['label']}]: {r['status']}")
1496
+ for k, v in r["checks"].items():
1497
+ print(f" {'ok ' if v else 'BAD'} {k}")
1498
+ print(f" deep well volume {r['deep_mean_volume_bbl_day']} bbl/day; probe offset {r['probe_offset_psi']} psi")
1499
+ print(_SRA.report_text(r["packet"]))
1500
+ return 0 if r["status"] == "OK" else 1
1446
1501
  elif a.cmd == "doctor":
1447
1502
  from .doctor import run as _doctor
1448
1503
  return _doctor(a.workspace, a.host, a.port, a.json)
@@ -1630,6 +1685,44 @@ def main(argv=None) -> int:
1630
1685
  if not a.site:
1631
1686
  raise SystemExit("remove-site needs --site <id>")
1632
1687
  ws.remove_site(a.site, actor=a.actor); print("removed (folder kept):", a.site); return 0
1688
+ elif a.action == "set-well":
1689
+ if not a.well:
1690
+ raise SystemExit("set-well needs --well <id>")
1691
+ surface = {"lat": a.lat, "lon": a.lon, "datum": a.datum} if (a.lat is not None or a.lon is not None) else None
1692
+ disposal = {k: v for k, v in {"api_number": a.api, "uic_number": a.uic, "depth_tier": a.depth_tier, "formation_basis": a.formation,
1693
+ "channel_pressure": a.channel_pressure, "channel_rate": a.channel_rate, "rate_unit": a.rate_unit,
1694
+ "bhp_method": a.bhp_method, "channel_bhp": a.channel_bhp, "top_interval_ft": a.top_interval_ft,
1695
+ "gradient_psi_ft": a.gradient_psi_ft}.items() if v is not None}
1696
+ if not surface and not disposal:
1697
+ raise SystemExit("set-well: nothing to declare (give --lat/--lon, or any of --api --uic --depth-tier --channel-pressure ...)")
1698
+ w = ws.set_well(a.well, surface=surface, disposal=disposal or None, actor=a.actor)
1699
+ print(f"well {a.well}: surface {w.get('surface')}; disposal {w.get('disposal')}"); return 0
1700
+ elif a.action == "sra-define":
1701
+ if not a.site or not a.name or a.lat is None or a.lon is None or a.radius_km is None or not a.plan_date:
1702
+ raise SystemExit("sra-define needs --site, --name, --lat, --lon, --radius-km and --plan-date")
1703
+ sra = ws.sra_define(a.site, actor=a.actor, name=a.name, lat=a.lat, lon=a.lon, radius_km=a.radius_km, plan_date=a.plan_date,
1704
+ datum=a.datum or "WGS84", threshold_m=a.threshold_m, goal_months=a.goal_months, response_hours=a.response_hours,
1705
+ checkpoint_months=a.checkpoint_months, formation_boundary=a.formation_boundary,
1706
+ triggering_event=a.triggering_event, note=a.note or "")
1707
+ print(f"sra: {sra['name']} on site {a.site} - {sra['radius_km']} km around {sra['centre']['lat']}, {sra['centre']['lon']} "
1708
+ f"({sra['centre']['datum']}); plan {sra['plan_date']}; M {sra['threshold_m']}; goal {sra['goal_months']} months"); return 0
1709
+ elif a.action == "sra-report":
1710
+ from .sra import report_text as _sra_text
1711
+ if not a.site:
1712
+ raise SystemExit("sra-report needs --site <id>")
1713
+ r = ws.write_sra_packet(a.site, catalog_csv=a.catalog, aftershocks=a.aftershocks, start=a.start, end=a.end, actor=a.actor)
1714
+ print(_sra_text(r["packet"]))
1715
+ print(" report:", r["paths"]["html"]); print(" daily export:", r["paths"]["daily_csv"], f"({r['export_rows']} rows)"); return 0
1716
+ elif a.action == "associate":
1717
+ from .seismic_assoc import report_text as _assoc_text
1718
+ if not a.site:
1719
+ raise SystemExit("associate needs --site <id>")
1720
+ r = ws.associate_site(a.site, vp_km_s=a.vp, depth_km=a.depth_km, band=a.band, start=a.start, end=a.end, catalog_csv=a.catalog,
1721
+ rms_tol_s=a.rms_tol, actor=a.actor)
1722
+ for sid, x in r["stations"].items():
1723
+ print(f" {sid}: {x['status']}, {x['picks']} pick(s)")
1724
+ print(_assoc_text(r["association"], r["catalogue"]))
1725
+ print(" written:", os.path.join(ws.path, "reports", "sites", a.site, "association.json")); return 0
1633
1726
  elif a.action == "seismic-dataset":
1634
1727
  r = ws.write_seismic_dataset_report(actor=a.actor)
1635
1728
  inv = r["inventory"]
@@ -2914,7 +2914,8 @@ def section_am_report_samples(tmp: str) -> None:
2914
2914
  "model_card_well_baseline.html", "model_card_well_test_detector.html", "model_card_rock_density_inventory.html",
2915
2915
  "model_card_strata_property_estimator.html", "gauge_drift_report_monitored_SYNTHETIC.html", "alarm_event_report_SYNTHETIC.html",
2916
2916
  "data_resilience_report_SYNTHETIC.html", "sla_report_SYNTHETIC.html", "sat_protocol.html", "seismic_station_report_SYNTHETIC.html", "seismic_track_report_SYNTHETIC.html",
2917
- "seismic_dataset_report_SYNTHETIC.html", "seismic_field_report_SYNTHETIC.html", "site_report_SYNTHETIC.html"}
2917
+ "seismic_dataset_report_SYNTHETIC.html", "seismic_field_report_SYNTHETIC.html", "site_report_SYNTHETIC.html",
2918
+ "sra_packet_SYNTHETIC.html"}
2918
2919
  have = {p.name for p in d.glob("*.html")}
2919
2920
  md = (d / "SAMPLES.md").read_text(encoding="utf-8") if (d / "SAMPLES.md").exists() else ""
2920
2921
  texts = {n: (d / n).read_text(encoding="utf-8", errors="replace") for n in sorted(have & want)}
@@ -4593,9 +4594,248 @@ def section_bb_supervisor(tmp: str) -> None:
4593
4594
  "panel's own page carries the Stop and Restart controls, the authorisation step behind a popup, and the auto-restart switch")
4594
4595
 
4595
4596
 
4597
+ def section_bc_sra(tmp: str) -> None:
4598
+ """Section BC - the Seismicity Response Area packet. The Railroad Commission's operator-led response plans
4599
+ and its Notice to Operators on disposal-well monitoring name a shape; the packet is built to it from what
4600
+ the site holds, and names what it is missing rather than leaving a column blank."""
4601
+ import csv as _csv
4602
+ import subprocess as _sp
4603
+ from datetime import datetime as _dt, timedelta as _td, timezone as _tz
4604
+ from . import sra as SR
4605
+ from .client_reports import forbidden_terms, sra_packet_report, write
4606
+ from .workspace import Workspace, WorkspaceError
4607
+ pkg = Path(__file__).parent
4608
+ d = Path(tmp, "bc"); d.mkdir()
4609
+
4610
+ # -- the self-test: the labelled scene ----------------------------------------------
4611
+ r = SR.selftest()
4612
+ ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
4613
+ and abs(r["deep_mean_volume_bbl_day"] - 8640.0) / 8640.0 < 0.03 and abs(r["probe_offset_psi"] - 0.465 * 9800) < 15,
4614
+ "BC1 the self-test on a labelled scene: membership by geodesic distance with a NAD27 well's shift named, the four daily parameters from "
4615
+ "the well's own channels with the volume integrated from the rate to within 3 %, two shut-in days at zero, the probe's offset from "
4616
+ "surface pressure recovered, the shallow well refusing a volume it has no rate channel for, the catalogue's trigger and exempt "
4617
+ "aftershock and goal clock, the gaps named, the checkpoints from the plan date")
4618
+
4619
+ # -- the area: declarations, refused when not an area --------------------------------
4620
+ sra = SR.define("Test SRA", 31.95, -102.25, 9.08, "2026-02-25", "NAD27", triggering_event="x")
4621
+ bad = 0
4622
+ for kw in ({"datum": "UNKNOWN"}, {"radius_km": 0}, {"plan_date": "not a date"}, {"name": " "}):
4623
+ try:
4624
+ SR.define(**{**{"name": "T", "lat": 31.95, "lon": -102.25, "radius_km": 9.08, "plan_date": "2026-02-25", "datum": "WGS84"}, **kw});
4625
+ except ValueError:
4626
+ bad += 1
4627
+ cps = SR.checkpoints(sra)
4628
+ ok(sra["centre"]["datum"] == "NAD27" and sra["centre_wgs84"]["shift_m"] > 10 and sra["area_km2"] == 259.0 and sra["threshold_m"] == 3.5
4629
+ and sra["goal_months"] == 18 and sra["response_hours"] == 48 and sra["checkpoint_months"] == 3 and bad == 4
4630
+ and [c["date"] for c in cps][:2] == ["2026-05-25", "2026-08-25"] and cps[-1]["n"] == "goal" and cps[-1]["date"] == "2027-08-25"
4631
+ and "not a measurement" in " ".join(sra["not_a_measurement"]).lower().replace("never derived here", "not a measurement"),
4632
+ "BC2 an area is a centre on a stated datum (carried to WGS84 with its shift named), a radius, a plan date and the plan's numbers - "
4633
+ "M 3.5, 18 months, 48 hours, quarterly - recorded as declarations; an unknown datum, a zero radius, a non-date or a blank name is refused; "
4634
+ "the checkpoints fall every three months from the plan date and the goal date closes the list")
4635
+
4636
+ # -- the catalogue reader: a TexNet-shaped export ------------------------------------
4637
+ cat = d / "texnet.csv"
4638
+ with open(cat, "w", newline="") as f:
4639
+ w = _csv.writer(f); w.writerow(["EventID", "Origin Date", "Origin Time", "Local Magnitude", "Latitude", "Longitude", "Depth of Hypocenter (Km)"])
4640
+ w.writerow(["texnet2026aaaa", "2026-02-20", "04:15:10", "4.4", "31.950", "-102.250", "7.2"]) # before the plan date
4641
+ w.writerow(["texnet2026aaab", "2026-03-05", "11:02:33", "3.7", "31.960", "-102.240", "6.8"]) # counts
4642
+ w.writerow(["texnet2026aaac", "2026-03-06", "01:00:00", "3.6", "31.955", "-102.245", "6.5"]) # declared aftershock
4643
+ w.writerow(["texnet2026aaad", "2026-03-09", "21:40:00", "2.0", "31.940", "-102.260", "6.1"])
4644
+ w.writerow(["texnet2026aaae", "2026-03-12", "03:00:00", "3.9", "32.500", "-102.700", "8.0"]) # outside
4645
+ w.writerow(["", "", "", "", "", "", ""]) # a blank line is skipped, not an event
4646
+ c = SR.read_catalog(str(cat))
4647
+ se = SR.seismicity(sra, c, as_of=_dt(2026, 4, 1, tzinfo=_tz.utc), aftershocks=["texnet2026aaac"])
4648
+ ok(c["n_events"] == 5 and c["n_skipped"] == 1 and c["columns"]["origin_date"] == "Origin Date" and c["columns"]["origin_time"] == "Origin Time"
4649
+ and c["events"][0]["time"] == "2026-02-20T04:15:10Z" and c["events"][0]["depth_km"] == 7.2
4650
+ and se["n_inside"] == 4 and se["n_at_or_above"] == 3 and se["n_counted"] == 1 and se["n_exempt"] == 1
4651
+ and se["largest"]["event_id"] == "texnet2026aaaa" and se["trigger"]["status"] == "TRIGGERED" and se["trigger"]["event_id"] == "texnet2026aaab"
4652
+ and se["trigger"]["respond_by"] == "2026-03-07T11:02:33Z" and se["goal"]["clock_started"] == "2026-03-05" and se["goal"]["status"] == "RUNNING"
4653
+ and se["goal"]["goal_date"] == "2027-09-05",
4654
+ "BC3 a TexNet-shaped export is read by its column names (date and time in separate columns, the depth under its long name), a blank line "
4655
+ "is skipped and counted; against the plan: the M 4.4 before the plan date is the largest but does not count, the declared aftershock is "
4656
+ "exempt, the M 3.9 outside the circle is outside, the M 3.7 triggers the 48-hour response with its deadline, and the 18-month clock "
4657
+ "starts from it")
4658
+
4659
+ # -- the daily record from a stream: refused without a calendar --------------------------------
4660
+ from .ports import LiveStream, StreamChannel
4661
+ import numpy as _np
4662
+ st = LiveStream(name="x", source_format="synthetic", index_kind="time_s", index=_np.arange(100) * 600.0,
4663
+ channels={"P": StreamChannel("P", "psi", _np.full(100, 1500.0))}, meta={})
4664
+ rr = SR.daily_records(st, {"channel_pressure": "P"})
4665
+ sc = SR.synthetic_scene(days=3)
4666
+ dr = SR.daily_records(sc["streams"]["deep-1"], {**sc["wells"][0]["disposal"], "rate_unit": "furlongs"})
4667
+ ok(rr["status"] == "REFUSED" and "calendar" in rr["detail"]
4668
+ and dr["parameters"]["injection_volume_bbl"]["status"] == "NOT RECORDED" and "furlongs" in dr["parameters"]["injection_volume_bbl"]["why"]
4669
+ and dr["parameters"]["max_surface_injection_pressure_psi"]["status"] == "RECORDED",
4670
+ "BC4 a daily record needs a calendar: a stream with no start time is refused, not dated from zero; a rate in a unit the program does not "
4671
+ "convert leaves the volume NOT RECORDED with the unit named, while the pressures are still recorded")
4672
+
4673
+ # -- through the workspace and the command line ----------------------------------------
4674
+ t0 = _dt(2026, 3, 1, tzinfo=_tz.utc); n = 6 * 144
4675
+ hist = d / "swd.csv"
4676
+ with open(hist, "w", newline="") as f:
4677
+ w = _csv.writer(f); w.writerow(["timestamp", "P_surf_psi", "Q_bpm", "P_bh_psi"])
4678
+ for i in range(n):
4679
+ w.writerow([(t0 + _td(seconds=600 * i)).strftime("%Y-%m-%dT%H:%M:%SZ"), "1500.0", "5.0", f"{1500.0 + 0.465 * 9000:.1f}"])
4680
+ ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
4681
+ w1 = ws.add_well_file(str(hist), display="SWD 1", actor="tester")
4682
+ try:
4683
+ ws.set_well(w1["id"], disposal={"depth_tier": "middle"}, actor="tester"); tier_bad = False
4684
+ except WorkspaceError:
4685
+ tier_bad = True
4686
+ run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
4687
+ r1 = run("--action", "set-well", "--well", w1["id"], "--lat", "31.96", "--lon", "-102.24", "--datum", "NAD27", "--api", "42-329-12345", "--uic", "123456",
4688
+ "--depth-tier", "deep", "--formation", "completed below the base of the Wolfcamp", "--channel-pressure", "P_surf_psi", "--channel-rate", "Q_bpm",
4689
+ "--rate-unit", "bbl/min", "--bhp-method", "probe", "--channel-bhp", "P_bh_psi")
4690
+ r2 = run("--action", "add-site", "--name", "SRA block", "--wells", w1["id"], "--client", "Acme Operating")
4691
+ r3 = run("--action", "sra-report", "--site", "SRA-block") # no area yet: refused
4692
+ r4 = run("--action", "sra-define", "--site", "SRA-block", "--name", "Test SRA", "--lat", "31.95", "--lon", "-102.25", "--datum", "WGS84",
4693
+ "--radius-km", "9.08", "--plan-date", "2026-02-25")
4694
+ r5 = run("--action", "sra-report", "--site", "SRA-block", "--catalog", str(cat), "--aftershocks", "texnet2026aaac")
4695
+ ws2 = Workspace(ws.path)
4696
+ out = Path(ws.path, "reports", "sites", "SRA-block")
4697
+ rows = list(_csv.DictReader(open(out / "sra_daily_export.csv", encoding="utf-8")))
4698
+ md = (out / "sra_packet.md").read_text(encoding="utf-8")
4699
+ audit = Path(ws.path, "records", "audit.jsonl").read_text(encoding="utf-8")
4700
+ pkj = json.loads((out / "sra_packet.json").read_text(encoding="utf-8"))["packet"]
4701
+ ok(tier_bad and r1.returncode == 0 and r2.returncode == 0 and r3.returncode != 0 and "sra-define" in (r3.stderr + r3.stdout)
4702
+ and r4.returncode == 0 and r5.returncode == 0 and "COMPLETE" in r5.stdout and "TRIGGERED" in r5.stdout
4703
+ and ws2.site("SRA-block")["sra"]["name"] == "Test SRA" and pkj["status"] == "COMPLETE" and pkj["wells"][0]["inside"] and pkj["wells"][0]["depth_tier"] == "deep"
4704
+ and len(rows) == 6 and rows[0]["API number"] == "42-329-12345" and rows[0]["UIC permit number"] == "123456"
4705
+ and abs(float(rows[1]["Injection volume (barrels per day)"]) - 7200.0) < 10 and rows[1]["Maximum surface injection pressure (pounds per square inch)"] == "1500.0"
4706
+ and abs(float(rows[1]["Bottomhole pressure (pounds per square inch)"]) - (1500 + 0.465 * 9000)) < 1 and rows[1]["BHP method"] == "probe"
4707
+ and "Maximum surface injection pressure (pounds per square inch)" in rows[0]
4708
+ and not forbidden_terms(md) and "well.declare" in audit and "site.sra_define" in audit and "site.sra_packet" in audit,
4709
+ "BC5 `set-well` records what the operator declares - surface position and datum, API and UIC, depth tier by the named formation, which "
4710
+ "channels are the surface pressure, the rate and the downhole gauge, the BHP method - and refuses a tier that is not shallow or deep; "
4711
+ "`sra-define` puts the area on the site; `sra-report` is refused until it does; then the packet is COMPLETE for a well with everything "
4712
+ "declared, the daily export carries the Notice's parameter names with the API and UIC on every line, the volume integrates to 7,200 bbl "
4713
+ "a day from 5 bbl/min, the probe is the bottomhole pressure, the report passes the vocabulary gate, and every step is audited")
4714
+
4715
+ # -- the service offers it and the page carries it ------------------------------------------
4716
+ svc_src = (pkg / "service.py").read_text(encoding="utf-8"); page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
4717
+ r6 = _sp.run([sys.executable, "-m", "gea", "sra"], capture_output=True, text=True)
4718
+ ok("'sra_report'" in svc_src and "sra_packet.html" in svc_src and "Seismicity Response Area</th>" in page_src and "sra-define" in page_src
4719
+ and r6.returncode == 0 and "SIMULATION_SELF_TEST" in r6.stdout and "OK" in r6.stdout
4720
+ and (pkg / "help" / "sra.md").is_file(),
4721
+ "BC6 the Sites page carries the area and the packet's status beside each site; `gea sra` runs the self-test from the command line and "
4722
+ "labels it SIMULATION_SELF_TEST; the help page exists")
4723
+
4724
+
4725
+ def section_bd_association(tmp: str) -> None:
4726
+ """Section BD - association: several stations heard the same thing, or they did not. The onset picker,
4727
+ the associator with its grid search and its refusals, the location with its misfit region, the array as
4728
+ one station, the catalogue comparison, and the workspace run that feeds the SRA packet."""
4729
+ import csv as _csv
4730
+ import subprocess as _sp
4731
+ import numpy as _np
4732
+ from . import seismic as S
4733
+ from . import seismic_assoc as SA
4734
+ from .workspace import Workspace, WorkspaceError
4735
+ d = Path(tmp, "bd"); d.mkdir()
4736
+
4737
+ # -- the self-test -----------------------------------------------------------------------
4738
+ r = SA.selftest()
4739
+ ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
4740
+ and r["errors_km"]["ev-A"] < 1.0 and r["errors_km"]["ev-B"] < 1.0,
4741
+ "BD1 the self-test on a labelled scene: picks at every station, two events located to within a kilometre and a third of a second with all "
4742
+ "five stations and RMS under 0.05 s, a two-station event refused rather than located, a spurious burst left unused, finite misfit "
4743
+ "regions, no magnitude, and against the catalogue one AGREES, one DIFFERS by its separation and one catalogue event the stations did "
4744
+ "not associate")
4745
+
4746
+ # -- the picker: the onset is the first energy, not half a window early ------------------
4747
+ sc = SA.synthetic_scene()
4748
+ fr = sc["frame"]
4749
+ from .seismic_array import local_xy
4750
+ devs = []
4751
+ for sid, s in sc["stations"].items():
4752
+ ex, ey = local_xy(sc["truth"][0]["lat"], sc["truth"][0]["lon"], fr["lat0"], fr["lon0"])
4753
+ tt = SA.travel_time(ex, ey, s, sc["vp"], sc["depth_km"])
4754
+ pk = [p for p in SA.picks(sc["traces"][sid], band=(2.0, 20.0)) if abs(p.time - (sc["truth"][0]["t0"] + tt)) < 2.0]
4755
+ devs.append(pk[0].time - sc["truth"][0]["t0"] - tt if pk else 9.0)
4756
+ quiet = S.Trace("XX", "Q", "", "HHZ", 0.0, 100.0, _np.random.default_rng(1).normal(0, 1, 60000).astype(_np.float32), "synthetic", "FLOAT32")
4757
+ ok(all(abs(x) < 0.08 for x in devs) and not SA.picks(quiet, band=(2.0, 20.0)) and all(p.snr > 10 for p in SA.picks(sc["traces"]["STA5"], band=(2.0, 20.0))),
4758
+ "BD2 the picker puts every onset within 80 ms of the true arrival (the first refinement walked back into the noise and every pick came "
4759
+ "out 150 ms early, which the location absorbed into the origin time and nobody would have seen), picks nothing on a minute of plain "
4760
+ "noise, and carries a signal-to-noise ratio on every pick")
4761
+
4762
+ # -- the locator: refuses two stations, refines about the coarse best, names the model ----
4763
+ two = SA.locate({"STA1": 10.0, "STA2": 10.5}, sc["stations"], sc["vp"], sc["depth_km"])
4764
+ ex, ey = local_xy(sc["truth"][1]["lat"], sc["truth"][1]["lon"], fr["lat0"], fr["lon0"])
4765
+ pset = {sid: sc["truth"][1]["t0"] + SA.travel_time(ex, ey, s, sc["vp"], sc["depth_km"]) for sid, s in sc["stations"].items()}
4766
+ exact = SA.locate(pset, sc["stations"], sc["vp"], sc["depth_km"])
4767
+ off = SA.locate({**pset, "STA2": pset["STA2"] + 0.6}, sc["stations"], sc["vp"], sc["depth_km"])
4768
+ ok(two["status"] == "NOT ASSOCIATED" and "three" in two["detail"]
4769
+ and exact["status"] == "LOCATED" and abs(exact["best"]["x_km"] - ex) < 0.1 and abs(exact["best"]["y_km"] - ey) < 0.1 and exact["best"]["rms_s"] < 0.01
4770
+ and abs(exact["best"]["t0"] - sc["truth"][1]["t0"]) < 0.02 and not exact["region_km"]["reaches_grid_edge"]
4771
+ and "flat earth" in exact["model"]["basis"] and exact["model"]["depth_km"] == sc["depth_km"]
4772
+ and off["best"]["rms_s"] > 0.15 and abs(off["residuals_s"]["STA2"]) == max(abs(v) for v in off["residuals_s"].values()),
4773
+ "BD3 the locator refuses two stations, finds exact picks to within 100 m of the true epicentre and 20 ms of the origin time (the first "
4774
+ "version refined about the stations' centroid instead of the coarse best node and lost every event more than a kilometre from the "
4775
+ "centre), names the straight-ray flat-earth model and the declared depth on the result, and a pick 0.6 s off shows as the largest residual")
4776
+
4777
+ # -- the associator: the outlier is dropped, the event stands on the rest ----------------
4778
+ picks_all = []
4779
+ for sid, tr in sc["traces"].items():
4780
+ picks_all += SA.picks(tr, band=(2.0, 20.0))
4781
+ bad = [SA.Pick("STA4", sc["truth"][0]["t0"] + 3.2, 50.0, 1.0)] # a wrong pick at STA4 near event A
4782
+ picks_mix = [p for p in picks_all if not (p.station == "STA4" and abs(p.time - sc["truth"][0]["t0"]) < 3.0)] + bad
4783
+ res = SA.associate(picks_mix, sc["stations"], sc["vp"], sc["depth_km"], frame_info=fr)
4784
+ evA = sorted(res["events"], key=lambda e: e["origin_utc"])[0]
4785
+ ok(res["n_events"] == 2 and evA["n_stations"] == 4 and evA["dropped"] == ["STA4"] and evA["rms_s"] < 0.05
4786
+ and evA["magnitude"] is None and "not estimated" in evA["magnitude_basis"],
4787
+ "BD4 a wrong pick at one station is dropped as the largest residual and the event stands on the other four; an event carries no "
4788
+ "magnitude and says why")
4789
+
4790
+ # -- an array is one station ----------------------------------------------------------------
4791
+ raw = [SA.Pick("ARR", 100.0 + k * 0.03, 20.0 + k, 1.0, channel=f"S{k}") for k in range(6)] + [SA.Pick("ARR", 130.0, 9.0, 0.5, channel="S3")]
4792
+ col = SA.collapse_array_picks(raw, "ARR")
4793
+ ok(len(col) == 1 and abs(col[0].time - 100.075) < 0.02 and "6 sensor" in col[0].channel,
4794
+ "BD5 an array's sensors' picks within half a second become one pick at the median time at the array's position, and a burst one sensor "
4795
+ "alone saw is not an arrival at the array")
4796
+
4797
+ # -- through the workspace and the command line, into the packet ---------------------------
4798
+ for sid, tr in sc["traces"].items():
4799
+ S.write_mseed([tr], str(d / f"{sid}.mseed"))
4800
+ cat = d / "cat.csv"
4801
+ with open(cat, "w", newline="") as f:
4802
+ w = _csv.writer(f); w.writerow(["EventID", "Origin Time", "Local Magnitude", "Latitude", "Longitude", "Depth"])
4803
+ for c in sc["catalog"]:
4804
+ w.writerow([c["event_id"], c["time"], c["magnitude"], c["lat"], c["lon"], 6.0])
4805
+ ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
4806
+ ids = [ws.add_seismic_station(sid, [str(d / f"{sid}.mseed")], st.lat, st.lon, "tester", datum=st.datum, band=[2.0, 20.0])["id"]
4807
+ for sid, st in sc["stations"].items()]
4808
+ ws.add_site("Assoc block", seismic=ids[:2], client="Acme", actor="tester")
4809
+ try:
4810
+ ws.associate_site("Assoc-block"); few = False
4811
+ except WorkspaceError as e:
4812
+ few = "three" in str(e)
4813
+ ws.add_site("Assoc block 5", seismic=ids, client="Acme", actor="tester")
4814
+ run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
4815
+ r1 = run("--action", "associate", "--site", "Assoc-block-5", "--catalog", str(cat))
4816
+ r2 = run("--action", "sra-define", "--site", "Assoc-block-5", "--name", "Test SRA", "--lat", "31.95", "--lon", "-102.25", "--radius-km", "9.08", "--plan-date", "2026-02-25")
4817
+ r3 = run("--action", "sra-report", "--site", "Assoc-block-5", "--catalog", str(cat))
4818
+ aj = json.loads(Path(ws.path, "reports", "sites", "Assoc-block-5", "association.json").read_text(encoding="utf-8"))
4819
+ md = Path(ws.path, "reports", "sites", "Assoc-block-5", "sra_packet.md").read_text(encoding="utf-8")
4820
+ pk = json.loads(Path(ws.path, "reports", "sites", "Assoc-block-5", "sra_packet.json").read_text(encoding="utf-8"))["packet"]
4821
+ audit = Path(ws.path, "records", "audit.jsonl").read_text(encoding="utf-8")
4822
+ r4 = _sp.run([sys.executable, "-m", "gea", "seismic", "--action", "assoc-selftest"], capture_output=True, text=True)
4823
+ ok(few and r1.returncode == 0 and "2 event(s)" in r1.stdout and "AGREES" in r1.stdout and "DIFFERS" in r1.stdout and "NOT ASSOCIATED BY THE STATIONS" in r1.stdout
4824
+ and aj["association"]["n_events"] == 2 and all(v["status"] == "PICKED" for v in aj["stations"].values())
4825
+ and r2.returncode == 0 and r3.returncode == 0 and pk["association"]["n_inside"] == 2
4826
+ and "Events located by the site's own stations" in md and "AGREES" in md and "no magnitude is estimated here" in md
4827
+ and "site.associate" in audit and r4.returncode == 0 and "SIMULATION_SELF_TEST" in r4.stdout,
4828
+ "BD6 `--action associate` refuses a site with fewer than three stations, picks five miniSEED stations, associates and locates two events, "
4829
+ "sets them against the catalogue, writes association.json and audits it; the SRA packet then carries the events the site's own stations "
4830
+ "located beside the catalogue's, inside or outside the area, with the model named and no magnitude; `gea seismic --action assoc-selftest` "
4831
+ "runs the labelled scene from the command line")
4832
+
4833
+
4596
4834
  def main() -> int:
4597
4835
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
4598
- with tempfile.TemporaryDirectory() as tmp:
4836
+ # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
4837
+ # the checks have already passed or failed on their own, and the folder is the temp folder
4838
+ with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp:
4599
4839
  section_a_cli(tmp)
4600
4840
  section_b_las(tmp)
4601
4841
  section_c_reconciler(tmp)
@@ -4648,6 +4888,8 @@ def main() -> int:
4648
4888
  section_az_uncertainty(tmp)
4649
4889
  section_ba_sites(tmp)
4650
4890
  section_bb_supervisor(tmp)
4891
+ section_bc_sra(tmp)
4892
+ section_bd_association(tmp)
4651
4893
  if _FAILS:
4652
4894
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
4653
4895
  for f in _FAILS: