gea-program 0.52.0__tar.gz → 0.54.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.52.0 → gea_program-0.54.0}/PKG-INFO +24 -1
- {gea_program-0.52.0 → gea_program-0.54.0}/README.md +23 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/__init__.py +1 -1
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/__main__.py +94 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/acceptance_tests.py +336 -0
- gea_program-0.54.0/gea/decline.py +553 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/dossier.py +27 -3
- gea_program-0.54.0/gea/help/decline.md +17 -0
- gea_program-0.54.0/gea/help/torque-drag.md +17 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/helplib.py +3 -1
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/service.py +37 -0
- gea_program-0.54.0/gea/torque_drag.py +509 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/web/app.html +118 -1
- {gea_program-0.52.0 → gea_program-0.54.0}/gea_program.egg-info/PKG-INFO +24 -1
- {gea_program-0.52.0 → gea_program-0.54.0}/gea_program.egg-info/SOURCES.txt +4 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/pyproject.toml +1 -1
- {gea_program-0.52.0 → gea_program-0.54.0}/LICENSE +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/alerts.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/backup.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/bench.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/blind_harness.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog_map.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/catalog_register.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/certificates.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/cli.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/client_reports.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/collision.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/config_versioning.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/conformance.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/control_room.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/correlation.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/dashboard.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/declarations.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/deviation.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/dlis.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/dnp3.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/doctor.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/dossier_verify.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/drill_string.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/earth_model.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/economics.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/ed25519.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/edr.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/elevation.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/etp.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/example_register_map.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/fat_sat.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/files.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/fixtures/comcat_mahoning_1990-2026.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/follower.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/forward_model.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/frac_hits.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/friction_priors.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/gamma.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/geodesy.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/geomag.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/hartip.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/alarms.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/alerts.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/backup.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/catalog.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/charts.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/collision.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/conformance.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/control-room.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/data-in.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/declare.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/dlis.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/doctor.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/dossier.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/drift.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/drillstring.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/economics.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/edr.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/etp.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/files.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/frac-hits.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/historian.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/instruments.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/interval.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/invoice-check.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/map.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/month-end.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/notifications.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/opseis.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/osdu.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/pad-summary.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/patches.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/penetrator.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/plan.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/pore-pressure.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/quality.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/report-history.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/rig-time.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/seismic.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/setup.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/simulation.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/site.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/sites.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/slip.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/soak.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/sra.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/start.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/structure.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/tools.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/transients.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/vibration.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/well-report.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/help/well3d.md +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/housekeeping.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/interval.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/invoice_check.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/jobs.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/live_ports.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/loadtest.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/modbus.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/model_card.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/month_end.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/notify.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/op_seismicity.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/opcua_port.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/operator_app.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/osdu.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/pad_summary.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/patches.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/pdf_report.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/penetrator.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/permits.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/piwebapi.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/pore_pressure.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/ports.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/ppdm.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/project.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/quakeml.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reconciler.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/WMM2025.COF +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/eia_prices.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/eia_wti_daily.csv.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/iscwsa_rev4_example.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/iscwsa_rev5_example.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/l0701_sonic_gr.las.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/l0701_sonic_gr.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/mahoning_example.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/mud_motor_specs.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/owsg_tool_models.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/penetrator_ratings.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/seismicity_protocols.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_3d.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_f4_interpretation.las.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_f4_logs.provenance.json +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_f4_lwd.las.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_surfaces.json.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_traj_F-10.xml.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_traj_F-4.xml.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/report_history.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/rig_state.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/sample_record.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/sbom.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seedlink.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/segy.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_array.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_film.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_response.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_track.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/seismic_watch.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/series_cache.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/service_life.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/shapefile.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/shell.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/shut_in.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/simulation.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/site_map.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/sla_report.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/slip.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/soak.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/sra.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/store_forward.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/strata_join.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/structure.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/supervisor.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/survey_view.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/telemetry.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/tool_library.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/transient.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/vibration.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/well3d.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/well_assembler.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/well_report.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/wellplan.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/wellpos.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/wits0.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/witsml.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/workspace.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea/xlsx_read.py +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.52.0 → gea_program-0.54.0}/setup.cfg +0 -0
- {gea_program-0.52.0 → gea_program-0.54.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.54.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
|
|
@@ -648,6 +648,29 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
648
648
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
649
649
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
650
650
|
|
|
651
|
+
### Decline curves
|
|
652
|
+
|
|
653
|
+
Each well's monthly production fitted by Arps's curves - the modified
|
|
654
|
+
hyperbolic, turned exponential at a terminal decline - forecast month by
|
|
655
|
+
month to its economic limit and valued: the estimated ultimate recovery,
|
|
656
|
+
and the net cash flow discounted at the declared rate, at the SEC 12-month
|
|
657
|
+
average of the first-of-month WTI and Henry Hub prices (shipped from the
|
|
658
|
+
EIA) or a declared price, with the net revenue interest, severance tax and
|
|
659
|
+
operating cost declared. A forecast is not a reserves report, and the page
|
|
660
|
+
says so. The **Decline curves** tab, `gea decline`, `gea help decline`.
|
|
661
|
+
|
|
662
|
+
### Torque and drag
|
|
663
|
+
|
|
664
|
+
The hook load and torque the drill string should take at each bit depth, by
|
|
665
|
+
the soft-string model of Johancsik et al.: pick-up, slack-off and rotating
|
|
666
|
+
off bottom for the declared friction factors and a fan of others (the
|
|
667
|
+
broomstick), the torque rotating off bottom. Against them, the rig's own
|
|
668
|
+
hook loads and torque from the drilling record, read by the rig states -
|
|
669
|
+
tripping out and in, rotating off bottom - and the friction factors that fit
|
|
670
|
+
them, cased and open hole apart. Volve 15/9-F-10 with an example string is
|
|
671
|
+
the worked example. The **Torque & drag** tab, `gea torque-drag`,
|
|
672
|
+
`gea help torque-drag`.
|
|
673
|
+
|
|
651
674
|
### Pore pressure
|
|
652
675
|
|
|
653
676
|
The mud-weight window from a well's logs or its drilling record. The
|
|
@@ -609,6 +609,29 @@ against it; `gea seedlink --config ... --hello` prints what a server says of
|
|
|
609
609
|
itself; `gea help seedlink`. A TexNet station is served by EarthScope at
|
|
610
610
|
`rtserve.iris.washington.edu:18000`, network `TX`.
|
|
611
611
|
|
|
612
|
+
### Decline curves
|
|
613
|
+
|
|
614
|
+
Each well's monthly production fitted by Arps's curves - the modified
|
|
615
|
+
hyperbolic, turned exponential at a terminal decline - forecast month by
|
|
616
|
+
month to its economic limit and valued: the estimated ultimate recovery,
|
|
617
|
+
and the net cash flow discounted at the declared rate, at the SEC 12-month
|
|
618
|
+
average of the first-of-month WTI and Henry Hub prices (shipped from the
|
|
619
|
+
EIA) or a declared price, with the net revenue interest, severance tax and
|
|
620
|
+
operating cost declared. A forecast is not a reserves report, and the page
|
|
621
|
+
says so. The **Decline curves** tab, `gea decline`, `gea help decline`.
|
|
622
|
+
|
|
623
|
+
### Torque and drag
|
|
624
|
+
|
|
625
|
+
The hook load and torque the drill string should take at each bit depth, by
|
|
626
|
+
the soft-string model of Johancsik et al.: pick-up, slack-off and rotating
|
|
627
|
+
off bottom for the declared friction factors and a fan of others (the
|
|
628
|
+
broomstick), the torque rotating off bottom. Against them, the rig's own
|
|
629
|
+
hook loads and torque from the drilling record, read by the rig states -
|
|
630
|
+
tripping out and in, rotating off bottom - and the friction factors that fit
|
|
631
|
+
them, cased and open hole apart. Volve 15/9-F-10 with an example string is
|
|
632
|
+
the worked example. The **Torque & drag** tab, `gea torque-drag`,
|
|
633
|
+
`gea help torque-drag`.
|
|
634
|
+
|
|
612
635
|
### Pore pressure
|
|
613
636
|
|
|
614
637
|
The mud-weight window from a well's logs or its drilling record. The
|
|
@@ -622,6 +622,35 @@ def main(argv=None) -> int:
|
|
|
622
622
|
p_pl.add_argument("--actor", type=str, default="cli")
|
|
623
623
|
p_pl.add_argument("--json", action="store_true")
|
|
624
624
|
|
|
625
|
+
p_dc = sub.add_parser("decline", help="each well's monthly production fitted by Arps's curves, forecast to its economic limit and valued at the SEC 12-month price or a declared one")
|
|
626
|
+
p_dc.add_argument("--workspace", type=str, required=True)
|
|
627
|
+
p_dc.add_argument("--well", type=str, default=None)
|
|
628
|
+
p_dc.add_argument("--attach", type=str, default=None, help="attach FILE as the well's monthly production (CSV or Excel)")
|
|
629
|
+
p_dc.add_argument("--remove", action="store_true")
|
|
630
|
+
for k_, h_ in (("terminal-decline", "the terminal decline, effective annual (0.08 = 8 %%)"), ("b-max", "the largest b fitted"), ("oil-price", "USD/bbl"),
|
|
631
|
+
("gas-price", "USD/MMBtu"), ("price-basis", "sec or flat"), ("nri", "the net revenue interest"), ("severance", "the severance tax (0.046 = 4.6 %%)"),
|
|
632
|
+
("opex", "the operating cost, USD a month"), ("discount", "the discount rate (0.10)"), ("start", "fit from this month (YYYY-MM)"),
|
|
633
|
+
("as-of", "value as of this date (YYYY-MM-DD)")):
|
|
634
|
+
p_dc.add_argument("--" + k_, type=str, default=None, help=h_)
|
|
635
|
+
p_dc.add_argument("--source", type=str, default="", help="where the production or the terms came from")
|
|
636
|
+
p_dc.add_argument("--write", action="store_true")
|
|
637
|
+
p_dc.add_argument("--actor", type=str, default="cli")
|
|
638
|
+
p_dc.add_argument("--json", action="store_true")
|
|
639
|
+
|
|
640
|
+
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
|
+
p_td.add_argument("--workspace", type=str, default=None)
|
|
642
|
+
p_td.add_argument("--well", type=str, default=None)
|
|
643
|
+
p_td.add_argument("--example", action="store_true", help="the worked example (Volve 15/9-F-10 with an example string)")
|
|
644
|
+
p_td.add_argument("--string", type=str, default=None, help="sections from the bit up: 'BHA:300:6.75:95; drill pipe::5:22.6:6.625' (what:length_ft:od_in:lb_ft[:contact_od_in])")
|
|
645
|
+
p_td.add_argument("--mud", type=str, default=None, help="mud density, ppg")
|
|
646
|
+
p_td.add_argument("--mu-cased", type=str, default=None)
|
|
647
|
+
p_td.add_argument("--mu-open", type=str, default=None)
|
|
648
|
+
p_td.add_argument("--block", type=str, default=None, help="the travelling block's weight, klbf (estimated from the record when not declared)")
|
|
649
|
+
p_td.add_argument("--source", type=str, default="", help="where the string and its figures came from")
|
|
650
|
+
p_td.add_argument("--write", action="store_true")
|
|
651
|
+
p_td.add_argument("--actor", type=str, default="cli")
|
|
652
|
+
p_td.add_argument("--json", action="store_true")
|
|
653
|
+
|
|
625
654
|
p_pp = sub.add_parser("pore-pressure", help="the mud-weight window: overburden, pore pressure by Eaton from the sonic, the resistivity or the d-exponent, and the fracture gradient")
|
|
626
655
|
p_pp.add_argument("--workspace", type=str, default=None)
|
|
627
656
|
p_pp.add_argument("--well", type=str, default=None)
|
|
@@ -2472,6 +2501,71 @@ def main(argv=None) -> int:
|
|
|
2472
2501
|
raise SystemExit(f"collision: {e.args[0] if e.args else e}")
|
|
2473
2502
|
print(_json.dumps({k: v for k, v in r.items()}, default=str) if a.json else _CO.text(r))
|
|
2474
2503
|
return 0
|
|
2504
|
+
elif a.cmd == "decline":
|
|
2505
|
+
import json as _json
|
|
2506
|
+
from . import decline as _DCA
|
|
2507
|
+
from .cli import _safe_stdout
|
|
2508
|
+
from .workspace import Workspace, WorkspaceError
|
|
2509
|
+
_safe_stdout()
|
|
2510
|
+
try:
|
|
2511
|
+
ws = Workspace(a.workspace)
|
|
2512
|
+
terms = {"terminal_decline": a.terminal_decline, "b_max": a.b_max, "oil_price": a.oil_price, "gas_price": a.gas_price, "price_basis": a.price_basis,
|
|
2513
|
+
"nri": a.nri, "severance": a.severance, "opex_month": a.opex, "discount": a.discount, "start": a.start}
|
|
2514
|
+
if (a.attach or a.remove or any(v is not None for v in terms.values()) or a.write) and not a.well:
|
|
2515
|
+
raise ValueError("--well is needed")
|
|
2516
|
+
if a.attach:
|
|
2517
|
+
with open(a.attach, "rb") as f_:
|
|
2518
|
+
r_ = _DCA.attach(ws, a.well, os.path.basename(a.attach), f_.read(), a.actor, a.source)
|
|
2519
|
+
print(f"production attached to {a.well}: {r_['months']} months, {r_['first']} to {r_['last']}")
|
|
2520
|
+
return 0
|
|
2521
|
+
if a.remove:
|
|
2522
|
+
_DCA.remove(ws, a.well, a.actor)
|
|
2523
|
+
print(f"production removed from {a.well}")
|
|
2524
|
+
return 0
|
|
2525
|
+
if any(v is not None for v in terms.values()):
|
|
2526
|
+
c_ = _DCA.set_config(ws, a.actor, a.well, a.source, **{k: v for k, v in terms.items() if v is not None})
|
|
2527
|
+
print(f"{a.well}: terminal {c_['terminal_decline']:g}, price {c_['price_basis']}, NRI {c_['nri']:g}, severance {c_['severance']:g}, opex {c_['opex_month']:,.0f}/month, discount {c_['discount']:g}")
|
|
2528
|
+
return 0
|
|
2529
|
+
if a.write:
|
|
2530
|
+
r_ = _DCA.write_report(ws, __version__, a.well, a.actor)
|
|
2531
|
+
print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
|
|
2532
|
+
if r_['paths'].get('pdf'):
|
|
2533
|
+
print(f" printable: {r_['paths']['pdf']}")
|
|
2534
|
+
return 0
|
|
2535
|
+
r = _DCA.page(ws, a.well, a.as_of)
|
|
2536
|
+
except (KeyError, ValueError, OSError, WorkspaceError) as e:
|
|
2537
|
+
raise SystemExit(f"decline: {e.args[0] if e.args else e}")
|
|
2538
|
+
print(_json.dumps(r, default=str) if a.json else _DCA.text(r))
|
|
2539
|
+
return 0
|
|
2540
|
+
elif a.cmd == "torque-drag":
|
|
2541
|
+
import json as _json
|
|
2542
|
+
from . import torque_drag as _TND
|
|
2543
|
+
from .cli import _safe_stdout
|
|
2544
|
+
from .workspace import Workspace, WorkspaceError
|
|
2545
|
+
_safe_stdout()
|
|
2546
|
+
try:
|
|
2547
|
+
ws = Workspace(a.workspace) if a.workspace else None
|
|
2548
|
+
scene = "example" if (a.example or ws is None) else "workspace"
|
|
2549
|
+
if ws is not None and any(v is not None for v in (a.string, a.mud, a.mu_cased, a.mu_open, a.block)):
|
|
2550
|
+
if not a.well:
|
|
2551
|
+
raise ValueError("--well is needed")
|
|
2552
|
+
c_ = _TND.set_well(ws, a.actor, a.well, a.source, _TND.parse_string(a.string) if a.string else None, a.mud, a.mu_cased, a.mu_open, a.block)
|
|
2553
|
+
print(f"{a.well}: " + "; ".join(f"{x['what']} {('to surface' if x['length_ft'] is None else format(x['length_ft'], ',.0f') + ' ft')}" for x in (c_.get('string') or []))
|
|
2554
|
+
+ f"; mud {c_['mud_ppg']:g} ppg, mu cased {c_['mu_cased']:g}, open {c_['mu_open']:g}")
|
|
2555
|
+
return 0
|
|
2556
|
+
if a.write:
|
|
2557
|
+
if ws is None:
|
|
2558
|
+
raise ValueError("--write needs --workspace")
|
|
2559
|
+
r_ = _TND.write_report(ws, __version__, a.well, a.actor, scene)
|
|
2560
|
+
print(f"wrote {r_['report_id']}: {r_['paths']['html']}")
|
|
2561
|
+
if r_['paths'].get('pdf'):
|
|
2562
|
+
print(f" printable: {r_['paths']['pdf']}")
|
|
2563
|
+
return 0
|
|
2564
|
+
r = _TND.page(ws, scene, a.well)
|
|
2565
|
+
except (KeyError, ValueError, OSError, WorkspaceError) as e:
|
|
2566
|
+
raise SystemExit(f"torque-drag: {e.args[0] if e.args else e}")
|
|
2567
|
+
print(_json.dumps(r, default=str) if a.json else _TND.text(r))
|
|
2568
|
+
return 0
|
|
2475
2569
|
elif a.cmd == "pore-pressure":
|
|
2476
2570
|
import json as _json
|
|
2477
2571
|
from . import pore_pressure as _PPF
|
|
@@ -11905,6 +11905,340 @@ def section_cu_pore_pressure(tmp: str) -> None:
|
|
|
11905
11905
|
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
11906
11906
|
|
|
11907
11907
|
|
|
11908
|
+
def section_cv_torque_drag(tmp: str) -> None:
|
|
11909
|
+
"""Section CV - torque and drag: the soft-string model against closed forms (a vertical string, a straight inclined
|
|
11910
|
+
one pulled and rotated, the capstan over a bend in the vertical plane and in azimuth); a drilling record made by the
|
|
11911
|
+
model with known friction factors, read back by the rig states and fitted to them; the worked example; the API, the
|
|
11912
|
+
CLI, the report, the dossier, the tab and the help."""
|
|
11913
|
+
import http.cookiejar
|
|
11914
|
+
import math as _m
|
|
11915
|
+
import subprocess as _sp
|
|
11916
|
+
import urllib.request
|
|
11917
|
+
import urllib.error
|
|
11918
|
+
import zipfile as _zf
|
|
11919
|
+
import numpy as _np
|
|
11920
|
+
from . import __version__ as _ver
|
|
11921
|
+
from . import dossier as DZ
|
|
11922
|
+
from . import helplib as H
|
|
11923
|
+
from . import rig_state as RS
|
|
11924
|
+
from . import torque_drag as TND
|
|
11925
|
+
from . import well3d as W
|
|
11926
|
+
from .workspace import Workspace
|
|
11927
|
+
from .service import Service, Users
|
|
11928
|
+
pkg = Path(__file__).parent
|
|
11929
|
+
d = Path(tmp, "torquedrag"); d.mkdir()
|
|
11930
|
+
|
|
11931
|
+
# -- CV1 the model against closed forms ------------------------------------------------------------------------------
|
|
11932
|
+
md = _np.linspace(0.0, 10000.0, 101); n = len(md)
|
|
11933
|
+
bf = TND.buoyancy(10.0)
|
|
11934
|
+
Fv, _ = TND.soft_string(md, _np.zeros(n), _np.zeros(n), _np.full(n - 1, 20.0 * bf), _np.full(n - 1, 3.3125), _np.full(n - 1, 0.3), "out")
|
|
11935
|
+
s60, c60 = _m.sin(_m.radians(60)), _m.cos(_m.radians(60))
|
|
11936
|
+
Fo, _ = TND.soft_string(md, _np.full(n, 60.0), _np.zeros(n), _np.full(n - 1, 20.0), _np.full(n - 1, 3.0), _np.full(n - 1, 0.3), "out")
|
|
11937
|
+
Fi, _ = TND.soft_string(md, _np.full(n, 60.0), _np.zeros(n), _np.full(n - 1, 20.0), _np.full(n - 1, 3.0), _np.full(n - 1, 0.3), "in")
|
|
11938
|
+
Fr, Mr = TND.soft_string(md, _np.full(n, 60.0), _np.zeros(n), _np.full(n - 1, 20.0), _np.full(n - 1, 3.0), _np.full(n - 1, 0.3), "rotate")
|
|
11939
|
+
N_ = 9001
|
|
11940
|
+
mc = _np.linspace(0.0, 1000.0, N_)
|
|
11941
|
+
Fc, _ = TND.soft_string(mc, _np.linspace(0.0, 90.0, N_), _np.zeros(N_), _np.zeros(N_ - 1), _np.full(N_ - 1, 3.0), _np.full(N_ - 1, 0.3), "out", 10000.0)
|
|
11942
|
+
Fa, _ = TND.soft_string(mc, _np.full(N_, 90.0), _np.linspace(0.0, 90.0, N_), _np.zeros(N_ - 1), _np.full(N_ - 1, 3.0), _np.full(N_ - 1, 0.3), "out", 10000.0)
|
|
11943
|
+
cap = 10000.0 * _m.exp(0.3 * _m.pi / 2)
|
|
11944
|
+
refused = 0
|
|
11945
|
+
for bad in ([], [{"what": "x", "length_ft": None, "od_in": 5, "weight_lb_ft": 20}, {"what": "y", "length_ft": None, "od_in": 5, "weight_lb_ft": 20}],
|
|
11946
|
+
[{"what": "x", "length_ft": 100, "od_in": 0.5, "weight_lb_ft": 20}], [{"what": "x", "length_ft": 100, "od_in": 5, "weight_lb_ft": 900}],
|
|
11947
|
+
[{"what": "x", "length_ft": 100, "od_in": 6, "weight_lb_ft": 20, "contact_od_in": 5}]):
|
|
11948
|
+
try:
|
|
11949
|
+
TND.check_string(bad)
|
|
11950
|
+
except ValueError:
|
|
11951
|
+
refused += 1
|
|
11952
|
+
c1 = {
|
|
11953
|
+
"vertical": abs(Fv[0] - 20.0 * bf * 10000.0) < 1e-6 and abs(bf - (1 - 10.0 / 65.5)) < 1e-12,
|
|
11954
|
+
"inclined": abs(Fo[0] - 200000.0 * (c60 + 0.3 * s60)) < 1e-6 and abs(Fi[0] - 200000.0 * (c60 - 0.3 * s60)) < 1e-6 and abs(Fr[0] - 200000.0 * c60) < 1e-6
|
|
11955
|
+
and abs(Mr[0] - 0.3 * 200000.0 * s60 * 3.0 / 12.0) < 1e-6,
|
|
11956
|
+
"capstan": abs(Fc[0] / cap - 1) < 5e-5 and abs(Fa[0] / cap - 1) < 5e-5,
|
|
11957
|
+
"string": refused == 5 and TND.parse_string("BHA:300:6.75:95; dp::5:22.6:6.625")[1]["length_ft"] is None,
|
|
11958
|
+
}
|
|
11959
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
11960
|
+
ok(not failed1,
|
|
11961
|
+
"CV1 the soft-string model against closed forms: a vertical string weighs its buoyed weight; a straight 60-degree string "
|
|
11962
|
+
"pulled, run and rotated gives W(cos + mu sin), W(cos - mu sin), W cos and the torque mu W sin r exactly; a weightless string "
|
|
11963
|
+
"over a 90-degree bend, in the vertical plane or in azimuth, comes to the capstan's e^(mu theta) as the elements shrink; a "
|
|
11964
|
+
"string without sections, with two open-ended, or with a diameter, weight or contact diameter out of range refused"
|
|
11965
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
11966
|
+
|
|
11967
|
+
# -- CV2 a record made by the model, read back ---------------------------------------------------------------------------
|
|
11968
|
+
f = TND.selftest_files(str(d))
|
|
11969
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
11970
|
+
wid = ws.add_well_file(f["record"], display="TD-1", actor="tester")["id"]
|
|
11971
|
+
ws.set_well(wid, surface={"lat": 31.42, "lon": -104.10, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
|
|
11972
|
+
no_survey = TND.page(ws, "workspace", wid)["detail"]
|
|
11973
|
+
W.attach_trajectory(ws, wid, "s.csv", Path(f["survey"]).read_bytes(), "tester", "true", "ft", None, None, None, "test survey")
|
|
11974
|
+
no_string = TND.page(ws, "workspace", wid)["detail"]
|
|
11975
|
+
RS.set_casing(ws, "tester", wid, "9-5/8", 4000, "tally (test)")
|
|
11976
|
+
TND.set_well(ws, "tester", wid, "BHA report (test)", f["string"], 10.0, 0.25, 0.30)
|
|
11977
|
+
r = TND.page(ws, "workspace", wid)["result"]
|
|
11978
|
+
fi = r["fits"]
|
|
11979
|
+
m_ = r["measured"]
|
|
11980
|
+
c2 = {
|
|
11981
|
+
"needs": "survey" in no_survey and "string" in no_string,
|
|
11982
|
+
"measured": len(m_["out"]["depth_ft"]) >= 70 and len(m_["in"]["depth_ft"]) >= 70 and len(m_["rotate"]["depth_ft"]) == 9 and abs(r["block_klbf"] - 40.0) < 0.5 and m_["block_estimated"],
|
|
11983
|
+
"open": all(abs(fi[k]["mu"] - 0.24) <= 0.02 for k in ("out:open", "in:open", "torque:open")),
|
|
11984
|
+
"cased": all(abs(fi[k]["mu"] - 0.18) <= 0.02 for k in ("out:cased", "in:cased", "torque:cased")),
|
|
11985
|
+
"weights": abs(r["string_air_klbf"] - (300 * 95.0 + (r["td_ft"] - 300) * 22.6) / 1000.0) < 0.05 and r["shoe_ft"] == 4000.0,
|
|
11986
|
+
}
|
|
11987
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
11988
|
+
ok(not failed2,
|
|
11989
|
+
"CV2 a drilling record made by the model - a trip out and in stand by stand, the string rotated off bottom every 900 ft, "
|
|
11990
|
+
"friction 0.18 cased above a 4,000 ft shoe and 0.24 open, a 40 klbf block, noise - read back by the rig states: the block "
|
|
11991
|
+
"estimated to half a klbf, the pick-up, slack-off and torque each fitting 0.18 cased and 0.24 open to within 0.02; a well "
|
|
11992
|
+
"without a survey or a string says what it needs"
|
|
11993
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
11994
|
+
|
|
11995
|
+
# -- CV3 the worked example -----------------------------------------------------------------------------------------------
|
|
11996
|
+
ex = TND.example()
|
|
11997
|
+
de = ex["declared"]
|
|
11998
|
+
c3 = {
|
|
11999
|
+
"example": ex["well"] == "15/9-F-10" and abs(ex["td_ft"] - 5300.07 / 0.3048) < 5.0 and ex["max_inc_deg"] > 80
|
|
12000
|
+
and all(de["out"][i] >= de["rotate"][i] >= de["in"][i] for i in range(len(de["depth_ft"])))
|
|
12001
|
+
and de["torque"][-1] > de["torque"][len(de["torque"]) // 2] > 0 and "not from Volve" in ex["config"]["source"],
|
|
12002
|
+
"fan": sorted(ex["fan"]) == ["0.1", "0.2", "0.3", "0.4", "0.5"] and ex["fan"]["0.5"]["out"][-1] > ex["fan"]["0.1"]["out"][-1] and ex["fan"]["0.5"]["in"][-1] < ex["fan"]["0.1"]["in"][-1],
|
|
12003
|
+
}
|
|
12004
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
12005
|
+
ok(not failed3,
|
|
12006
|
+
"CV3 the worked example, Volve 15/9-F-10's survey to its 5,300 m TD with a string declared for the example: pick-up above "
|
|
12007
|
+
"rotating above slack-off at every depth, the torque growing with depth, the broomstick fanning out with the friction factor"
|
|
12008
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
12009
|
+
|
|
12010
|
+
# -- CV4 the API, the CLI, the report, the dossier, the tab and the help -------------------------------------------------
|
|
12011
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
12012
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
12013
|
+
base_url = svc.url.rstrip("/")
|
|
12014
|
+
|
|
12015
|
+
def opener(name, pw_):
|
|
12016
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
12017
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
|
|
12018
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
12019
|
+
return o
|
|
12020
|
+
|
|
12021
|
+
def call(o, method, path, body=None):
|
|
12022
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
12023
|
+
if method == "POST":
|
|
12024
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
12025
|
+
try:
|
|
12026
|
+
with o.open(rq, timeout=120) as rr_:
|
|
12027
|
+
return rr_.status, json.loads(rr_.read())
|
|
12028
|
+
except urllib.error.HTTPError as e:
|
|
12029
|
+
return e.code, json.loads(e.read())
|
|
12030
|
+
try:
|
|
12031
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
12032
|
+
s_vg, vg = call(V, "GET", "/api/torque-drag?well=" + wid)
|
|
12033
|
+
s_ve, ve = call(V, "GET", "/api/torque-drag?scene=example")
|
|
12034
|
+
s_vs, _ = call(V, "POST", "/api/torque-drag/settings", {"well_id": wid, "mud_ppg": 11, "source": "x"})
|
|
12035
|
+
s_os, os_ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "mud_ppg": "11", "string": "BHA:300:6.75:95:6.75; dp::5:22.6:6.625", "source": "mud report (test)"})
|
|
12036
|
+
s_bs, _ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "string": "BHA:300", "source": "x"})
|
|
12037
|
+
s_bm, _ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "mu_open": 0.9, "source": "x"})
|
|
12038
|
+
s_ns, _ = call(O, "POST", "/api/torque-drag/settings", {"well_id": wid, "mud_ppg": 12})
|
|
12039
|
+
s_vw, _ = call(V, "POST", "/api/torque-drag/write", {"well_id": wid})
|
|
12040
|
+
s_ow, ow = call(O, "POST", "/api/torque-drag/write", {"well_id": wid})
|
|
12041
|
+
finally:
|
|
12042
|
+
svc.stop()
|
|
12043
|
+
cli_x = _sp.run([sys.executable, "-m", "gea", "torque-drag", "--example"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12044
|
+
cli_s = _sp.run([sys.executable, "-m", "gea", "torque-drag", "--workspace", wsp, "--well", wid, "--mu-open", "0.26", "--source", "offset well (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12045
|
+
cli_t = _sp.run([sys.executable, "-m", "gea", "torque-drag", "--workspace", wsp, "--well", wid], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12046
|
+
dz = DZ.build(ws, "tester", _ver, None)
|
|
12047
|
+
names = _zf.ZipFile(Path(ws.reports_dir, dz["path"])).namelist()
|
|
12048
|
+
audit = Path(ws.audit_path).read_text(encoding="utf-8")
|
|
12049
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
12050
|
+
c4 = {
|
|
12051
|
+
"api": s_vg == 200 and vg["result"]["fits"] and s_ve == 200 and ve["result"]["well"] == "15/9-F-10" and s_vs == 403 and s_os == 200 and os_["mud_ppg"] == 11.0
|
|
12052
|
+
and len(os_["string"]) == 2 and s_bs == 400 and s_bm == 400 and s_ns == 400 and s_vw == 403 and s_ow == 200 and ow["report_id"].startswith("TND-"),
|
|
12053
|
+
"cli": cli_x.returncode == 0 and "15/9-F-10" in cli_x.stdout and cli_s.returncode == 0 and "open 0.26" in cli_s.stdout and cli_t.returncode == 0
|
|
12054
|
+
and "fits mu" in cli_t.stdout and "mud 11 ppg" in cli_t.stdout,
|
|
12055
|
+
"report": PR_read_ok(Path(ws.reports_dir, "wells", wid, "torque_drag.pdf")) and "torque_drag.declare" in audit and "report.torque_drag" in audit,
|
|
12056
|
+
"dossier": "declarations/drillstring/torque_drag.json" in names and f"results/torque_drag/{wid}.json" in names,
|
|
12057
|
+
"page": all(x in page_src for x in ("VIEWS.torquedrag", "['#/torquedrag', 'Torque & drag']", "/api/torque-drag/settings", "tdChart")),
|
|
12058
|
+
"help": not H.check("torque-drag") and H.VIEW_TOPIC.get("torquedrag") == "torque-drag",
|
|
12059
|
+
}
|
|
12060
|
+
failed4 = [k for k, v in c4.items() if not v]
|
|
12061
|
+
ok(not failed4,
|
|
12062
|
+
"CV4 on the API (anyone reads a well's model and the worked example; only an operator declares the string, the mud and the "
|
|
12063
|
+
"friction factors - a section without its figures, a friction factor of 0.9 or a change without its source refused - or "
|
|
12064
|
+
"writes the report), the CLI, the report as PDF, a signed dossier carrying the string and the model, the tab and the help"
|
|
12065
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
12066
|
+
|
|
12067
|
+
|
|
12068
|
+
def section_cw_decline(tmp: str) -> None:
|
|
12069
|
+
"""Section CW - decline curves: Arps's rate and cumulative and the modified hyperbolic against their closed forms
|
|
12070
|
+
and the decline conversions; a production table built on known curves fitted back and forecast; the economic limit,
|
|
12071
|
+
the discounted value and the SEC 12-month price; the API, the CLI, the report, the dossier, the tab and the help."""
|
|
12072
|
+
import http.cookiejar
|
|
12073
|
+
import math as _m
|
|
12074
|
+
import subprocess as _sp
|
|
12075
|
+
import urllib.request
|
|
12076
|
+
import urllib.error
|
|
12077
|
+
import zipfile as _zf
|
|
12078
|
+
from datetime import date as _date
|
|
12079
|
+
import numpy as _np
|
|
12080
|
+
from . import __version__ as _ver
|
|
12081
|
+
from . import decline as DCA
|
|
12082
|
+
from . import dossier as DZ
|
|
12083
|
+
from . import edr as E
|
|
12084
|
+
from . import helplib as H
|
|
12085
|
+
from . import xlsx_read as XR
|
|
12086
|
+
from .workspace import Workspace
|
|
12087
|
+
from .service import Service, Users
|
|
12088
|
+
pkg = Path(__file__).parent
|
|
12089
|
+
d = Path(tmp, "decline"); d.mkdir()
|
|
12090
|
+
|
|
12091
|
+
# -- CW1 the curves by hand --------------------------------------------------------------------------------------------
|
|
12092
|
+
qi, Di = 1000.0, 0.002
|
|
12093
|
+
t = _np.array([0.0, 100.0, 1000.0])
|
|
12094
|
+
e_ok = _np.allclose(DCA.rate(t, qi, Di, 0.0), qi * _np.exp(-Di * t)) and _np.allclose(DCA.cum(t, qi, Di, 0.0), qi / Di * (1 - _np.exp(-Di * t)))
|
|
12095
|
+
h_ok = _np.allclose(DCA.rate(t, qi, Di, 0.5), qi / (1 + 0.5 * Di * t) ** 2) and _np.allclose(DCA.cum(t, qi, Di, 0.5), qi / (0.5 * Di) * (1 - (1 + 0.5 * Di * t) ** -1))
|
|
12096
|
+
a_ok = _np.allclose(DCA.cum(t, qi, Di, 1.0), qi / Di * _np.log(1 + Di * t))
|
|
12097
|
+
tt = _np.linspace(0, 20000, 200001)
|
|
12098
|
+
q_mh = DCA.rate(tt, qi, Di, 1.5, 0.0002)
|
|
12099
|
+
trap = float(_np.sum(0.5 * (q_mh[1:] + q_mh[:-1]) * _np.diff(tt)))
|
|
12100
|
+
ts = (Di / 0.0002 - 1) / (1.5 * Di)
|
|
12101
|
+
i_s = int(_np.searchsorted(tt, ts))
|
|
12102
|
+
dq = _np.diff(_np.log(q_mh))
|
|
12103
|
+
c1 = {
|
|
12104
|
+
"arps": e_ok and h_ok and a_ok,
|
|
12105
|
+
"modified": abs(float(DCA.cum(_np.array([20000.0]), qi, Di, 1.5, 0.0002)[0]) / trap - 1) < 1e-6 and abs(dq[i_s + 5] / 0.1 + 0.0002) < 1e-9 and q_mh[i_s - 1] > q_mh[i_s],
|
|
12106
|
+
"conversions": abs(DCA.effective_annual(DCA.nominal_daily(0.65, 1.1), 1.1) - 0.65) < 1e-12 and abs(DCA.effective_annual(DCA.nominal_daily(0.08, 0.0), 0.0) - 0.08) < 1e-12
|
|
12107
|
+
and abs(DCA.effective_annual(0.001, 0.0) - (1 - _m.exp(-0.36525))) < 1e-12,
|
|
12108
|
+
"sec": DCA.sec_price([f"2025-{m:02d}-0{k}" for m in range(1, 13) for k in (2, 3)] + ["2024-12-31"], [float(m * 10 + k) for m in range(1, 13) for k in (2, 3)] + [999.0],
|
|
12109
|
+
_date(2026, 1, 1))["price"] == round(sum(m * 10 + 2 for m in range(1, 13)) / 12.0, 4)
|
|
12110
|
+
and DCA.sec_price(["2025-06-01"], [50.0], _date(2026, 1, 1)) is None,
|
|
12111
|
+
}
|
|
12112
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
12113
|
+
ok(not failed1,
|
|
12114
|
+
"CW1 Arps's curves by hand: the exponential, hyperbolic and harmonic rates and cumulatives in closed form; the modified "
|
|
12115
|
+
"hyperbolic's cumulative equal to the integral of its rate, exponential at the terminal decline from the switch; the "
|
|
12116
|
+
"effective and nominal declines inverse to each other; the SEC price the average of each month's first quote of the 12 "
|
|
12117
|
+
"months before, and none when a month is missing"
|
|
12118
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
12119
|
+
|
|
12120
|
+
# -- CW2 a table built on known curves, fitted back ---------------------------------------------------------------------
|
|
12121
|
+
f = DCA.selftest_files(str(d))
|
|
12122
|
+
prod = DCA.read_production(Path(f["path"]).read_bytes())
|
|
12123
|
+
so = DCA.analyse_stream(prod, "oil", {**DCA.DEFAULTS})
|
|
12124
|
+
Dn, Dm = DCA.nominal_daily(f["De"], f["b"]), DCA.nominal_daily(f["Dterm"], 0.0)
|
|
12125
|
+
th = _np.array(so["history"]["t"])
|
|
12126
|
+
q_true = DCA.rate(th, f["qi"], Dn, f["b"], Dm)
|
|
12127
|
+
q_fit = DCA.rate(th - so["t0_days"], so["fit"]["qi"], so["fit"]["Di"], so["fit"]["b"], so["Dmin"])
|
|
12128
|
+
t_end = th[-1] + 30 * 365.25
|
|
12129
|
+
eur_true = float(DCA.cum(_np.array([t_end]), f["qi"], Dn, f["b"], Dm)[0])
|
|
12130
|
+
eur_fit = float(DCA.cum(_np.array([t_end - so["t0_days"]]), so["fit"]["qi"], so["fit"]["Di"], so["fit"]["b"], so["Dmin"])[0])
|
|
12131
|
+
days_ok = all(abs(so["history"]["q"][i] - so["history"]["v"][i] / prod[i]["days"]) < 1e-3 for i in (7, 19))
|
|
12132
|
+
XR.write_simple(str(d / "p.xlsx"), [["Production Month", "Oil Volume", "Gas Volume", "Days On"], ["2024-01", 3000, 6000, 31], ["2024-02", 2700, 5500, 29]])
|
|
12133
|
+
px = DCA.read_production((d / "p.xlsx").read_bytes())
|
|
12134
|
+
refused = 0
|
|
12135
|
+
for bad in ("Month,Oil\n2024-01,-5\n", "Month,Oil\n2024-01,5\n2024-01,6\n", "Oil,Gas\n1,2\n", "Month,Oil,Days\n2024-01,5,40\n", "Month,Note\n2024-01,x\n"):
|
|
12136
|
+
try:
|
|
12137
|
+
DCA.read_production(bad.encode())
|
|
12138
|
+
except ValueError:
|
|
12139
|
+
refused += 1
|
|
12140
|
+
c2 = {
|
|
12141
|
+
"fit": abs(so["fit"]["b"] - f["b"]) <= 0.15 and float(_np.max(_np.abs(q_fit - q_true) / q_true)) < 0.04 and so["months_fitted"] == 48,
|
|
12142
|
+
"eur": abs(eur_fit / eur_true - 1) < 0.05,
|
|
12143
|
+
"days": days_ok,
|
|
12144
|
+
"table": len(prod) == 48 and prod[0]["month"] == "2022-01" and px[1]["days"] == 29 and px[0]["oil"] == 3000 and refused == 5,
|
|
12145
|
+
}
|
|
12146
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
12147
|
+
ok(not failed2,
|
|
12148
|
+
"CW2 a well's 48 months built on a known curve (900 bbl/d, b 1.1, 65 % a year, 8 % terminal, 3 % noise, four months part "
|
|
12149
|
+
"shut in) fitted back: b within 0.15, the rate within 4 % over the history, the 30-year recovery within 5 %; a part month's "
|
|
12150
|
+
"rate its volume over its producing days; a table read from Excel by its names; a negative volume, a month twice, no month, "
|
|
12151
|
+
"more than 31 days or no stream refused"
|
|
12152
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
12153
|
+
|
|
12154
|
+
# -- CW3 the forecast, the limit and the value ---------------------------------------------------------------------------
|
|
12155
|
+
one = [{"month": f"2020-{m:02d}", "oil": 3000.0 * _m.exp(-0.01 * 30.4375 * (m - 1)), "gas": None, "water": None, "days": None} for m in range(1, 13)]
|
|
12156
|
+
cfg = {**DCA.DEFAULTS, "terminal_decline": None, "nri": 1.0, "severance": 0.0, "opex_month": 0.0, "discount": 0.0, "years": 1.0}
|
|
12157
|
+
r1 = DCA.analyse(one, cfg, {"oil": 50.0, "gas": 0.0})
|
|
12158
|
+
vol12 = sum(m_["oil"] for m_ in r1["forecast"]["months"])
|
|
12159
|
+
cfg2 = {**cfg, "opex_month": 500.0, "years": 20.0}
|
|
12160
|
+
r2 = DCA.analyse(one, cfg2, {"oil": 50.0, "gas": 0.0})
|
|
12161
|
+
last_net = r2["forecast"]["months"][-1]["net_usd"]
|
|
12162
|
+
cfg3 = {**cfg, "discount": 0.10, "years": 1.0}
|
|
12163
|
+
r3 = DCA.analyse(one, cfg3, {"oil": 50.0, "gas": 0.0})
|
|
12164
|
+
pv_hand = sum(m_["net_usd"] * 1.1 ** (-((i + 0.5) / 12)) for i, m_ in enumerate(r3["forecast"]["months"]))
|
|
12165
|
+
c3 = {
|
|
12166
|
+
"exponential": r1["streams"]["oil"]["fit"]["b"] == 0.0 and abs(r1["streams"]["oil"]["fit"]["Di"] / (0.01) - 1) < 0.01 and len(r1["forecast"]["months"]) == 12
|
|
12167
|
+
and abs(r1["forecast"]["undiscounted_usd"] / (50.0 * vol12) - 1) < 1e-3,
|
|
12168
|
+
"limit": r2["forecast"]["limit_month"] is not None and r2["forecast"]["stopped_by"] == "the economic limit" and last_net > 0
|
|
12169
|
+
and 3 <= len(r2["forecast"]["months"]) <= 12 and 50.0 * r2["forecast"]["months"][-1]["oil"] > 500.0,
|
|
12170
|
+
"pv": abs(r3["forecast"]["pv_usd"] - pv_hand) < 2.0 and r3["forecast"]["pv_usd"] < r3["forecast"]["undiscounted_usd"],
|
|
12171
|
+
}
|
|
12172
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
12173
|
+
ok(not failed3,
|
|
12174
|
+
"CW3 the forecast: a year of exponential decline at 1 % a day fitted as exponential and carried a year forward, its revenue "
|
|
12175
|
+
"the volume times the price; an operating cost stopping it at the economic limit on the last month that still pays; the "
|
|
12176
|
+
"value discounted mid-month as by hand"
|
|
12177
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
12178
|
+
|
|
12179
|
+
# -- CW4 the API, the CLI, the report, the dossier, the tab and the help -------------------------------------------------
|
|
12180
|
+
ev = E.selftest_events(str(d))
|
|
12181
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
12182
|
+
wid = ws.add_well_file(ev["path"], display="PROD-1", actor="tester")["id"]
|
|
12183
|
+
nothing = DCA.page(ws, wid)["detail"]
|
|
12184
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
12185
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
12186
|
+
base_url = svc.url.rstrip("/")
|
|
12187
|
+
|
|
12188
|
+
def opener(name, pw_):
|
|
12189
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
12190
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw_}).encode())
|
|
12191
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
12192
|
+
return o
|
|
12193
|
+
|
|
12194
|
+
def call(o, method, path, body=None):
|
|
12195
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
12196
|
+
if method == "POST":
|
|
12197
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
12198
|
+
try:
|
|
12199
|
+
with o.open(rq, timeout=120) as rr_:
|
|
12200
|
+
return rr_.status, json.loads(rr_.read())
|
|
12201
|
+
except urllib.error.HTTPError as e:
|
|
12202
|
+
return e.code, json.loads(e.read())
|
|
12203
|
+
content = Path(f["path"]).read_text(encoding="utf-8")
|
|
12204
|
+
try:
|
|
12205
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
12206
|
+
s_va, _ = call(V, "POST", "/api/decline/attach", {"well_id": wid, "filename": "p.csv", "content": content, "source": "x"})
|
|
12207
|
+
s_ns, _ = call(O, "POST", "/api/decline/attach", {"well_id": wid, "filename": "p.csv", "content": content, "source": ""})
|
|
12208
|
+
s_oa, oa = call(O, "POST", "/api/decline/attach", {"well_id": wid, "filename": "p.csv", "content": content, "source": "monthly report (test)"})
|
|
12209
|
+
s_vg, vg = call(V, "GET", "/api/decline?well=" + wid + "&as_of=2026-01-01")
|
|
12210
|
+
s_bt, _ = call(O, "POST", "/api/decline/settings", {"well_id": wid, "nri": 1.5, "source": "x"})
|
|
12211
|
+
s_bp, _ = call(O, "POST", "/api/decline/settings", {"well_id": wid, "price_basis": "spot", "source": "x"})
|
|
12212
|
+
s_ot, ot = call(O, "POST", "/api/decline/settings", {"well_id": wid, "price_basis": "flat", "oil_price": "70", "gas_price": "3", "source": "budget deck (test)"})
|
|
12213
|
+
s_fg, fg = call(V, "GET", "/api/decline?well=" + wid)
|
|
12214
|
+
s_vw, _ = call(V, "POST", "/api/decline/write", {"well_id": wid})
|
|
12215
|
+
s_ow, ow = call(O, "POST", "/api/decline/write", {"well_id": wid})
|
|
12216
|
+
finally:
|
|
12217
|
+
svc.stop()
|
|
12218
|
+
cli_t = _sp.run([sys.executable, "-m", "gea", "decline", "--workspace", wsp, "--well", wid], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12219
|
+
cli_s = _sp.run([sys.executable, "-m", "gea", "decline", "--workspace", wsp, "--well", wid, "--opex", "9000", "--source", "LOE (test)"], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
12220
|
+
dz = DZ.build(ws, "tester", _ver, None)
|
|
12221
|
+
names = _zf.ZipFile(Path(ws.reports_dir, dz["path"])).namelist()
|
|
12222
|
+
audit = Path(ws.audit_path).read_text(encoding="utf-8")
|
|
12223
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
12224
|
+
c4 = {
|
|
12225
|
+
"api": "production" in nothing and s_va == 403 and s_ns == 400 and s_oa == 200 and oa["months"] == 48 and s_vg == 200 and vg["result"]["as_of"] == "2026-01-01"
|
|
12226
|
+
and "SEC average" in vg["result"]["price_how"]["oil"] and s_bt == 400 and s_bp == 400 and s_ot == 200 and ot["oil_price"] == 70.0
|
|
12227
|
+
and s_fg == 200 and fg["result"]["prices"]["oil"] == 70.0 and s_vw == 403 and s_ow == 200 and ow["report_id"].startswith("DCA-"),
|
|
12228
|
+
"cli": cli_t.returncode == 0 and "EUR" in cli_t.stdout and "70 USD/bbl declared" in cli_t.stdout and cli_s.returncode == 0 and "opex 9,000/month" in cli_s.stdout,
|
|
12229
|
+
"report": PR_read_ok(Path(ws.reports_dir, "wells", wid, "decline_curves.pdf")) and all(x in audit for x in ("production.attach", "production.terms", "report.decline_curves")),
|
|
12230
|
+
"dossier": "declarations/economics/production/production.json" in names and f"declarations/economics/production/{wid}/p.csv" in names and f"results/decline/{wid}.json" in names,
|
|
12231
|
+
"page": all(x in page_src for x in ("VIEWS.decline", "['#/decline', 'Decline curves']", "/api/decline/attach", "dcChart")),
|
|
12232
|
+
"help": not H.check("decline") and H.VIEW_TOPIC.get("decline") == "decline",
|
|
12233
|
+
}
|
|
12234
|
+
failed4 = [k for k, v in c4.items() if not v]
|
|
12235
|
+
ok(not failed4,
|
|
12236
|
+
"CW4 on the API (anyone reads a well's curves at a chosen date, valued at the SEC price; only an operator attaches production "
|
|
12237
|
+
"- with its source - or declares terms - a net revenue interest of 1.5 or an unknown price basis refused - or writes the "
|
|
12238
|
+
"report), the CLI, the report as PDF, a signed dossier carrying the production and the curves, the tab and the help"
|
|
12239
|
+
+ (f" [failed: {', '.join(failed4)}]" if failed4 else ""))
|
|
12240
|
+
|
|
12241
|
+
|
|
11908
12242
|
def PR_read_ok(path) -> bool:
|
|
11909
12243
|
from . import pdf_report as PR
|
|
11910
12244
|
try:
|
|
@@ -12015,6 +12349,8 @@ def main() -> int:
|
|
|
12015
12349
|
section_cs_plan_and_tools(tmp)
|
|
12016
12350
|
section_ct_frac_hits(tmp)
|
|
12017
12351
|
section_cu_pore_pressure(tmp)
|
|
12352
|
+
section_cv_torque_drag(tmp)
|
|
12353
|
+
section_cw_decline(tmp)
|
|
12018
12354
|
if _FAILS:
|
|
12019
12355
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
12020
12356
|
for f in _FAILS:
|