gea-program 0.5.0__tar.gz → 0.6.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 (214) hide show
  1. {gea_program-0.5.0 → gea_program-0.6.0}/PKG-INFO +70 -6
  2. {gea_program-0.5.0 → gea_program-0.6.0}/README.md +69 -5
  3. {gea_program-0.5.0 → gea_program-0.6.0}/gea/__init__.py +3 -1
  4. {gea_program-0.5.0 → gea_program-0.6.0}/gea/__main__.py +177 -5
  5. {gea_program-0.5.0 → gea_program-0.6.0}/gea/acceptance_tests.py +344 -1
  6. {gea_program-0.5.0 → gea_program-0.6.0}/gea/client_reports.py +100 -0
  7. gea_program-0.6.0/gea/help/seismic.md +13 -0
  8. {gea_program-0.5.0 → gea_program-0.6.0}/gea/helplib.py +1 -1
  9. gea_program-0.6.0/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +122 -0
  10. gea_program-0.6.0/gea/reference/IU_ANMO_10_BHZ_response.xml +188 -0
  11. {gea_program-0.5.0 → gea_program-0.6.0}/gea/seismic.py +175 -89
  12. gea_program-0.6.0/gea/seismic_array.py +592 -0
  13. {gea_program-0.5.0 → gea_program-0.6.0}/gea/seismic_detect.py +1 -1
  14. gea_program-0.6.0/gea/seismic_response.py +358 -0
  15. {gea_program-0.5.0 → gea_program-0.6.0}/gea/service.py +74 -1
  16. {gea_program-0.5.0 → gea_program-0.6.0}/gea/web/app.html +54 -1
  17. {gea_program-0.5.0 → gea_program-0.6.0}/gea/workspace.py +189 -1
  18. {gea_program-0.5.0 → gea_program-0.6.0}/gea_program.egg-info/PKG-INFO +70 -6
  19. {gea_program-0.5.0 → gea_program-0.6.0}/gea_program.egg-info/SOURCES.txt +4 -0
  20. {gea_program-0.5.0 → gea_program-0.6.0}/pyproject.toml +1 -1
  21. gea_program-0.5.0/gea/help/seismic.md +0 -13
  22. {gea_program-0.5.0 → gea_program-0.6.0}/LICENSE +0 -0
  23. {gea_program-0.5.0 → gea_program-0.6.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  24. {gea_program-0.5.0 → gea_program-0.6.0}/gea/accuracy_statement.py +0 -0
  25. {gea_program-0.5.0 → gea_program-0.6.0}/gea/alarm_engine.py +0 -0
  26. {gea_program-0.5.0 → gea_program-0.6.0}/gea/bench.py +0 -0
  27. {gea_program-0.5.0 → gea_program-0.6.0}/gea/blind_harness.py +0 -0
  28. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  29. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  30. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  31. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  32. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  33. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  34. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  35. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  36. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  37. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  38. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  39. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  40. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  41. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  42. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  43. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  44. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  45. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  46. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  47. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  48. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  49. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  50. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  51. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  52. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  53. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  54. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  55. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  56. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  57. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  58. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  59. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  60. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  61. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  62. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  63. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  64. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  65. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  66. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  67. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  68. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  69. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  70. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  71. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  72. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  73. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  74. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  75. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  76. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  77. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  78. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  79. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  80. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  81. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  82. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  83. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  84. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  85. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  86. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  87. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  88. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  89. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  90. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  91. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  92. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  93. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  94. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  95. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/med_160_sapropels.txt +0 -0
  96. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  97. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  98. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  99. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  100. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  101. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  102. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  103. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  104. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  105. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  106. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  107. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  108. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  109. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  110. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  111. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  112. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  113. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  114. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  115. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  116. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  117. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  118. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  119. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  120. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  121. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  122. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  123. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  124. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  125. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  126. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  127. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  128. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  129. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  130. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  131. {gea_program-0.5.0 → gea_program-0.6.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  132. {gea_program-0.5.0 → gea_program-0.6.0}/gea/certificates.py +0 -0
  133. {gea_program-0.5.0 → gea_program-0.6.0}/gea/cli.py +0 -0
  134. {gea_program-0.5.0 → gea_program-0.6.0}/gea/config_versioning.py +0 -0
  135. {gea_program-0.5.0 → gea_program-0.6.0}/gea/correlation.py +0 -0
  136. {gea_program-0.5.0 → gea_program-0.6.0}/gea/dashboard.py +0 -0
  137. {gea_program-0.5.0 → gea_program-0.6.0}/gea/deviation.py +0 -0
  138. {gea_program-0.5.0 → gea_program-0.6.0}/gea/doctor.py +0 -0
  139. {gea_program-0.5.0 → gea_program-0.6.0}/gea/downhole_engine.py +0 -0
  140. {gea_program-0.5.0 → gea_program-0.6.0}/gea/drift_monitor.py +0 -0
  141. {gea_program-0.5.0 → gea_program-0.6.0}/gea/earth_model.py +0 -0
  142. {gea_program-0.5.0 → gea_program-0.6.0}/gea/example_register_map.json +0 -0
  143. {gea_program-0.5.0 → gea_program-0.6.0}/gea/fat_sat.py +0 -0
  144. {gea_program-0.5.0 → gea_program-0.6.0}/gea/files.py +0 -0
  145. {gea_program-0.5.0 → gea_program-0.6.0}/gea/follower.py +0 -0
  146. {gea_program-0.5.0 → gea_program-0.6.0}/gea/forward_model.py +0 -0
  147. {gea_program-0.5.0 → gea_program-0.6.0}/gea/gamma.py +0 -0
  148. {gea_program-0.5.0 → gea_program-0.6.0}/gea/gauge_aging.py +0 -0
  149. {gea_program-0.5.0 → gea_program-0.6.0}/gea/gauge_specs.py +0 -0
  150. {gea_program-0.5.0 → gea_program-0.6.0}/gea/gravity_reference.py +0 -0
  151. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/TESTER_GUIDE.md +0 -0
  152. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/alarms.md +0 -0
  153. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/data-in.md +0 -0
  154. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/doctor.md +0 -0
  155. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/drift.md +0 -0
  156. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/files.md +0 -0
  157. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/instruments.md +0 -0
  158. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/notifications.md +0 -0
  159. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/patches.md +0 -0
  160. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/quality.md +0 -0
  161. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/site.md +0 -0
  162. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/start.md +0 -0
  163. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/transients.md +0 -0
  164. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/upkeep.md +0 -0
  165. {gea_program-0.5.0 → gea_program-0.6.0}/gea/help/well-tests.md +0 -0
  166. {gea_program-0.5.0 → gea_program-0.6.0}/gea/housekeeping.py +0 -0
  167. {gea_program-0.5.0 → gea_program-0.6.0}/gea/inverse_engine.py +0 -0
  168. {gea_program-0.5.0 → gea_program-0.6.0}/gea/jobs.py +0 -0
  169. {gea_program-0.5.0 → gea_program-0.6.0}/gea/live_ports.py +0 -0
  170. {gea_program-0.5.0 → gea_program-0.6.0}/gea/loadtest.py +0 -0
  171. {gea_program-0.5.0 → gea_program-0.6.0}/gea/matplotlib_demo.py +0 -0
  172. {gea_program-0.5.0 → gea_program-0.6.0}/gea/modbus.py +0 -0
  173. {gea_program-0.5.0 → gea_program-0.6.0}/gea/model_card.py +0 -0
  174. {gea_program-0.5.0 → gea_program-0.6.0}/gea/mqtt_port.py +0 -0
  175. {gea_program-0.5.0 → gea_program-0.6.0}/gea/notify.py +0 -0
  176. {gea_program-0.5.0 → gea_program-0.6.0}/gea/opcua_port.py +0 -0
  177. {gea_program-0.5.0 → gea_program-0.6.0}/gea/operator_app.py +0 -0
  178. {gea_program-0.5.0 → gea_program-0.6.0}/gea/patches.py +0 -0
  179. {gea_program-0.5.0 → gea_program-0.6.0}/gea/ports.py +0 -0
  180. {gea_program-0.5.0 → gea_program-0.6.0}/gea/profile_catalog.py +0 -0
  181. {gea_program-0.5.0 → gea_program-0.6.0}/gea/project.py +0 -0
  182. {gea_program-0.5.0 → gea_program-0.6.0}/gea/qt6_downhole_app.py +0 -0
  183. {gea_program-0.5.0 → gea_program-0.6.0}/gea/reconciler.py +0 -0
  184. {gea_program-0.5.0 → gea_program-0.6.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  185. {gea_program-0.5.0 → gea_program-0.6.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  186. {gea_program-0.5.0 → gea_program-0.6.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  187. {gea_program-0.5.0 → gea_program-0.6.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  188. {gea_program-0.5.0 → gea_program-0.6.0}/gea/rock_inventory.py +0 -0
  189. {gea_program-0.5.0 → gea_program-0.6.0}/gea/sample_record.py +0 -0
  190. {gea_program-0.5.0 → gea_program-0.6.0}/gea/sample_well_profile.csv +0 -0
  191. {gea_program-0.5.0 → gea_program-0.6.0}/gea/sbom.py +0 -0
  192. {gea_program-0.5.0 → gea_program-0.6.0}/gea/segy.py +0 -0
  193. {gea_program-0.5.0 → gea_program-0.6.0}/gea/sensor_swap.py +0 -0
  194. {gea_program-0.5.0 → gea_program-0.6.0}/gea/service_life.py +0 -0
  195. {gea_program-0.5.0 → gea_program-0.6.0}/gea/shell.py +0 -0
  196. {gea_program-0.5.0 → gea_program-0.6.0}/gea/shut_in.py +0 -0
  197. {gea_program-0.5.0 → gea_program-0.6.0}/gea/sla_report.py +0 -0
  198. {gea_program-0.5.0 → gea_program-0.6.0}/gea/store_forward.py +0 -0
  199. {gea_program-0.5.0 → gea_program-0.6.0}/gea/strata_join.py +0 -0
  200. {gea_program-0.5.0 → gea_program-0.6.0}/gea/survey_cmd.py +0 -0
  201. {gea_program-0.5.0 → gea_program-0.6.0}/gea/survey_view.py +0 -0
  202. {gea_program-0.5.0 → gea_program-0.6.0}/gea/telemetry.py +0 -0
  203. {gea_program-0.5.0 → gea_program-0.6.0}/gea/tool_library.py +0 -0
  204. {gea_program-0.5.0 → gea_program-0.6.0}/gea/transient.py +0 -0
  205. {gea_program-0.5.0 → gea_program-0.6.0}/gea/well_assembler.py +0 -0
  206. {gea_program-0.5.0 → gea_program-0.6.0}/gea/well_test_validation.py +0 -0
  207. {gea_program-0.5.0 → gea_program-0.6.0}/gea/wits0.py +0 -0
  208. {gea_program-0.5.0 → gea_program-0.6.0}/gea/witsml.py +0 -0
  209. {gea_program-0.5.0 → gea_program-0.6.0}/gea_program.egg-info/dependency_links.txt +0 -0
  210. {gea_program-0.5.0 → gea_program-0.6.0}/gea_program.egg-info/entry_points.txt +0 -0
  211. {gea_program-0.5.0 → gea_program-0.6.0}/gea_program.egg-info/requires.txt +0 -0
  212. {gea_program-0.5.0 → gea_program-0.6.0}/gea_program.egg-info/top_level.txt +0 -0
  213. {gea_program-0.5.0 → gea_program-0.6.0}/setup.cfg +0 -0
  214. {gea_program-0.5.0 → gea_program-0.6.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.5.0
3
+ Version: 0.6.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
@@ -98,8 +98,19 @@ reaches a client document. Nothing unmeasured is ever reported as met.
98
98
  lines above the local floor out, and the detectability test: one station's
99
99
  record against a list of known rigs with positions and working windows,
100
100
  verdict per rig, the detection radius bracketed between the farthest rig
101
- heard and the nearest not heard. The decoder is proven against records
102
- written by libmseed. See *The second leg*.
101
+ heard and the nearest not heard. The instrument response (`seismic_response.py`):
102
+ the station's StationXML read into its stages, evaluated as evalresp does,
103
+ removed with a water level - counts to m/s, m or m/s^2. The decoder is proven
104
+ against libmseed on a corpus of real-station files; the response against
105
+ evalresp on a real IRIS channel. The array step (`seismic_array.py`):
106
+ beamforming over a slowness grid with the array response function's width
107
+ beside every bearing, the crossing of bearings from several arrays with its
108
+ ellipse, location from lags with the velocity named as an input, and the
109
+ array's detectability test - did it point at each known rig. On a site the
110
+ leg lives on the dashboard: a Seismic page where a station or an array is
111
+ added by upload with its rigs list and station file, refreshed as a job,
112
+ and read - spectrum, lines, verdicts, bearing - with its Seismic Station
113
+ Report under Reports. See *The second leg*.
103
114
 
104
115
  ## Quick start
105
116
 
@@ -111,7 +122,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
111
122
  gea client-report --report accuracy --out client_report
112
123
  gea model-cards --out model_cards
113
124
  gea sbom --out sbom
114
- gea accept # the product gate (246 checks)
125
+ gea accept # the product gate (258 checks)
115
126
  gea help drift # the help library, by the job (15 pages; the same text is on every dashboard page)
116
127
  gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
117
128
  gea gui # the desktop window (pip install "gea-program[desktop]")
@@ -309,7 +320,60 @@ produces the standard spectral products: the Welch PSD, a spectrogram, and
309
320
  the lines that persist above a running-median floor in the band where rig
310
321
  machinery sits (1-50 Hz by default). The decoder is proven in the acceptance
311
322
  suite against records written by libmseed, the format's reference
312
- implementation (`gea/reference/`).
323
+ implementation (`gea/reference/`), and `tools/seismic_reader_check.py` runs it
324
+ against libmseed on about ninety real-station files of every encoding and
325
+ byte order (the obspy test corpus, fetched with pip, never redistributed):
326
+ every time-series file sample-exact.
327
+
328
+ Physical units come from the station's own response file. `gea seismic
329
+ --action remove-response --stationxml station.xml` reads the FDSN StationXML
330
+ the archives serve beside the data (`fetch --with-response` fetches it),
331
+ builds the response from its stages - the poles and zeros of the sensor, the
332
+ gain of each stage, the FIR and IIR coefficients of the digitizer with their
333
+ delay corrections - the way evalresp builds it, and removes it in the
334
+ frequency domain with a water level and a pre-filter, giving velocity,
335
+ displacement or acceleration in SI units. The acceptance suite holds the
336
+ evaluation to evalresp's numbers on a real IRIS channel (IU.ANMO.10.BHZ,
337
+ `gea/reference/`) to one part in a hundred thousand. A station file whose
338
+ stages disagree with their own declared sensitivity, a polynomial stage, a
339
+ channel the file does not cover: refused with the reason, never patched, and
340
+ the record stays in counts, labelled.
341
+
342
+ One station detects; an array gives a direction. `gea seismic --action beam`
343
+ takes three or more sensors' records with their positions and returns the
344
+ back-azimuth and slowness the band's energy crossed the array with, by
345
+ frequency-domain beamforming over a slowness grid (the conventional Bartlett
346
+ beam, or Capon), refined around its maximum - and, beside the bearing, the
347
+ array response function's half-power width, which is the resolution the
348
+ geometry allows at that band, and whether the geometry has aliasing lobes
349
+ there, so a lone peak is not mistaken for a source. `--action array-detect`
350
+ is the array's form of the detectability test: for each rig on the
351
+ ground-truth list, in the windows it worked alone, did the array point at it
352
+ within its own tolerance (POINTED, NOT_POINTED, INCOHERENT). `--action
353
+ locate` crosses the bearings of two or more arrays, weighted by their
354
+ uncertainties, and returns the point with its 1-sigma ellipse, the crossing
355
+ angle and the residual of each bearing; a location from station-pair lags is
356
+ there too, with the medium velocity it needs named as the input it is. On
357
+ the labelled synthetic scene (`--action array-selftest`) a 1.2 km,
358
+ nine-sensor array points at rigs from 6 to 45 km within 0.3 degrees against
359
+ a 7-degree tolerance, and three arrays' bearings cross within a kilometre of
360
+ the rig. What none of it claims: a position from one array, a bearing to a
361
+ source nearer than five apertures, a direction finer than the array response
362
+ function, or a velocity that was not measured.
363
+
364
+ On a site the leg is a page. Seismic, beside Wells: a station (one record)
365
+ or an array (one record per sensor and a sensors CSV) is added by upload with
366
+ its position, its rigs list and, when there is one, its StationXML, and lives
367
+ under `seismic/<station>/` in the workspace with its files copied in verbatim
368
+ and hashed. Refresh - a button, or `gea workspace --action refresh-seismic`,
369
+ and like every other action a job with its log - reads the record, removes
370
+ the response when the station file is there, writes the spectrum, the
371
+ persistent lines, the detectability verdicts and, for an array, the beam and
372
+ the array verdicts under `reports/seismic/<station>/`, and the Seismic
373
+ Station Report beside them; the station page draws the spectrum and shows
374
+ the tables with the limits printed under each. The home page counts the
375
+ stations and the listed rigs heard. The sample report is in
376
+ `docs/report_samples/`.
313
377
 
314
378
  The first number is the detectability test (`--action detect`): one station's
315
379
  continuous record, the station's position, and a CSV of known rigs - position
@@ -377,7 +441,7 @@ attaches the zips to the GitHub release of that tag.
377
441
  gea/ the package: engines, record layer, reports, monitor, dashboard, cli, shell, acceptance suite,
378
442
  workspace, jobs, service, patches and web/app.html (the served dashboard)
379
443
  gea/catalog/ 52 public archive entries, each with a provenance file
380
- gea/reference/ miniSEED records written by libmseed, with their provenance: the decoder's independent reference
444
+ gea/reference/ miniSEED records written by libmseed and an IRIS StationXML with evalresp's numbers, with provenance: the independent references
381
445
  docs/report_samples/ one rendered example of every client report, from this build (tools/render_report_samples.py; SAMPLES.md names each command)
382
446
  docs/ TESTER_GUIDE.md, REQUIREMENTS_MATRIX.md (the scope-of-work mirror that shaped the reports), examples/ (port configs),
383
447
  SESSION_LOG.md (the working record, session by session),
@@ -59,8 +59,19 @@ reaches a client document. Nothing unmeasured is ever reported as met.
59
59
  lines above the local floor out, and the detectability test: one station's
60
60
  record against a list of known rigs with positions and working windows,
61
61
  verdict per rig, the detection radius bracketed between the farthest rig
62
- heard and the nearest not heard. The decoder is proven against records
63
- written by libmseed. See *The second leg*.
62
+ heard and the nearest not heard. The instrument response (`seismic_response.py`):
63
+ the station's StationXML read into its stages, evaluated as evalresp does,
64
+ removed with a water level - counts to m/s, m or m/s^2. The decoder is proven
65
+ against libmseed on a corpus of real-station files; the response against
66
+ evalresp on a real IRIS channel. The array step (`seismic_array.py`):
67
+ beamforming over a slowness grid with the array response function's width
68
+ beside every bearing, the crossing of bearings from several arrays with its
69
+ ellipse, location from lags with the velocity named as an input, and the
70
+ array's detectability test - did it point at each known rig. On a site the
71
+ leg lives on the dashboard: a Seismic page where a station or an array is
72
+ added by upload with its rigs list and station file, refreshed as a job,
73
+ and read - spectrum, lines, verdicts, bearing - with its Seismic Station
74
+ Report under Reports. See *The second leg*.
64
75
 
65
76
  ## Quick start
66
77
 
@@ -72,7 +83,7 @@ gea dashboard --catalog-well volve_f12_f14_production_excerpt:15/9-F-12:10000 --
72
83
  gea client-report --report accuracy --out client_report
73
84
  gea model-cards --out model_cards
74
85
  gea sbom --out sbom
75
- gea accept # the product gate (246 checks)
86
+ gea accept # the product gate (258 checks)
76
87
  gea help drift # the help library, by the job (15 pages; the same text is on every dashboard page)
77
88
  gea guide # the click-by-click tester guide (docs/TESTER_GUIDE.md)
78
89
  gea gui # the desktop window (pip install "gea-program[desktop]")
@@ -270,7 +281,60 @@ produces the standard spectral products: the Welch PSD, a spectrogram, and
270
281
  the lines that persist above a running-median floor in the band where rig
271
282
  machinery sits (1-50 Hz by default). The decoder is proven in the acceptance
272
283
  suite against records written by libmseed, the format's reference
273
- implementation (`gea/reference/`).
284
+ implementation (`gea/reference/`), and `tools/seismic_reader_check.py` runs it
285
+ against libmseed on about ninety real-station files of every encoding and
286
+ byte order (the obspy test corpus, fetched with pip, never redistributed):
287
+ every time-series file sample-exact.
288
+
289
+ Physical units come from the station's own response file. `gea seismic
290
+ --action remove-response --stationxml station.xml` reads the FDSN StationXML
291
+ the archives serve beside the data (`fetch --with-response` fetches it),
292
+ builds the response from its stages - the poles and zeros of the sensor, the
293
+ gain of each stage, the FIR and IIR coefficients of the digitizer with their
294
+ delay corrections - the way evalresp builds it, and removes it in the
295
+ frequency domain with a water level and a pre-filter, giving velocity,
296
+ displacement or acceleration in SI units. The acceptance suite holds the
297
+ evaluation to evalresp's numbers on a real IRIS channel (IU.ANMO.10.BHZ,
298
+ `gea/reference/`) to one part in a hundred thousand. A station file whose
299
+ stages disagree with their own declared sensitivity, a polynomial stage, a
300
+ channel the file does not cover: refused with the reason, never patched, and
301
+ the record stays in counts, labelled.
302
+
303
+ One station detects; an array gives a direction. `gea seismic --action beam`
304
+ takes three or more sensors' records with their positions and returns the
305
+ back-azimuth and slowness the band's energy crossed the array with, by
306
+ frequency-domain beamforming over a slowness grid (the conventional Bartlett
307
+ beam, or Capon), refined around its maximum - and, beside the bearing, the
308
+ array response function's half-power width, which is the resolution the
309
+ geometry allows at that band, and whether the geometry has aliasing lobes
310
+ there, so a lone peak is not mistaken for a source. `--action array-detect`
311
+ is the array's form of the detectability test: for each rig on the
312
+ ground-truth list, in the windows it worked alone, did the array point at it
313
+ within its own tolerance (POINTED, NOT_POINTED, INCOHERENT). `--action
314
+ locate` crosses the bearings of two or more arrays, weighted by their
315
+ uncertainties, and returns the point with its 1-sigma ellipse, the crossing
316
+ angle and the residual of each bearing; a location from station-pair lags is
317
+ there too, with the medium velocity it needs named as the input it is. On
318
+ the labelled synthetic scene (`--action array-selftest`) a 1.2 km,
319
+ nine-sensor array points at rigs from 6 to 45 km within 0.3 degrees against
320
+ a 7-degree tolerance, and three arrays' bearings cross within a kilometre of
321
+ the rig. What none of it claims: a position from one array, a bearing to a
322
+ source nearer than five apertures, a direction finer than the array response
323
+ function, or a velocity that was not measured.
324
+
325
+ On a site the leg is a page. Seismic, beside Wells: a station (one record)
326
+ or an array (one record per sensor and a sensors CSV) is added by upload with
327
+ its position, its rigs list and, when there is one, its StationXML, and lives
328
+ under `seismic/<station>/` in the workspace with its files copied in verbatim
329
+ and hashed. Refresh - a button, or `gea workspace --action refresh-seismic`,
330
+ and like every other action a job with its log - reads the record, removes
331
+ the response when the station file is there, writes the spectrum, the
332
+ persistent lines, the detectability verdicts and, for an array, the beam and
333
+ the array verdicts under `reports/seismic/<station>/`, and the Seismic
334
+ Station Report beside them; the station page draws the spectrum and shows
335
+ the tables with the limits printed under each. The home page counts the
336
+ stations and the listed rigs heard. The sample report is in
337
+ `docs/report_samples/`.
274
338
 
275
339
  The first number is the detectability test (`--action detect`): one station's
276
340
  continuous record, the station's position, and a CSV of known rigs - position
@@ -338,7 +402,7 @@ attaches the zips to the GitHub release of that tag.
338
402
  gea/ the package: engines, record layer, reports, monitor, dashboard, cli, shell, acceptance suite,
339
403
  workspace, jobs, service, patches and web/app.html (the served dashboard)
340
404
  gea/catalog/ 52 public archive entries, each with a provenance file
341
- gea/reference/ miniSEED records written by libmseed, with their provenance: the decoder's independent reference
405
+ gea/reference/ miniSEED records written by libmseed and an IRIS StationXML with evalresp's numbers, with provenance: the independent references
342
406
  docs/report_samples/ one rendered example of every client report, from this build (tools/render_report_samples.py; SAMPLES.md names each command)
343
407
  docs/ TESTER_GUIDE.md, REQUIREMENTS_MATRIX.md (the scope-of-work mirror that shaped the reports), examples/ (port configs),
344
408
  SESSION_LOG.md (the working record, session by session),
@@ -100,6 +100,8 @@ from .seismic import (
100
100
  welch_psd, spectrogram, persistent_lines, band_power,
101
101
  )
102
102
  from .seismic_detect import Source as SeismicSource, detectability_test, load_sources_csv, haversine_km, DRILLING_BAND_HZ
103
+ from .seismic_response import read_stationxml, remove_response, transfer as response_transfer, fdsn_stationxml
104
+ from .seismic_array import Sensor as SeismicSensor, load_sensors_csv, beam as array_beam, array_detectability, intersect_backazimuths, locate_from_lags
103
105
  from .bench import (
104
106
  bench_analysis, bench_selftest,
105
107
  )
@@ -120,7 +122,7 @@ from .profile_catalog import (
120
122
  read_pangaea_txt,
121
123
  )
122
124
 
123
- __version__ = "0.5.0"
125
+ __version__ = "0.6.0"
124
126
  __all__ = [
125
127
  "aging_rate", "rate_psi_yr", "accuracy_psi",
126
128
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -279,7 +279,16 @@ def main(argv=None) -> int:
279
279
  p_ws = sub.add_parser("workspace", help="the client site folder: init, add wells (file | catalogue | live), list, migrate a --out folder, refresh the dashboard, audit log")
280
280
  p_ws.add_argument("--path", type=str, required=True, help="the workspace folder")
281
281
  p_ws.add_argument("--action", type=str, default="list",
282
- choices=["init", "add-file", "add-catalog", "add-live", "remove", "list", "migrate", "refresh", "audit"])
282
+ choices=["init", "add-file", "add-catalog", "add-live", "remove", "list", "migrate", "refresh", "audit",
283
+ "add-seismic", "refresh-seismic", "remove-seismic"])
284
+ p_ws.add_argument("--files", type=str, nargs="+", default=None, help="add-seismic: the station's record (or one record per sensor of an array, in the sensors CSV's order)")
285
+ p_ws.add_argument("--lat", type=float, default=None, help="add-seismic: the station's latitude")
286
+ p_ws.add_argument("--lon", type=float, default=None, help="add-seismic: the station's longitude")
287
+ p_ws.add_argument("--stationxml", type=str, default=None, help="add-seismic: the station's FDSN StationXML (the response is removed at every refresh)")
288
+ p_ws.add_argument("--sensors", type=str, default=None, help="add-seismic: sensors CSV (sensor_id, lat, lon) - makes the station an array")
289
+ p_ws.add_argument("--sources", type=str, default=None, help="add-seismic: the rigs CSV (source_id, lat, lon, start_utc, end_utc[, kind, note])")
290
+ p_ws.add_argument("--band", type=float, nargs=2, default=None, metavar=("LO", "HI"), help="add-seismic: the band, Hz (default 1 50)")
291
+ p_ws.add_argument("--station", type=str, default=None, help="refresh-seismic / remove-seismic: the station id")
283
292
  p_ws.add_argument("--name", type=str, default=None, help="init: site name; add-*: display name")
284
293
  p_ws.add_argument("--file", type=str, default=None, help="add-file: the client's data file; add-live: the port configuration JSON")
285
294
  p_ws.add_argument("--entry", type=str, default=None, help="add-catalog: catalogue entry")
@@ -347,7 +356,19 @@ def main(argv=None) -> int:
347
356
  p_nt.add_argument("--actor", type=str, default="cli")
348
357
 
349
358
  p_se = sub.add_parser("seismic", help="the second leg's ingest: miniSEED/SAC in, spectra and persistent lines out, an FDSN fetch, and the detectability test against known rigs")
350
- p_se.add_argument("--action", choices=["info", "spectrum", "lines", "fetch", "stations", "detect", "selftest", "convert"], default="info")
359
+ p_se.add_argument("--action", choices=["info", "spectrum", "lines", "fetch", "stations", "detect", "selftest", "convert", "response", "remove-response",
360
+ "beam", "array-detect", "locate", "array-selftest"], default="info")
361
+ p_se.add_argument("--files", type=str, nargs="+", default=None, help="beam/array-detect: one record per sensor, in the order of --sensors")
362
+ p_se.add_argument("--sensors", type=str, default=None, help="beam/array-detect: CSV of the array's sensors (sensor_id, lat, lon[, elevation_m])")
363
+ p_se.add_argument("--bearings", type=str, default=None, help="locate: CSV of arrays' bearings (lat, lon, back_azimuth_deg, sigma_deg)")
364
+ p_se.add_argument("--method", type=str, default="bartlett", choices=["bartlett", "capon"], help="beam: the beamformer")
365
+ p_se.add_argument("--seg", type=float, default=10.0, help="beam: Welch segment length, s (default 10)")
366
+ p_se.add_argument("--smax", type=float, default=3.0, help="beam: slowness grid half-width, s/km (default 3: apparent velocities down to 0.33 km/s)")
367
+ p_se.add_argument("--stationxml", type=str, default=None, help="the station's FDSN StationXML (level=response); with spectrum/lines/detect the response is removed first")
368
+ p_se.add_argument("--output", type=str, default="VEL", choices=["VEL", "DISP", "ACC"], help="ground-motion unit after response removal (default VEL, m/s)")
369
+ p_se.add_argument("--water-level", type=float, default=60.0, help="water level for the deconvolution, dB (default 60)")
370
+ p_se.add_argument("--pre-filt", type=float, nargs=4, default=None, metavar=("F1", "F2", "F3", "F4"), help="cosine band window applied before the deconvolution, Hz")
371
+ p_se.add_argument("--with-response", action="store_true", help="fetch: also fetch the StationXML with responses, beside the record")
351
372
  p_se.add_argument("--file", type=str, default=None, help="a miniSEED or SAC file (decided by content)")
352
373
  p_se.add_argument("--trace", type=int, default=0, help="which trace of the file when it holds several (default the first)")
353
374
  p_se.add_argument("--band", type=float, nargs=2, default=[1.0, 50.0], metavar=("LO", "HI"), help="frequency band, Hz (default 1 50: rig machinery)")
@@ -720,7 +741,7 @@ def main(argv=None) -> int:
720
741
  from . import seismic as S
721
742
  from . import seismic_detect as D
722
743
 
723
- def _trace():
744
+ def _trace(apply_response=True):
724
745
  if not a.file:
725
746
  raise SystemExit("seismic: --file is needed for this action")
726
747
  trs = S.read_any(a.file)
@@ -728,8 +749,133 @@ def main(argv=None) -> int:
728
749
  raise SystemExit(f"seismic: {a.file} holds no samples")
729
750
  if a.trace >= len(trs):
730
751
  raise SystemExit(f"seismic: {a.file} holds {len(trs)} trace(s); --trace {a.trace} is out of range")
731
- return trs, trs[a.trace]
752
+ tr = trs[a.trace]
753
+ if a.stationxml and apply_response:
754
+ from . import seismic_response as R
755
+ try:
756
+ chan = R.select(R.read_stationxml(a.stationxml), tr)
757
+ tr, note = R.remove_response(tr, chan, a.output, a.water_level, tuple(a.pre_filt) if a.pre_filt else None)
758
+ except (LookupError, ValueError) as e:
759
+ raise SystemExit(f"seismic: response not removed - {e}")
760
+ print(f"seismic: response of {chan.id} removed ({chan.sensor or 'sensor unnamed'}); the record is now in {note['unit']}", file=sys.stderr)
761
+ return trs, tr
732
762
  band = (float(a.band[0]), float(a.band[1]))
763
+ if a.action == "array-selftest":
764
+ from . import seismic_array as AR
765
+ r = AR.selftest()
766
+ if a.json:
767
+ print(_json.dumps({k: v for k, v in r.items() if k != "detectability"} | {"detectability": {kk: vv for kk, vv in r["detectability"].items()}}, indent=1, default=str))
768
+ else:
769
+ print(f"seismic array-selftest: {r['status']} ok={r['ok']}")
770
+ print(AR.report_text(r["detectability"]))
771
+ c = r["crossing"]
772
+ print(f" three arrays' bearings cross {c['miss_km']:.2f} km from the rig; 1-sigma ellipse {c['ellipse_1sigma']['major_km']} x {c['ellipse_1sigma']['minor_km']} km; "
773
+ f"crossing angle {c['crossing_angle_deg']} deg; in front of every array: {c['in_front_of_every_array']}")
774
+ return 0 if r["ok"] else 1
775
+ if a.action in ("beam", "array-detect"):
776
+ from . import seismic_array as AR
777
+ if not a.files or not a.sensors:
778
+ raise SystemExit(f"seismic {a.action} needs --files (one per sensor) and --sensors CSV")
779
+ sensors = AR.load_sensors_csv(a.sensors)
780
+ if len(a.files) != len(sensors):
781
+ raise SystemExit(f"seismic {a.action}: {len(a.files)} files for {len(sensors)} sensors")
782
+ trs = []
783
+ for f in a.files:
784
+ t = S.read_any(f)
785
+ if not t:
786
+ raise SystemExit(f"seismic: {f} holds no samples")
787
+ tr = t[0]
788
+ if a.start or a.end:
789
+ tr = tr.slice(S.parse_time(a.start) if a.start else tr.starttime, S.parse_time(a.end) if a.end else tr.endtime)
790
+ trs.append(tr)
791
+ if a.action == "beam":
792
+ b = AR.beam(trs, sensors, band, a.seg, a.smax, 61, a.method)
793
+ b = {k: v for k, v in b.items() if not k.startswith("_")}
794
+ if a.out:
795
+ with open(a.out, "w", encoding="utf-8") as fh:
796
+ _json.dump(b, fh, indent=1)
797
+ if a.json:
798
+ print(_json.dumps(b, indent=1))
799
+ else:
800
+ print(f"seismic beam: {b['n_sensors']} sensors, aperture {b['aperture_km']} km, {b['window']['start']} to {b['window']['end']}, band {band[0]:g}-{band[1]:g} Hz ({b['method']})")
801
+ print(f" back-azimuth {b['back_azimuth_deg']} deg +/- {b['resolution']['azimuth_half_width_deg']} (array response), slowness {b['slowness_s_km']} s/km "
802
+ f"(apparent velocity {b['apparent_velocity_km_s']} km/s), best-bin coherence {b['coherence_max_bin']} with {b['coherent_bins']} coherent bins"
803
+ + (f" at {', '.join(f'{x:g}' for x in b['coherent_frequencies_hz'][:6])} Hz" if b['coherent_frequencies_hz'] else ''))
804
+ if b["resolution"]["aliasing_lobes"]:
805
+ print(" the array response has aliasing lobes at this band: a lone peak may be a lobe of the geometry")
806
+ for pk in b["peaks"][1:4]:
807
+ print(f" also: {pk['back_azimuth_deg']} deg at {pk['slowness_s_km']} s/km (power {pk['power']})")
808
+ print(f" plane-wave limit: sources nearer than {b['plane_wave_limit_km']} km are not plane waves here")
809
+ print(" not a measurement: " + "; ".join(b["not_a_measurement"]))
810
+ return 0
811
+ if not a.sources:
812
+ raise SystemExit("seismic array-detect needs --sources CSV")
813
+ res = AR.array_detectability(trs, sensors, D.load_sources_csv(a.sources), band, a.win or 600.0, a.seg, a.smax, 61, a.method)
814
+ if a.out:
815
+ with open(a.out, "w", encoding="utf-8") as fh:
816
+ _json.dump(res, fh, indent=1, default=str)
817
+ if a.json:
818
+ print(_json.dumps(res, indent=1, default=str))
819
+ else:
820
+ print(AR.report_text(res))
821
+ return 0
822
+ if a.action == "locate":
823
+ from . import seismic_array as AR
824
+ import csv as _csv
825
+ if not a.bearings:
826
+ raise SystemExit("seismic locate needs --bearings CSV (lat, lon, back_azimuth_deg, sigma_deg)")
827
+ with open(a.bearings, newline="", encoding="utf-8") as fh:
828
+ rows = [{k.strip(): float(v) for k, v in r.items() if k} for r in _csv.DictReader(fh)]
829
+ c = AR.intersect_backazimuths(rows)
830
+ if a.out:
831
+ with open(a.out, "w", encoding="utf-8") as fh:
832
+ _json.dump(c, fh, indent=1)
833
+ if a.json:
834
+ print(_json.dumps(c, indent=1))
835
+ else:
836
+ e = c["ellipse_1sigma"]
837
+ print(f"seismic locate: {len(rows)} bearings cross at ({c['lat']:.5f}, {c['lon']:.5f}); 1-sigma ellipse {e['major_km']} x {e['minor_km']} km "
838
+ f"(major axis at {e['major_azimuth_deg']} deg); crossing angle {c['crossing_angle_deg']} deg; residuals {c['residual_deg']} deg; "
839
+ f"in front of every array: {c['in_front_of_every_array']}")
840
+ print(" not a measurement: " + "; ".join(c["not_a_measurement"]))
841
+ return 0
842
+ if a.action == "response":
843
+ from . import seismic_response as R
844
+ if not a.stationxml:
845
+ raise SystemExit("seismic response needs --stationxml")
846
+ chans = R.read_stationxml(a.stationxml)
847
+ if a.json:
848
+ print(_json.dumps([c.summary() for c in chans], indent=1))
849
+ else:
850
+ for c in chans:
851
+ sm = c.summary()
852
+ print(f"{c.id:18s} {sm['start'] or '..'} to {sm['end'] or '..'} ({sm['latitude']}, {sm['longitude']}) {sm['sample_rate_hz']} Hz "
853
+ f"{sm['sensor']} sensitivity {sm['sensitivity']} {sm['output_units']}/({sm['input_units']}) at {sm['sensitivity_frequency_hz']} Hz stages {', '.join(sm['stages'])}")
854
+ print(f"seismic: {len(chans)} channel(s) in {a.stationxml}")
855
+ return 0
856
+ if a.action == "remove-response":
857
+ from . import seismic_response as R
858
+ if not a.stationxml or not a.out:
859
+ raise SystemExit("seismic remove-response needs --stationxml and --out")
860
+ trs, _ = _trace(apply_response=False)
861
+ resp = R.read_stationxml(a.stationxml)
862
+ done, notes = [], []
863
+ for tr in trs:
864
+ try:
865
+ chan = R.select(resp, tr)
866
+ out_tr, note = R.remove_response(tr, chan, a.output, a.water_level, tuple(a.pre_filt) if a.pre_filt else None)
867
+ except (LookupError, ValueError) as e:
868
+ print(f" {tr.id}: left in counts - {e}", file=sys.stderr)
869
+ continue
870
+ done.append(out_tr)
871
+ notes.append({"id": tr.id, **note})
872
+ if not done:
873
+ raise SystemExit("seismic remove-response: no trace had a usable response")
874
+ S.write_mseed(done, a.out, "FLOAT64")
875
+ with open(a.out + ".units.json", "w", encoding="utf-8") as fh:
876
+ _json.dump({"unit": done[0].unit, "traces": notes}, fh, indent=1)
877
+ print(f"seismic: {len(done)} trace(s) in {done[0].unit} -> {a.out} (FLOAT64 miniSEED; the unit and the response record are in {a.out}.units.json)")
878
+ return 0
733
879
  if a.action == "selftest":
734
880
  r = D.selftest()
735
881
  if a.json:
@@ -763,7 +909,7 @@ def main(argv=None) -> int:
763
909
  m = (f >= band[0]) & (f <= band[1])
764
910
  if a.out:
765
911
  S.spectrum_csv(f[m], p[m], a.out, tr)
766
- print(f"seismic: Welch PSD of {tr.id} ({tr.duration_s:.0f} s) in {band[0]:g}-{band[1]:g} Hz -> {a.out} [counts^2/Hz, response not removed]")
912
+ print(f"seismic: Welch PSD of {tr.id} ({tr.duration_s:.0f} s) in {band[0]:g}-{band[1]:g} Hz -> {a.out} [{'counts^2/Hz, response not removed' if tr.unit == 'counts' else '(' + tr.unit + ')^2/Hz, response removed'}]")
767
913
  else:
768
914
  top = sorted(zip(f[m], p[m]), key=lambda z: -z[1])[:10]
769
915
  print(f"seismic: Welch PSD of {tr.id}, {int(m.sum())} bins in {band[0]:g}-{band[1]:g} Hz (df {f[1] - f[0]:.4f} Hz); the ten strongest bins:")
@@ -801,6 +947,11 @@ def main(argv=None) -> int:
801
947
  raise SystemExit(f"seismic fetch needs --{' --'.join(miss)}")
802
948
  rec = S.fdsn_fetch(a.base, a.network, a.station, a.channel, a.start, a.end, a.out, a.location)
803
949
  print(f"seismic: {rec['bytes']} bytes -> {a.out} ({rec['unit']}); request written to {a.out}.request.json")
950
+ if a.with_response:
951
+ from . import seismic_response as R
952
+ xml_out = os.path.splitext(a.out)[0] + ".stationxml"
953
+ rx = R.fdsn_stationxml(a.base, a.network, a.station, a.channel, a.start, a.end, xml_out, a.location)
954
+ print(f"seismic: station file with responses -> {xml_out} ({', '.join(rx['channels']) or 'no channels'})")
804
955
  return 0
805
956
  if a.action == "detect":
806
957
  _, tr = _trace()
@@ -935,6 +1086,27 @@ def main(argv=None) -> int:
935
1086
  for row in ws.audit_log(limit=50):
936
1087
  print(row["utc"], row["actor"], row["action"], _json.dumps(row["detail"]))
937
1088
  return 0
1089
+ elif a.action == "add-seismic":
1090
+ if not (a.name and a.files):
1091
+ raise SystemExit("add-seismic needs --name and --files (one record, or one per sensor with --sensors)")
1092
+ st = ws.add_seismic_station(a.name, a.files, a.lat, a.lon, actor=a.actor, stationxml=a.stationxml, sensors_csv=a.sensors,
1093
+ sources_csv=a.sources, band=a.band)
1094
+ print("seismic station:", st["id"], f"({st['kind']}, {len(st['files'])} record(s))"); return 0
1095
+ elif a.action == "refresh-seismic":
1096
+ ids = [a.station] if a.station else [x["id"] for x in ws.seismic_stations()]
1097
+ if not ids:
1098
+ raise SystemExit("refresh-seismic: no seismic station in the workspace")
1099
+ for sid in ids:
1100
+ r = ws.refresh_seismic(sid, actor=a.actor)
1101
+ print(f"seismic {sid}: {r['records']} record(s), {r['hours']} h, {r['unit']}; {r['lines']} line(s)"
1102
+ + (f"; detectability {r['detect_status']}: {r['detected']} of {r['n_sources']} detected - {r['radius']}" if r["detect_status"] else "; no detectability test (needs --sources and a position)")
1103
+ + (f"; beam {r['beam_back_azimuth_deg']} deg, pointed at {r['pointed']}" if r["beam_back_azimuth_deg"] is not None else "")
1104
+ + f"\n report: {r['report']}")
1105
+ return 0
1106
+ elif a.action == "remove-seismic":
1107
+ if not a.station:
1108
+ raise SystemExit("remove-seismic needs --station <id>")
1109
+ ws.remove_seismic_station(a.station, actor=a.actor); print("removed (folder kept):", a.station); return 0
938
1110
  else:
939
1111
  print(_json.dumps(ws.summary(), indent=1)); return 0
940
1112
  print("well:", w["id"], f"({w['kind']})"); return 0