gea-program 0.11.0__tar.gz → 0.12.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 (232) hide show
  1. {gea_program-0.11.0/gea_program.egg-info → gea_program-0.12.0}/PKG-INFO +20 -2
  2. {gea_program-0.11.0 → gea_program-0.12.0}/README.md +19 -1
  3. {gea_program-0.11.0 → gea_program-0.12.0}/gea/__init__.py +1 -1
  4. {gea_program-0.11.0 → gea_program-0.12.0}/gea/__main__.py +20 -2
  5. {gea_program-0.11.0 → gea_program-0.12.0}/gea/acceptance_tests.py +143 -3
  6. gea_program-0.12.0/gea/help/quakeml.md +11 -0
  7. {gea_program-0.11.0 → gea_program-0.12.0}/gea/helplib.py +1 -0
  8. gea_program-0.12.0/gea/quakeml.py +525 -0
  9. {gea_program-0.11.0 → gea_program-0.12.0}/gea/workspace.py +39 -1
  10. {gea_program-0.11.0 → gea_program-0.12.0/gea_program.egg-info}/PKG-INFO +20 -2
  11. {gea_program-0.11.0 → gea_program-0.12.0}/gea_program.egg-info/SOURCES.txt +2 -0
  12. {gea_program-0.11.0 → gea_program-0.12.0}/pyproject.toml +1 -1
  13. {gea_program-0.11.0 → gea_program-0.12.0}/LICENSE +0 -0
  14. {gea_program-0.11.0 → gea_program-0.12.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  15. {gea_program-0.11.0 → gea_program-0.12.0}/gea/accuracy_statement.py +0 -0
  16. {gea_program-0.11.0 → gea_program-0.12.0}/gea/alarm_engine.py +0 -0
  17. {gea_program-0.11.0 → gea_program-0.12.0}/gea/bench.py +0 -0
  18. {gea_program-0.11.0 → gea_program-0.12.0}/gea/blind_harness.py +0 -0
  19. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  20. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  21. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  22. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  23. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  24. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  25. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  26. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  27. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  28. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  29. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  30. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  31. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  32. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  33. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  34. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  35. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  36. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  37. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  38. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  39. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  40. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  41. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  42. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  43. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  44. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  45. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  46. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  47. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  48. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  49. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  50. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  51. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  52. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  53. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  54. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  55. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  56. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  57. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  58. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  59. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  60. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  61. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  62. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  63. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  64. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  65. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  66. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  67. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  68. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  69. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  70. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  71. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  72. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  73. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  74. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  75. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  76. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  77. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  78. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  79. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  80. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  81. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  82. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  83. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  84. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  85. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  86. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/med_160_sapropels.txt +0 -0
  87. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  88. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  89. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  90. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  91. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  92. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  93. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  94. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  95. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  96. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  97. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  98. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  99. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  100. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  101. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  102. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  103. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  104. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  105. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  106. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  107. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  108. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  109. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  110. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  111. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  112. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  113. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  114. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  115. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  116. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  117. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  118. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  119. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  120. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  121. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  122. {gea_program-0.11.0 → gea_program-0.12.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  123. {gea_program-0.11.0 → gea_program-0.12.0}/gea/certificates.py +0 -0
  124. {gea_program-0.11.0 → gea_program-0.12.0}/gea/cli.py +0 -0
  125. {gea_program-0.11.0 → gea_program-0.12.0}/gea/client_reports.py +0 -0
  126. {gea_program-0.11.0 → gea_program-0.12.0}/gea/config_versioning.py +0 -0
  127. {gea_program-0.11.0 → gea_program-0.12.0}/gea/correlation.py +0 -0
  128. {gea_program-0.11.0 → gea_program-0.12.0}/gea/dashboard.py +0 -0
  129. {gea_program-0.11.0 → gea_program-0.12.0}/gea/deviation.py +0 -0
  130. {gea_program-0.11.0 → gea_program-0.12.0}/gea/doctor.py +0 -0
  131. {gea_program-0.11.0 → gea_program-0.12.0}/gea/downhole_engine.py +0 -0
  132. {gea_program-0.11.0 → gea_program-0.12.0}/gea/drift_monitor.py +0 -0
  133. {gea_program-0.11.0 → gea_program-0.12.0}/gea/earth_model.py +0 -0
  134. {gea_program-0.11.0 → gea_program-0.12.0}/gea/example_register_map.json +0 -0
  135. {gea_program-0.11.0 → gea_program-0.12.0}/gea/fat_sat.py +0 -0
  136. {gea_program-0.11.0 → gea_program-0.12.0}/gea/files.py +0 -0
  137. {gea_program-0.11.0 → gea_program-0.12.0}/gea/follower.py +0 -0
  138. {gea_program-0.11.0 → gea_program-0.12.0}/gea/forward_model.py +0 -0
  139. {gea_program-0.11.0 → gea_program-0.12.0}/gea/gamma.py +0 -0
  140. {gea_program-0.11.0 → gea_program-0.12.0}/gea/gauge_aging.py +0 -0
  141. {gea_program-0.11.0 → gea_program-0.12.0}/gea/gauge_specs.py +0 -0
  142. {gea_program-0.11.0 → gea_program-0.12.0}/gea/geodesy.py +0 -0
  143. {gea_program-0.11.0 → gea_program-0.12.0}/gea/gravity_reference.py +0 -0
  144. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/TESTER_GUIDE.md +0 -0
  145. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/alarms.md +0 -0
  146. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/audit-update.md +0 -0
  147. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/data-in.md +0 -0
  148. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/doctor.md +0 -0
  149. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/drift.md +0 -0
  150. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/files.md +0 -0
  151. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/instruments.md +0 -0
  152. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/notifications.md +0 -0
  153. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/osdu.md +0 -0
  154. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/patches.md +0 -0
  155. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/quality.md +0 -0
  156. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/seismic.md +0 -0
  157. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/site.md +0 -0
  158. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/sites.md +0 -0
  159. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/sra.md +0 -0
  160. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/start.md +0 -0
  161. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/transients.md +0 -0
  162. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/upkeep.md +0 -0
  163. {gea_program-0.11.0 → gea_program-0.12.0}/gea/help/well-tests.md +0 -0
  164. {gea_program-0.11.0 → gea_program-0.12.0}/gea/housekeeping.py +0 -0
  165. {gea_program-0.11.0 → gea_program-0.12.0}/gea/inverse_engine.py +0 -0
  166. {gea_program-0.11.0 → gea_program-0.12.0}/gea/jobs.py +0 -0
  167. {gea_program-0.11.0 → gea_program-0.12.0}/gea/live_ports.py +0 -0
  168. {gea_program-0.11.0 → gea_program-0.12.0}/gea/loadtest.py +0 -0
  169. {gea_program-0.11.0 → gea_program-0.12.0}/gea/matplotlib_demo.py +0 -0
  170. {gea_program-0.11.0 → gea_program-0.12.0}/gea/modbus.py +0 -0
  171. {gea_program-0.11.0 → gea_program-0.12.0}/gea/model_card.py +0 -0
  172. {gea_program-0.11.0 → gea_program-0.12.0}/gea/mqtt_port.py +0 -0
  173. {gea_program-0.11.0 → gea_program-0.12.0}/gea/notify.py +0 -0
  174. {gea_program-0.11.0 → gea_program-0.12.0}/gea/opcua_port.py +0 -0
  175. {gea_program-0.11.0 → gea_program-0.12.0}/gea/operator_app.py +0 -0
  176. {gea_program-0.11.0 → gea_program-0.12.0}/gea/osdu.py +0 -0
  177. {gea_program-0.11.0 → gea_program-0.12.0}/gea/patches.py +0 -0
  178. {gea_program-0.11.0 → gea_program-0.12.0}/gea/permits.py +0 -0
  179. {gea_program-0.11.0 → gea_program-0.12.0}/gea/ports.py +0 -0
  180. {gea_program-0.11.0 → gea_program-0.12.0}/gea/profile_catalog.py +0 -0
  181. {gea_program-0.11.0 → gea_program-0.12.0}/gea/project.py +0 -0
  182. {gea_program-0.11.0 → gea_program-0.12.0}/gea/qt6_downhole_app.py +0 -0
  183. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reconciler.py +0 -0
  184. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  185. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  186. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  187. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  188. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  189. {gea_program-0.11.0 → gea_program-0.12.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  190. {gea_program-0.11.0 → gea_program-0.12.0}/gea/rock_inventory.py +0 -0
  191. {gea_program-0.11.0 → gea_program-0.12.0}/gea/sample_record.py +0 -0
  192. {gea_program-0.11.0 → gea_program-0.12.0}/gea/sample_well_profile.csv +0 -0
  193. {gea_program-0.11.0 → gea_program-0.12.0}/gea/sbom.py +0 -0
  194. {gea_program-0.11.0 → gea_program-0.12.0}/gea/segy.py +0 -0
  195. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic.py +0 -0
  196. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_array.py +0 -0
  197. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_assoc.py +0 -0
  198. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_detect.py +0 -0
  199. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_film.py +0 -0
  200. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_harmonic.py +0 -0
  201. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_qc.py +0 -0
  202. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_response.py +0 -0
  203. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_signature.py +0 -0
  204. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_track.py +0 -0
  205. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_uncertainty.py +0 -0
  206. {gea_program-0.11.0 → gea_program-0.12.0}/gea/seismic_unlisted.py +0 -0
  207. {gea_program-0.11.0 → gea_program-0.12.0}/gea/sensor_swap.py +0 -0
  208. {gea_program-0.11.0 → gea_program-0.12.0}/gea/service.py +0 -0
  209. {gea_program-0.11.0 → gea_program-0.12.0}/gea/service_life.py +0 -0
  210. {gea_program-0.11.0 → gea_program-0.12.0}/gea/shell.py +0 -0
  211. {gea_program-0.11.0 → gea_program-0.12.0}/gea/shut_in.py +0 -0
  212. {gea_program-0.11.0 → gea_program-0.12.0}/gea/sla_report.py +0 -0
  213. {gea_program-0.11.0 → gea_program-0.12.0}/gea/sra.py +0 -0
  214. {gea_program-0.11.0 → gea_program-0.12.0}/gea/store_forward.py +0 -0
  215. {gea_program-0.11.0 → gea_program-0.12.0}/gea/strata_join.py +0 -0
  216. {gea_program-0.11.0 → gea_program-0.12.0}/gea/supervisor.py +0 -0
  217. {gea_program-0.11.0 → gea_program-0.12.0}/gea/survey_cmd.py +0 -0
  218. {gea_program-0.11.0 → gea_program-0.12.0}/gea/survey_view.py +0 -0
  219. {gea_program-0.11.0 → gea_program-0.12.0}/gea/telemetry.py +0 -0
  220. {gea_program-0.11.0 → gea_program-0.12.0}/gea/tool_library.py +0 -0
  221. {gea_program-0.11.0 → gea_program-0.12.0}/gea/transient.py +0 -0
  222. {gea_program-0.11.0 → gea_program-0.12.0}/gea/web/app.html +0 -0
  223. {gea_program-0.11.0 → gea_program-0.12.0}/gea/well_assembler.py +0 -0
  224. {gea_program-0.11.0 → gea_program-0.12.0}/gea/well_test_validation.py +0 -0
  225. {gea_program-0.11.0 → gea_program-0.12.0}/gea/wits0.py +0 -0
  226. {gea_program-0.11.0 → gea_program-0.12.0}/gea/witsml.py +0 -0
  227. {gea_program-0.11.0 → gea_program-0.12.0}/gea_program.egg-info/dependency_links.txt +0 -0
  228. {gea_program-0.11.0 → gea_program-0.12.0}/gea_program.egg-info/entry_points.txt +0 -0
  229. {gea_program-0.11.0 → gea_program-0.12.0}/gea_program.egg-info/requires.txt +0 -0
  230. {gea_program-0.11.0 → gea_program-0.12.0}/gea_program.egg-info/top_level.txt +0 -0
  231. {gea_program-0.11.0 → gea_program-0.12.0}/setup.cfg +0 -0
  232. {gea_program-0.11.0 → gea_program-0.12.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.11.0
3
+ Version: 0.12.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 (320 checks)
128
+ gea accept # the product gate (324 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]")
@@ -566,6 +566,24 @@ operation between them written out; a point on an unknown datum gets no WGS
566
566
  84 coordinates and is named. Without the operator's partition, ACL and legal
567
567
  tag the manifest is NOT LOADABLE and says so. `gea help osdu`.
568
568
 
569
+ ### The QuakeML catalogue export
570
+
571
+ The site's own events in the format the regulator's catalogue tools read.
572
+ After `associate`, `gea workspace --action quakeml-export --site <id>
573
+ [--agency "<name>"]` writes `reports/sites/<site>/quakeml/<site>_events.xml`
574
+ as QuakeML 1.2 to the Basic Event Description schema: one event per
575
+ associated event with its origin (the misfit region as the uncertainties,
576
+ depth in metres and "operator assigned" because it was declared, the method
577
+ and the earth model named, the quality block with the counts, the RMS, the
578
+ azimuthal gap and the distances), one pick per station with the record's own
579
+ FDSN codes, one arrival per pick with its residual. Every origin and pick is
580
+ automatic and preliminary in the schema's own words; the catalogue's AGREES
581
+ or DIFFERS is a comment with the catalogue's event id; no magnitude is
582
+ written and a comment says why. The shape is checked the way a reader checks
583
+ it first - identifiers, allowed children, required children, enumerations,
584
+ references - and the file validates against the published XSD. `gea help
585
+ quakeml`; `gea quakeml` runs the labelled scene.
586
+
569
587
  ### Audit / Update
570
588
 
571
589
  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 (320 checks)
89
+ gea accept # the product gate (324 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]")
@@ -527,6 +527,24 @@ operation between them written out; a point on an unknown datum gets no WGS
527
527
  84 coordinates and is named. Without the operator's partition, ACL and legal
528
528
  tag the manifest is NOT LOADABLE and says so. `gea help osdu`.
529
529
 
530
+ ### The QuakeML catalogue export
531
+
532
+ The site's own events in the format the regulator's catalogue tools read.
533
+ After `associate`, `gea workspace --action quakeml-export --site <id>
534
+ [--agency "<name>"]` writes `reports/sites/<site>/quakeml/<site>_events.xml`
535
+ as QuakeML 1.2 to the Basic Event Description schema: one event per
536
+ associated event with its origin (the misfit region as the uncertainties,
537
+ depth in metres and "operator assigned" because it was declared, the method
538
+ and the earth model named, the quality block with the counts, the RMS, the
539
+ azimuthal gap and the distances), one pick per station with the record's own
540
+ FDSN codes, one arrival per pick with its residual. Every origin and pick is
541
+ automatic and preliminary in the schema's own words; the catalogue's AGREES
542
+ or DIFFERS is a comment with the catalogue's event id; no magnitude is
543
+ written and a comment says why. The shape is checked the way a reader checks
544
+ it first - identifiers, allowed children, required children, enumerations,
545
+ references - and the file validates against the published XSD. `gea help
546
+ quakeml`; `gea quakeml` runs the labelled scene.
547
+
530
548
  ### Audit / Update
531
549
 
532
550
  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.11.0"
125
+ __version__ = "0.12.0"
126
126
  __all__ = [
127
127
  "aging_rate", "rate_psi_yr", "accuracy_psi",
128
128
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -282,7 +282,7 @@ def main(argv=None) -> int:
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
284
  "seismic-dataset", "seismic-field", "add-site", "sites", "site-report", "remove-site", "rename",
285
- "set-well", "sra-define", "sra-report", "associate", "osdu-export"])
285
+ "set-well", "sra-define", "sra-report", "associate", "osdu-export", "quakeml-export"])
286
286
  p_ws.add_argument("--stations", type=str, nargs="+", default=None, help="add-track: two or more array station ids of this workspace")
287
287
  p_ws.add_argument("--site", type=str, default=None, help="site-report/remove-site: the site id")
288
288
  p_ws.add_argument("--api", type=str, default=None, help="set-well: the API number")
@@ -315,7 +315,8 @@ def main(argv=None) -> int:
315
315
  p_ws.add_argument("--legal-tag", type=str, nargs="*", default=None, help="osdu-export: legal tag(s)")
316
316
  p_ws.add_argument("--country", type=str, nargs="*", default=None, help="osdu-export: otherRelevantDataCountries (default US)")
317
317
  p_ws.add_argument("--operator-org", type=str, default="", help="osdu-export: the operator's Organisation record id on the platform")
318
- p_ws.add_argument("--out", type=str, default=None, help="osdu-export: the folder to write the manifest and files into")
318
+ p_ws.add_argument("--out", type=str, default=None, help="osdu-export: the folder to write the manifest and files into; quakeml-export: the file to write")
319
+ p_ws.add_argument("--agency", type=str, default="", help="quakeml-export: the agencyID written in creationInfo (default: the site's name)")
319
320
  p_ws.add_argument("--depth-km", type=float, default=6.0, help="associate: the hypocentral depth the location is made under, km (declared)")
320
321
  p_ws.add_argument("--rms-tol", type=float, default=0.15, help="associate: the RMS residual an event must fit within, s")
321
322
  p_ws.add_argument("--wells", type=str, nargs="+", default=None, help="add-site: well ids of this workspace")
@@ -399,6 +400,9 @@ def main(argv=None) -> int:
399
400
 
400
401
  p_osdu = sub.add_parser("osdu", help="the OSDU-shaped export: the self-test on a labelled site")
401
402
  p_osdu.add_argument("--action", choices=["selftest"], default="selftest")
403
+ p_qml = sub.add_parser("quakeml", help="the QuakeML 1.2 catalogue export: the self-test on the association leg's labelled scene")
404
+ p_qml.add_argument("--action", choices=["selftest"], default="selftest")
405
+ p_qml.add_argument("--out", type=str, default=None, help="where to leave the self-test's file (default: a temporary folder)")
402
406
  p_sra = sub.add_parser("sra", help="the Seismicity Response Area packet: the self-test on a labelled scene")
403
407
  p_sra.add_argument("--action", choices=["selftest"], default="selftest")
404
408
  p_sra.add_argument("--json", action="store_true")
@@ -1496,6 +1500,14 @@ def main(argv=None) -> int:
1496
1500
  return 0
1497
1501
  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)"))
1498
1502
  return 0
1503
+ elif a.cmd == "quakeml":
1504
+ from . import quakeml as _Q
1505
+ r = _Q.selftest(a.out)
1506
+ print(f"quakeml selftest [{r['label']}]: {r['status']}")
1507
+ for k, v in r["checks"].items():
1508
+ print(f" {'ok ' if v else 'BAD'} {k}")
1509
+ print(_Q.report_text({**r, 'status': 'WRITTEN' if r['status'] == 'OK' else 'FAILED', 'agency': 'SIMULATION_SELF_TEST site', 'refused': 2}))
1510
+ return 0 if r["status"] == "OK" else 1
1499
1511
  elif a.cmd == "osdu":
1500
1512
  from . import osdu as _O
1501
1513
  r = _O.selftest()
@@ -1753,6 +1765,12 @@ def main(argv=None) -> int:
1753
1765
  countries=a.country, operator_org_id=a.operator_org, out_dir=a.out, actor=a.actor)
1754
1766
  print(_osdu_text(r)); v = r["validate"]
1755
1767
  print(f" structure: {'ok' if v['ok'] else 'PROBLEMS'} ({v['records']} record(s))" + ('' if v['ok'] else '; ' + '; '.join(v['problems'][:5]))); return 0
1768
+ elif a.action == "quakeml-export":
1769
+ from .quakeml import report_text as _qml_text
1770
+ if not a.site:
1771
+ raise SystemExit("quakeml-export needs --site <id>")
1772
+ r = ws.quakeml_export(a.site, agency=a.agency, out=a.out, actor=a.actor)
1773
+ print(_qml_text(r)); return 0 if r["status"] == "WRITTEN" else 1
1756
1774
  elif a.action == "seismic-dataset":
1757
1775
  r = ws.write_seismic_dataset_report(actor=a.actor)
1758
1776
  inv = r["inventory"]
@@ -4473,6 +4473,7 @@ def section_bb_supervisor(tmp: str) -> None:
4473
4473
  "BB4 a code that is neither 0 nor 86 is a death, not a decision: the launcher reads the operator's answer from records/auto_restart.flag, "
4474
4474
  "tries at most five times in a row so a panel that crashes while starting hands over the error instead of flickering all night, and reads "
4475
4475
  "that file outside an if-block because a redirect inside parentheses is parsed before the block runs")
4476
+ cmd_real = cmd # the launcher as shipped (template or installed file), before the stand-in below
4476
4477
  if cmd is None: # a Linux kit: BB6 needs a Windows launcher text to build its good and bad kits from
4477
4478
  cmd = ("@echo off\ntitle GEA - Operations Control Panel\n:gea_run\npython\\python.exe -m gea serve\nset GEA_RC=%ERRORLEVEL%\n"
4478
4479
  "if \"%GEA_RC%\"==\"86\" goto gea_run\n:gea_read_flag\nset /p GEA_AUTO=<\"%GEA_WORKSPACE%\\records\\auto_restart.flag\"\n"
@@ -4636,12 +4637,13 @@ def section_bb_supervisor(tmp: str) -> None:
4636
4637
  ok('"--no-browser", action="store_true"' in main_src
4637
4638
  and "svc.open_browser = not a.no_browser" in main_src and "svc.console = True" in main_src
4638
4639
  and hasattr(Service, "_open_browser_later") and Service(wsp, host="127.0.0.1", port=0, scheduler=False).open_browser is False
4639
- and (cmd is None or ("%GEA_BROWSER%" in cmd and "timeout /t 4" not in cmd and 'if "%1"=="--no-browser" set GEA_BROWSER=--no-browser' in cmd
4640
- and "set GEA_BROWSER=--no-browser" in cmd))
4640
+ and (cmd_real is None or ("%GEA_BROWSER%" in cmd_real and "timeout /t 4" not in cmd_real
4641
+ and 'if "%1"=="--no-browser" set GEA_BROWSER=--no-browser' in cmd_real and "set GEA_BROWSER=--no-browser" in cmd_real))
4641
4642
  and (sh is None or ("$browser" in sh and 'browser="--no-browser"' in sh)),
4642
4643
  "BB13 `gea serve` opens the control panel in the browser itself once its port is listening, and the launchers no longer race it with a "
4643
4644
  "browser of their own - opened first, it showed a failure page from the second before the server was up; a scheduled start passes "
4644
- "--no-browser through, a restart from the panel does not open a second page, and the service run in-process by this gate opens nothing")
4645
+ "--no-browser through, a restart from the panel does not open a second page, and the service run in-process by this gate opens nothing; a Linux "
4646
+ "kit, which carries no Windows launcher, is judged on its shell launcher alone")
4645
4647
 
4646
4648
 
4647
4649
  def section_bc_sra(tmp: str) -> None:
@@ -4965,6 +4967,143 @@ def section_be_osdu(tmp: str) -> None:
4965
4967
  "file is a named gap; the structure validates; every export is audited; `gea osdu` runs the self-test from the command line")
4966
4968
 
4967
4969
 
4970
+
4971
+ def section_bf_quakeml(tmp: str) -> None:
4972
+ """Section BF - the QuakeML catalogue export: the site's own events in the exchange format a regulator's
4973
+ catalogue tool reads, built to the Basic Event Description schema, automatic and preliminary in the
4974
+ schema's own words, with no magnitude and with the catalogue's standing as a comment."""
4975
+ import csv as _csv
4976
+ import subprocess as _sp
4977
+ import xml.etree.ElementTree as _ET
4978
+ from . import quakeml as Q
4979
+ from . import seismic as S
4980
+ from . import seismic_assoc as SA
4981
+ from .workspace import Workspace, WorkspaceError
4982
+ from . import helplib as H
4983
+ pkg = Path(__file__).parent
4984
+ d = Path(tmp, "bf"); d.mkdir()
4985
+ B = lambda t: "{%s}%s" % (Q.NS_BED, t)
4986
+
4987
+ # -- the self-test on the labelled scene, read back ------------------------------------------
4988
+ r = Q.selftest(str(d / "self"))
4989
+ root = _ET.parse(r["path"]).getroot()
4990
+ origins = root.findall(".//" + B("origin"))
4991
+ o = origins[0]
4992
+ lat_u = float(o.find(B("latitude")).find(B("uncertainty")).text); lon_u = float(o.find(B("longitude")).find(B("uncertainty")).text)
4993
+ hu = float(o.find(B("originUncertainty")).find(B("horizontalUncertainty")).text)
4994
+ q = o.find(B("quality"))
4995
+ ok(r["status"] == "OK" and all(r["checks"].values()) and r["label"] == "SIMULATION_SELF_TEST"
4996
+ and root.tag == "{%s}quakeml" % Q.NS_Q and len(origins) == 2
4997
+ and 0 < lat_u < 0.1 and 0 < lon_u < 0.1 and 0 < hu < 10000 and abs(hu - max(lat_u * Q.KM_PER_DEG, 0) * 1000) < 1.0 + hu * 0.5
4998
+ and q.find(B("usedStationCount")).text == "5" and q.find(B("usedPhaseCount")).text == "5"
4999
+ and float(q.find(B("standardError")).text) < 0.05 and 0 < float(q.find(B("azimuthalGap")).text) < 180
5000
+ and 0 < float(q.find(B("minimumDistance")).text) < float(q.find(B("maximumDistance")).text) < 0.2
5001
+ and o.find(B("methodID")).text.startswith("smi:local/gea/method/") and "vp-" in o.find(B("earthModelID")).text
5002
+ and all(float(a.find(B("timeWeight")).text) == 1.0 and abs(float(a.find(B("timeResidual")).text)) < 0.15 and 0 < float(a.find(B("distance")).text) < 0.2
5003
+ for a in o.findall(B("arrival"))),
5004
+ "BF1 the self-test on the association leg's labelled scene: two events written and read back under the QuakeML 1.2 namespaces, five picks "
5005
+ "and five arrivals each, depth in metres with depthType 'operator assigned' because it was declared, the misfit region as the latitude and "
5006
+ "longitude uncertainties in degrees and the horizontal uncertainty in metres, the quality block with the counts, the RMS, the azimuthal gap "
5007
+ "and the epicentral distances in degrees, every arrival with its residual and unit weight, the method and the earth model named, every "
5008
+ "origin and pick automatic and preliminary, the catalogue's AGREES and DIFFERS as comments, the record's own FDSN codes in every waveformID, "
5009
+ "and no magnitude anywhere")
5010
+
5011
+ # -- the shape check catches what a reader would refuse ------------------------------------------
5012
+ text = Path(r["path"]).read_text(encoding="utf-8")
5013
+ broken = []
5014
+ bad1 = text.replace('publicID="smi:local/gea/SYNTHETIC/event/', 'publicID="bad id ', 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad1))))
5015
+ bad2 = text.replace("<pickID>smi:local/gea/SYNTHETIC/pick/", "<pickID>smi:local/gea/SYNTHETIC/nopick/", 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad2))))
5016
+ bad3 = text.replace("<evaluationStatus>preliminary</evaluationStatus>", "<evaluationStatus>reported</evaluationStatus>", 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad3))))
5017
+ bad4 = text.replace("<depthType>", "<depthKind>").replace("</depthType>", "</depthKind>"); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad4))))
5018
+ first_origin_end = text.index("</origin>")
5019
+ bad5 = text[:first_origin_end] + '<magnitude publicID="smi:local/gea/x/mag/1"><mag><value>2.0</value></mag></magnitude>' + text[first_origin_end:]
5020
+ broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad5))))
5021
+ bad6 = text.replace("<value>2026-03-05T11:03:", "<value>2026-03-05 11:03:", 1); broken.append(Q.validate(_ET.ElementTree(_ET.fromstring(bad6))))
5022
+ ok(Q.validate(r["path"])["status"] == "VALID_SHAPE" and all(b["status"] == "INVALID" for b in broken)
5023
+ and any("ResourceIdentifier" in p for p in broken[0]["problems"]) and any("pickID" in p for p in broken[1]["problems"])
5024
+ and any("enumeration" in p for p in broken[2]["problems"]) and any("not in the schema" in p for p in broken[3]["problems"])
5025
+ and any("magnitude" in p for p in broken[4]["problems"]) and any("dateTime" in p for p in broken[5]["problems"])
5026
+ and all(Q.RID_PATTERN.match(x) for x in (Q.rid("Pad 3", "event", "assoc 1"), Q.rid("a", "x", authority="ab"), Q.rid("s", "comment", "a/b")))
5027
+ and not Q.RID_PATTERN.match("smi:ab/x") and not Q.RID_PATTERN.match("urn:local/x"),
5028
+ "BF2 the shape check is the first thing a QuakeML reader does, done here: an identifier off the schema's pattern, an arrival whose pick is "
5029
+ "not in its event, an evaluation status outside the enumeration, a child element the schema does not know, a magnitude this program did "
5030
+ "not estimate, and a time that is not an xs:dateTime are each named as a problem; and the identifier builder cleans a site name with a "
5031
+ "space and a short authority to the pattern")
5032
+
5033
+ # -- through the workspace and the command line ---------------------------------------------------
5034
+ sc = SA.synthetic_scene()
5035
+ for sid, tr in sc["traces"].items():
5036
+ S.write_mseed([tr], str(d / f"{sid}.mseed"))
5037
+ cat = d / "cat.csv"
5038
+ with open(cat, "w", newline="") as f:
5039
+ w = _csv.writer(f); w.writerow(["EventID", "Origin Time", "Local Magnitude", "Latitude", "Longitude", "Depth"])
5040
+ for c in sc["catalog"]:
5041
+ w.writerow([c["event_id"], c["time"], c["magnitude"], c["lat"], c["lon"], 6.0])
5042
+ ws = Workspace.create(str(d / "ws"), "Pad 3", actor="tester")
5043
+ 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"]
5044
+ for sid, st in sc["stations"].items()]
5045
+ ws.add_site("QML block", seismic=ids, client="Acme", actor="tester")
5046
+ try:
5047
+ ws.quakeml_export("QML-block"); early = False
5048
+ except WorkspaceError as e:
5049
+ early = "associate" in str(e)
5050
+ run = lambda *a: _sp.run([sys.executable, "-m", "gea", "workspace", "--path", ws.path, "--actor", "tester", *a], capture_output=True, text=True)
5051
+ r1 = run("--action", "associate", "--site", "QML-block", "--catalog", str(cat))
5052
+ aj = json.loads(Path(ws.path, "reports", "sites", "QML-block", "association.json").read_text(encoding="utf-8"))
5053
+ r2 = run("--action", "quakeml-export", "--site", "QML-block", "--agency", "Acme Operating")
5054
+ xml_path = Path(ws.path, "reports", "sites", "QML-block", "quakeml", "QML-block_events.xml")
5055
+ summ = json.loads(Path(ws.path, "reports", "sites", "QML-block", "quakeml", "export_summary.json").read_text(encoding="utf-8"))
5056
+ root2 = _ET.parse(str(xml_path)).getroot()
5057
+ agencies = {c.find(B("agencyID")).text for c in root2.iter(B("creationInfo"))}
5058
+ wids = root2.findall(".//" + B("waveformID"))
5059
+ audit = Path(ws.path, "records", "audit.jsonl").read_text(encoding="utf-8")
5060
+ r3 = _sp.run([sys.executable, "-m", "gea", "quakeml", "--out", str(d / "cli")], capture_output=True, text=True)
5061
+ help_md = (pkg / "help" / "quakeml.md").read_text(encoding="utf-8")
5062
+ ok(early and r1.returncode == 0 and all((v.get("stream") or {}).get("station") == sid for sid, v in aj["stations"].items())
5063
+ and r2.returncode == 0 and "WRITTEN - 2 event(s), 10 pick(s), 10 arrival(s)" in r2.stdout and "VALID_SHAPE" in r2.stdout and "gap:" not in r2.stdout
5064
+ and xml_path.is_file() and summ["status"] == "WRITTEN" and summ["schema"] == "QuakeML-BED-1.2" and summ["n_events"] == 2 and not summ["gaps"]
5065
+ and summ["validate"]["status"] == "VALID_SHAPE" and summ["association_generated_utc"] == aj["generated_utc"]
5066
+ and agencies == {"Acme Operating"} and len(wids) == 10 and all(w.get("networkCode") == "XX" and w.get("channelCode") == "HHZ" for w in wids)
5067
+ and "site.quakeml_export" in audit and r3.returncode == 0 and "SIMULATION_SELF_TEST" in r3.stdout and (d / "cli" / "SYNTHETIC_events.xml").is_file()
5068
+ and H.VIEW_TOPIC.get("quakeml", "seismic") == "seismic" and any(t["topic"] == "quakeml" for t in H.topics()) and not H.check("quakeml")
5069
+ and "operator assigned" in help_md and "preliminary" in help_md and "magnitude" in help_md.split("will not call a measurement")[1],
5070
+ "BF3 `--action quakeml-export` refuses a site that has not been associated, and after `--action associate` (which now keeps each record's "
5071
+ "FDSN codes) writes reports/sites/<site>/quakeml/<site>_events.xml with the agency the operator named in every creationInfo, the record's "
5072
+ "codes in every waveformID, a summary beside it that carries the association's own timestamp, and audits it; `gea quakeml` runs the "
5073
+ "labelled scene from the command line; the help page names the declared depth, the preliminary status and the magnitude it does not write")
5074
+
5075
+ # -- a station without codes or position is named as a gap, never invented ------------------------
5076
+ doc = {"association": {"events": [{"id": "e1", "origin_utc": "2026-03-05T11:03:00.000Z", "lat": 31.95, "lon": -102.25, "depth_km": 6.0,
5077
+ "depth_basis": "declared", "rms_s": 0.02, "n_stations": 3, "stations": ["A", "B", "C"], "dropped": [],
5078
+ "picks": {"A": {"time": "2026-03-05T11:03:01.000Z", "snr": 10.0, "residual_s": 0.01},
5079
+ "B": {"time": "2026-03-05T11:03:01.200Z", "snr": 9.0, "residual_s": -0.01},
5080
+ "C": {"time": "2026-03-05T11:03:01.400Z", "snr": 8.0, "residual_s": 0.0}},
5081
+ "region_km": {"extent_east_km": 1.0, "extent_north_km": 2.0, "nodes": 8, "reaches_grid_edge": False},
5082
+ "magnitude": None, "magnitude_basis": "not estimated",
5083
+ "model": {"vp_km_s": 5.8, "depth_km": 6.0, "pick_sigma_s": 0.05, "basis": "flat earth"}}],
5084
+ "refused": [], "n_unused_picks": 0, "model": {}, "basis": "b", "not_a_measurement": []}, "catalogue": None}
5085
+ stations = {"A": {"id": "A", "lat": 31.99, "lon": -102.30, "stream": {"network": "TX", "station": "PB01", "location": "00", "channel": "HHZ"}},
5086
+ "B": {"id": "B", "lat": 31.90, "lon": -102.20}, # no codes
5087
+ "C": {"id": "C"}} # no codes, no position
5088
+ b = Q.build(doc, {"id": "s", "name": "Gap site"}, stations, version="0.0")
5089
+ out = Path(d, "gaps.xml"); Q.write(b["tree"], str(out))
5090
+ v = Q.validate(str(out))
5091
+ rootg = _ET.parse(str(out)).getroot()
5092
+ ws_ids = {w.get("stationCode"): w for w in rootg.findall(".//" + B("waveformID"))}
5093
+ arrs = {a.find(B("pickID")).text.rsplit("/", 1)[1]: a for a in rootg.findall(".//" + B("arrival"))}
5094
+ og = rootg.find(".//" + B("origin"))
5095
+ ok(v["status"] == "VALID_SHAPE" and len(b["gaps"]) == 3 and sum("no network and station code" in g for g in b["gaps"]) == 2
5096
+ and sum("no position" in g for g in b["gaps"]) == 1
5097
+ and ws_ids["PB01"].get("networkCode") == "TX" and ws_ids["PB01"].get("locationCode") == "00"
5098
+ and ws_ids["B"].get("networkCode") == "XX" and ws_ids["C"].get("networkCode") == "XX"
5099
+ and arrs["A"].find(B("azimuth")) is not None and arrs["C"].find(B("azimuth")) is None and arrs["C"].find(B("distance")) is None
5100
+ and abs(float(og.find(B("originUncertainty")).find(B("horizontalUncertainty")).text) - 1000.0) < 1e-6
5101
+ and abs(float(og.find(B("latitude")).find(B("uncertainty")).text) - 1.0 / Q.KM_PER_DEG) < 1e-6
5102
+ and og.find(B("quality")).find(B("azimuthalGap")) is not None,
5103
+ "BF4 a station whose record carried no network and station code goes out under the test network XX with its own id and is named as a gap; "
5104
+ "a station with no position gets a pick but an arrival without azimuth or distance and is named as a gap; neither is invented; the misfit "
5105
+ "region of 1 km by 2 km becomes a 1000 m horizontal uncertainty and a latitude uncertainty of one kilometre in degrees")
5106
+
4968
5107
  def main() -> int:
4969
5108
  print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
4970
5109
  # a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
@@ -5025,6 +5164,7 @@ def main() -> int:
5025
5164
  section_bc_sra(tmp)
5026
5165
  section_bd_association(tmp)
5027
5166
  section_be_osdu(tmp)
5167
+ section_bf_quakeml(tmp)
5028
5168
  if _FAILS:
5029
5169
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
5030
5170
  for f in _FAILS:
@@ -0,0 +1,11 @@
1
+ # The QuakeML catalogue export
2
+
3
+ > The site's own events - what its stations located together - in QuakeML 1.2, the exchange format a regulator's catalogue tool, a university network's review desk and every seismological package read.
4
+
5
+ **The command**: `gea workspace --path C:\site --action quakeml-export --site <id> [--agency "<name>"] [--out <file.xml>]`, after `--action associate` has run on the site (the export is built from `reports/sites/<site>/association.json`). `--agency` is the agencyID written in every creationInfo; the site's name by default. `gea quakeml` runs the self-test on the association leg's labelled scene and leaves the file where `--out` says.
6
+
7
+ **What it writes**: `reports/sites/<site>/quakeml/<site>_events.xml` - a `q:quakeml` document in the QuakeML 1.2 namespaces, built to the Basic Event Description schema (QuakeML-BED-1.2.xsd): one `event` per associated event with its `origin` (time; latitude and longitude with the misfit region's half-extent as their uncertainty in degrees; depth in metres with `depthType` "operator assigned", because the depth was declared; `methodID` and `earthModelID` naming the grid search and the one-velocity flat-earth model; `quality` with the station and phase counts, the RMS as standardError, the azimuthal gap and the epicentral distances in degrees; `originUncertainty` with the misfit region in metres), one `pick` per station that made the event (the onset time with the pick uncertainty, the record's own FDSN codes in `waveformID`, phaseHint P), and one `arrival` per pick on the origin (azimuth, distance, the residual, weight 1). Every origin and pick is `evaluationMode` automatic and `evaluationStatus` preliminary. The catalogue's standing - AGREES, DIFFERS with both positions, NOT IN CATALOGUE - is a comment on the event with the catalogue's own event id. `export_summary.json` beside it carries the counts, the gaps and the shape check. Every export is in `records/audit.jsonl`.
8
+
9
+ **The number to check**: `shape VALID_SHAPE` on the second line - the first checks a QuakeML reader makes, made here first: the namespaces; every publicID to the schema's ResourceIdentifier pattern and used once; every child element one the schema allows for its parent; time, latitude and longitude on every origin; time and waveformID on every pick; every arrival's pickID a pick of the same event; every event's preferredOriginID one of its origins; every enumerated value in its enumeration. A `gap:` line names a station whose record carried no network and station code (written under the test network XX with the station id) or no position (its arrival carries no azimuth or distance).
10
+
11
+ **What this page will not call a measurement**: a magnitude - the association does not estimate one, the file carries no `magnitude` element, and a comment on every event says why. The catalogue's events as this site's - they are the catalogue's to publish; the file names them in comments and carries only what the site's own stations located. A reviewed or final event - a person has not looked at these, and the file says automatic and preliminary in the schema's own words. A located depth - it was declared, and `depthType` says so. A full schema validation - the shape check is what a reader reads first, not a validator; the file was validated against the published XSD when this export was written, and the check here holds it to that shape.
@@ -40,6 +40,7 @@ INDEX: List[tuple] = [
40
40
  ('notifications', 'Notifications', 'rules, channels, the delivery log'),
41
41
  ('sra', 'The Seismicity Response Area', 'the packet an operator inside an SRA puts in front of the Commission'),
42
42
  ('osdu', 'The OSDU-shaped export', 'the site as the manifest an operator\'s data platform loads'),
43
+ ('quakeml', 'The QuakeML catalogue export', 'the site\'s own events in the format the regulator\'s catalogue tools read'),
43
44
  ('doctor', 'Which code is running', 'gea doctor and the serve start-up gate'),
44
45
  ('audit-update', 'Audit / Update', 'the whole audit log with filters, the program against PyPI, every report against its source'),
45
46
  ('upkeep', 'Upkeep', 'housekeeping, sessions, the TLS proxy, the load test'),