gea-program 0.60.0__tar.gz → 0.62.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 (372) hide show
  1. {gea_program-0.60.0 → gea_program-0.62.0}/PKG-INFO +14 -1
  2. {gea_program-0.60.0 → gea_program-0.62.0}/README.md +13 -0
  3. {gea_program-0.60.0 → gea_program-0.62.0}/gea/__init__.py +1 -1
  4. {gea_program-0.60.0 → gea_program-0.62.0}/gea/__main__.py +53 -0
  5. {gea_program-0.60.0 → gea_program-0.62.0}/gea/acceptance_tests.py +356 -1
  6. gea_program-0.62.0/gea/casing_design.py +797 -0
  7. {gea_program-0.60.0 → gea_program-0.62.0}/gea/declarations.py +72 -1
  8. {gea_program-0.60.0 → gea_program-0.62.0}/gea/dossier.py +12 -1
  9. gea_program-0.62.0/gea/help/casing.md +19 -0
  10. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/declare.md +1 -1
  11. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/interval.md +1 -1
  12. {gea_program-0.60.0 → gea_program-0.62.0}/gea/helplib.py +2 -1
  13. {gea_program-0.60.0 → gea_program-0.62.0}/gea/hydraulics.py +37 -0
  14. {gea_program-0.60.0 → gea_program-0.62.0}/gea/interval.py +27 -11
  15. {gea_program-0.60.0 → gea_program-0.62.0}/gea/service.py +14 -0
  16. {gea_program-0.60.0 → gea_program-0.62.0}/gea/web/app.html +63 -1
  17. {gea_program-0.60.0 → gea_program-0.62.0}/gea/workspace.py +8 -1
  18. {gea_program-0.60.0 → gea_program-0.62.0}/gea_program.egg-info/PKG-INFO +14 -1
  19. {gea_program-0.60.0 → gea_program-0.62.0}/gea_program.egg-info/SOURCES.txt +2 -0
  20. {gea_program-0.60.0 → gea_program-0.62.0}/pyproject.toml +1 -1
  21. {gea_program-0.60.0 → gea_program-0.62.0}/LICENSE +0 -0
  22. {gea_program-0.60.0 → gea_program-0.62.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
  23. {gea_program-0.60.0 → gea_program-0.62.0}/gea/accuracy_statement.py +0 -0
  24. {gea_program-0.60.0 → gea_program-0.62.0}/gea/alarm_engine.py +0 -0
  25. {gea_program-0.60.0 → gea_program-0.62.0}/gea/alerts.py +0 -0
  26. {gea_program-0.60.0 → gea_program-0.62.0}/gea/backup.py +0 -0
  27. {gea_program-0.60.0 → gea_program-0.62.0}/gea/bench.py +0 -0
  28. {gea_program-0.60.0 → gea_program-0.62.0}/gea/blind_harness.py +0 -0
  29. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
  30. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
  31. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
  32. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
  33. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
  34. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
  35. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
  36. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
  37. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
  38. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
  39. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
  40. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
  41. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
  42. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
  43. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
  44. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
  45. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
  46. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
  47. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
  48. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
  49. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
  50. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
  51. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
  52. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
  53. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
  54. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
  55. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
  56. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
  57. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
  58. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
  59. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
  60. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
  61. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
  62. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
  63. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
  64. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
  65. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
  66. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
  67. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
  68. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
  69. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
  70. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
  71. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
  72. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
  73. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
  74. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
  75. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
  76. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
  77. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
  78. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
  79. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
  80. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
  81. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
  82. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
  83. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
  84. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
  85. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
  86. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
  87. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
  88. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
  89. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
  90. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
  91. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
  92. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
  93. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
  94. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
  95. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
  96. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/med_160_sapropels.txt +0 -0
  97. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
  98. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
  99. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
  100. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
  101. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
  102. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
  103. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
  104. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
  105. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
  106. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
  107. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
  108. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
  109. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
  110. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
  111. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
  112. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
  113. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
  114. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
  115. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
  116. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
  117. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
  118. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
  119. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
  120. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
  121. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
  122. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
  123. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
  124. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
  125. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
  126. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
  127. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
  128. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
  129. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
  130. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
  131. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
  132. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
  133. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog_map.py +0 -0
  134. {gea_program-0.60.0 → gea_program-0.62.0}/gea/catalog_register.py +0 -0
  135. {gea_program-0.60.0 → gea_program-0.62.0}/gea/certificates.py +0 -0
  136. {gea_program-0.60.0 → gea_program-0.62.0}/gea/cli.py +0 -0
  137. {gea_program-0.60.0 → gea_program-0.62.0}/gea/client_reports.py +0 -0
  138. {gea_program-0.60.0 → gea_program-0.62.0}/gea/collision.py +0 -0
  139. {gea_program-0.60.0 → gea_program-0.62.0}/gea/config_versioning.py +0 -0
  140. {gea_program-0.60.0 → gea_program-0.62.0}/gea/conformance.py +0 -0
  141. {gea_program-0.60.0 → gea_program-0.62.0}/gea/control_room.py +0 -0
  142. {gea_program-0.60.0 → gea_program-0.62.0}/gea/correlation.py +0 -0
  143. {gea_program-0.60.0 → gea_program-0.62.0}/gea/dashboard.py +0 -0
  144. {gea_program-0.60.0 → gea_program-0.62.0}/gea/decline.py +0 -0
  145. {gea_program-0.60.0 → gea_program-0.62.0}/gea/deviation.py +0 -0
  146. {gea_program-0.60.0 → gea_program-0.62.0}/gea/dlis.py +0 -0
  147. {gea_program-0.60.0 → gea_program-0.62.0}/gea/dnp3.py +0 -0
  148. {gea_program-0.60.0 → gea_program-0.62.0}/gea/doctor.py +0 -0
  149. {gea_program-0.60.0 → gea_program-0.62.0}/gea/dossier_verify.py +0 -0
  150. {gea_program-0.60.0 → gea_program-0.62.0}/gea/downhole_engine.py +0 -0
  151. {gea_program-0.60.0 → gea_program-0.62.0}/gea/drift_monitor.py +0 -0
  152. {gea_program-0.60.0 → gea_program-0.62.0}/gea/drill_string.py +0 -0
  153. {gea_program-0.60.0 → gea_program-0.62.0}/gea/earth_model.py +0 -0
  154. {gea_program-0.60.0 → gea_program-0.62.0}/gea/economics.py +0 -0
  155. {gea_program-0.60.0 → gea_program-0.62.0}/gea/ed25519.py +0 -0
  156. {gea_program-0.60.0 → gea_program-0.62.0}/gea/edr.py +0 -0
  157. {gea_program-0.60.0 → gea_program-0.62.0}/gea/elevation.py +0 -0
  158. {gea_program-0.60.0 → gea_program-0.62.0}/gea/etp.py +0 -0
  159. {gea_program-0.60.0 → gea_program-0.62.0}/gea/etp12_schemas.json +0 -0
  160. {gea_program-0.60.0 → gea_program-0.62.0}/gea/example_register_map.json +0 -0
  161. {gea_program-0.60.0 → gea_program-0.62.0}/gea/fat_sat.py +0 -0
  162. {gea_program-0.60.0 → gea_program-0.62.0}/gea/files.py +0 -0
  163. {gea_program-0.60.0 → gea_program-0.62.0}/gea/fixtures/README.md +0 -0
  164. {gea_program-0.60.0 → gea_program-0.62.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
  165. {gea_program-0.60.0 → gea_program-0.62.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
  166. {gea_program-0.60.0 → gea_program-0.62.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
  167. {gea_program-0.60.0 → gea_program-0.62.0}/gea/fixtures/ne_110m_coastline.json +0 -0
  168. {gea_program-0.60.0 → gea_program-0.62.0}/gea/follower.py +0 -0
  169. {gea_program-0.60.0 → gea_program-0.62.0}/gea/forward_model.py +0 -0
  170. {gea_program-0.60.0 → gea_program-0.62.0}/gea/frac_hits.py +0 -0
  171. {gea_program-0.60.0 → gea_program-0.62.0}/gea/friction_priors.py +0 -0
  172. {gea_program-0.60.0 → gea_program-0.62.0}/gea/gamma.py +0 -0
  173. {gea_program-0.60.0 → gea_program-0.62.0}/gea/gauge_aging.py +0 -0
  174. {gea_program-0.60.0 → gea_program-0.62.0}/gea/gauge_specs.py +0 -0
  175. {gea_program-0.60.0 → gea_program-0.62.0}/gea/geodesy.py +0 -0
  176. {gea_program-0.60.0 → gea_program-0.62.0}/gea/geomag.py +0 -0
  177. {gea_program-0.60.0 → gea_program-0.62.0}/gea/gravity_reference.py +0 -0
  178. {gea_program-0.60.0 → gea_program-0.62.0}/gea/hartip.py +0 -0
  179. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/IT_SECURITY.md +0 -0
  180. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/TESTER_GUIDE.md +0 -0
  181. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/alarms.md +0 -0
  182. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/alerts.md +0 -0
  183. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/audit-update.md +0 -0
  184. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/backup.md +0 -0
  185. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/catalog.md +0 -0
  186. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/charts.md +0 -0
  187. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/collision.md +0 -0
  188. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/company-sign-in.md +0 -0
  189. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/conformance.md +0 -0
  190. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/control-room.md +0 -0
  191. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/data-in.md +0 -0
  192. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/decline.md +0 -0
  193. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/dlis.md +0 -0
  194. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/doctor.md +0 -0
  195. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/dossier.md +0 -0
  196. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/drift.md +0 -0
  197. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/drillstring.md +0 -0
  198. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/economics.md +0 -0
  199. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/edr.md +0 -0
  200. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/etp.md +0 -0
  201. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/field-ports.md +0 -0
  202. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/files.md +0 -0
  203. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/frac-hits.md +0 -0
  204. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/historian.md +0 -0
  205. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/https.md +0 -0
  206. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/hydraulics.md +0 -0
  207. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/instruments.md +0 -0
  208. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/invoice-check.md +0 -0
  209. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/map.md +0 -0
  210. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/month-end.md +0 -0
  211. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/notifications.md +0 -0
  212. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/opseis.md +0 -0
  213. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/osdu.md +0 -0
  214. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/pad-summary.md +0 -0
  215. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/patches.md +0 -0
  216. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/penetrator.md +0 -0
  217. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/plan.md +0 -0
  218. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/pore-pressure.md +0 -0
  219. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/ppdm.md +0 -0
  220. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/quakeml.md +0 -0
  221. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/quality.md +0 -0
  222. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/report-history.md +0 -0
  223. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/rig-time.md +0 -0
  224. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/seedlink.md +0 -0
  225. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/seismic.md +0 -0
  226. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/setup.md +0 -0
  227. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/simulation.md +0 -0
  228. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/site.md +0 -0
  229. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/sites.md +0 -0
  230. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/slip.md +0 -0
  231. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/soak.md +0 -0
  232. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/sra.md +0 -0
  233. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/start.md +0 -0
  234. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/structure.md +0 -0
  235. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/tools.md +0 -0
  236. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/torque-drag.md +0 -0
  237. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/transients.md +0 -0
  238. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/upkeep.md +0 -0
  239. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/vibration.md +0 -0
  240. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/well-report.md +0 -0
  241. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/well-tests.md +0 -0
  242. {gea_program-0.60.0 → gea_program-0.62.0}/gea/help/well3d.md +0 -0
  243. {gea_program-0.60.0 → gea_program-0.62.0}/gea/housekeeping.py +0 -0
  244. {gea_program-0.60.0 → gea_program-0.62.0}/gea/inverse_engine.py +0 -0
  245. {gea_program-0.60.0 → gea_program-0.62.0}/gea/invoice_check.py +0 -0
  246. {gea_program-0.60.0 → gea_program-0.62.0}/gea/jobs.py +0 -0
  247. {gea_program-0.60.0 → gea_program-0.62.0}/gea/live_ports.py +0 -0
  248. {gea_program-0.60.0 → gea_program-0.62.0}/gea/loadtest.py +0 -0
  249. {gea_program-0.60.0 → gea_program-0.62.0}/gea/matplotlib_demo.py +0 -0
  250. {gea_program-0.60.0 → gea_program-0.62.0}/gea/modbus.py +0 -0
  251. {gea_program-0.60.0 → gea_program-0.62.0}/gea/modbus_rtu.py +0 -0
  252. {gea_program-0.60.0 → gea_program-0.62.0}/gea/model_card.py +0 -0
  253. {gea_program-0.60.0 → gea_program-0.62.0}/gea/month_end.py +0 -0
  254. {gea_program-0.60.0 → gea_program-0.62.0}/gea/mqtt_port.py +0 -0
  255. {gea_program-0.60.0 → gea_program-0.62.0}/gea/notify.py +0 -0
  256. {gea_program-0.60.0 → gea_program-0.62.0}/gea/oidc.py +0 -0
  257. {gea_program-0.60.0 → gea_program-0.62.0}/gea/oidc_mock.py +0 -0
  258. {gea_program-0.60.0 → gea_program-0.62.0}/gea/op_seismicity.py +0 -0
  259. {gea_program-0.60.0 → gea_program-0.62.0}/gea/opcua_port.py +0 -0
  260. {gea_program-0.60.0 → gea_program-0.62.0}/gea/operator_app.py +0 -0
  261. {gea_program-0.60.0 → gea_program-0.62.0}/gea/osdu.py +0 -0
  262. {gea_program-0.60.0 → gea_program-0.62.0}/gea/pad_summary.py +0 -0
  263. {gea_program-0.60.0 → gea_program-0.62.0}/gea/patches.py +0 -0
  264. {gea_program-0.60.0 → gea_program-0.62.0}/gea/pdf_report.py +0 -0
  265. {gea_program-0.60.0 → gea_program-0.62.0}/gea/penetrator.py +0 -0
  266. {gea_program-0.60.0 → gea_program-0.62.0}/gea/permits.py +0 -0
  267. {gea_program-0.60.0 → gea_program-0.62.0}/gea/piwebapi.py +0 -0
  268. {gea_program-0.60.0 → gea_program-0.62.0}/gea/pore_pressure.py +0 -0
  269. {gea_program-0.60.0 → gea_program-0.62.0}/gea/ports.py +0 -0
  270. {gea_program-0.60.0 → gea_program-0.62.0}/gea/ppdm.py +0 -0
  271. {gea_program-0.60.0 → gea_program-0.62.0}/gea/profile_catalog.py +0 -0
  272. {gea_program-0.60.0 → gea_program-0.62.0}/gea/project.py +0 -0
  273. {gea_program-0.60.0 → gea_program-0.62.0}/gea/qt6_downhole_app.py +0 -0
  274. {gea_program-0.60.0 → gea_program-0.62.0}/gea/quakeml.py +0 -0
  275. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reconciler.py +0 -0
  276. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
  277. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
  278. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/WMM2025.COF +0 -0
  279. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
  280. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/eia_prices.provenance.json +0 -0
  281. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
  282. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/iscwsa_rev4_example.json +0 -0
  283. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/iscwsa_rev5_example.json +0 -0
  284. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
  285. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
  286. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/libmseed_steim.provenance.json +0 -0
  287. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
  288. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
  289. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
  290. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/mahoning_example.json +0 -0
  291. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/mud_motor_specs.json +0 -0
  292. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/owsg_tool_models.json +0 -0
  293. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/penetrator_ratings.json +0 -0
  294. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/seismicity_protocols.json +0 -0
  295. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/tool_sources.json +0 -0
  296. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/us_outline.json +0 -0
  297. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/us_outline.provenance.json +0 -0
  298. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_3d.provenance.json +0 -0
  299. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
  300. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
  301. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
  302. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_surfaces.json.gz +0 -0
  303. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
  304. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
  305. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
  306. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
  307. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
  308. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
  309. {gea_program-0.60.0 → gea_program-0.62.0}/gea/reference/volve_well_picks.csv.gz +0 -0
  310. {gea_program-0.60.0 → gea_program-0.62.0}/gea/report_history.py +0 -0
  311. {gea_program-0.60.0 → gea_program-0.62.0}/gea/rig_state.py +0 -0
  312. {gea_program-0.60.0 → gea_program-0.62.0}/gea/rock_inventory.py +0 -0
  313. {gea_program-0.60.0 → gea_program-0.62.0}/gea/sample_record.py +0 -0
  314. {gea_program-0.60.0 → gea_program-0.62.0}/gea/sample_well_profile.csv +0 -0
  315. {gea_program-0.60.0 → gea_program-0.62.0}/gea/sbom.py +0 -0
  316. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seedlink.py +0 -0
  317. {gea_program-0.60.0 → gea_program-0.62.0}/gea/segy.py +0 -0
  318. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic.py +0 -0
  319. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_array.py +0 -0
  320. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_assoc.py +0 -0
  321. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_detect.py +0 -0
  322. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_film.py +0 -0
  323. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_harmonic.py +0 -0
  324. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_qc.py +0 -0
  325. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_response.py +0 -0
  326. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_signature.py +0 -0
  327. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_track.py +0 -0
  328. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_uncertainty.py +0 -0
  329. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_unlisted.py +0 -0
  330. {gea_program-0.60.0 → gea_program-0.62.0}/gea/seismic_watch.py +0 -0
  331. {gea_program-0.60.0 → gea_program-0.62.0}/gea/sensor_swap.py +0 -0
  332. {gea_program-0.60.0 → gea_program-0.62.0}/gea/series_cache.py +0 -0
  333. {gea_program-0.60.0 → gea_program-0.62.0}/gea/service_life.py +0 -0
  334. {gea_program-0.60.0 → gea_program-0.62.0}/gea/setup_checklist.py +0 -0
  335. {gea_program-0.60.0 → gea_program-0.62.0}/gea/shapefile.py +0 -0
  336. {gea_program-0.60.0 → gea_program-0.62.0}/gea/shell.py +0 -0
  337. {gea_program-0.60.0 → gea_program-0.62.0}/gea/shut_in.py +0 -0
  338. {gea_program-0.60.0 → gea_program-0.62.0}/gea/simulation.py +0 -0
  339. {gea_program-0.60.0 → gea_program-0.62.0}/gea/site_map.py +0 -0
  340. {gea_program-0.60.0 → gea_program-0.62.0}/gea/sla_report.py +0 -0
  341. {gea_program-0.60.0 → gea_program-0.62.0}/gea/slip.py +0 -0
  342. {gea_program-0.60.0 → gea_program-0.62.0}/gea/soak.py +0 -0
  343. {gea_program-0.60.0 → gea_program-0.62.0}/gea/sra.py +0 -0
  344. {gea_program-0.60.0 → gea_program-0.62.0}/gea/store_forward.py +0 -0
  345. {gea_program-0.60.0 → gea_program-0.62.0}/gea/strata_join.py +0 -0
  346. {gea_program-0.60.0 → gea_program-0.62.0}/gea/structure.py +0 -0
  347. {gea_program-0.60.0 → gea_program-0.62.0}/gea/supervisor.py +0 -0
  348. {gea_program-0.60.0 → gea_program-0.62.0}/gea/survey_cmd.py +0 -0
  349. {gea_program-0.60.0 → gea_program-0.62.0}/gea/survey_view.py +0 -0
  350. {gea_program-0.60.0 → gea_program-0.62.0}/gea/telemetry.py +0 -0
  351. {gea_program-0.60.0 → gea_program-0.62.0}/gea/tls.py +0 -0
  352. {gea_program-0.60.0 → gea_program-0.62.0}/gea/tool_catalog.py +0 -0
  353. {gea_program-0.60.0 → gea_program-0.62.0}/gea/tool_library.py +0 -0
  354. {gea_program-0.60.0 → gea_program-0.62.0}/gea/tool_taxonomy.py +0 -0
  355. {gea_program-0.60.0 → gea_program-0.62.0}/gea/torque_drag.py +0 -0
  356. {gea_program-0.60.0 → gea_program-0.62.0}/gea/transient.py +0 -0
  357. {gea_program-0.60.0 → gea_program-0.62.0}/gea/vibration.py +0 -0
  358. {gea_program-0.60.0 → gea_program-0.62.0}/gea/well3d.py +0 -0
  359. {gea_program-0.60.0 → gea_program-0.62.0}/gea/well_assembler.py +0 -0
  360. {gea_program-0.60.0 → gea_program-0.62.0}/gea/well_report.py +0 -0
  361. {gea_program-0.60.0 → gea_program-0.62.0}/gea/well_test_validation.py +0 -0
  362. {gea_program-0.60.0 → gea_program-0.62.0}/gea/wellplan.py +0 -0
  363. {gea_program-0.60.0 → gea_program-0.62.0}/gea/wellpos.py +0 -0
  364. {gea_program-0.60.0 → gea_program-0.62.0}/gea/wits0.py +0 -0
  365. {gea_program-0.60.0 → gea_program-0.62.0}/gea/witsml.py +0 -0
  366. {gea_program-0.60.0 → gea_program-0.62.0}/gea/xlsx_read.py +0 -0
  367. {gea_program-0.60.0 → gea_program-0.62.0}/gea_program.egg-info/dependency_links.txt +0 -0
  368. {gea_program-0.60.0 → gea_program-0.62.0}/gea_program.egg-info/entry_points.txt +0 -0
  369. {gea_program-0.60.0 → gea_program-0.62.0}/gea_program.egg-info/requires.txt +0 -0
  370. {gea_program-0.60.0 → gea_program-0.62.0}/gea_program.egg-info/top_level.txt +0 -0
  371. {gea_program-0.60.0 → gea_program-0.62.0}/setup.cfg +0 -0
  372. {gea_program-0.60.0 → gea_program-0.62.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.60.0
3
+ Version: 0.62.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
@@ -683,6 +683,19 @@ catalogue well is added only from an entry that carries a downhole gauge
683
683
  series, with a well inside it and its gauge depth; a live seismic station
684
684
  whose feed has written nothing yet is listed as having no recording yet.
685
685
 
686
+ ### Casing design and kick tolerance
687
+
688
+ Each casing string's burst, collapse and tension ratings by API TR 5C3,
689
+ from its size, weight and grade, against the loads the hole can put on it
690
+ - gas to the surface held by the shoe, an emptied string, the buoyed
691
+ weight with overpull - with the smallest safety factor and where, and the
692
+ gas kick each open hole below a shoe can take and still be shut in - the
693
+ gas a real gas at the well's pressure and temperature, with a swabbed
694
+ kick beside each. `gea
695
+ casing --example`; declared on the **Casing & kick tolerance** tab, with
696
+ `gea casing --workspace C:\site --well SWD-1 --strings ...`, or in the
697
+ declarations sheet. `gea help casing`.
698
+
686
699
  ### Hydraulics and ECD
687
700
 
688
701
  The pressures the mud makes while it circulates, from the six-speed
@@ -644,6 +644,19 @@ catalogue well is added only from an entry that carries a downhole gauge
644
644
  series, with a well inside it and its gauge depth; a live seismic station
645
645
  whose feed has written nothing yet is listed as having no recording yet.
646
646
 
647
+ ### Casing design and kick tolerance
648
+
649
+ Each casing string's burst, collapse and tension ratings by API TR 5C3,
650
+ from its size, weight and grade, against the loads the hole can put on it
651
+ - gas to the surface held by the shoe, an emptied string, the buoyed
652
+ weight with overpull - with the smallest safety factor and where, and the
653
+ gas kick each open hole below a shoe can take and still be shut in - the
654
+ gas a real gas at the well's pressure and temperature, with a swabbed
655
+ kick beside each. `gea
656
+ casing --example`; declared on the **Casing & kick tolerance** tab, with
657
+ `gea casing --workspace C:\site --well SWD-1 --strings ...`, or in the
658
+ declarations sheet. `gea help casing`.
659
+
647
660
  ### Hydraulics and ECD
648
661
 
649
662
  The pressures the mud makes while it circulates, from the six-speed
@@ -123,7 +123,7 @@ from .profile_catalog import (
123
123
  read_pangaea_txt,
124
124
  )
125
125
 
126
- __version__ = "0.60.0"
126
+ __version__ = "0.62.0"
127
127
  __all__ = [
128
128
  "aging_rate", "rate_psi_yr", "accuracy_psi",
129
129
  "Sensor", "SimulatorConfig", "DownholeEngine",
@@ -646,6 +646,25 @@ def main(argv=None) -> int:
646
646
  p_dc.add_argument("--actor", type=str, default="cli")
647
647
  p_dc.add_argument("--json", action="store_true")
648
648
 
649
+ p_cs = sub.add_parser("casing", help="casing design and kick tolerance: each string's burst, collapse and tension against the loads the hole can put on it (API TR 5C3 ratings), and the gas kick each open hole can take")
650
+ p_cs.add_argument("--workspace", type=str, default=None)
651
+ p_cs.add_argument("--well", type=str, default=None)
652
+ p_cs.add_argument("--example", action="store_true", help="the worked example (a vertical 10,000 ft well with three strings, declared for the example)")
653
+ p_cs.add_argument("--strings", type=str, default=None, help="shallowest shoe first: 'surface:13-3/8:54.5:J55:0-1500:17.5:9.0; intermediate:9-5/8:47:N80:0-7000:12.25:9.8' (what:od:lb_ft:grade:top-shoe:hole_in:mud_ppg[:wall_in])")
654
+ for k_, h_ in (("td", "the well's TD below the deepest string, ft MD"), ("td-hole", "the hole below the deepest string, in"), ("td-mw", "the mud below the deepest string, ppg"),
655
+ ("pp", "a pore pressure for the well when the Pore pressure tab has none, ppg"), ("fg", "a fracture gradient for the well when the Pore pressure tab has none, ppg"),
656
+ ("kick", "kick intensity above the mud, ppg (0.5)"), ("gas", "a fixed gas gradient, psi/ft, in place of the real gas ('none' for the real gas again)"),
657
+ ("gas-sg", "the gas's specific gravity, air 1 (0.65)"), ("surface-temp", "the temperature at the surface, F (70)"),
658
+ ("geothermal", "the geothermal gradient, F per 100 ft (1.5)"), ("backup", "burst backup gradient outside, psi/ft (0.465)"),
659
+ ("evacuation", "how far the string is emptied for collapse, 0 to 1 (1: to the shoe)"), ("df-burst", "burst design factor (1.1)"),
660
+ ("df-collapse", "collapse design factor (1.125)"), ("df-tension", "tension design factor (1.6)"), ("overpull", "overpull, klbf (100)"),
661
+ ("bha-od", "BHA outside diameter, in"), ("bha-length", "BHA length, ft"), ("dp-od", "drill pipe outside diameter, in")):
662
+ p_cs.add_argument("--" + k_, type=str, default=None, help=h_)
663
+ p_cs.add_argument("--source", type=str, default="", help="where the casing program came from")
664
+ p_cs.add_argument("--write", action="store_true")
665
+ p_cs.add_argument("--actor", type=str, default="cli")
666
+ p_cs.add_argument("--json", action="store_true")
667
+
649
668
  p_hy = sub.add_parser("hydraulics", help="the pressures the mud makes while it circulates - the standpipe, the bit, the annulus, the ECD against the pore pressure and fracture gradient, surge and swab - and the standpipe pressure the rig measured")
650
669
  p_hy.add_argument("--workspace", type=str, default=None)
651
670
  p_hy.add_argument("--well", type=str, default=None)
@@ -2598,6 +2617,40 @@ def main(argv=None) -> int:
2598
2617
  raise SystemExit(f"decline: {e.args[0] if e.args else e}")
2599
2618
  print(_json.dumps(r, default=str) if a.json else _DCA.text(r))
2600
2619
  return 0
2620
+ elif a.cmd == "casing":
2621
+ import json as _json
2622
+ from . import casing_design as _CSG
2623
+ from .cli import _safe_stdout
2624
+ from .workspace import Workspace, WorkspaceError
2625
+ _safe_stdout()
2626
+ try:
2627
+ ws = Workspace(a.workspace) if a.workspace else None
2628
+ scene = "example" if (a.example or ws is None) else "workspace"
2629
+ keys_ = {"td": "td_md_ft", "td_hole": "td_hole_in", "td_mw": "td_mw_ppg", "pp": "pp_ppg", "fg": "fg_ppg", "kick": "kick_intensity_ppg",
2630
+ "gas": "gas_gradient_psi_ft", "gas_sg": "gas_sg", "surface_temp": "surface_temp_f", "geothermal": "geothermal_f_100ft",
2631
+ "backup": "backup_gradient_psi_ft", "evacuation": "evacuation", "df_burst": "df_burst",
2632
+ "df_collapse": "df_collapse", "df_tension": "df_tension", "overpull": "overpull_klbf", "bha_od": "bha_od_in",
2633
+ "bha_length": "bha_length_ft", "dp_od": "dp_od_in"}
2634
+ vals_ = {v: getattr(a, k) for k, v in keys_.items() if getattr(a, k) is not None}
2635
+ if ws is not None and (vals_ or a.strings is not None):
2636
+ if not a.well:
2637
+ raise ValueError("--well is needed")
2638
+ c_ = _CSG.set_well(ws, a.actor, a.well, a.source, a.strings, **vals_)
2639
+ print(f"{a.well}: {len(c_.get('strings') or [])} string(s): {_CSG.strings_text(c_.get('strings') or []) or 'none declared'}")
2640
+ return 0
2641
+ if a.write:
2642
+ if ws is None:
2643
+ raise ValueError("--write needs --workspace")
2644
+ r_ = _CSG.write_report(ws, __version__, a.well, a.actor, scene)
2645
+ print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
2646
+ if r_['paths'].get('pdf'):
2647
+ print(f" printable: {r_['paths']['pdf']}")
2648
+ return 0
2649
+ r = _CSG.page(ws, scene, a.well)
2650
+ except (KeyError, ValueError, OSError, WorkspaceError) as e:
2651
+ raise SystemExit(f"casing: {e.args[0] if e.args else e}")
2652
+ print(_json.dumps(r, default=str) if a.json else _CSG.text(r))
2653
+ return 0
2601
2654
  elif a.cmd == "hydraulics":
2602
2655
  import json as _json
2603
2656
  from . import hydraulics as _HYD
@@ -13058,7 +13058,7 @@ def section_db_declarations_engineering(tmp: str) -> None:
13058
13058
  hcfg, tcfg, pcfg = HY.well_config(ws, w1), TND.well_config(ws, w1), PPF.well_config(ws, w1)
13059
13059
  meas = (PPF._load(ws).get("measured") or {}).get(w1) or []
13060
13060
  c1 = {
13061
- "planned": not p1["errors"] and p1["counts"]["NEW"] == 10 and p1["counts"]["CHANGE"] == 0 and sorted({s["table"] for s in p1["steps"]}) == sorted(DC.ENG_ORDER),
13061
+ "planned": not p1["errors"] and p1["counts"]["NEW"] == 10 and p1["counts"]["CHANGE"] == 0 and sorted({s["table"] for s in p1["steps"]}) == sorted(set(DC.ENG_ORDER) - {"casing_design", "casing_loads"}), # the casing tables: section DD
13062
13062
  "written": a1["applied"] and len(a1["written"]) == 10,
13063
13063
  "landed": WPL.well_settings(ws, w1)["program"] == [{"to_md_m": 1000.0, "model": "MWD+SRGM"}, {"to_md_m": None, "model": "MWD+IFR1"}]
13064
13064
  and bool(ws.well(w1).get("plan")) and pcfg["indicator"] == "dexponent" and pcfg["bit_in"] == 8.75
@@ -13310,6 +13310,359 @@ def section_dc_every_tab(tmp: str) -> None:
13310
13310
  + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
13311
13311
 
13312
13312
 
13313
+ def section_dd_casing_design(tmp: str) -> None:
13314
+ """Section DD - casing design and kick tolerance: each string's ratings by API TR 5C3 as API's tables give them, in
13315
+ every collapse regime and reduced under tension; the burst, collapse and tension loads and the smallest safety factor
13316
+ against the design factor, worked by hand on the example; the kick tolerance below each shoe; the program declared
13317
+ from the tab, the command line and the declarations sheet, read on the survey or the recorder's own TVD, written,
13318
+ in the dossier; an interval declared in measured depth converted on the survey or the recorder's TVD."""
13319
+ import http.cookiejar
13320
+ import subprocess as _sp
13321
+ import urllib.error
13322
+ import urllib.request
13323
+ import zipfile as _zf
13324
+ from . import __version__ as _ver
13325
+ from . import casing_design as CSG
13326
+ from . import declarations as DC
13327
+ from . import dossier as DZ
13328
+ from . import edr as E
13329
+ from . import helplib as H
13330
+ from . import hydraulics as HY
13331
+ from . import interval as IV
13332
+ from . import well3d as W
13333
+ from .service import Service, Users
13334
+ from .workspace import Workspace
13335
+ pkg = Path(__file__).parent
13336
+
13337
+ # -- DD1 the ratings -------------------------------------------------------------------------------------------------------
13338
+ def one(od, ppf, grade, **kw):
13339
+ return CSG.check_strings([{"what": "x", "od_in": od, "weight_lbft": ppf, "grade": grade, "top_md_ft": 0, "shoe_md_ft": 1000, "hole_in": 26, "mw_ppg": 9, **kw}])[0]
13340
+ table = [("9-5/8", 47, "N80", 6870, 4750, "plastic", 1086), (7, 29, "P110", 11220, 8530, "plastic", 929), ("13-3/8", 54.5, "J55", 2730, 1130, "transition", 853),
13341
+ (20, 94, "J55", 2110, 520, "elastic", 1481), ("9-5/8", 40, "N80", 5750, 3090, "transition", 916)]
13342
+ rated = [(one(od, w_, g), b, c, reg, by) for od, w_, g, b, c, reg, by in table]
13343
+ thick = CSG.collapse(5.0, 0.5, 110000.0) # D/t 10: the yield regime, 2 Yp (D/t - 1) / (D/t)^2
13344
+ ya0, ya1, ya2 = CSG.reduced_yield(80000.0, 0.0), CSG.reduced_yield(80000.0, 20000.0), CSG.reduced_yield(80000.0, 40000.0)
13345
+
13346
+ def refused(rows):
13347
+ try:
13348
+ CSG.check_strings(rows)
13349
+ return ""
13350
+ except ValueError as e:
13351
+ return str(e)
13352
+ base = {"what": "s", "od_in": "9-5/8", "weight_lbft": 47, "grade": "N80", "top_md_ft": 0, "shoe_md_ft": 5000, "hole_in": 12.25, "mw_ppg": 10}
13353
+ m_grade = refused([{**base, "grade": "X99"}])
13354
+ m_size = refused([{**base, "weight_lbft": 41.3}])
13355
+ m_wall = refused([{**base, "wall_in": 0.3}])
13356
+ m_drift = refused([{**base, "what": "a", "od_in": 7, "weight_lbft": 29, "shoe_md_ft": 3000, "hole_in": 8.5}, {**base, "what": "b"}])
13357
+ try:
13358
+ CSG.parse_strings("surface:13-3/8:54.5:J55")
13359
+ m_parse = ""
13360
+ except ValueError as e:
13361
+ m_parse = str(e)
13362
+ c1 = {
13363
+ "api_tables": all(s["burst_psi"] == b and s["collapse_psi"] == c and s["collapse_regime"] == reg and abs(s["body_yield_klbf"] - by) <= 1.0
13364
+ for s, b, c, reg, by in rated),
13365
+ "yield_regime": thick[1] == "yield" and abs(thick[0] - 2 * 110000.0 * 9.0 / 100.0) < 1e-6,
13366
+ "sizes": CSG.size_in("9-5/8") == 9.625 and CSG.size_in("13 3/8") == 13.375 and CSG.size_in(7) == 7.0 and rated[0][0]["wall_in"] == 0.472
13367
+ and rated[0][0]["id_in"] == 8.681,
13368
+ "tension": ya0 == 80000.0 and ya0 > ya1 > ya2 and CSG.collapse_with_tension(9.625, 0.472, 80000.0, 20000.0) < 4750.0
13369
+ and CSG.reduced_yield(80000.0, -20000.0) == 80000.0,
13370
+ "answered": "the grade is one of" in m_grade and "not in the program's table" in m_size and "weighs about" in m_wall
13371
+ and "does not pass through" in m_drift and "what:od:weight:grade:top-shoe:hole:mw" in m_parse,
13372
+ }
13373
+ failed1 = [k for k, v in c1.items() if not v]
13374
+ ok(not failed1,
13375
+ "DD1 the ratings by API TR 5C3 as API's tables give them - 9-5/8 in 47 lb/ft N80 6,870 psi burst, 4,750 collapse, 1,086 klbf body; 7 in 29 "
13376
+ "P110 11,220 and 8,530; 13-3/8 in 54.5 J55 2,730 and 1,130 (transition); 20 in 94 J55 520 (elastic); 9-5/8 in 40 N80 3,090 (transition); a "
13377
+ "D/t of 10 in the yield regime - the collapse reduced under tension and not raised by compression; a grade, a size, a wall that does not "
13378
+ "match its weight, a string that does not pass through the one above and a short line each answered" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
13379
+
13380
+ # -- DD2 the loads and the kick tolerance, by hand on the example -------------------------------------------------------------------
13381
+ ex0 = CSG.example() # worked by hand with the fixed 0.1 psi/ft gas of the textbook method (section DE: the real gas)
13382
+ ex = CSG.analyse({**ex0["config"], "gas_gradient_psi_ft": 0.1}, lambda md: float(md), CSG.EXAMPLE_WINDOW)
13383
+ sv, iv_, pv = ex["strings"]
13384
+ win = CSG.EXAMPLE_WINDOW
13385
+
13386
+ def at(key, z):
13387
+ return float(np.interp(z, win["tvd_ft"], win[key]))
13388
+ # surface string: gas from the pore pressure at the intermediate shoe (7,000 ft), held by the fracture pressure at 1,500 ft
13389
+ p_res, p_frac = 0.052 * at("pp_ppg", 7000.0) * 7000.0, 0.052 * at("fg_ppg", 1500.0) * 1500.0
13390
+ top_in = min(p_res - 0.1 * 7000.0, p_frac - 0.1 * 1500.0)
13391
+ sf_b_top = 2730.0 / top_in
13392
+ sf_c_shoe = 1130.0 / (0.052 * 9.0 * 1500.0)
13393
+ sf_t = 853.2 / ((1 - 9.0 / 65.5) * 54.5 * 1500.0 / 1000.0)
13394
+ k0 = ex["kicks"][0]
13395
+ maasp = 0.052 * (at("fg_ppg", 1500.0) - 9.8) * 1500.0
13396
+ sidpp = 0.052 * 0.5 * 7000.0
13397
+ hgt = (maasp - sidpp) / (0.052 * 9.8 - 0.1)
13398
+ v_bot = hgt * (12.25 ** 2 - 6.5 ** 2) / 1029.4
13399
+ v_shoe = hgt * (12.25 ** 2 - 5.0 ** 2) / 1029.4 * p_frac / (0.052 * 10.3 * 7000.0)
13400
+ weak = {**CSG.DEFAULTS, "strings": CSG.check_strings([{**base, "what": "9-5/8 in J55 36 lb/ft", "weight_lbft": 36, "grade": "J55", "shoe_md_ft": 7000, "mw_ppg": 10}]),
13401
+ "td_md_ft": 10000.0, "td_hole_in": 8.5, "td_mw_ppg": 12.0, "bha_od_in": 6.5, "bha_length_ft": 600.0, "dp_od_in": 5.0}
13402
+ rw = CSG.analyse(weak, lambda md: float(md), win)
13403
+ no_fg = CSG.analyse({**weak, "strings": weak["strings"]}, lambda md: float(md), {"pp_const": 9.0, "fg_const": None})
13404
+ c2 = {
13405
+ "burst": abs(sv["rows"][0]["burst_net_psi"] - top_in) <= 1.0 and sv["rows"][0]["burst_limited_by_shoe"] and abs(sv["rows"][0]["sf_burst"] - sf_b_top) < 0.005,
13406
+ "collapse": abs(sv["rows"][-1]["sf_collapse"] - sf_c_shoe) < 0.005 and sv["rows"][-1]["collapse_rating_psi"] == 1130,
13407
+ "tension": abs(sv["checks"]["tension"]["sf"] - sf_t) < 0.01 and sv["checks"]["tension"]["ok"],
13408
+ "least": sv["checks"]["burst"]["sf"] == min(r_["sf_burst"] for r_ in sv["rows"] if r_["sf_burst"]) and ex["ok"],
13409
+ "kick": abs(k0["maasp_psi"] - maasp) <= 1 and abs(k0["height_ft"] - hgt) < 0.5 and abs(k0["volume_at_bottom_bbl"] - v_bot) < 0.15
13410
+ and abs(k0["volume_shoe_as_bottom_bbl"] - v_shoe) < 0.15 and k0["volume_bbl"] == min(k0["volume_at_bottom_bbl"], k0["volume_shoe_as_bottom_bbl"]),
13411
+ "weak": not rw["ok"] and not rw["strings"][0]["checks"]["burst"]["ok"] and not rw["strings"][0]["checks"]["collapse"]["ok"],
13412
+ "no_fg": no_fg["kicks"][0]["status"] == "NOT KNOWN" and "fracture gradient at the shoe" in no_fg["kicks"][0]["detail"],
13413
+ "production": pv["below"] is None and "at the shoe" in pv["burst_case"],
13414
+ }
13415
+ failed2 = [k for k, v in c2.items() if not v]
13416
+ ok(not failed2,
13417
+ "DD2 the worked example by hand: the surface string's burst load at the top is gas from the pore pressure at the next shoe held to the fracture "
13418
+ "pressure at its own shoe, the collapse at its shoe the 9.0 ppg mud against an empty string, the tension its buoyed weight; the smallest "
13419
+ "safety factor where it falls; the kick tolerance below it MAASP, SIDPP, the bubble's height and its volume at the bottom and at the shoe "
13420
+ "carried back by Boyle's law, the smaller; a 36 lb/ft J55 string at 7,000 ft below its design factors in burst and collapse; no fracture "
13421
+ "gradient, no kick tolerance - said so" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
13422
+
13423
+ # -- DD3 a well: declared three ways, read on its survey or the recorder's TVD, written, in the dossier ----------------------------------
13424
+ d = Path(tmp, "casing"); d.mkdir()
13425
+ hy = HY.selftest_files(str(d / "hy"))
13426
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
13427
+ w1 = ws.add_well_file(hy["record"], display="HY-1", actor="tester")["id"]
13428
+ w2 = ws.add_well_file(hy["record"], display="HY-2", actor="tester")["id"]
13429
+ W.attach_trajectory(ws, w1, "s.csv", Path(hy["survey"]).read_bytes(), "tester", "true", "ft", None, None, None, "test survey")
13430
+ for wid in (w2,):
13431
+ out = Path(ws.reports_dir, "wells", wid); out.mkdir(parents=True, exist_ok=True)
13432
+ E.write_record(E.read(hy["record"]), str(out), _ver)
13433
+ prog = "surface:13-3/8:54.5:J55:0-1500:17.5:9.0; intermediate:9-5/8:47:N80:0-6000:12.25:10"
13434
+ cli = _sp.run([sys.executable, "-m", "gea", "casing", "--workspace", wsp, "--well", w1, "--strings", prog, "--td", "10000", "--td-hole", "8-1/2", "--td-mw", "11",
13435
+ "--pp", "9.5", "--fg", "14.5", "--bha-od", "6.75", "--bha-length", "400", "--dp-od", "5", "--source", "drilling program (test)"],
13436
+ capture_output=True, text=True, cwd=str(pkg.parent))
13437
+ p1 = CSG.page(ws, "workspace", w1)
13438
+ r1 = p1["result"]
13439
+ sheet = d / "sheet.csv"
13440
+ cols_d, cols_l = DC.TABLES["casing_design"]["cols"], DC.TABLES["casing_loads"]["cols"]
13441
+ rows = [["[casing_design]", *cols_d], ["", "HY-2", "surface", "13-3/8", "54.5", "J55", "0", "1500", "17.5", "9.0", "", "casing tally (test)"],
13442
+ ["", "HY-2", "intermediate", "9.625", "47", "N80", "0", "6000", "12.25", "10", "", "casing tally (test)"],
13443
+ ["[casing_loads]", *cols_l], [""] + [{"well": "HY-2", "td_md_ft": "10000", "td_hole_in": "8.5", "td_mw_ppg": "11", "pp_ppg": "9.5", "fg_ppg": "14.5",
13444
+ "kick_intensity_ppg": "0.5", "bha_od_in": "6.75", "bha_length_ft": "400", "dp_od_in": "5",
13445
+ "source": "drilling program (test)"}.get(c_, "") for c_ in cols_l]]
13446
+ import csv as _csv
13447
+ import io as _io
13448
+ buf = _io.StringIO(); _csv.writer(buf).writerows(rows); sheet.write_text(buf.getvalue(), encoding="utf-8")
13449
+ pl = DC.apply(ws, sheet.read_text(encoding="utf-8"), "tester", str(d))
13450
+ pw = DC.apply(ws, sheet.read_text(encoding="utf-8"), "tester", str(d), write=True)
13451
+ again = DC.apply(ws, sheet.read_text(encoding="utf-8"), "tester", str(d))
13452
+ tmpl = DC.apply(ws, DC.template(ws), "tester", str(d))
13453
+ bad = sheet.read_text(encoding="utf-8").replace(",N80,", ",X99,")
13454
+ pb = DC.apply(ws, bad, "tester", str(d), write=True)
13455
+ p2 = CSG.page(ws, "workspace", w2)
13456
+ r2 = p2["result"]
13457
+ Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
13458
+ svc = Service(wsp, port=0, scheduler=False).start()
13459
+ base_url = svc.url.rstrip("/")
13460
+ o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
13461
+
13462
+ def call(method, path, body=None):
13463
+ rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
13464
+ rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
13465
+ try:
13466
+ with o.open(rq, timeout=120) as rr_:
13467
+ return rr_.status, json.loads(rr_.read())
13468
+ except urllib.error.HTTPError as e:
13469
+ return e.code, json.loads(e.read())
13470
+ try:
13471
+ call("POST", "/api/login", {"name": "op", "password": "op-pass-123"})
13472
+ st_g, g = call("GET", f"/api/casing-design?well={w1}")
13473
+ st_ex, gx = call("GET", "/api/casing-design?scene=example")
13474
+ st_s, sset = call("POST", "/api/casing-design/settings", {"well_id": w1, "df_burst": "1.2", "source": "drilling program rev 2 (test)"})
13475
+ st_bad, sbad = call("POST", "/api/casing-design/settings", {"well_id": w1, "strings": "surface:13-3/8:54.5:X99:0-1500:17.5:9", "source": "x"})
13476
+ st_w, wr = call("POST", "/api/casing-design/write", {"well_id": w1})
13477
+ finally:
13478
+ svc.stop()
13479
+ cli_ex = _sp.run([sys.executable, "-m", "gea", "casing", "--example"], capture_output=True, text=True, cwd=str(pkg.parent))
13480
+ dz = DZ.build(ws, "tester", _ver, None)
13481
+ with _zf.ZipFile(Path(ws.reports_dir, dz["path"])) as z_:
13482
+ names = set(z_.namelist())
13483
+ rep_json = json.loads(Path(ws.reports_dir, "wells", w1, "casing_design.json").read_text(encoding="utf-8"))
13484
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
13485
+ c3 = {
13486
+ "cli": cli.returncode == 0 and "2 string(s)" in cli.stdout,
13487
+ "survey": r1 and r1["path_from"] == "survey" and r1["strings"][1]["shoe_tvd_ft"] < 6000.0 - 100.0 and len(r1["kicks"]) == 2
13488
+ and r1["kicks"][1]["bottom_md_ft"] == 10000.0 and r1["kicks"][1].get("volume_bbl") is not None,
13489
+ "sheet": not pl["errors"] and [s_["action"] for s_ in pl["steps"] if s_["table"] in ("casing_design", "casing_loads")] == ["NEW", "NEW"]
13490
+ and pw["applied"] and not again["errors"] and again["counts"]["NEW"] == 0 and again["counts"]["CHANGE"] == 0
13491
+ and not tmpl["errors"] and tmpl["counts"]["NEW"] == 0 and tmpl["counts"]["CHANGE"] == 0,
13492
+ "sheet_bad": bool(pb["errors"]) and any("[casing_design]" in e and "the grade is one of" in e for e in pb["errors"]) and not pb["written"],
13493
+ "recorder": r2 and r2["path_from"] == "recorder" and "not a survey" in r2["path_how"] and abs(r2["strings"][1]["shoe_tvd_ft"] - r1["strings"][1]["shoe_tvd_ft"]) < 40.0,
13494
+ "api": st_g == 200 and g["result"]["well"] == w1 and st_ex == 200 and gx["scene"] == "example" and st_s == 200 and sset["df_burst"] == 1.2
13495
+ and st_bad == 400 and "the grade is one of" in sbad.get("error", "") and st_w == 200 and wr["report_id"].startswith("CSG-")
13496
+ and rep_json["config"]["df_burst"] == 1.2,
13497
+ "example_cli": cli_ex.returncode == 0 and "6,870 psi" in cli_ex.stdout and "kick tolerance below" in cli_ex.stdout,
13498
+ "dossier": "declarations/drillstring/casing_design.json" in names and f"results/casing_design/{w1}.json" in names,
13499
+ "page_help": "['#/casing', 'Casing & kick tolerance']" in page_src and "VIEWS.casing = async" in page_src and not H.check("casing")
13500
+ and any(t["topic"] == "casing" for t in H.topics()) and "[casing_design]" in (H.page("declare") or ""),
13501
+ }
13502
+ failed3 = [k for k, v in c3.items() if not v]
13503
+ ok(not failed3,
13504
+ "DD3 a well's casing program declared from the command line, the API and the declarations sheet (planned NEW, written, then SAME, and from "
13505
+ "the template; a bad grade answered with its row, nothing written); read on its survey, or on the drilling recorder's own TVD for a well with "
13506
+ "none, said so; the kick tolerance of each open hole to TD; the report written (CSG-...); the dossier carries the declarations and the "
13507
+ "result; the tab, the help and the sheet's tables" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
13508
+
13509
+ # -- DD4 an interval declared in measured depth -----------------------------------------------------------------------------------------
13510
+ base_w = {"id": "w", "surface": {"lat": 1.0, "lon": 1.0, "elevation": {"ft": 2950.0, "datum": "KB", "source": "report"}},
13511
+ "disposal": {"top_interval_ft": 7000.0, "base_interval_ft": 7600.0, "depth_reference": "MD"}}
13512
+ path1, path2 = HY.depth_path(ws, w1), HY.depth_path(ws, w2)
13513
+ s1 = IV.statement(base_w, {"status": "OK", "layers": []}, [], None, path1)
13514
+ s2 = IV.statement(base_w, {"status": "OK", "layers": []}, [], None, path2)
13515
+ s3 = IV.statement({**base_w, "disposal": {**base_w["disposal"], "base_interval_ft": 30000.0}}, {"status": "OK", "layers": []}, [], None, path1)
13516
+ tv_top = HY.tvd_at(path1, 7000.0)
13517
+ c4 = {
13518
+ "survey": path1["from"] == "survey" and s1["declared"]["depth_reference"] == "TVD" and abs(s1["declared"]["top_ft"] - tv_top) < 0.2
13519
+ and s1["declared"]["top_ft"] < 7000.0 - 500.0 and s1["declared"]["converted_from_md"]["top_md_ft"] == 7000.0
13520
+ and any("converted to" in n and "survey" in n for n in s1["notes"]) and abs(s1["declared"]["top_subsea_ft"] - (2950.0 - s1["declared"]["top_ft"])) < 0.2,
13521
+ "recorder": path2["from"] == "recorder" and abs(s2["declared"]["top_ft"] - s1["declared"]["top_ft"]) < 40.0 and any("not a survey" in n for n in s2["notes"]),
13522
+ "beyond": s3["status"] == "NOT RESOLVED" and "reaches" in s3["lines"][0],
13523
+ "help": "converted on the well's survey" in (H.page("interval") or ""),
13524
+ }
13525
+ failed4 = [k for k, v in c4.items() if not v]
13526
+ ok(not failed4,
13527
+ "DD4 an interval declared in measured depth is converted to true vertical depth on the well's survey, or the drilling recorder's own TVD for a "
13528
+ "well with none (within 40 ft of the survey's), each said so in the statement; an interval deeper than the depth table reaches stays NOT "
13529
+ "RESOLVED and says how far it reaches" + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
13530
+
13531
+
13532
+ def section_de_real_gas(tmp: str) -> None:
13533
+ """Section DE - the kick and the burst load on a real gas: Sutton's pseudocritical properties and Dranchuk and
13534
+ Abou-Kassem's z against an independent implementation; the gas's density, and a column of it integrated up the well
13535
+ along the geothermal temperature; the kick tolerance with the gas's gradient at the bubble's own pressure and
13536
+ temperature and the volume carried between shoe and bottom by p V / z T, and a swabbed kick beside it; a fixed
13537
+ gradient still taken when declared, and the real gas back with 'none'."""
13538
+ import subprocess as _sp
13539
+ from . import casing_design as CSG
13540
+ from . import declarations as DC
13541
+ from . import helplib as H
13542
+ from .workspace import Workspace
13543
+ pkg = Path(__file__).parent
13544
+
13545
+ # -- DE1 the gas's properties ------------------------------------------------------------------------------------------------
13546
+ # reference values from pyrestoolbox 3.8.9 (gas_z / gas_den, zmethod DAK, cmethod SUT), an independent open-source implementation
13547
+ ref = [(3000, 0.65, 200, 0.9048073771833917, 8.819352273478), (6000, 0.65, 250, 1.0923431854133714, 13.58107045798391),
13548
+ (1000, 0.8, 120, 0.8335178869315637, 4.469708326242926), (10000, 0.7, 300, 1.3920860511301727, 17.868657631013313),
13549
+ (500, 0.6, 80, 0.9330965197803314, 1.6082418312797764)]
13550
+ got = [CSG.gas_state(p_ - CSG.ATM, f_, sg_) for p_, sg_, f_, _, _ in ref]
13551
+ tpc, ppc = CSG.pseudo_critical(0.65)
13552
+ tpc8, ppc8 = CSG.pseudo_critical(0.8)
13553
+ low = CSG.z_factor(1.5, 0.01)
13554
+ c1 = {
13555
+ "sutton": abs(tpc - 365.11) < 0.01 and abs(ppc - 670.129) < 0.01 and abs(tpc8 - 401.44) < 0.01 and abs(ppc8 - 649.696) < 0.01,
13556
+ "z": all(abs(g_["z"] - r_[3]) < 1e-5 for g_, r_ in zip(got, ref)),
13557
+ "density": all(abs(g_["rho_lb_ft3"] - r_[4]) / r_[4] < 1e-5 for g_, r_ in zip(got, ref)),
13558
+ "gradient": all(abs(g_["psi_ft"] - g_["rho_lb_ft3"] / 144.0) < 1e-12 for g_ in got),
13559
+ "ideal_limit": abs(low - 1.0) < 0.005 and CSG.z_factor(1.5, 0.0) == 1.0,
13560
+ "chart_shape": CSG.z_factor(1.5, 2.0) < CSG.z_factor(1.5, 1.0) < 1.0 and CSG.z_factor(1.5, 10.0) > 1.0 and CSG.z_factor(1.2, 2.0) < CSG.z_factor(2.0, 2.0),
13561
+ }
13562
+ failed1 = [k for k, v in c1.items() if not v]
13563
+ ok(not failed1,
13564
+ "DE1 the gas: Sutton's pseudocritical temperature and pressure (365.11 R and 670.13 psia for a 0.65 gas), Dranchuk and Abou-Kassem's z and "
13565
+ "the density p M / z R T within 1e-5 of an independent implementation (pyrestoolbox) from 500 to 10,000 psia, 80 to 300 F, gravities 0.6 "
13566
+ "to 0.8; z to 1 as the pressure falls, the Standing-Katz chart's dip and rise" + (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
13567
+
13568
+ # -- DE2 a column of gas up the well ------------------------------------------------------------------------------------------
13569
+ cfg = {**CSG.DEFAULTS}
13570
+ p0 = 0.052 * 11.0 * 10000.0
13571
+ top = CSG.gas_column(cfg, p0, 10000.0, 0.0)
13572
+ p_, z_ = p0, 10000.0 # a fine Euler integration of the same gas, 1 ft at a time
13573
+ while z_ > 0.0:
13574
+ p_ -= CSG.gas_state(p_, CSG.temperature_f(cfg, z_ - 0.5), 0.65)["psi_ft"]
13575
+ z_ -= 1.0
13576
+ hot = CSG.gas_column({**cfg, "geothermal_f_100ft": 2.5}, p0, 10000.0, 0.0)
13577
+ heavy = CSG.gas_column({**cfg, "gas_sg": 0.9}, p0, 10000.0, 0.0)
13578
+ fixed = CSG.gas_column({**cfg, "gas_gradient_psi_ft": 0.1}, p0, 10000.0, 0.0)
13579
+ c2 = {
13580
+ "integrated": abs(top - p_) < 0.5 and abs(top - 4658.6) < 1.0,
13581
+ "hotter_lighter": hot > top and heavy < top,
13582
+ "fixed": abs(fixed - (p0 - 1000.0)) < 1e-9,
13583
+ "temperature": CSG.temperature_f(cfg, 0.0) == 70.0 and abs(CSG.temperature_f(cfg, 10000.0) - 220.0) < 1e-9,
13584
+ }
13585
+ failed2 = [k for k, v in c2.items() if not v]
13586
+ ok(not failed2,
13587
+ "DE2 a column of real gas from 5,720 psi at 10,000 ft (an 11 ppg pore pressure) integrated to the surface along 70 F rising 1.5 F per 100 ft: "
13588
+ "4,659 psi at the surface (an average 0.106 psi/ft), as a 1 ft step integration gives; a hotter well's gas lighter, a heavier gas heavier; a "
13589
+ "declared 0.1 psi/ft taken as it is" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
13590
+
13591
+ # -- DE3 the kick tolerance and the burst load on the real gas -----------------------------------------------------------------
13592
+ ex = CSG.example()
13593
+ exf = CSG.analyse({**ex["config"], "gas_gradient_psi_ft": 0.1}, lambda md: float(md), CSG.EXAMPLE_WINDOW)
13594
+ k_, kf = ex["kicks"][1], exf["kicks"][1]
13595
+ win = CSG.EXAMPLE_WINDOW
13596
+ fg_s = float(np.interp(7000.0, win["tvd_ft"], win["fg_ppg"]))
13597
+ p_frac, p_bot = 0.052 * fg_s * 7000.0, 0.052 * k_["kick_ppg"] * 10000.0
13598
+ maasp, sidpp = 0.052 * (fg_s - 11.5) * 7000.0, 0.052 * (k_["kick_ppg"] - 11.5) * 10000.0
13599
+ gm = 0.052 * 11.5
13600
+ h_b = (maasp - sidpp) / (gm - k_["gas_gradient_bottom_psi_ft"])
13601
+ st_b = CSG.gas_state(p_bot - k_["gas_gradient_bottom_psi_ft"] * h_b / 2, CSG.temperature_f(ex["config"], 10000.0 - h_b / 2), 0.65)
13602
+ h_s = (maasp - sidpp) / (gm - k_["gas_gradient_shoe_psi_ft"])
13603
+ st_s = CSG.gas_state(p_frac + k_["gas_gradient_shoe_psi_ft"] * h_s / 2, CSG.temperature_f(ex["config"], 7000.0 + h_s / 2), 0.65)
13604
+ carried = k_["volume_at_shoe_bbl"] * (st_s["p_psia"] / (st_s["z"] * st_s["t_r"])) / (st_b["p_psia"] / (st_b["z"] * st_b["t_r"]))
13605
+ sv = ex["strings"][0]
13606
+ top_load = CSG.gas_column(ex["config"], 0.052 * float(np.interp(1500.0, win["tvd_ft"], win["fg_ppg"])) * 1500.0, 1500.0, 0.0)
13607
+ c3 = {
13608
+ "bubble_gradient": abs(k_["gas_gradient_bottom_psi_ft"] - st_b["psi_ft"]) < 1e-5 and abs(k_["gas_gradient_shoe_psi_ft"] - st_s["psi_ft"]) < 1e-5
13609
+ and k_["gas_gradient_bottom_psi_ft"] > k_["gas_gradient_shoe_psi_ft"],
13610
+ "heights": abs(k_["height_ft"] - h_b) < 0.2 and abs(k_["height_shoe_ft"] - h_s) < 0.2,
13611
+ "carried": abs(k_["volume_shoe_as_bottom_bbl"] - carried) < 0.15 and "real-gas law" in k_["carried_by"] and "Boyle" in kf["carried_by"],
13612
+ "z_t": k_["z_bottom"] > 0 and abs(k_["temp_bottom_f"] - CSG.temperature_f(ex["config"], 10000.0 - h_b / 2)) < 0.1,
13613
+ "swabbed": all(k["swabbed"]["kick_ppg"] == k["mw_ppg"] and k["swabbed"]["volume_bbl"] > k["volume_bbl"] for k in ex["kicks"]),
13614
+ "burst_real": abs(sv["rows"][0]["burst_net_psi"] - top_load) <= 1.0 and sv["rows"][0]["burst_limited_by_shoe"]
13615
+ and "real gas" in sv["gas"] and "0.1 psi/ft" in exf["strings"][0]["gas"],
13616
+ }
13617
+ failed3 = [k for k, v in c3.items() if not v]
13618
+ ok(not failed3,
13619
+ "DE3 on the worked example: the gas's gradient in the bubble at its own middle's pressure and temperature, heavier on bottom than at the shoe; "
13620
+ "the bubble's heights from them; the shoe's volume carried to the bottom by p V / z T (Boyle's law with a fixed gradient); a swabbed kick "
13621
+ "(no underbalance) beside each, larger; the burst load at the top the real gas's column from the fracture pressure at the shoe"
13622
+ + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
13623
+
13624
+ # -- DE4 declared --------------------------------------------------------------------------------------------------------------
13625
+ d = Path(tmp, "realgas"); d.mkdir()
13626
+ wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
13627
+ wid = ws.add_well_catalog("volve_f12_f14_production_excerpt", "15/9-F-12", 10000.0, actor="tester")["id"]
13628
+ prog = "surface:13-3/8:54.5:J55:0-1500:17.5:9.0; intermediate:9-5/8:47:N80:0-7000:12.25:9.8"
13629
+ run = lambda *a: _sp.run([sys.executable, "-m", "gea", "casing", "--workspace", wsp, "--well", wid, *a], capture_output=True, text=True, cwd=str(pkg.parent))
13630
+ r1 = run("--strings", prog, "--td", "10000", "--td-hole", "8.5", "--td-mw", "11.5", "--pp", "10", "--fg", "15", "--gas-sg", "0.7", "--surface-temp", "80",
13631
+ "--geothermal", "1.2", "--bha-od", "6.5", "--bha-length", "600", "--dp-od", "5", "--source", "test")
13632
+ c_a = CSG.well_config(ws, wid)
13633
+ r2 = run("--gas", "0.1", "--source", "test")
13634
+ c_b = CSG.well_config(ws, wid)
13635
+ r3 = run("--gas", "none", "--source", "test")
13636
+ c_c = CSG.well_config(ws, wid)
13637
+ pr = run()
13638
+ cols = DC.TABLES["casing_loads"]["cols"]
13639
+ sheet = "\n".join([",".join(["[casing_loads]", *cols]), ",".join([""] + [{"well": wid, "gas_sg": "0.75", "surface_temp_f": "60", "geothermal_f_100ft": "1.8",
13640
+ "source": "drilling program (test)"}.get(c_, "") for c_ in cols])])
13641
+ ps = DC.apply(ws, sheet, "tester", str(d), write=True)
13642
+ c_d = CSG.well_config(ws, wid)
13643
+ try:
13644
+ CSG.set_well(ws, "tester", wid, "x", geothermal_f_100ft=9)
13645
+ lim = ""
13646
+ except ValueError as e:
13647
+ lim = str(e)
13648
+ page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
13649
+ help_txt = H.page("casing") or ""
13650
+ c4 = {
13651
+ "cli": r1.returncode == 0 and c_a["gas_sg"] == 0.7 and c_a["surface_temp_f"] == 80.0 and c_a["geothermal_f_100ft"] == 1.2 and c_a["gas_gradient_psi_ft"] is None,
13652
+ "fixed_and_back": r2.returncode == 0 and c_b["gas_gradient_psi_ft"] == 0.1 and r3.returncode == 0 and c_c["gas_gradient_psi_ft"] is None,
13653
+ "printed": pr.returncode == 0 and "specific gravity 0.7 as a real gas" in pr.stdout and "swabbed (no underbalance)" in pr.stdout,
13654
+ "sheet": ps["applied"] and c_d["gas_sg"] == 0.75 and c_d["surface_temp_f"] == 60.0 and c_d["geothermal_f_100ft"] == 1.8,
13655
+ "limits": "geothermal_f_100ft must be between" in lim,
13656
+ "page_help": "name=\"gas_gradient_psi_ft\"" in page_src and "'gas_sg', 'Gas gravity'" in page_src and "Swabbed (bbl)" in page_src
13657
+ and not H.check("casing") and "Dranchuk and Abou-Kassem" in help_txt and "--gas none" in help_txt,
13658
+ }
13659
+ failed4 = [k for k, v in c4.items() if not v]
13660
+ ok(not failed4,
13661
+ "DE4 the gas declared from the command line (its gravity, the surface temperature, the geothermal gradient), a fixed gradient taken in its place "
13662
+ "and the real gas back with 'none'; the declarations sheet's [casing_loads] carries them; out of range answered; the tab, its kick table and "
13663
+ "the help" + (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
13664
+
13665
+
13313
13666
  def PR_read_ok(path) -> bool:
13314
13667
  from . import pdf_report as PR
13315
13668
  try:
@@ -13428,6 +13781,8 @@ def main() -> int:
13428
13781
  section_da_hydraulics(tmp)
13429
13782
  section_db_declarations_engineering(tmp)
13430
13783
  section_dc_every_tab(tmp)
13784
+ section_dd_casing_design(tmp)
13785
+ section_de_real_gas(tmp)
13431
13786
  if _FAILS:
13432
13787
  print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
13433
13788
  for f in _FAILS: