gea-program 0.56.0__tar.gz → 0.58.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.
- {gea_program-0.56.0/gea_program.egg-info → gea_program-0.58.0}/PKG-INFO +27 -2
- {gea_program-0.56.0 → gea_program-0.58.0}/README.md +26 -1
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/__init__.py +1 -1
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/__main__.py +67 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/acceptance_tests.py +344 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/cli.py +14 -1
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/dossier.py +12 -1
- gea_program-0.58.0/gea/help/IT_SECURITY.md +138 -0
- gea_program-0.58.0/gea/help/hydraulics.md +19 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/helplib.py +10 -1
- gea_program-0.58.0/gea/hydraulics.py +739 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/service.py +17 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/web/app.html +78 -1
- {gea_program-0.56.0 → gea_program-0.58.0/gea_program.egg-info}/PKG-INFO +27 -2
- {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/SOURCES.txt +3 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/pyproject.toml +1 -1
- {gea_program-0.56.0 → gea_program-0.58.0}/LICENSE +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/alerts.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/backup.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/bench.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/blind_harness.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog_map.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/catalog_register.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/certificates.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/client_reports.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/collision.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/config_versioning.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/conformance.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/control_room.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/correlation.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/dashboard.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/declarations.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/decline.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/deviation.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/dlis.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/dnp3.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/doctor.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/dossier_verify.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/drill_string.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/earth_model.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/economics.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/ed25519.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/edr.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/elevation.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/etp.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/example_register_map.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/fat_sat.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/files.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/follower.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/forward_model.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/frac_hits.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/friction_priors.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/gamma.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/geodesy.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/geomag.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/hartip.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/alarms.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/alerts.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/backup.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/catalog.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/charts.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/collision.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/company-sign-in.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/conformance.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/control-room.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/data-in.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/declare.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/decline.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/dlis.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/doctor.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/dossier.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/drift.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/drillstring.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/economics.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/edr.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/etp.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/files.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/frac-hits.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/historian.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/https.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/instruments.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/interval.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/invoice-check.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/map.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/month-end.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/notifications.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/opseis.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/osdu.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/pad-summary.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/patches.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/penetrator.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/plan.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/pore-pressure.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/quality.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/report-history.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/rig-time.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/seismic.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/setup.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/simulation.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/site.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/sites.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/slip.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/soak.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/sra.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/start.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/structure.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/tools.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/torque-drag.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/transients.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/vibration.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/well-report.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/help/well3d.md +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/housekeeping.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/interval.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/invoice_check.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/jobs.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/live_ports.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/loadtest.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/modbus.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/model_card.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/month_end.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/notify.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/oidc.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/oidc_mock.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/op_seismicity.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/opcua_port.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/operator_app.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/osdu.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/pad_summary.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/patches.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/pdf_report.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/penetrator.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/permits.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/piwebapi.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/pore_pressure.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/ports.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/ppdm.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/project.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/quakeml.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reconciler.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/WMM2025.COF +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/iscwsa_rev4_example.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/iscwsa_rev5_example.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/mahoning_example.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/mud_motor_specs.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/owsg_tool_models.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/penetrator_ratings.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/seismicity_protocols.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/report_history.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/rig_state.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/sample_record.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/sbom.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seedlink.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/segy.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_array.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_film.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_response.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_track.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/seismic_watch.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/series_cache.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/service_life.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/shapefile.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/shell.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/shut_in.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/simulation.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/site_map.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/sla_report.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/slip.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/soak.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/sra.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/store_forward.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/strata_join.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/structure.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/supervisor.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/survey_view.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/telemetry.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/tls.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/tool_library.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/torque_drag.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/transient.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/vibration.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/well3d.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/well_assembler.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/well_report.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/wellplan.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/wellpos.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/wits0.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/witsml.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/workspace.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea/xlsx_read.py +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.56.0 → gea_program-0.58.0}/setup.cfg +0 -0
- {gea_program-0.56.0 → gea_program-0.58.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.
|
|
3
|
+
Version: 0.58.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
|
|
@@ -659,6 +659,17 @@ EIA) or a declared price, with the net revenue interest, severance tax and
|
|
|
659
659
|
operating cost declared. A forecast is not a reserves report, and the page
|
|
660
660
|
says so. The **Decline curves** tab, `gea decline`, `gea help decline`.
|
|
661
661
|
|
|
662
|
+
### Hydraulics and ECD
|
|
663
|
+
|
|
664
|
+
The pressures the mud makes while it circulates, from the six-speed
|
|
665
|
+
viscometer (the API RP 13D power law: the pipe from 600/300, the annulus
|
|
666
|
+
from 100/3): the standpipe pressure part by part - surface lines, string,
|
|
667
|
+
motor and MWD, bit, annulus - the bit's hydraulics, the ECD at every depth
|
|
668
|
+
against the pore pressure and fracture gradient, surge and swab and the
|
|
669
|
+
fastest safe trip at each bit depth, and the standpipe pressure the rig
|
|
670
|
+
measured against the model. The **Hydraulics & ECD** tab, `gea hydraulics`,
|
|
671
|
+
`gea help hydraulics`.
|
|
672
|
+
|
|
662
673
|
### Torque and drag
|
|
663
674
|
|
|
664
675
|
The hook load and torque the drill string should take at each bit depth, by
|
|
@@ -1291,6 +1302,19 @@ gea oidc --workspace C:\site --issuer https://login.microsoftonline.com/3f8b2c1d
|
|
|
1291
1302
|
Or Administration -> **Company sign-in**. `gea help company-sign-in` has the
|
|
1292
1303
|
Entra ID app registration step by step.
|
|
1293
1304
|
|
|
1305
|
+
### For an IT department
|
|
1306
|
+
|
|
1307
|
+
`docs/IT_SECURITY.md` (`gea security` prints it from any installation)
|
|
1308
|
+
answers what a reviewer asks before installing: where the data is and every
|
|
1309
|
+
connection the program can make, the one port, HTTPS, company sign-in and
|
|
1310
|
+
roles, the audit log, the bill of materials (`gea sbom`), and how a kit is
|
|
1311
|
+
checked - the build's attestation
|
|
1312
|
+
(`gea-program-0.57.0-win64.zip` checked with
|
|
1313
|
+
`gh attestation verify gea-program-0.57.0-win64.zip -R Daniel8Murphy0007/GEA-Program`)
|
|
1314
|
+
and the file catalog beside the kit's folder, Authenticode-signed once the
|
|
1315
|
+
publisher's certificate is in place. `SECURITY.md` says how to report a
|
|
1316
|
+
vulnerability.
|
|
1317
|
+
|
|
1294
1318
|
### Before a site goes live
|
|
1295
1319
|
|
|
1296
1320
|
`deploy/README.md` is the checklist: HTTPS from the service itself (above),
|
|
@@ -1422,4 +1446,5 @@ https://pypi.org/project/gea-program/ after the first successful run.
|
|
|
1422
1446
|
## Licence
|
|
1423
1447
|
|
|
1424
1448
|
Mozilla Public License 2.0 (MPL-2.0); see `LICENSE`. Every source file carries the MPL-2.0
|
|
1425
|
-
header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy.
|
|
1449
|
+
header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy. The signed kit, its updates and
|
|
1450
|
+
support are a subscription from ENRGYONE (`docs/commercial/COMMERCIAL.md`).
|
|
@@ -620,6 +620,17 @@ EIA) or a declared price, with the net revenue interest, severance tax and
|
|
|
620
620
|
operating cost declared. A forecast is not a reserves report, and the page
|
|
621
621
|
says so. The **Decline curves** tab, `gea decline`, `gea help decline`.
|
|
622
622
|
|
|
623
|
+
### Hydraulics and ECD
|
|
624
|
+
|
|
625
|
+
The pressures the mud makes while it circulates, from the six-speed
|
|
626
|
+
viscometer (the API RP 13D power law: the pipe from 600/300, the annulus
|
|
627
|
+
from 100/3): the standpipe pressure part by part - surface lines, string,
|
|
628
|
+
motor and MWD, bit, annulus - the bit's hydraulics, the ECD at every depth
|
|
629
|
+
against the pore pressure and fracture gradient, surge and swab and the
|
|
630
|
+
fastest safe trip at each bit depth, and the standpipe pressure the rig
|
|
631
|
+
measured against the model. The **Hydraulics & ECD** tab, `gea hydraulics`,
|
|
632
|
+
`gea help hydraulics`.
|
|
633
|
+
|
|
623
634
|
### Torque and drag
|
|
624
635
|
|
|
625
636
|
The hook load and torque the drill string should take at each bit depth, by
|
|
@@ -1252,6 +1263,19 @@ gea oidc --workspace C:\site --issuer https://login.microsoftonline.com/3f8b2c1d
|
|
|
1252
1263
|
Or Administration -> **Company sign-in**. `gea help company-sign-in` has the
|
|
1253
1264
|
Entra ID app registration step by step.
|
|
1254
1265
|
|
|
1266
|
+
### For an IT department
|
|
1267
|
+
|
|
1268
|
+
`docs/IT_SECURITY.md` (`gea security` prints it from any installation)
|
|
1269
|
+
answers what a reviewer asks before installing: where the data is and every
|
|
1270
|
+
connection the program can make, the one port, HTTPS, company sign-in and
|
|
1271
|
+
roles, the audit log, the bill of materials (`gea sbom`), and how a kit is
|
|
1272
|
+
checked - the build's attestation
|
|
1273
|
+
(`gea-program-0.57.0-win64.zip` checked with
|
|
1274
|
+
`gh attestation verify gea-program-0.57.0-win64.zip -R Daniel8Murphy0007/GEA-Program`)
|
|
1275
|
+
and the file catalog beside the kit's folder, Authenticode-signed once the
|
|
1276
|
+
publisher's certificate is in place. `SECURITY.md` says how to report a
|
|
1277
|
+
vulnerability.
|
|
1278
|
+
|
|
1255
1279
|
### Before a site goes live
|
|
1256
1280
|
|
|
1257
1281
|
`deploy/README.md` is the checklist: HTTPS from the service itself (above),
|
|
@@ -1383,4 +1407,5 @@ https://pypi.org/project/gea-program/ after the first successful run.
|
|
|
1383
1407
|
## Licence
|
|
1384
1408
|
|
|
1385
1409
|
Mozilla Public License 2.0 (MPL-2.0); see `LICENSE`. Every source file carries the MPL-2.0
|
|
1386
|
-
header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy.
|
|
1410
|
+
header (Exhibit A). Copyright (c) 2026 Daniel T. Murphy. The signed kit, its updates and
|
|
1411
|
+
support are a subscription from ENRGYONE (`docs/commercial/COMMERCIAL.md`).
|
|
@@ -67,7 +67,16 @@ def _build_config(a):
|
|
|
67
67
|
return SimulatorConfig(**kw)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
# the front door's own commands (gea.cli), reached the same way through `python -m gea` - the kit's gea.cmd and
|
|
71
|
+
# gea wrapper run `python -m gea`, and before v0.57 `gea guide`, `gea help` and the rest answered "invalid choice"
|
|
72
|
+
FRONT_ONLY = ('quickstart', 'guide', 'security', 'docs', 'gui', 'help')
|
|
73
|
+
|
|
74
|
+
|
|
70
75
|
def main(argv=None) -> int:
|
|
76
|
+
_argv = list(sys.argv[1:] if argv is None else argv)
|
|
77
|
+
if _argv and _argv[0] in FRONT_ONLY:
|
|
78
|
+
from .cli import main as _front
|
|
79
|
+
return _front(_argv)
|
|
71
80
|
ap = argparse.ArgumentParser(prog="gea",
|
|
72
81
|
description="GEA Downhole Simulator - headless CLI")
|
|
73
82
|
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
@@ -637,6 +646,24 @@ def main(argv=None) -> int:
|
|
|
637
646
|
p_dc.add_argument("--actor", type=str, default="cli")
|
|
638
647
|
p_dc.add_argument("--json", action="store_true")
|
|
639
648
|
|
|
649
|
+
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
|
+
p_hy.add_argument("--workspace", type=str, default=None)
|
|
651
|
+
p_hy.add_argument("--well", type=str, default=None)
|
|
652
|
+
p_hy.add_argument("--example", action="store_true", help="the worked example (Volve 15/9-F-10 with an example mud, string and hole)")
|
|
653
|
+
p_hy.add_argument("--string", type=str, default=None, help="sections from the bit up: 'BHA:350:6.75:2.875; drill pipe::5:4.276' (what:length_ft:od_in:id_in)")
|
|
654
|
+
p_hy.add_argument("--hole", type=str, default=None, help="sections from the surface down: '9-5/8 casing:3500:8.681; open hole::8.75' (what:bottom_md_ft:id_in)")
|
|
655
|
+
p_hy.add_argument("--fann", type=str, default=None, help="the viscometer dial: '600=60 300=38 200=30 100=21 6=7 3=6'")
|
|
656
|
+
p_hy.add_argument("--nozzles", type=str, default=None, help="nozzle sizes in 32nds of an inch: '14 14 14 14 14'")
|
|
657
|
+
for k_, h_ in (("mw", "mud weight, ppg"), ("flow", "pump rate, gal/min"), ("bit", "bit diameter, in"), ("surface", "surface lines' pressure loss, psi"),
|
|
658
|
+
("surface-ref", "the rate (gal/min) the surface loss was declared at"), ("tools", "motor and MWD pressure drop, psi"),
|
|
659
|
+
("trip", "average stand speed tripping, ft/min"), ("pp", "a pore pressure for the hole when the Pore pressure tab has none, ppg"),
|
|
660
|
+
("fg", "a fracture gradient for the hole when the Pore pressure tab has none, ppg")):
|
|
661
|
+
p_hy.add_argument("--" + k_, type=str, default=None, help=h_)
|
|
662
|
+
p_hy.add_argument("--source", type=str, default="", help="where the mud, string and hole figures came from")
|
|
663
|
+
p_hy.add_argument("--write", action="store_true")
|
|
664
|
+
p_hy.add_argument("--actor", type=str, default="cli")
|
|
665
|
+
p_hy.add_argument("--json", action="store_true")
|
|
666
|
+
|
|
640
667
|
p_td = sub.add_parser("torque-drag", help="the hook load and torque the string should take at each bit depth (soft-string model), the broomstick, and the friction factors that fit the rig's own trips")
|
|
641
668
|
p_td.add_argument("--workspace", type=str, default=None)
|
|
642
669
|
p_td.add_argument("--well", type=str, default=None)
|
|
@@ -2568,6 +2595,46 @@ def main(argv=None) -> int:
|
|
|
2568
2595
|
raise SystemExit(f"decline: {e.args[0] if e.args else e}")
|
|
2569
2596
|
print(_json.dumps(r, default=str) if a.json else _DCA.text(r))
|
|
2570
2597
|
return 0
|
|
2598
|
+
elif a.cmd == "hydraulics":
|
|
2599
|
+
import json as _json
|
|
2600
|
+
from . import hydraulics as _HYD
|
|
2601
|
+
from .cli import _safe_stdout
|
|
2602
|
+
from .workspace import Workspace, WorkspaceError
|
|
2603
|
+
_safe_stdout()
|
|
2604
|
+
try:
|
|
2605
|
+
ws = Workspace(a.workspace) if a.workspace else None
|
|
2606
|
+
scene = "example" if (a.example or ws is None) else "workspace"
|
|
2607
|
+
keys_ = {"mw": "mw_ppg", "flow": "flow_gpm", "bit": "bit_in", "surface": "surface_psi", "surface_ref": "surface_ref_gpm", "tools": "tools_psi",
|
|
2608
|
+
"trip": "trip_ft_min", "pp": "pp_ppg", "fg": "fg_ppg"}
|
|
2609
|
+
vals_ = {v: getattr(a, k) for k, v in keys_.items() if getattr(a, k) is not None}
|
|
2610
|
+
if ws is not None and (vals_ or any(x is not None for x in (a.string, a.hole, a.fann, a.nozzles))):
|
|
2611
|
+
if not a.well:
|
|
2612
|
+
raise ValueError("--well is needed")
|
|
2613
|
+
fann_ = None
|
|
2614
|
+
if a.fann is not None:
|
|
2615
|
+
fann_ = {}
|
|
2616
|
+
for p_ in a.fann.replace(",", " ").split():
|
|
2617
|
+
k_, _, v_ = p_.partition("=")
|
|
2618
|
+
if not v_:
|
|
2619
|
+
raise ValueError(f"--fann takes SPEED=DIAL pairs, e.g. 600=60 300=38, not {p_!r}")
|
|
2620
|
+
fann_[k_.strip()] = v_.strip()
|
|
2621
|
+
c_ = _HYD.set_well(ws, a.actor, a.well, a.source, a.string, a.hole, fann_, a.nozzles, **vals_)
|
|
2622
|
+
print(f"{a.well}: mud {c_['mw_ppg']:g} ppg, {c_['flow_gpm']:g} gpm; string {len(c_.get('string') or [])} section(s), hole {len(c_.get('hole') or [])} section(s); "
|
|
2623
|
+
f"viscometer {' '.join(f'{k}={v:g}' for k, v in (c_.get('fann') or {}).items()) or 'not declared'}")
|
|
2624
|
+
return 0
|
|
2625
|
+
if a.write:
|
|
2626
|
+
if ws is None:
|
|
2627
|
+
raise ValueError("--write needs --workspace")
|
|
2628
|
+
r_ = _HYD.write_report(ws, __version__, a.well, a.actor, scene)
|
|
2629
|
+
print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
|
|
2630
|
+
if r_['paths'].get('pdf'):
|
|
2631
|
+
print(f" printable: {r_['paths']['pdf']}")
|
|
2632
|
+
return 0
|
|
2633
|
+
r = _HYD.page(ws, scene, a.well)
|
|
2634
|
+
except (KeyError, ValueError, OSError, WorkspaceError) as e:
|
|
2635
|
+
raise SystemExit(f"hydraulics: {e.args[0] if e.args else e}")
|
|
2636
|
+
print(_json.dumps(r, default=str) if a.json else _HYD.text(r))
|
|
2637
|
+
return 0
|
|
2571
2638
|
elif a.cmd == "torque-drag":
|
|
2572
2639
|
import json as _json
|
|
2573
2640
|
from . import torque_drag as _TND
|
|
@@ -12660,6 +12660,348 @@ def section_cy_company_sign_in(tmp: str) -> None:
|
|
|
12660
12660
|
"the help page with the Entra ID steps" + (f" - failed: {failed4}" if failed4 else ""))
|
|
12661
12661
|
|
|
12662
12662
|
|
|
12663
|
+
def section_cz_it_review(tmp: str) -> None:
|
|
12664
|
+
"""Section CZ - what an IT department asks: the security overview ships with the program and agrees with the
|
|
12665
|
+
code it describes (the rounds, the sessions, the lock, the port, TLS, every host the program can reach); `gea
|
|
12666
|
+
security`, `gea guide` and `gea help` answer through `python -m gea` as the kit's launcher runs it; in a checkout,
|
|
12667
|
+
the kit tools zip the kit again with its catalog beside it and tell the site how to check it, and the build
|
|
12668
|
+
workflow attests both kits and signs the catalog only when a certificate is configured."""
|
|
12669
|
+
import importlib.util
|
|
12670
|
+
import json as _json
|
|
12671
|
+
import subprocess as _sp
|
|
12672
|
+
import urllib.parse as _up
|
|
12673
|
+
import zipfile as _zf
|
|
12674
|
+
from . import doctor as DR
|
|
12675
|
+
from . import elevation as EL
|
|
12676
|
+
from . import helplib as H
|
|
12677
|
+
from . import seismic_watch as SW
|
|
12678
|
+
from . import service as SVC
|
|
12679
|
+
from . import tls as TLS
|
|
12680
|
+
pkg = Path(__file__).parent
|
|
12681
|
+
checkout = (pkg.parent / "pyproject.toml").exists() and (pkg.parent / "tools" / "build_installer.py").exists()
|
|
12682
|
+
|
|
12683
|
+
# -- CZ1 the overview, and the code it describes -------------------------------------------------------------------
|
|
12684
|
+
text = H.security_text() or ""
|
|
12685
|
+
docs_copy = pkg.parent / "docs" / "IT_SECURITY.md"
|
|
12686
|
+
same = (not docs_copy.exists()) or docs_copy.read_text(encoding="utf-8") == text
|
|
12687
|
+
flat = " ".join(text.split())
|
|
12688
|
+
with open(Path(pkg, "reference", "eia_prices.provenance.json"), encoding="utf-8") as f:
|
|
12689
|
+
eia_hosts = {_up.urlsplit(s["refresh_url"]).hostname for s in _json.load(f)["series"].values() if s.get("refresh_url")}
|
|
12690
|
+
doctor_src = Path(DR.__file__).read_text(encoding="utf-8")
|
|
12691
|
+
hosts = {_up.urlsplit(EL.EPQS_URL).hostname, _up.urlsplit(SW.QUERY_URL).hostname, _up.urlsplit(SW.FEED_URL).hostname, *eia_hosts}
|
|
12692
|
+
hosts |= {"pypi.org"} if "https://pypi.org/pypi/gea-program/json" in doctor_src else {"(the update check moved)"}
|
|
12693
|
+
facts = {
|
|
12694
|
+
"rounds": f"{SVC.PBKDF2_ROUNDS:,}" in flat,
|
|
12695
|
+
"sessions": f"{SVC.SESSION_HOURS} hours" in flat and "256-bit" in flat,
|
|
12696
|
+
"lock": SVC.LOGIN_MAX_FAILURES == 5 and "five failures" in flat and f"{SVC.LOGIN_LOCK_S // 60} minutes" in flat,
|
|
12697
|
+
"port": "TCP 8765" in flat and "8765" in Path(DR.__file__).read_text(encoding="utf-8"),
|
|
12698
|
+
"tls": "TLS 1.2 minimum" in flat and f"last {TLS.EXPIRING_DAYS} days" in flat,
|
|
12699
|
+
"hosts": all(f"`{h}`" in flat for h in hosts),
|
|
12700
|
+
"no_telemetry": "No telemetry" in flat,
|
|
12701
|
+
}
|
|
12702
|
+
# Python 3.14's argparse formats every help string as the parser is built (help % params) and stops on a bare
|
|
12703
|
+
# "%": v0.54.0's first ship stopped at its gate that way while 3.13 passed. The same check, on any Python.
|
|
12704
|
+
import argparse as _ap
|
|
12705
|
+
import contextlib as _cl
|
|
12706
|
+
import io as _io
|
|
12707
|
+
from . import __main__ as _M
|
|
12708
|
+
bad_help = []
|
|
12709
|
+
_orig_add, _orig_sub = _ap.ArgumentParser.add_argument, _ap._SubParsersAction.add_parser
|
|
12710
|
+
_params = {k_: "x" for k_ in ("prog", "default", "dest", "type", "choices", "metavar", "required", "nargs", "const", "option_strings")}
|
|
12711
|
+
|
|
12712
|
+
def _fmt_ok(h):
|
|
12713
|
+
try:
|
|
12714
|
+
h % _params
|
|
12715
|
+
return True
|
|
12716
|
+
except (ValueError, TypeError, KeyError):
|
|
12717
|
+
return False
|
|
12718
|
+
|
|
12719
|
+
def _add(self, *a_, **k_):
|
|
12720
|
+
h_ = k_.get("help")
|
|
12721
|
+
if isinstance(h_, str) and h_ != _ap.SUPPRESS and not _fmt_ok(h_):
|
|
12722
|
+
bad_help.append(f"{self.prog} {'/'.join(a_)}")
|
|
12723
|
+
return _orig_add(self, *a_, **k_)
|
|
12724
|
+
|
|
12725
|
+
def _sub(self, name, **k_):
|
|
12726
|
+
h_ = k_.get("help")
|
|
12727
|
+
if isinstance(h_, str) and not _fmt_ok(h_):
|
|
12728
|
+
bad_help.append(f"gea {name}")
|
|
12729
|
+
return _orig_sub(self, name, **k_)
|
|
12730
|
+
_ap.ArgumentParser.add_argument, _ap._SubParsersAction.add_parser = _add, _sub
|
|
12731
|
+
try:
|
|
12732
|
+
with _cl.redirect_stdout(_io.StringIO()), _cl.redirect_stderr(_io.StringIO()):
|
|
12733
|
+
try:
|
|
12734
|
+
_M.main(["wells"])
|
|
12735
|
+
except SystemExit:
|
|
12736
|
+
pass
|
|
12737
|
+
finally:
|
|
12738
|
+
_ap.ArgumentParser.add_argument, _ap._SubParsersAction.add_parser = _orig_add, _orig_sub
|
|
12739
|
+
facts["help_strings"] = not bad_help
|
|
12740
|
+
r_sec = _sp.run([sys.executable, "-m", "gea", "security"], capture_output=True, text=True, encoding="utf-8", errors="replace")
|
|
12741
|
+
r_guide = _sp.run([sys.executable, "-m", "gea", "guide"], capture_output=True, text=True, encoding="utf-8", errors="replace")
|
|
12742
|
+
r_help = _sp.run([sys.executable, "-m", "gea", "help", "https"], capture_output=True, text=True, encoding="utf-8", errors="replace")
|
|
12743
|
+
bad = [k for k, v in facts.items() if not v]
|
|
12744
|
+
ok(text.startswith("# GEA-Program for an IT department") and same and not bad
|
|
12745
|
+
and r_sec.returncode == 0 and "GEA-Program for an IT department" in r_sec.stdout
|
|
12746
|
+
and r_guide.returncode == 0 and "HOW TO TEST GEA" in r_guide.stdout and r_help.returncode == 0 and "**The command**" in r_help.stdout,
|
|
12747
|
+
"CZ1 the security overview ships in the package (identical to docs/IT_SECURITY.md) and agrees with the code: the password hashing rounds, the "
|
|
12748
|
+
"session length and token size, the sign-in lock, the one port, the TLS floor and the expiry warning, every host the program can reach "
|
|
12749
|
+
"(ComCat, the elevation service, the price series, PyPI); `python -m gea security`, `guide` and `help` answer as the kit's own launcher "
|
|
12750
|
+
"runs them; every option's help text formats the way Python 3.14 checks it as the parser is built"
|
|
12751
|
+
+ (f" - disagrees: {bad}" if bad else "") + (f" - help texts: {bad_help}" if bad_help else ""))
|
|
12752
|
+
|
|
12753
|
+
# -- CZ2 the kit is zipped again with its catalog, and says how to check it ----------------------------------------------
|
|
12754
|
+
if checkout:
|
|
12755
|
+
spec = importlib.util.spec_from_file_location("cz_installer", str(pkg.parent / "tools" / "build_installer.py"))
|
|
12756
|
+
bi = importlib.util.module_from_spec(spec); spec.loader.exec_module(bi)
|
|
12757
|
+
kit = Path(tmp, "cz_kit", "gea-program-9.9.9-win64"); (kit / "python").mkdir(parents=True)
|
|
12758
|
+
(kit / "MANIFEST.json").write_text("{}", encoding="utf-8"); (kit / "python" / "python.exe").write_bytes(b"MZ stand-in")
|
|
12759
|
+
(kit / "SHA256SUMS.txt").write_text("x MANIFEST.json\n", encoding="utf-8")
|
|
12760
|
+
cat = Path(tmp, "cz_kit", "gea-program-9.9.9-win64.cat"); cat.write_bytes(b"0\x82 a catalog stand-in")
|
|
12761
|
+
zp = bi.rezip(kit, [cat])
|
|
12762
|
+
with _zf.ZipFile(zp) as z:
|
|
12763
|
+
names = sorted(z.namelist())
|
|
12764
|
+
cat_back = z.read("gea-program-9.9.9-win64.cat")
|
|
12765
|
+
try:
|
|
12766
|
+
bi.rezip(Path(tmp, "cz_kit"), [])
|
|
12767
|
+
not_kit = "accepted"
|
|
12768
|
+
except SystemExit as e:
|
|
12769
|
+
not_kit = str(e)
|
|
12770
|
+
readme = bi.README.format(version="9.9.9", target="Windows x64", pyline="p", install="i", start="s", verify="v", port="8765",
|
|
12771
|
+
service="sv", stop="st", uninstall="u", workspace="w", check=bi.WIN_CHECK.format(version="9.9.9"))
|
|
12772
|
+
build_src = (pkg.parent / "tools" / "build_installer.py").read_text(encoding="utf-8")
|
|
12773
|
+
c2 = {
|
|
12774
|
+
"rezip": names == ["gea-program-9.9.9-win64.cat", "gea-program-9.9.9-win64/MANIFEST.json", "gea-program-9.9.9-win64/SHA256SUMS.txt",
|
|
12775
|
+
"gea-program-9.9.9-win64/python/python.exe"] and cat_back == cat.read_bytes() and "not a kit folder" in not_kit,
|
|
12776
|
+
"readme": all(x in readme for x in ("gh attestation verify gea-program-9.9.9-win64.zip -R Daniel8Murphy0007/GEA-Program",
|
|
12777
|
+
"Test-FileCatalog -CatalogFilePath .\\gea-program-9.9.9-win64.cat -Path .\\gea-program-9.9.9-win64",
|
|
12778
|
+
"Get-AuthenticodeSignature", "IT_SECURITY.md", "gea help https"))
|
|
12779
|
+
and "368 checks" not in readme and "behind the site's TLS proxy" not in readme,
|
|
12780
|
+
"ships_overview": "docs' / 'IT_SECURITY.md'" in build_src and "kit / 'IT_SECURITY.md'" in build_src,
|
|
12781
|
+
}
|
|
12782
|
+
where = "the checkout's kit tools"
|
|
12783
|
+
else:
|
|
12784
|
+
c2 = {"installed": bool(H.security_text())}
|
|
12785
|
+
where = "an installed copy: the kit tools are checked in the checkout"
|
|
12786
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
12787
|
+
ok(not failed2,
|
|
12788
|
+
f"CZ2 ({where}) the kit is zipped again from its folder with its file catalog beside the folder, every file kept; a folder that is not a kit "
|
|
12789
|
+
"is refused; the kit's README tells the site how to check the build's attestation and every file against the catalog and its signature, "
|
|
12790
|
+
"and carries the IT overview" + (f" - failed: {failed2}" if failed2 else ""))
|
|
12791
|
+
|
|
12792
|
+
# -- CZ3 the build workflow: attested, signed only with a certificate, never signed in name only ----------------------------
|
|
12793
|
+
wf = pkg.parent / ".github" / "workflows" / "build-installer.yml"
|
|
12794
|
+
if wf.exists():
|
|
12795
|
+
y = wf.read_text(encoding="utf-8")
|
|
12796
|
+
win = y[y.index("windows-kit:"):y.index("linux-kit:")]
|
|
12797
|
+
lin = y[y.index("linux-kit:"):]
|
|
12798
|
+
order = [win.index(s) for s in ("New-FileCatalog", "azure/artifact-signing-action", "signtool", "Test-FileCatalog", "--rezip", "call install.cmd",
|
|
12799
|
+
"attest-build-provenance", "attach-kit.sh")]
|
|
12800
|
+
c3 = {
|
|
12801
|
+
"permissions": "id-token: write" in y and "attestations: write" in y and "contents: write" in y,
|
|
12802
|
+
"attested": "actions/attest-build-provenance" in win and "actions/attest-build-provenance" in lin
|
|
12803
|
+
and "subject-path: dist/gea-program-*-win64.zip" in win and "subject-path: dist/gea-program-*-linux-*.zip" in lin,
|
|
12804
|
+
"conditional": "if: env.ARTIFACT_SIGNING_ACCOUNT != ''" in win and "if: env.ARTIFACT_SIGNING_ACCOUNT == '' && env.SIGNING_PFX_BASE64 != ''" in win
|
|
12805
|
+
and "secrets.ARTIFACT_SIGNING_ACCOUNT" in win and "secrets.SIGNING_PFX_BASE64" in win,
|
|
12806
|
+
"checked": "-CatalogVersion 2.0" in win and "throw 'the catalog does not match the kit'" in win
|
|
12807
|
+
and "throw 'a certificate is configured but the catalog is not validly signed'" in win and "ships unsigned" in win
|
|
12808
|
+
and "Remove-Item $pfx" in win and "/tr http://timestamp" in win and "timestamp-rfc3161" in win,
|
|
12809
|
+
"order": order == sorted(order),
|
|
12810
|
+
"policy": (pkg.parent / "SECURITY.md").exists() and "Report a vulnerability" in " ".join((pkg.parent / "SECURITY.md").read_text(encoding="utf-8").split()),
|
|
12811
|
+
}
|
|
12812
|
+
where3 = "the checkout's workflow"
|
|
12813
|
+
else:
|
|
12814
|
+
c3 = {"installed": True}
|
|
12815
|
+
where3 = "an installed copy: the workflow is checked in the checkout"
|
|
12816
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
12817
|
+
ok(not failed3,
|
|
12818
|
+
f"CZ3 ({where3}) both kits are attested by the build that made them; the Windows kit's file catalog is made, signed by Azure Artifact "
|
|
12819
|
+
"Signing or a PFX only when its secrets are configured (timestamped; the PFX removed), checked against the kit - a mismatch, or a "
|
|
12820
|
+
"configured certificate that did not sign, stops the build - then zipped beside the kit before the kit is installed and its gate run; "
|
|
12821
|
+
"SECURITY.md says how to report a vulnerability" + (f" - failed: {failed3}" if failed3 else ""))
|
|
12822
|
+
|
|
12823
|
+
|
|
12824
|
+
def section_da_hydraulics(tmp: str) -> None:
|
|
12825
|
+
"""Section DA - hydraulics and ECD: the power law's losses against their closed forms (Hagen-Poiseuille, the slot,
|
|
12826
|
+
Blasius) and API RP 13D's field-unit forms, the bit against its classic formula; a record whose standpipe pressure is
|
|
12827
|
+
the model's plus a known amount, read back; the worked example's ECD against its window, surge and swab; the API, the
|
|
12828
|
+
CLI, the report, the dossier, the tab and the help."""
|
|
12829
|
+
import http.cookiejar
|
|
12830
|
+
import math as _m
|
|
12831
|
+
import subprocess as _sp
|
|
12832
|
+
import urllib.request
|
|
12833
|
+
import urllib.error
|
|
12834
|
+
import zipfile as _zf
|
|
12835
|
+
from . import __version__ as _ver
|
|
12836
|
+
from . import dossier as DZ
|
|
12837
|
+
from . import helplib as H
|
|
12838
|
+
from . import hydraulics as HY
|
|
12839
|
+
from . import well3d as W
|
|
12840
|
+
from .workspace import Workspace
|
|
12841
|
+
from .service import Service, Users
|
|
12842
|
+
pkg = Path(__file__).parent
|
|
12843
|
+
d = Path(tmp, "hydraulics"); d.mkdir()
|
|
12844
|
+
|
|
12845
|
+
# -- DA1 by hand --------------------------------------------------------------------------------------------------------
|
|
12846
|
+
mu, q = 0.03, 50.0
|
|
12847
|
+
lp = HY.pipe_loss(q, 4.0, 1000.0, 8.33, 1.0, mu)
|
|
12848
|
+
Q, D = q * HY.GPM, 4.0 * HY.IN
|
|
12849
|
+
V = Q / (_m.pi * D * D / 4.0)
|
|
12850
|
+
hp = 32.0 * mu * V * 1000.0 * HY.FT / D ** 2 / HY.PSI
|
|
12851
|
+
la = HY.annulus_loss(q, 8.5, 5.0, 1000.0, 8.33, 1.0, mu)
|
|
12852
|
+
Va = q * HY.GPM / (_m.pi * ((8.5 * HY.IN) ** 2 - (5.0 * HY.IN) ** 2) / 4.0)
|
|
12853
|
+
slot = 48.0 * mu * Va * 1000.0 * HY.FT / (3.5 * HY.IN) ** 2 / HY.PSI
|
|
12854
|
+
f_t = HY.friction(1e5, 1.0, 16.0)[0]
|
|
12855
|
+
rh = HY.rheology({"600": 56, "300": 36, "200": 28, "100": 20, "6": 6, "3": 5})
|
|
12856
|
+
n, K = rh["n_pipe"], rh["K_pipe"]
|
|
12857
|
+
api = HY.pipe_loss(550.0, 4.276, 1000.0, 10.5, n, K)
|
|
12858
|
+
Vf = 24.51 * 550.0 / 4.276 ** 2 # ft/min
|
|
12859
|
+
mu_api = 100.0 * (5.11 * 56 / 1022 ** n) * (1.6 * Vf / 4.276) ** (n - 1) * ((3 * n + 1) / (4 * n)) ** n
|
|
12860
|
+
re_api = 15.467 * Vf * 4.276 * 10.5 / mu_api
|
|
12861
|
+
dp_api = 1.076 * 10.5 * Vf ** 2 * api["f"] / 1e5 / 4.276 * 1000.0
|
|
12862
|
+
bt = HY.bit(550.0, 10.5, [13] * 6, 8.5)
|
|
12863
|
+
refused = []
|
|
12864
|
+
for bad in ({"600": 56}, {"600": 30, "300": 36}, {"600": 56, "300": 36, "450": 40}, {"600": 400, "300": 36}):
|
|
12865
|
+
try:
|
|
12866
|
+
HY.rheology(bad); refused.append(False)
|
|
12867
|
+
except ValueError:
|
|
12868
|
+
refused.append(True)
|
|
12869
|
+
for fn, arg in ((HY.check_string, [{"what": "x", "length_ft": 100, "od_in": 5, "id_in": 6}]), (HY.check_hole, [{"what": "a", "bottom_md_ft": 1000, "id_in": 8},
|
|
12870
|
+
{"what": "b", "bottom_md_ft": 500, "id_in": 8}, {"what": "c", "id_in": 8}]),
|
|
12871
|
+
(HY.parse_sections, "BHA:300:6.75"), (HY.annulus_loss, None)):
|
|
12872
|
+
try:
|
|
12873
|
+
fn(arg, "string") if fn is HY.parse_sections else (HY.annulus_loss(500, 5.0, 6.0, 100, 10, 0.5, 0.5) if fn is HY.annulus_loss else fn(arg))
|
|
12874
|
+
refused.append(False)
|
|
12875
|
+
except ValueError:
|
|
12876
|
+
refused.append(True)
|
|
12877
|
+
c1 = {
|
|
12878
|
+
"newtonian": abs(lp["psi"] / hp - 1) < 1e-4 and lp["regime"] == "laminar" and abs(la["psi"] / slot - 1) < 1e-9 and abs(f_t / (0.0791 / 1e5 ** 0.25) - 1) < 0.01,
|
|
12879
|
+
"rheology": rh["pv_cp"] == 20 and rh["yp_lbf_100ft2"] == 16 and abs(rh["n_pipe"] - _m.log2(56 / 36)) < 1e-4 and abs(rh["n_ann"] - _m.log(20 / 5) / _m.log(170.2 / 5.11)) < 1e-4
|
|
12880
|
+
and rh["tau0_lbf_100ft2"] == 4 and rh["annulus_from"] == "100 and 3 rpm",
|
|
12881
|
+
"api_forms": abs(api["v_ft_min"] / Vf - 1) < 1e-3 and abs(api["mu_cp"] / mu_api - 1) < 1e-3 and abs(api["re"] / re_api - 1) < 2e-3 and abs(api["psi"] / dp_api - 1) < 1e-3,
|
|
12882
|
+
"bit": abs(bt["psi"] / (156.5 * 10.5 * 550.0 ** 2 / (6 * 13 ** 2) ** 2) - 1) < 1e-3 and abs(bt["tfa_in2"] - 6 * _m.pi / 4 * (13 / 32) ** 2) < 1e-3
|
|
12883
|
+
and abs(bt["hhp"] - bt["psi"] * 550 / 1714) < 0.1,
|
|
12884
|
+
"refused": all(refused) and len(refused) == 8,
|
|
12885
|
+
}
|
|
12886
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
12887
|
+
ok(not failed1,
|
|
12888
|
+
"DA1 the losses by hand: a Newtonian fluid's laminar loss in a pipe is Hagen-Poiseuille's and in the annulus the slot's, exactly; its turbulent "
|
|
12889
|
+
"friction factor Blasius's to 1 %; the viscometer's PV, YP, the pipe's n from 600/300 and the annulus's from 100/3; the velocity, effective "
|
|
12890
|
+
"viscosity, Reynolds number and loss equal to API RP 13D's field-unit forms; the bit's loss the classic 156.5 rho Q^2 / (sum d^2)^2; readings "
|
|
12891
|
+
"missing, falling or out of range, a bore wider than the pipe, a hole out of order, a short section and a pipe wider than its hole refused"
|
|
12892
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
12893
|
+
|
|
12894
|
+
# -- DA2 a record made by the model, read back ---------------------------------------------------------------------------
|
|
12895
|
+
f = HY.selftest_files(str(d))
|
|
12896
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
12897
|
+
wid = ws.add_well_file(f["record"], display="HY-1", actor="tester")["id"]
|
|
12898
|
+
ws.set_well(wid, surface={"lat": 31.42, "lon": -104.10, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
|
|
12899
|
+
no_survey = HY.page(ws, "workspace", wid)["detail"]
|
|
12900
|
+
W.attach_trajectory(ws, wid, "s.csv", Path(f["survey"]).read_bytes(), "tester", "true", "ft", None, None, None, "test survey")
|
|
12901
|
+
no_decl = HY.page(ws, "workspace", wid)["detail"]
|
|
12902
|
+
c = f["config"]
|
|
12903
|
+
HY.set_well(ws, "tester", wid, "mud report (test)", string=c["string"], hole=c["hole"], fann=c["fann"], nozzles_32=c["nozzles_32"], mw_ppg=c["mw_ppg"],
|
|
12904
|
+
flow_gpm=500, bit_in=8.75, tools_psi=400, pp_ppg=9.5, fg_ppg=14.5)
|
|
12905
|
+
r = HY.page(ws, "workspace", wid)["result"]
|
|
12906
|
+
rc = r["record"]
|
|
12907
|
+
cc = r["circulating"]
|
|
12908
|
+
c2 = {
|
|
12909
|
+
"needs": "survey" in no_survey and "string" in no_decl and "viscometer" in no_decl,
|
|
12910
|
+
"record": rc and rc["bins"] >= 20 and abs(rc["median_difference_psi"] - f["offset_psi"]) <= 10.0 and rc["spread_psi"] < 30.0,
|
|
12911
|
+
"parts": abs(sum(cc["parts"].values()) - cc["spp_psi"]) < 1.0 and cc["parts"]["tools"] == 400.0 and cc["parts"]["surface"] == 0.0,
|
|
12912
|
+
"ecd": cc["ecd_bit_ppg"] > c["mw_ppg"] and all(e_ >= c["mw_ppg"] for e_ in r["ecd"]["ecd_ppg"])
|
|
12913
|
+
and abs(cc["ecd_bit_ppg"] - (c["mw_ppg"] + cc["parts"]["annulus"] / (0.052 * cc["tvd_bit_ft"]))) < 0.002,
|
|
12914
|
+
"window": r["window_how"].startswith("declared") and r["min_fg_margin"]["ppg"] == min(x for x in r["ecd"]["margin_fg_ppg"] if x is not None),
|
|
12915
|
+
}
|
|
12916
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
12917
|
+
ok(not failed2,
|
|
12918
|
+
"DA2 a drilling record made by the model - drilling from 4,000 to 10,000 ft at 500 gpm and circulating at 350 gpm, its standpipe pressure the "
|
|
12919
|
+
"model's plus 120 psi and noise - read back while pumping: the median difference 120 psi to within 10 over 20 or more depth bins; the standpipe "
|
|
12920
|
+
"pressure the sum of its parts; the ECD at the bit the mud weight plus the annular loss over 0.052 TVD; a well without a survey or its "
|
|
12921
|
+
"declarations says what it needs" + (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
12922
|
+
|
|
12923
|
+
# -- DA3 the worked example ----------------------------------------------------------------------------------------------
|
|
12924
|
+
ex = HY.example()
|
|
12925
|
+
t = ex["trips"]
|
|
12926
|
+
s_hi = HY.surge_swab(ex["config"], ex["rheology"], ex["td_ft"], 180.0, lambda z: ex["tvd_td_ft"] * z / ex["td_ft"])
|
|
12927
|
+
s_lo = HY.surge_swab(ex["config"], ex["rheology"], ex["td_ft"], 90.0, lambda z: ex["tvd_td_ft"] * z / ex["td_ft"])
|
|
12928
|
+
rates = ex["by_rate"]
|
|
12929
|
+
c3 = {
|
|
12930
|
+
"example": ex["well"] == "15/9-F-10" and abs(ex["td_ft"] - 5300.07 / 0.3048) < 5.0 and "not from Volve" in ex["config"]["source"]
|
|
12931
|
+
and 2000 < ex["circulating"]["spp_psi"] < 5000 and ex["circulating"]["ecd_bit_ppg"] > ex["config"]["mw_ppg"],
|
|
12932
|
+
"rates": all(b["spp_psi"] > a["spp_psi"] and b["ecd_bit_ppg"] >= a["ecd_bit_ppg"] for a, b in zip(rates, rates[1:])),
|
|
12933
|
+
"surge": all(su > ex["config"]["mw_ppg"] > sw for su, sw in zip(t["surge_ppg"], t["swab_ppg"])) and s_hi["psi"] > s_lo["psi"] > 0,
|
|
12934
|
+
"speeds": all(v is not None and v > 0 for v in t["run_max_ft_min"] + t["pull_max_ft_min"])
|
|
12935
|
+
and HY.surge_swab(ex["config"], ex["rheology"], ex["td_ft"], t["run_max_ft_min"][-1], lambda z: ex["tvd_td_ft"] * z / ex["td_ft"])["surge_ppg"] <= 14.0 + 0.02,
|
|
12936
|
+
}
|
|
12937
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
12938
|
+
ok(not failed3,
|
|
12939
|
+
"DA3 the worked example, Volve 15/9-F-10's survey to its 5,300 m TD with a mud, string and hole declared for the example: the standpipe pressure "
|
|
12940
|
+
"and the ECD rising with the rate; surge above and swab below the mud weight at every bit depth, more at a faster trip; the fastest run keeping "
|
|
12941
|
+
"the surge within the declared fracture gradient" + (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
12942
|
+
|
|
12943
|
+
# -- DA4 the API, the CLI, the report, the dossier, the tab and the help -------------------------------------------------
|
|
12944
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
12945
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
12946
|
+
base_url = svc.url.rstrip("/")
|
|
12947
|
+
|
|
12948
|
+
def opener(name, pw_):
|
|
12949
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
12950
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
|
|
12951
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
12952
|
+
return o
|
|
12953
|
+
|
|
12954
|
+
def call(o, method, path, body=None):
|
|
12955
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
12956
|
+
if method == "POST":
|
|
12957
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
12958
|
+
try:
|
|
12959
|
+
with o.open(rq, timeout=120) as rr_:
|
|
12960
|
+
return rr_.status, json.loads(rr_.read())
|
|
12961
|
+
except urllib.error.HTTPError as e:
|
|
12962
|
+
return e.code, json.loads(e.read())
|
|
12963
|
+
try:
|
|
12964
|
+
V_, O_ = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
12965
|
+
s_vg, vg = call(V_, "GET", "/api/hydraulics?well=" + wid)
|
|
12966
|
+
s_ve, ve = call(V_, "GET", "/api/hydraulics?scene=example")
|
|
12967
|
+
s_vs, _ = call(V_, "POST", "/api/hydraulics/settings", {"well_id": wid, "mw_ppg": 11.2, "source": "x"})
|
|
12968
|
+
s_os, os_ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "mw_ppg": "11.2", "r600": "62", "r300": "40", "r100": "22", "r3": "6",
|
|
12969
|
+
"nozzles_32": "13 13 13 13 13", "hole": "9-5/8 casing:3500:8.681; open hole::8.75", "source": "mud report (test)"})
|
|
12970
|
+
s_bf, _ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "r600": "30", "r300": "40", "source": "x"})
|
|
12971
|
+
s_bh, _ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "flow_gpm": 9000, "source": "x"})
|
|
12972
|
+
s_ns, _ = call(O_, "POST", "/api/hydraulics/settings", {"well_id": wid, "mw_ppg": 12})
|
|
12973
|
+
s_vw, _ = call(V_, "POST", "/api/hydraulics/write", {"well_id": wid})
|
|
12974
|
+
s_ow, ow = call(O_, "POST", "/api/hydraulics/write", {"well_id": wid})
|
|
12975
|
+
finally:
|
|
12976
|
+
svc.stop()
|
|
12977
|
+
cli_x = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--example"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12978
|
+
cli_s = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--workspace", wsp, "--well", wid, "--flow", "450", "--fann", "600=60 300=38 100=21 3=6",
|
|
12979
|
+
"--source", "mud report (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12980
|
+
cli_b = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--workspace", wsp, "--well", wid, "--fann", "600:60", "--source", "x"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12981
|
+
cli_t = _sp.run([sys.executable, "-m", "gea", "hydraulics", "--workspace", wsp, "--well", wid], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12982
|
+
dz = DZ.build(ws, "tester", _ver, None)
|
|
12983
|
+
names = _zf.ZipFile(Path(ws.reports_dir, dz["path"])).namelist()
|
|
12984
|
+
audit = Path(ws.audit_path).read_text(encoding="utf-8")
|
|
12985
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
12986
|
+
c4 = {
|
|
12987
|
+
"api": s_vg == 200 and vg["result"]["record"]["bins"] >= 20 and s_ve == 200 and ve["result"]["well"] == "15/9-F-10" and s_vs == 403 and s_os == 200
|
|
12988
|
+
and os_["fann"] == {"600": 62.0, "300": 40.0, "100": 22.0, "3": 6.0} and os_["nozzles_32"] == [13.0] * 5 and os_["mw_ppg"] == 11.2
|
|
12989
|
+
and s_bf == 400 and s_bh == 400 and s_ns == 400 and s_vw == 403 and s_ow == 200 and ow["report_id"].startswith("HYD-"),
|
|
12990
|
+
"cli": cli_x.returncode == 0 and "standpipe" in cli_x.stdout and "ECD at the bit" in cli_x.stdout and cli_s.returncode == 0 and "450 gpm" in cli_s.stdout
|
|
12991
|
+
and cli_b.returncode != 0 and "SPEED=DIAL" in (cli_b.stderr + cli_b.stdout) and cli_t.returncode == 0 and "against the record" in cli_t.stdout,
|
|
12992
|
+
"report": PR_read_ok(Path(ws.reports_dir, "wells", wid, "hydraulics.pdf")) and all(x in audit for x in ("hydraulics.declare", "report.hydraulics")),
|
|
12993
|
+
"dossier": "declarations/drillstring/hydraulics.json" in names and f"results/hydraulics/{wid}.json" in names,
|
|
12994
|
+
"page": all(x in page_src for x in ("VIEWS.hydraulics", "['#/hydraulics', 'Hydraulics & ECD']", "/api/hydraulics/settings", "hyChart", "hySppChart")),
|
|
12995
|
+
"help": not H.check("hydraulics") and H.VIEW_TOPIC.get("hydraulics") == "hydraulics",
|
|
12996
|
+
}
|
|
12997
|
+
failed4 = [k for k, v in c4.items() if not v]
|
|
12998
|
+
ok(not failed4,
|
|
12999
|
+
"DA4 the tab's data for a viewer (the well, the worked example); the declarations by an operator only, with a source, readings and rates in "
|
|
13000
|
+
"range; `gea hydraulics` prints the example and a well and declares the viscometer as SPEED=DIAL; the report with its PDF, audited; the "
|
|
13001
|
+
"signed dossier carries the declarations and the result; the tab and the help page"
|
|
13002
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
13003
|
+
|
|
13004
|
+
|
|
12663
13005
|
def PR_read_ok(path) -> bool:
|
|
12664
13006
|
from . import pdf_report as PR
|
|
12665
13007
|
try:
|
|
@@ -12774,6 +13116,8 @@ def main() -> int:
|
|
|
12774
13116
|
section_cw_decline(tmp)
|
|
12775
13117
|
section_cx_https(tmp)
|
|
12776
13118
|
section_cy_company_sign_in(tmp)
|
|
13119
|
+
section_cz_it_review(tmp)
|
|
13120
|
+
section_da_hydraulics(tmp)
|
|
12777
13121
|
if _FAILS:
|
|
12778
13122
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
12779
13123
|
for f in _FAILS:
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
gea model-cards ... gea store-forward ... gea sla-report ... gea fat-sat ...
|
|
11
11
|
gea help [topic] the help library, by the job (gea help drift); same text as the dashboard's panels
|
|
12
12
|
gea guide the click-by-click tester guide
|
|
13
|
+
gea security what an IT department asks before installing, answered (docs/IT_SECURITY.md)
|
|
13
14
|
gea docs where the catalogue and the reports live on this machine
|
|
14
15
|
gea gui the desktop window (desktop extra)
|
|
15
16
|
gea accept the product gate
|
|
@@ -23,7 +24,7 @@ import os
|
|
|
23
24
|
import sys
|
|
24
25
|
from pathlib import Path
|
|
25
26
|
|
|
26
|
-
FRONT = {'quickstart', 'survey', 'guide', 'docs', 'gui', 'help', '-h', '--help'}
|
|
27
|
+
FRONT = {'quickstart', 'survey', 'guide', 'security', 'docs', 'gui', 'help', '-h', '--help'}
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def _pkg_root() -> Path:
|
|
@@ -62,6 +63,16 @@ def cmd_guide(_argv) -> int:
|
|
|
62
63
|
return 0
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
def cmd_security(_argv) -> int:
|
|
67
|
+
from .helplib import security_text
|
|
68
|
+
text = security_text() # ships inside the package (gea/help/IT_SECURITY.md)
|
|
69
|
+
if text is None:
|
|
70
|
+
print('the security overview is missing from this installation; reinstall: python -m pip install --upgrade --force-reinstall "gea-program[live]"')
|
|
71
|
+
return 2
|
|
72
|
+
print(text)
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
|
|
65
76
|
def cmd_help(argv) -> int:
|
|
66
77
|
from .helplib import page, index_text, topics
|
|
67
78
|
if not argv:
|
|
@@ -141,6 +152,8 @@ def main(argv=None) -> int:
|
|
|
141
152
|
return cmd_survey(rest)
|
|
142
153
|
if cmd == 'guide':
|
|
143
154
|
return cmd_guide(rest)
|
|
155
|
+
if cmd == 'security':
|
|
156
|
+
return cmd_security(rest)
|
|
144
157
|
if cmd == 'docs':
|
|
145
158
|
return cmd_docs(rest)
|
|
146
159
|
if cmd == 'gui':
|
|
@@ -278,7 +278,7 @@ def _gather(ws, well_id: Optional[str]) -> Dict[str, List[tuple]]:
|
|
|
278
278
|
('economics', 'invoices', 'terms.json'), ('economics', 'invoices', 'files.json'), ('drillstring', 'rig_time.json'),
|
|
279
279
|
('drillstring', 'casing.json'), ('wellpath', 'collision.json'), ('wellpath', 'survey_program.json'),
|
|
280
280
|
('completion', 'frac', 'stages.json'), ('completion', 'frac', 'settings.json'), ('geomech', 'pore_pressure.json'),
|
|
281
|
-
('drillstring', 'torque_drag.json'), ('economics', 'production', 'production.json')):
|
|
281
|
+
('drillstring', 'torque_drag.json'), ('economics', 'production', 'production.json'), ('drillstring', 'hydraulics.json')):
|
|
282
282
|
p = os.path.join(ws.path, *rel)
|
|
283
283
|
if os.path.isfile(p):
|
|
284
284
|
out_list.append((p, 'declarations/' + '/'.join(rel)))
|
|
@@ -351,6 +351,17 @@ def _results(ws, well_id: Optional[str]) -> List[tuple]:
|
|
|
351
351
|
indent=1, sort_keys=True, default=str).encode('utf-8')))
|
|
352
352
|
except Exception as e:
|
|
353
353
|
out.append(('results/decline_not_run.txt', f'the decline curves did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
|
|
354
|
+
try:
|
|
355
|
+
from . import hydraulics as HYD
|
|
356
|
+
declared = (HYD._load(ws).get('wells') or {})
|
|
357
|
+
for wid in wells:
|
|
358
|
+
if wid in declared:
|
|
359
|
+
r = HYD.page(ws, 'workspace', wid).get('result')
|
|
360
|
+
if r:
|
|
361
|
+
out.append((f'results/hydraulics/{wid}.json', json.dumps({k: v for k, v in r.items() if k != 'measured'}, indent=1, sort_keys=True,
|
|
362
|
+
default=str).encode('utf-8')))
|
|
363
|
+
except Exception as e:
|
|
364
|
+
out.append(('results/hydraulics_not_run.txt', f'the hydraulics model did not run: {type(e).__name__}: {e}\n'.encode('utf-8')))
|
|
354
365
|
try:
|
|
355
366
|
from . import torque_drag as TND
|
|
356
367
|
declared = (TND._load(ws).get('wells') or {})
|