gea-program 0.35.0__tar.gz → 0.37.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.35.0/gea_program.egg-info → gea_program-0.37.0}/PKG-INFO +29 -1
- {gea_program-0.35.0 → gea_program-0.37.0}/README.md +28 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/__init__.py +1 -1
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/__main__.py +100 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/acceptance_tests.py +359 -0
- gea_program-0.37.0/gea/economics.py +454 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/geodesy.py +1 -0
- gea_program-0.37.0/gea/help/economics.md +11 -0
- gea_program-0.37.0/gea/help/well3d.md +11 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/helplib.py +3 -1
- gea_program-0.37.0/gea/reference/eia_henry_hub_daily.csv.gz +0 -0
- gea_program-0.37.0/gea/reference/eia_prices.provenance.json +51 -0
- gea_program-0.37.0/gea/reference/eia_wti_daily.csv.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_3d.provenance.json +198 -0
- gea_program-0.37.0/gea/reference/volve_surfaces.json.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_traj_F-10.xml.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_traj_F-4.xml.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_traj_F-5_12.25in.xml.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_traj_F-5_17.5in.xml.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_traj_F-5_36in.xml.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_traj_F-5_8.5in.xml.gz +0 -0
- gea_program-0.37.0/gea/reference/volve_well_picks.csv.gz +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/service.py +54 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/web/app.html +268 -2
- gea_program-0.37.0/gea/well3d.py +688 -0
- {gea_program-0.35.0 → gea_program-0.37.0/gea_program.egg-info}/PKG-INFO +29 -1
- {gea_program-0.35.0 → gea_program-0.37.0}/gea_program.egg-info/SOURCES.txt +16 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/pyproject.toml +1 -1
- {gea_program-0.35.0 → gea_program-0.37.0}/LICENSE +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/BENCH_TEST_PROTOCOL.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/accuracy_statement.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/alarm_engine.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/backup.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/bench.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/blind_harness.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/acex_lomonosov_age_depth_model.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/acex_lomonosov_age_depth_model.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/agassiz77_canada_temperature.csv +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/agassiz77_canada_temperature.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/barbados_110_consolidation.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/barbados_110_consolidation.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/bengal_u1452_grain_size.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/bengal_u1452_grain_size.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/blake_164_methane_isotopes.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/blake_164_methane_isotopes.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/chicxulub_m0077a_pwave_velocity.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/collingwood_1_28_ks_complete.las +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/collingwood_1_28_ks_complete.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/costa_rica_odp_friction_envelope.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/costa_rica_odp_friction_envelope.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/dead_sea_5017_debrite_xrf_ms.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_physical_properties.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_physical_properties.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_sound_velocity.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/dsdp_504b_sound_velocity.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/elgygytgyn_5011_turbidites.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/elgygytgyn_5011_turbidites.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/epica_domec_co2_800kyr.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/epica_domec_co2_800kyr.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/fram_909_organic_petrography.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/fram_909_organic_petrography.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/gbr_325_coral_uth_ages.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/gbr_325_coral_uth_ages.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/gisp2_greenland_temperature.csv +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/gisp2_greenland_temperature.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/gom_308_t2p_insitu.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/gom_308_t2p_insitu.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/guaymas_385_dom_d13c.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/guaymas_385_dom_d13c.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/hikurangi_u1520_friction_insitu.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/hikurangi_u1520_friction_insitu.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/hydrate_ridge_204_ncr_hydrate.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/iodp_u1324_pore_pressure.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/iodp_u1324_pore_pressure.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/jfast_c0019_slow_slip_events.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/jfast_c0019_slow_slip_events.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/kennetcook_2_p129_excerpt.las +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/kennetcook_2_p129_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_bhgm_density.dat +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_bhgm_density.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_complog_6020_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_complog_6020_excerpt.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_hlog246_temperature.dat +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_hlog246_temperature.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_rockmech_compress.dat +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.dat +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_hb_tvd_0_9080_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_rockmech_compress.dat +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_rockmech_compress.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_vlog251_temperature.dat +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ktb_vb_vlog251_temperature.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/l06_06_nl_survey.csv +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/l06_06_nl_survey.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/l07_01_nl_excerpt.las +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/l07_01_nl_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/mariana_1200_serpentinite_geochem.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/mariana_1200_serpentinite_geochem.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/med_160_sapropels.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/med_160_sapropels.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/nankai_megasplay_shear_strength.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/nankai_megasplay_shear_strength.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1027b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1027b_thermal_conductivity.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1027c_cork_temperature.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1027c_cork_temperature.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1165b_thermal_conductivity.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1165b_thermal_conductivity.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_1274a_mantle_peridotite_mad.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_504b_dike_elastic_moduli.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_504b_dike_elastic_moduli.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_504b_leg137_borehole_fluids.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_504b_leg137_borehole_fluids.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/odp_735b_gabbro_elastic_moduli.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/peru_201_sulfate_reduction.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/peru_201_sulfate_reduction.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/scorpio_e1_sa_excerpt.las +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/scorpio_e1_sa_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/sumatra_362_cohesion.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/sumatra_362_cohesion.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/university_6_17_no1_tx_excerpt.las +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/university_6_17_no1_tx_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ursa_308_xrd_mineralogy.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/ursa_308_xrd_mineralogy.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19_sr_excerpt.las +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19_sr_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19a_core_excerpt.csv +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/volve_15_9_19a_core_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/volve_f12_f14_production_excerpt.csv +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/volve_f12_f14_production_excerpt.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/walvis_208_petm_carbonate.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/walvis_208_petm_carbonate.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/woodlark_1109_rock_eval.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog/woodlark_1109_rock_eval.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog_map.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/catalog_register.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/certificates.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/cli.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/client_reports.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/config_versioning.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/conformance.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/control_room.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/correlation.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/dashboard.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/deviation.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/dlis.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/dnp3.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/doctor.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/downhole_engine.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/drift_monitor.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/earth_model.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/edr.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/elevation.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/etp.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/etp12_schemas.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/example_register_map.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/fat_sat.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/files.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/fixtures/README.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/fixtures/comcat_delaware_basin_2026-09.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/fixtures/comcat_youngstown_2011-2012.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/fixtures/ne_110m_coastline.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/follower.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/forward_model.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/friction_priors.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/gamma.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/gauge_aging.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/gauge_specs.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/gravity_reference.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/hartip.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/TESTER_GUIDE.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/alarms.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/audit-update.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/backup.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/catalog.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/charts.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/conformance.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/control-room.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/data-in.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/dlis.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/doctor.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/drift.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/edr.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/etp.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/field-ports.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/files.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/historian.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/instruments.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/interval.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/map.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/month-end.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/notifications.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/osdu.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/patches.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/ppdm.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/quakeml.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/quality.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/seedlink.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/seismic.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/setup.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/site.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/sites.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/slip.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/soak.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/sra.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/start.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/structure.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/tools.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/transients.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/upkeep.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/vibration.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/help/well-tests.md +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/housekeeping.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/interval.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/inverse_engine.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/jobs.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/live_ports.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/loadtest.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/matplotlib_demo.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/modbus.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/modbus_rtu.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/model_card.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/month_end.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/mqtt_port.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/notify.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/opcua_port.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/operator_app.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/osdu.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/patches.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/permits.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/piwebapi.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/ports.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/ppdm.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/profile_catalog.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/project.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/qt6_downhole_app.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/quakeml.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reconciler.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/IU_ANMO_10_BHZ_response.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/IU_ANMO_10_BHZ_response.xml +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/libmseed_steim.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/libmseed_steim1_4096.mseed +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/libmseed_steim2_4096.mseed +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/libmseed_steim2_512.mseed +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/tool_sources.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/us_outline.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/reference/us_outline.provenance.json +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/rock_inventory.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/sample_record.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/sample_well_profile.csv +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/sbom.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seedlink.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/segy.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_array.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_assoc.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_detect.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_film.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_harmonic.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_qc.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_response.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_signature.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_track.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_uncertainty.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/seismic_unlisted.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/sensor_swap.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/series_cache.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/service_life.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/setup_checklist.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/shapefile.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/shell.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/shut_in.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/site_map.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/sla_report.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/slip.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/soak.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/sra.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/store_forward.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/strata_join.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/structure.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/supervisor.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/survey_cmd.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/survey_view.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/telemetry.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/tool_catalog.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/tool_library.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/tool_taxonomy.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/transient.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/vibration.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/well_assembler.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/well_test_validation.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/wits0.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/witsml.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea/workspace.py +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea_program.egg-info/dependency_links.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea_program.egg-info/entry_points.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea_program.egg-info/requires.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/gea_program.egg-info/top_level.txt +0 -0
- {gea_program-0.35.0 → gea_program-0.37.0}/setup.cfg +0 -0
- {gea_program-0.35.0 → gea_program-0.37.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.37.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,34 @@ 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
|
+
### Economics
|
|
652
|
+
|
|
653
|
+
The Economics tab puts each well's days on its drilling record, feet
|
|
654
|
+
drilled and feet per day beside what it cost by the operator's own declared
|
|
655
|
+
day rates, items and AFE - cost per foot, per day and by section, never
|
|
656
|
+
divided while the rates leave time uncovered - and sets each well against
|
|
657
|
+
the U.S. Energy Information Administration's daily WTI and Henry Hub spot
|
|
658
|
+
prices while it was drilled. The prices ship with the program and refresh
|
|
659
|
+
on request; any other series (a rig count) is imported with its source.
|
|
660
|
+
`gea economics`, `gea help economics`.
|
|
661
|
+
|
|
662
|
+
### The well in 3D
|
|
663
|
+
|
|
664
|
+
The 3D tab draws each well's path from its directional survey by the
|
|
665
|
+
minimum-curvature method, below sea level from its declared rig floor, among
|
|
666
|
+
the mapped formations - interpreted horizon surfaces, or a formation-top
|
|
667
|
+
map's contours at their elevations - with its picks on it. Colour the wells
|
|
668
|
+
by formation, inclination, dogleg severity, or a drilling recorder's gamma,
|
|
669
|
+
rate of penetration or weight on bit along the bit depth; replay the bit
|
|
670
|
+
going down the hole from the recorder's bit depth or the survey stations'
|
|
671
|
+
own times. Turn, pan and zoom by pointer, touch or keys. The Volve field
|
|
672
|
+
(Equinor's open data: three original surveys, the field's formation picks,
|
|
673
|
+
five mapped horizons) ships as the worked example, checked against itself.
|
|
674
|
+
Until then the workspace's formation-top contours are drawn on their own.
|
|
675
|
+
A workspace well is drawn once it has a position, a reference elevation and
|
|
676
|
+
a survey: `gea trajectory` attaches one, verbatim, with its azimuth
|
|
677
|
+
reference stated. `gea well3d`, `gea help well3d`.
|
|
678
|
+
|
|
651
679
|
### The control room
|
|
652
680
|
|
|
653
681
|
The Control room tab opens the wall: a screen read from across the room, one
|
|
@@ -609,6 +609,34 @@ 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
|
+
### Economics
|
|
613
|
+
|
|
614
|
+
The Economics tab puts each well's days on its drilling record, feet
|
|
615
|
+
drilled and feet per day beside what it cost by the operator's own declared
|
|
616
|
+
day rates, items and AFE - cost per foot, per day and by section, never
|
|
617
|
+
divided while the rates leave time uncovered - and sets each well against
|
|
618
|
+
the U.S. Energy Information Administration's daily WTI and Henry Hub spot
|
|
619
|
+
prices while it was drilled. The prices ship with the program and refresh
|
|
620
|
+
on request; any other series (a rig count) is imported with its source.
|
|
621
|
+
`gea economics`, `gea help economics`.
|
|
622
|
+
|
|
623
|
+
### The well in 3D
|
|
624
|
+
|
|
625
|
+
The 3D tab draws each well's path from its directional survey by the
|
|
626
|
+
minimum-curvature method, below sea level from its declared rig floor, among
|
|
627
|
+
the mapped formations - interpreted horizon surfaces, or a formation-top
|
|
628
|
+
map's contours at their elevations - with its picks on it. Colour the wells
|
|
629
|
+
by formation, inclination, dogleg severity, or a drilling recorder's gamma,
|
|
630
|
+
rate of penetration or weight on bit along the bit depth; replay the bit
|
|
631
|
+
going down the hole from the recorder's bit depth or the survey stations'
|
|
632
|
+
own times. Turn, pan and zoom by pointer, touch or keys. The Volve field
|
|
633
|
+
(Equinor's open data: three original surveys, the field's formation picks,
|
|
634
|
+
five mapped horizons) ships as the worked example, checked against itself.
|
|
635
|
+
Until then the workspace's formation-top contours are drawn on their own.
|
|
636
|
+
A workspace well is drawn once it has a position, a reference elevation and
|
|
637
|
+
a survey: `gea trajectory` attaches one, verbatim, with its azimuth
|
|
638
|
+
reference stated. `gea well3d`, `gea help well3d`.
|
|
639
|
+
|
|
612
640
|
### The control room
|
|
613
641
|
|
|
614
642
|
The Control room tab opens the wall: a screen read from across the room, one
|
|
@@ -475,6 +475,41 @@ def main(argv=None) -> int:
|
|
|
475
475
|
p_ck.add_argument("--note", type=str, default="", help="--confirm: what it was checked against (required)")
|
|
476
476
|
p_ck.add_argument("--actor", type=str, default="cli")
|
|
477
477
|
|
|
478
|
+
p_ec = sub.add_parser("economics", help="each well's time, footage and declared cost against the market while it was drilled; declare rates, items and an AFE; refresh or import price series")
|
|
479
|
+
p_ec.add_argument("--workspace", type=str, required=True)
|
|
480
|
+
p_ec.add_argument("--json", action="store_true")
|
|
481
|
+
p_ec.add_argument("--well", type=str, default=None, help="the well a rate, item or AFE is for")
|
|
482
|
+
p_ec.add_argument("--rate", type=float, default=None, help="a day rate, USD/day, over --from/--to (open when left out)")
|
|
483
|
+
p_ec.add_argument("--item", type=float, default=None, help="a one-off cost, USD (a bit, a cement job), optionally --section N")
|
|
484
|
+
p_ec.add_argument("--afe", type=float, default=None, help="the well's AFE, USD")
|
|
485
|
+
p_ec.add_argument("--what", type=str, default="", help="what the rate or item pays for")
|
|
486
|
+
p_ec.add_argument("--section", type=int, default=None)
|
|
487
|
+
p_ec.add_argument("--from", dest="date_from", type=str, default=None, help="rate start, ISO date or date-time on the recorder's clock")
|
|
488
|
+
p_ec.add_argument("--to", dest="date_to", type=str, default=None, help="rate end")
|
|
489
|
+
p_ec.add_argument("--source", type=str, default="", help="where the number came from (contract, invoice, AFE number)")
|
|
490
|
+
p_ec.add_argument("--refresh-prices", action="store_true", help="fetch the latest EIA spot-price copies into the workspace")
|
|
491
|
+
p_ec.add_argument("--import-series", type=str, default=None, help="a CSV of dates and values to keep beside the prices (a rig count)")
|
|
492
|
+
p_ec.add_argument("--name", type=str, default="")
|
|
493
|
+
p_ec.add_argument("--unit", type=str, default="")
|
|
494
|
+
p_ec.add_argument("--actor", type=str, default="cli")
|
|
495
|
+
|
|
496
|
+
p_tj = sub.add_parser("trajectory", help="attach a well's directional survey (WITSML trajectory or a table of MD, Inc, Azi), kept verbatim beside the well, so the 3D tab can draw its path")
|
|
497
|
+
p_tj.add_argument("--workspace", type=str, required=True)
|
|
498
|
+
p_tj.add_argument("--well", type=str, required=True)
|
|
499
|
+
p_tj.add_argument("--file", type=str, required=True)
|
|
500
|
+
p_tj.add_argument("--azimuth-reference", type=str, required=True, choices=("grid", "true", "magnetic"))
|
|
501
|
+
p_tj.add_argument("--grid", type=str, default=None, help="grid azimuths: the grid, e.g. TX_CENTRAL or UTM14N")
|
|
502
|
+
p_tj.add_argument("--declination", type=float, default=None, help="magnetic azimuths: the declination used, degrees east positive")
|
|
503
|
+
p_tj.add_argument("--md-unit", type=str, default="ft", choices=("ft", "m"))
|
|
504
|
+
p_tj.add_argument("--depth-reference", type=str, default=None, help="KB, DF or GL: must match the well's declared reference elevation")
|
|
505
|
+
p_tj.add_argument("--source", type=str, required=True, help='where the survey came from, e.g. "RRC imaged records, directional survey"')
|
|
506
|
+
p_tj.add_argument("--actor", type=str, default="cli")
|
|
507
|
+
|
|
508
|
+
p_3d = sub.add_parser("well3d", help="the 3D scene in words: each well's path, how it was checked, the surfaces and the replay (the 3D tab draws it)")
|
|
509
|
+
p_3d.add_argument("--scene", type=str, default="volve", choices=("volve", "workspace"))
|
|
510
|
+
p_3d.add_argument("--workspace", type=str, default=None)
|
|
511
|
+
p_3d.add_argument("--json", action="store_true")
|
|
512
|
+
|
|
478
513
|
p_wl = sub.add_parser("wall", help="the control-room board as text: a line per well, worst first, its state and why (the same board the wall screen shows)")
|
|
479
514
|
p_wl.add_argument("--workspace", type=str, default=None, help="the workspace folder, e.g. C:\\site")
|
|
480
515
|
p_wl.add_argument("--json", action="store_true")
|
|
@@ -1937,6 +1972,71 @@ def main(argv=None) -> int:
|
|
|
1937
1972
|
else:
|
|
1938
1973
|
print(_SC.text(r))
|
|
1939
1974
|
return 0 if r["ready"] else 2
|
|
1975
|
+
elif a.cmd == "economics":
|
|
1976
|
+
import json as _json
|
|
1977
|
+
from . import economics as _EC
|
|
1978
|
+
from .cli import _safe_stdout
|
|
1979
|
+
from .workspace import Workspace, WorkspaceError
|
|
1980
|
+
_safe_stdout()
|
|
1981
|
+
try:
|
|
1982
|
+
ws = Workspace(a.workspace)
|
|
1983
|
+
except WorkspaceError as e:
|
|
1984
|
+
raise SystemExit(f"economics: {e}")
|
|
1985
|
+
try:
|
|
1986
|
+
if a.rate is not None:
|
|
1987
|
+
r = _EC.set_rate(ws, a.well or "", a.actor, a.rate, a.what, a.source, a.date_from, a.date_to)
|
|
1988
|
+
print(f"{a.well}: {r['usd_per_day']:,.2f} USD/day {r['what']} from {r['from'] or 'open'} to {r['to'] or 'open'}")
|
|
1989
|
+
return 0
|
|
1990
|
+
if a.item is not None:
|
|
1991
|
+
r = _EC.add_item(ws, a.well or "", a.actor, a.item, a.what, a.source, a.section)
|
|
1992
|
+
print(f"{a.well}: {r['usd']:,.2f} USD {r['what']}")
|
|
1993
|
+
return 0
|
|
1994
|
+
if a.afe is not None:
|
|
1995
|
+
r = _EC.set_afe(ws, a.well or "", a.actor, a.afe, a.source)
|
|
1996
|
+
print(f"{a.well}: AFE {r['usd']:,.2f} USD ({r['source']})")
|
|
1997
|
+
return 0
|
|
1998
|
+
if a.refresh_prices:
|
|
1999
|
+
for r in _EC.refresh_prices(ws, a.actor):
|
|
2000
|
+
print(f"{r['id']}: {r['status']}" + (f" to {r['as_of']} ({r['rows']} days)" if r.get('as_of') else f" - {r.get('why', '')}"))
|
|
2001
|
+
return 0
|
|
2002
|
+
if a.import_series:
|
|
2003
|
+
with open(a.import_series, "rb") as f:
|
|
2004
|
+
r = _EC.import_series(ws, a.actor, a.name, a.unit, a.source, f.read())
|
|
2005
|
+
print(f"{r['name']}: {r['rows']} values, {r['first']} to {r['as_of']} ({r['unit']})")
|
|
2006
|
+
return 0
|
|
2007
|
+
except (ValueError, KeyError, OSError, WorkspaceError) as e:
|
|
2008
|
+
raise SystemExit(f"economics: {e.args[0] if e.args else e}")
|
|
2009
|
+
e = _EC.economics(ws)
|
|
2010
|
+
print(_json.dumps(e, indent=1) if a.json else _EC.text(e))
|
|
2011
|
+
return 0
|
|
2012
|
+
elif a.cmd in ("trajectory", "well3d"):
|
|
2013
|
+
import json as _json
|
|
2014
|
+
from . import well3d as _W3
|
|
2015
|
+
from .cli import _safe_stdout
|
|
2016
|
+
from .workspace import Workspace, WorkspaceError
|
|
2017
|
+
_safe_stdout()
|
|
2018
|
+
ws = None
|
|
2019
|
+
if a.cmd == "trajectory" or a.scene == "workspace":
|
|
2020
|
+
if not a.workspace:
|
|
2021
|
+
raise SystemExit(f"{a.cmd} needs --workspace, e.g. --workspace C:\\site")
|
|
2022
|
+
try:
|
|
2023
|
+
ws = Workspace(a.workspace)
|
|
2024
|
+
except WorkspaceError as e:
|
|
2025
|
+
raise SystemExit(f"{a.cmd}: {e}")
|
|
2026
|
+
if a.cmd == "trajectory":
|
|
2027
|
+
try:
|
|
2028
|
+
with open(a.file, "rb") as f:
|
|
2029
|
+
content = f.read()
|
|
2030
|
+
r = _W3.attach_trajectory(ws, a.well, a.file, content, a.actor, a.azimuth_reference, a.md_unit, a.depth_reference, a.grid,
|
|
2031
|
+
a.declination, a.source)
|
|
2032
|
+
except (ValueError, OSError, WorkspaceError) as e:
|
|
2033
|
+
raise SystemExit(f"trajectory: {e}")
|
|
2034
|
+
print(f"{a.well}: {r['stations']} stations, {r['md_range_m'][0]:,.1f} to {r['md_range_m'][1]:,.1f} m MD, azimuths {r['azimuth_reference']}"
|
|
2035
|
+
+ (f" ({r['grid']})" if r.get('grid') else "") + f"; kept as wells/{a.well}/trajectory/{r['file']}")
|
|
2036
|
+
return 0
|
|
2037
|
+
sc = _W3.scene(ws, a.scene)
|
|
2038
|
+
print(_json.dumps(sc, indent=1) if a.json else _W3.text(sc))
|
|
2039
|
+
return 0
|
|
1940
2040
|
elif a.cmd in ("wall", "handover"):
|
|
1941
2041
|
import json as _json
|
|
1942
2042
|
from . import control_room as _CR
|
|
@@ -8613,6 +8613,363 @@ def section_cd_control_room(tmp: str) -> None:
|
|
|
8613
8613
|
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8614
8614
|
|
|
8615
8615
|
|
|
8616
|
+
|
|
8617
|
+
def section_ce_well3d(tmp: str) -> None:
|
|
8618
|
+
"""Section CE - the well in 3D: the minimum-curvature method against a known arc and against the survey companies'
|
|
8619
|
+
own positions, the shipped Volve excerpt checked against itself (frame, picks, surfaces, provenance), a workspace
|
|
8620
|
+
well drawn from an attached survey among its formation-top contours with its recorder replay, the API and the tab."""
|
|
8621
|
+
import base64 as _b64
|
|
8622
|
+
import gzip as _gz
|
|
8623
|
+
import hashlib as _hl
|
|
8624
|
+
import http.cookiejar
|
|
8625
|
+
import math as _m
|
|
8626
|
+
import subprocess as _sp
|
|
8627
|
+
import urllib.request
|
|
8628
|
+
import urllib.error
|
|
8629
|
+
from . import edr as E
|
|
8630
|
+
from . import geodesy as G
|
|
8631
|
+
from . import helplib as H
|
|
8632
|
+
from . import shapefile as SF
|
|
8633
|
+
from . import well3d as W
|
|
8634
|
+
from .workspace import Workspace
|
|
8635
|
+
from .service import Service, Users
|
|
8636
|
+
pkg = Path(__file__).parent
|
|
8637
|
+
d = Path(tmp, "ce"); d.mkdir()
|
|
8638
|
+
|
|
8639
|
+
R = 1000.0
|
|
8640
|
+
q = W.min_curvature([0.0, _m.pi * R / 2], [0.0, 90.0], [0.0, 0.0])
|
|
8641
|
+
p_arc = W.Path([0.0, _m.pi * R / 2], [0.0, 90.0], [0.0, 0.0], 0.0, 0.0, 0.0)
|
|
8642
|
+
mid = p_arc.at(_m.pi * R / 4)
|
|
8643
|
+
tr4 = W.read_witsml_trajectory(_gz.decompress((pkg / "reference" / "volve_traj_F-4.xml.gz").read_bytes()).decode())[0]
|
|
8644
|
+
tr10 = W.read_witsml_trajectory(_gz.decompress((pkg / "reference" / "volve_traj_F-10.xml.gz").read_bytes()).decode())[0]
|
|
8645
|
+
f5 = [t for n in ("36in", "17.5in", "12.25in", "8.5in") for t in W.read_witsml_trajectory(_gz.decompress((pkg / "reference" / f"volve_traj_F-5_{n}.xml.gz").read_bytes()).decode())]
|
|
8646
|
+
rep5, _ = W.replay_from_stations(f5)
|
|
8647
|
+
rep4, note4 = W.replay_from_stations([tr4])
|
|
8648
|
+
csv_ok = "MD (ft),Inc (deg),Azi (deg)\n0,0,0\n1000,0,0\n2000,10,90\n3000,30,90\n4000,45,95\n5750,60,100\n"
|
|
8649
|
+
refused = []
|
|
8650
|
+
for bad in ("Depth,Angle\n0,0\n", "MD,Inc,Azi\n0,0,0\n0,1,2\n", "MD,Inc,Azi\n0,0,0\n100,x,2\n", "MD,Inc,Azi\n0,0,0\n100,200,2\n"):
|
|
8651
|
+
try:
|
|
8652
|
+
W.read_survey_csv(bad)
|
|
8653
|
+
except ValueError:
|
|
8654
|
+
refused.append(True)
|
|
8655
|
+
st_csv = W.read_survey_csv(csv_ok, "ft")
|
|
8656
|
+
c1 = {
|
|
8657
|
+
"arc": abs(q["n"][1] - R) < 1e-9 and abs(q["v"][1] - R) < 1e-9 and abs(mid[1] - R * (1 - _m.cos(_m.pi / 4))) < 1e-6
|
|
8658
|
+
and abs(mid[2] - R * _m.sin(_m.pi / 4)) < 1e-6 and abs(q["dls"][1] - 90.0 / (_m.pi * R / 2) * 30.0) < 1e-9,
|
|
8659
|
+
"units": tr4["azi_ref"] == "true north" and abs(tr4["stations"][40]["incl"] - _m.degrees(0.446106147512686)) < 1e-9 and tr10["grid_cor_deg"] == -0.95
|
|
8660
|
+
and len(tr4["stations"]) == 87 and abs(st_csv[-1]["md"] - 5750 * 0.3048) < 1e-9,
|
|
8661
|
+
"company": all(x is not None and x < 0.01 for x in (W.file_position_miss(tr4["stations"]), W.file_position_miss(tr10["stations"]),
|
|
8662
|
+
W.file_position_miss(f5[-1]["stations"]))),
|
|
8663
|
+
"csv": len(refused) == 4,
|
|
8664
|
+
"replay": rep5 is not None and len(rep5["t"]) == 100 and rep5["t"][0].startswith("2007-11-16") and rep5["t"][-1].startswith("2008-07-29")
|
|
8665
|
+
and rep4 is None and "1 distinct time" in note4 and all(m0 <= m1 or t0 < t1 for (t0, m0), (t1, m1) in zip(zip(rep5["t"], rep5["md"]), zip(rep5["t"][1:], rep5["md"][1:]))),
|
|
8666
|
+
}
|
|
8667
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
8668
|
+
ok(not failed1,
|
|
8669
|
+
"CE1 the minimum-curvature method: a quarter circle of 1,000 m radius ends 1,000 m out and 1,000 m down, and its middle lies on the arc "
|
|
8670
|
+
"(292.9 m out, 707.1 m down), not on a chord; the WITSML reader takes radians and degrees alike; the paths computed from the Volve files "
|
|
8671
|
+
"reproduce the TVD and displacements the survey companies wrote in them to under a centimetre; a survey table without its columns, with a "
|
|
8672
|
+
"repeated depth or a bad row refused; the replay only from stations measured while drilling (F-5: 100 steps, Nov 2007 to Jul 2008; F-4's "
|
|
8673
|
+
"one-time resurvey not replayed)"
|
|
8674
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
8675
|
+
|
|
8676
|
+
sc = W.volve_scene()
|
|
8677
|
+
wl = {w["id"]: w for w in sc["wells"]}
|
|
8678
|
+
picks = W.volve_picks()
|
|
8679
|
+
dms = lambda dd, mm, ss: dd + mm / 60 + ss / 3600
|
|
8680
|
+
frame = []
|
|
8681
|
+
for wn, la, lo in (("NO 15/9-F-1 C", dms(58, 26, 29.907), dms(1, 53, 14.708)), ("NO 15/9-F-11 B", dms(58, 26, 29.960), dms(1, 53, 14.870))):
|
|
8682
|
+
sb = next(p for p in picks if p["WELL"] == wn and p["PICKS"] == "Seabed")
|
|
8683
|
+
a = G.unproject(float(sb["EASTING"]), float(sb["NORTHING"]), G.utm(31, True, "INTL1924"))
|
|
8684
|
+
b = G.unproject(float(sb["EASTING"]), float(sb["NORTHING"]), G.utm(31, True, "WGS84"))
|
|
8685
|
+
frame.append((G.geodesic_m(a[0], a[1], la, lo)["distance_m"], G.geodesic_m(b[0], b[1], la, lo)["distance_m"]))
|
|
8686
|
+
prov = json.loads((pkg / "reference" / "volve_3d.provenance.json").read_text(encoding="utf-8"))
|
|
8687
|
+
hashes_ok = all(_hl.sha256((pkg / "reference" / k).read_bytes()).hexdigest() == v["sha256"] for k, v in prov["outputs"].items())
|
|
8688
|
+
pts = prov["outputs"]["volve_surfaces.json.gz"]["points"]
|
|
8689
|
+
# the repository stores every text file with LF line ends (.gitattributes: text=auto eol=lf), so a package file holding
|
|
8690
|
+
# CRLF would reach a checkout - and the published wheel - as different bytes from the ones hashed here
|
|
8691
|
+
crlf = []
|
|
8692
|
+
for dp, dn, fn in os.walk(pkg):
|
|
8693
|
+
dn[:] = [x for x in dn if x not in ("__pycache__", "catalog_operator")]
|
|
8694
|
+
for f_ in fn:
|
|
8695
|
+
b_ = Path(dp, f_).read_bytes()
|
|
8696
|
+
if b"\0" not in b_[:8000] and b"\r\n" in b_:
|
|
8697
|
+
crlf.append(os.path.relpath(os.path.join(dp, f_), pkg))
|
|
8698
|
+
c2 = {
|
|
8699
|
+
"frame": all(a < 0.1 and 140 < b < 160 for a, b in frame) and "ED50" in sc["frame"]["crs"],
|
|
8700
|
+
"picks": all(wl[k]["pick_miss_max_m"] < 0.3 and abs(wl[k]["ref_elev_m"] - 54.9) < 1e-6 for k in ("F-4", "F-5", "F-10"))
|
|
8701
|
+
and abs(wl["F-10"]["td_md"] - 5300.07) < 0.01 and len(sc["picks_only"]) == 31,
|
|
8702
|
+
"azimuth": wl["F-4"]["azimuth"]["applied"] == "as grid" and "14 m" in wl["F-4"]["azimuth"]["note"] and wl["F-5"]["azimuth"]["file_reference"] == "grid north",
|
|
8703
|
+
"surfaces": len(sc["surfaces"]) == 5 and all(s["affine_miss_m"] < 0.01 and s["tie_median_m"] < 7.0 and len(s["ties"]) >= 23 for s in sc["surfaces"])
|
|
8704
|
+
and all(pts[s["id"]] == [s["n_source_points"], s["n_points"]] for s in sc["surfaces"])
|
|
8705
|
+
and [s["id"] for s in sc["surfaces"]] == ["Ty", "SHETLAND", "BCU", "Hugin_Fm_Top", "Hugin_Base"],
|
|
8706
|
+
"line_ends": not crlf and all(k.endswith(".gz") or b"\r\n" not in (pkg / "reference" / k).read_bytes() for k in prov["outputs"]),
|
|
8707
|
+
"provenance": hashes_ok and "commercial purposes" in prov["license"] and "may not be sold" in prov["license"] and len(prov["sources"]) == 12
|
|
8708
|
+
and all(len(v["commit"]) == 40 for v in prov["sources"].values()) and "Equinor ASA" in sc["attribution"] and "not sold" in sc["attribution"],
|
|
8709
|
+
}
|
|
8710
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
8711
|
+
ok(not failed2,
|
|
8712
|
+
"CE2 the Volve excerpt checked against itself: its eastings and northings are ED50 zone 31N (the slots unproject within 0.1 m of the "
|
|
8713
|
+
"operator's own log headers, about 150 m off if read as WGS84); the three paths land on Equinor's picks to under 0.3 m from a 54.9 m rig "
|
|
8714
|
+
"floor; 15/9-F-4's azimuths read as grid, with the 14 m it would miss by otherwise stated; five surfaces drawn as delivered, every fourth "
|
|
8715
|
+
"line, each within a few metres of its picks at the median; every shipped file hashed in its provenance with the pinned public copy it "
|
|
8716
|
+
"came from (and no package file with CRLF line ends, which a checkout would change from the bytes hashed), the licence (commercial use, no sale) and the attribution on the page"
|
|
8717
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
8718
|
+
|
|
8719
|
+
ev = E.selftest_events(str(d))
|
|
8720
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
8721
|
+
wb = ws.add_well_file(ev["path"], display="Well B", actor="tester"); wc = ws.add_well_file(ev["path"], display="Well C", actor="tester")
|
|
8722
|
+
ws.set_well(wb["id"], surface={"lat": 31.42, "lon": -104.10, "datum": "NAD83", "elevation": {"ft": 3000.0, "datum": "KB", "source": "test plat"}}, actor="tester")
|
|
8723
|
+
lat1, lon1 = 31.42, -104.10
|
|
8724
|
+
kmlon = 1.0 / (111.195 * _m.cos(_m.radians(lat1)))
|
|
8725
|
+
SF.write(str(d / "Top_A"), "PolyLine", [([[(lon1 + (i - 1.5) * kmlon + 0.0005 * k, lat1 - 0.05 + 0.001 * k) for k in range(100)]], {"ElevTop_ft": -1500.0 - 100.0 * i}) for i in range(4)],
|
|
8726
|
+
[("ElevTop_ft", "F", 10, 1)], SF.NAD83_GEOGCS)
|
|
8727
|
+
SF.write(str(d / "Faults"), "PolyLine", [([[(lon1 - 0.02 + 0.001 * j, lat1) for j in range(40)]], {"TYPE": "normal fault"})], [("TYPE", "C", 20, 0)], SF.NAD83_GEOGCS)
|
|
8728
|
+
ws.add_layer(str(d / "Top_A.shp"), "Top A", actor="tester", formation="A", role="top", citation="test")
|
|
8729
|
+
ws.add_layer(str(d / "Faults.shp"), "Faults", actor="tester", citation="test")
|
|
8730
|
+
sc0 = W.workspace_scene(ws) # no survey yet: the formation tops alone
|
|
8731
|
+
att_refused = 0
|
|
8732
|
+
for kw in ({"azimuth_reference": ""}, {"azimuth_reference": "grid"}, {"azimuth_reference": "magnetic"}, {"azimuth_reference": "true", "source": " "}):
|
|
8733
|
+
k = {"azimuth_reference": "true", "source": "s", **kw}
|
|
8734
|
+
try:
|
|
8735
|
+
W.attach_trajectory(ws, wb["id"], "s.csv", csv_ok.encode(), "tester", k["azimuth_reference"], "ft", None, None, None, k["source"])
|
|
8736
|
+
except ValueError:
|
|
8737
|
+
att_refused += 1
|
|
8738
|
+
ws.refresh_dashboard(actor="tester")
|
|
8739
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
8740
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
8741
|
+
base_url = svc.url.rstrip("/")
|
|
8742
|
+
|
|
8743
|
+
def opener(name, pw):
|
|
8744
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
8745
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw}).encode())
|
|
8746
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
8747
|
+
return o
|
|
8748
|
+
|
|
8749
|
+
def call(o, method, path, body=None):
|
|
8750
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
8751
|
+
if method == "POST":
|
|
8752
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
8753
|
+
try:
|
|
8754
|
+
with o.open(rq, timeout=120) as rr_:
|
|
8755
|
+
return rr_.status, json.loads(rr_.read())
|
|
8756
|
+
except urllib.error.HTTPError as e:
|
|
8757
|
+
return e.code, json.loads(e.read())
|
|
8758
|
+
try:
|
|
8759
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
8760
|
+
st_l, ls = call(V, "GET", "/api/3d/scenes")
|
|
8761
|
+
st_v, vs = call(V, "GET", "/api/3d/scene/volve")
|
|
8762
|
+
st_n, _ = call(V, "GET", "/api/3d/scene/nope")
|
|
8763
|
+
body = {"filename": "survey.csv", "content_b64": _b64.b64encode(csv_ok.encode()).decode(), "azimuth_reference": "grid", "grid": "TX_CENTRAL",
|
|
8764
|
+
"md_unit": "ft", "source": "test survey"}
|
|
8765
|
+
st_pv, _ = call(V, "POST", f"/api/wells/{wb['id']}/trajectory", body)
|
|
8766
|
+
st_b64, _ = call(O, "POST", f"/api/wells/{wb['id']}/trajectory", {**body, "content_b64": "%%%"})
|
|
8767
|
+
st_at, at = call(O, "POST", f"/api/wells/{wb['id']}/trajectory", body)
|
|
8768
|
+
st_w, wsc = call(V, "GET", "/api/3d/scene/workspace")
|
|
8769
|
+
finally:
|
|
8770
|
+
svc.stop()
|
|
8771
|
+
kept = (Path(wsp, "wells", wb["id"], "trajectory", "survey.csv").read_bytes() == csv_ok.encode())
|
|
8772
|
+
audit = [e["action"] for e in ws.audit_log(500)]
|
|
8773
|
+
cli = _sp.run([sys.executable, "-m", "gea", "well3d", "--scene", "workspace", "--workspace", wsp], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
8774
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
8775
|
+
help_md = (pkg / "help" / "well3d.md").read_text(encoding="utf-8")
|
|
8776
|
+
w0 = (wsc.get("wells") or [{}])[0]
|
|
8777
|
+
c3 = {
|
|
8778
|
+
"attach": att_refused == 4 and st_pv == 403 and st_b64 == 400 and st_at == 200 and at["stations"] == 6 and at["grid"] == "TX_CENTRAL" and kept
|
|
8779
|
+
and at["sha256"] == _hl.sha256(csv_ok.encode()).hexdigest() and "well.trajectory" in audit,
|
|
8780
|
+
"scene": st_l == 200 and [s["id"] for s in ls["scenes"]] == ["workspace", "volve"] and st_v == 200 and len(vs["wells"]) == 3 and st_n == 404
|
|
8781
|
+
and st_w == 200 and len(wsc["wells"]) == 1 and abs(w0["td_md"] - 1752.6) < 0.01 and "TX_CENTRAL grid north" in w0["azimuth"]["applied"]
|
|
8782
|
+
and w0["replay"]["kind"] == "drilling recorder" and "Gamma" in w0["channels"] and not w0["replay"]["t"][0].endswith("Z")
|
|
8783
|
+
and [n["id"] for n in wsc["not_placed"]] == [wc["id"]] and wsc["layers"][0]["lines"][0]["z"] == round(1500 * 0.3048, 1)
|
|
8784
|
+
and any(n["display"] == "Faults" and "no depth" in n["why"] for n in wsc["layers_not_drawn"]),
|
|
8785
|
+
"tops_alone": sc0["wells"] == [] and [l["name"] for l in sc0["layers"]] == ["Top A"] and sc0["frame"]["area"] == "the placed wells"
|
|
8786
|
+
and {n["id"]: n["why"] for n in sc0["not_placed"]}[wb["id"]] == "no directional survey attached",
|
|
8787
|
+
"cli": cli.returncode == 0 and "Well B: 6 stations" in cli.stdout and "contour lines at their elevations" in cli.stdout,
|
|
8788
|
+
"page": all(x in page_src for x in ("VIEWS['3d']", "['#/3d', '3D']", "getContext('webgl'", "OES_element_index_uint", "w3Tube", "w3SurfMesh",
|
|
8789
|
+
"/api/3d/scene/", "w3Play", "the recorder's clock", "ArrowLeft", "see-through", "/trajectory`", "w3Hit")),
|
|
8790
|
+
"help": any(t["topic"] == "well3d" for t in H.topics()) and not H.check("well3d") and H.VIEW_TOPIC.get("3d") == "well3d"
|
|
8791
|
+
and "a well with no survey" in help_md.split("will not call a measurement")[1],
|
|
8792
|
+
}
|
|
8793
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
8794
|
+
ok(not failed3,
|
|
8795
|
+
"CE3 a workspace well in 3D: a survey attached only with its azimuth reference stated (a grid named, a declination given) and its source, "
|
|
8796
|
+
"kept byte for byte and audited; the path turned from TX_CENTRAL grid north by the convergence there, set below sea level from the "
|
|
8797
|
+
"declared rig floor among the formation-top contours at their elevations (a fault trace listed as having no depth; the tops drawn alone "
|
|
8798
|
+
"before any survey), the recorder's bit depth replayed on its own clock and its gamma along the path; a well without a position listed, not drawn; the API, the CLI and the 3D "
|
|
8799
|
+
"tab (WebGL by hand, the replay, turn and zoom by pointer and keys); the help page"
|
|
8800
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8801
|
+
|
|
8802
|
+
|
|
8803
|
+
|
|
8804
|
+
def section_cf_economics(tmp: str) -> None:
|
|
8805
|
+
"""Section CF - economics: the shipped EIA spot prices checked against their provenance and against two days EIA
|
|
8806
|
+
published, series refreshed and imported only when they read, a well's cost from declared rates done by hand and
|
|
8807
|
+
left undivided when the rates leave time uncovered, the market while the well was drilled, the API, the CLI, the tab."""
|
|
8808
|
+
import base64 as _b64
|
|
8809
|
+
import gzip as _gz
|
|
8810
|
+
import hashlib as _hl
|
|
8811
|
+
import http.cookiejar
|
|
8812
|
+
import subprocess as _sp
|
|
8813
|
+
import urllib.request
|
|
8814
|
+
import urllib.error
|
|
8815
|
+
from datetime import datetime as _dt
|
|
8816
|
+
from . import economics as EC
|
|
8817
|
+
from . import edr as E
|
|
8818
|
+
from . import helplib as H
|
|
8819
|
+
from .workspace import Workspace
|
|
8820
|
+
from .service import Service, Users
|
|
8821
|
+
pkg = Path(__file__).parent
|
|
8822
|
+
d = Path(tmp, "cf"); d.mkdir()
|
|
8823
|
+
|
|
8824
|
+
prov = json.loads((pkg / "reference" / "eia_prices.provenance.json").read_text(encoding="utf-8"))
|
|
8825
|
+
hashes = all(_hl.sha256((pkg / "reference" / s["file"]).read_bytes()).hexdigest() == s["sha256"]
|
|
8826
|
+
and _hl.sha256(_gz.decompress((pkg / "reference" / s["file"]).read_bytes())).hexdigest() == s["sha256_uncompressed"]
|
|
8827
|
+
and len(s["source"]["commit"]) == 40 for s in prov["series"].values())
|
|
8828
|
+
wd, wv, _ = EC.read_series(None, "wti")
|
|
8829
|
+
hd, hv, _ = EC.read_series(None, "henry_hub")
|
|
8830
|
+
refused = 0
|
|
8831
|
+
for bad in ("Date,Price\n2020-01-01,1\n2020-01-01,2\n", "Date,Price\n2020-01-01,1\n2020-01-02,x\n", "Date,Price\n2020-13-01,1\n2020-01-02,2\n", "Date,Price\n"):
|
|
8832
|
+
try:
|
|
8833
|
+
EC._parse_series(bad)
|
|
8834
|
+
except ValueError:
|
|
8835
|
+
refused += 1
|
|
8836
|
+
bd, bv, _ = EC._parse_series("Date,Price\n2020-01-03,3\n2020-01-01,1\n2020-01-02,\n")
|
|
8837
|
+
ev = E.selftest_events(str(d))
|
|
8838
|
+
wsp = str(d / "ws"); ws = Workspace.create(wsp, "Pad 3", actor="tester")
|
|
8839
|
+
wb = ws.add_well_file(ev["path"], display="Well B", actor="tester"); wc = ws.add_well_file(ev["path"], display="Well C", actor="tester")
|
|
8840
|
+
later = ("Date,Price\n" + "".join(f"{x},{y}\n" for x, y in zip(wd, wv)) + "2099-01-01,50.0\n").encode()
|
|
8841
|
+
hh_short = "Date,Price\n2001-01-02,9.0\n2001-01-03,9.5\n".encode()
|
|
8842
|
+
calls = {}
|
|
8843
|
+
|
|
8844
|
+
def fake(u):
|
|
8845
|
+
calls[u] = True
|
|
8846
|
+
if "oil-prices" in u:
|
|
8847
|
+
return later
|
|
8848
|
+
if "natural-gas" in u:
|
|
8849
|
+
return hh_short
|
|
8850
|
+
raise OSError("no network")
|
|
8851
|
+
ref = EC.refresh_prices(ws, "tester", fetch=fake)
|
|
8852
|
+
ref_status = {r["id"]: r["status"] for r in ref}
|
|
8853
|
+
imp_refused = 0
|
|
8854
|
+
for kw in ({"name": "", "unit": "rigs", "source": "s"}, {"name": "Rigs", "unit": "", "source": "s"}, {"name": "Rigs", "unit": "rigs", "source": ""}, {"name": "wti", "unit": "x", "source": "s"}):
|
|
8855
|
+
try:
|
|
8856
|
+
EC.import_series(ws, "tester", kw["name"], kw["unit"], kw["source"], b"Date,Count\n2026-06-26,300\n2026-07-03,301\n")
|
|
8857
|
+
except ValueError:
|
|
8858
|
+
imp_refused += 1
|
|
8859
|
+
imp = EC.import_series(ws, "tester", "Permian rig count", "rigs", "test", b"Week,Rigs\n2026-06-26,300\n2026-07-03,301\n2026-07-10,299\n")
|
|
8860
|
+
c1 = {
|
|
8861
|
+
"shipped": hashes and prov["series"]["wti"]["eia_series"] == "RWTC" and prov["series"]["henry_hub"]["eia_series"] == "RNGWHHD"
|
|
8862
|
+
and "public domain" in prov["license"].lower() and wv[wd.index("2020-04-20")] == -36.98 and hv[hd.index("2021-02-17")] == 23.86
|
|
8863
|
+
and len(wd) == prov["series"]["wti"]["rows"] and wd == sorted(wd),
|
|
8864
|
+
"parse": refused == 4 and bd == ["2020-01-01", "2020-01-03"] and bv == [1.0, 3.0],
|
|
8865
|
+
"refresh": ref_status == {"wti": "REFRESHED", "henry_hub": "NOT REFRESHED"} and EC.read_series(ws, "wti")[0][-1] == "2099-01-01"
|
|
8866
|
+
and EC.read_series(ws, "henry_hub")[2]["kind"] == "shipped" and len(calls) == 2,
|
|
8867
|
+
"import": imp_refused == 4 and imp["id"] == "permian_rig_count" and imp["rows"] == 3 and imp["date_column"] == "Week" and imp["value_column"] == "Rigs"
|
|
8868
|
+
and Path(wsp, "economics", "series", "permian_rig_count.csv").read_bytes().startswith(b"Week,Rigs"),
|
|
8869
|
+
}
|
|
8870
|
+
failed1 = [k for k, v in c1.items() if not v]
|
|
8871
|
+
ok(not failed1,
|
|
8872
|
+
"CF1 the price series: EIA's daily WTI (RWTC) and Henry Hub (RNGWHHD) spot prices shipped with their provenance and hashes, carrying the "
|
|
8873
|
+
"-36.98 of 20 April 2020 and the 23.86 of 17 February 2021 as EIA published them; a series with a repeated or bad date or a bad number "
|
|
8874
|
+
"refused, a blank day left out; a refreshed copy kept only when it reads and runs later than the one held; an imported series kept byte "
|
|
8875
|
+
"for byte with its name, unit and source"
|
|
8876
|
+
+ (f" [failed: {', '.join(failed1)}]" if failed1 else ""))
|
|
8877
|
+
|
|
8878
|
+
ws.refresh_dashboard(actor="tester")
|
|
8879
|
+
rec = json.loads(Path(ws.reports_dir, "wells", wb["id"], "drilling_record.json").read_text(encoding="utf-8"))
|
|
8880
|
+
t0, t1 = _dt.fromisoformat(rec["start"]), _dt.fromisoformat(rec["end"])
|
|
8881
|
+
days = (t1 - t0).total_seconds() / 86400.0
|
|
8882
|
+
mid = "2026-07-02T12:00:00"
|
|
8883
|
+
EC.set_rate(ws, wb["id"], "tester", 30000, "rig spread", "contract", rec["start"], mid)
|
|
8884
|
+
part = EC.economics(ws)
|
|
8885
|
+
pb = next(w for w in part["wells"] if w["well_id"] == wb["id"])
|
|
8886
|
+
bad = 0
|
|
8887
|
+
for args in ((30000, "rig", "c", "2026-07-02", "2026-07-05"), (30000, "", "c", None, None), (30000, "rig", "", None, None), (-5, "rig", "c", "2027-01-01", None),
|
|
8888
|
+
(30000, "rig", "c", "2027-01-02", "2027-01-01")):
|
|
8889
|
+
try:
|
|
8890
|
+
EC.set_rate(ws, wb["id"], "tester", *args)
|
|
8891
|
+
except ValueError:
|
|
8892
|
+
bad += 1
|
|
8893
|
+
EC.set_rate(ws, wb["id"], "tester", 32000, "rig spread", "contract", mid, None)
|
|
8894
|
+
EC.add_item(ws, wb["id"], "tester", 18000, "bit", "invoice", 1)
|
|
8895
|
+
EC.set_afe(ws, wb["id"], "tester", 150000, "AFE 1")
|
|
8896
|
+
full = EC.economics(ws)
|
|
8897
|
+
fb = next(w for w in full["wells"] if w["well_id"] == wb["id"])
|
|
8898
|
+
first = (_dt.fromisoformat(mid) - t0).total_seconds() / 86400.0
|
|
8899
|
+
by_hand = round(30000 * first + 32000 * (days - first) + 18000, 2)
|
|
8900
|
+
win = [v for x, v in zip(wd, wv) if rec["start"][:10] <= x <= rec["end"][:10]]
|
|
8901
|
+
c2 = {
|
|
8902
|
+
"partial": pb["cost"]["complete"] is False and pb["cost"]["per_ft"] is None and "cover" in pb["cost"]["why_incomplete"]
|
|
8903
|
+
and abs(pb["cost"]["usd"] - round(30000 * first, 2)) < 0.01,
|
|
8904
|
+
"refused": bad == 5,
|
|
8905
|
+
"by_hand": fb["cost"]["complete"] and abs(fb["cost"]["usd"] - by_hand) < 0.01 and abs(fb["cost"]["per_ft"] - round(by_hand / rec["feet_drilled"], 2)) < 0.01
|
|
8906
|
+
and abs(fb["cost"]["vs_afe_pct"] - round(100 * (by_hand - 150000) / 150000, 1)) < 0.05 and fb["days"] == round(days, 2),
|
|
8907
|
+
"sections": all(s["complete"] for s in fb["sections"]) and fb["sections"][0]["cost_usd"] > 18000,
|
|
8908
|
+
"market": fb["market"]["wti"]["n"] == len(win) and abs(fb["market"]["wti"]["mean"] - round(sum(win) / len(win), 3)) < 1e-9
|
|
8909
|
+
and full["chart"]["wells"][0]["from"] == rec["start"][:10] and "permian_rig_count" in full["chart"]["series"],
|
|
8910
|
+
"correlation": "spearman_cost_per_ft_vs_wti" not in full["correlation"] and full["correlation"]["n_wells"] == 1
|
|
8911
|
+
and abs(EC._spearman([1, 2, 3, 4], [10, 20, 30, 40]) - 1) < 1e-12 and abs(EC._spearman([1, 2, 3, 4], [4, 3, 2, 1]) + 1) < 1e-12,
|
|
8912
|
+
"audit": all(a in [x["action"] for x in ws.audit_log(500)] for a in ("economics.rate", "economics.item", "economics.afe", "economics.prices.refresh", "economics.series.import")),
|
|
8913
|
+
}
|
|
8914
|
+
failed2 = [k for k, v in c2.items() if not v]
|
|
8915
|
+
ok(not failed2,
|
|
8916
|
+
"CF2 a well's cost: two day rates and a bit over the drilling record's own time come to the dollar the hand sum gives, with cost per foot "
|
|
8917
|
+
"and the difference from the AFE; while the rates leave time uncovered the cost is shown, marked incomplete and not divided by the footage; "
|
|
8918
|
+
"overlapping periods, a rate with no source or no purpose, a negative rate and a period that ends before it starts refused; the WTI mean "
|
|
8919
|
+
"while drilling equal to the days EIA published in that window; no correlation across fewer than ten wells; every declaration audited"
|
|
8920
|
+
+ (f" [failed: {', '.join(failed2)}]" if failed2 else ""))
|
|
8921
|
+
|
|
8922
|
+
Users(str(Path(wsp, "users.json"))).add("v", "viewer-pass-1", "viewer"); Users(str(Path(wsp, "users.json"))).add("op", "op-pass-123", "operator")
|
|
8923
|
+
svc = Service(wsp, port=0, scheduler=False).start()
|
|
8924
|
+
base_url = svc.url.rstrip("/")
|
|
8925
|
+
|
|
8926
|
+
def opener(name, pw):
|
|
8927
|
+
o = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
|
|
8928
|
+
rq = urllib.request.Request(base_url + "/api/login", method="POST", data=json.dumps({"name": name, "password": pw}).encode())
|
|
8929
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1"); o.open(rq, timeout=30)
|
|
8930
|
+
return o
|
|
8931
|
+
|
|
8932
|
+
def call(o, method, path, body=None):
|
|
8933
|
+
rq = urllib.request.Request(base_url + path, method=method, data=(json.dumps(body).encode() if body is not None else None))
|
|
8934
|
+
if method == "POST":
|
|
8935
|
+
rq.add_header("Content-Type", "application/json"); rq.add_header("X-GEA-Action", "1")
|
|
8936
|
+
try:
|
|
8937
|
+
with o.open(rq, timeout=120) as rr_:
|
|
8938
|
+
return rr_.status, json.loads(rr_.read())
|
|
8939
|
+
except urllib.error.HTTPError as e:
|
|
8940
|
+
return e.code, json.loads(e.read())
|
|
8941
|
+
try:
|
|
8942
|
+
V, O = opener("v", "viewer-pass-1"), opener("op", "op-pass-123")
|
|
8943
|
+
st_g, eg = call(V, "GET", "/api/economics")
|
|
8944
|
+
st_vr, _ = call(V, "POST", "/api/economics/rate", {"well_id": wc["id"], "usd_per_day": 1000, "what": "rig", "source": "s"})
|
|
8945
|
+
st_or, _ = call(O, "POST", "/api/economics/rate", {"well_id": wc["id"], "usd_per_day": 25000, "what": "rig spread", "source": "contract"})
|
|
8946
|
+
st_bad, _ = call(O, "POST", "/api/economics/rate", {"well_id": wc["id"], "usd_per_day": "x", "what": "rig", "source": "s"})
|
|
8947
|
+
st_imp, imp2 = call(O, "POST", "/api/economics/import", {"name": "Midland WTI", "unit": "USD/bbl", "source": "test", "content_b64": _b64.b64encode(b"Date,Price\n2026-07-01,70\n2026-07-02,71\n").decode()})
|
|
8948
|
+
st_rm, _ = call(O, "POST", "/api/economics/remove", {"well_id": wc["id"], "kind": "rate", "index": 0})
|
|
8949
|
+
st_rm2, _ = call(O, "POST", "/api/economics/remove", {"well_id": wc["id"], "kind": "rate", "index": 0})
|
|
8950
|
+
st_ac, _ = call(O, "POST", "/api/economics/nope", {})
|
|
8951
|
+
finally:
|
|
8952
|
+
svc.stop()
|
|
8953
|
+
cli = _sp.run([sys.executable, "-m", "gea", "economics", "--workspace", wsp], capture_output=True, text=True, cwd=str(pkg.parent))
|
|
8954
|
+
page_src = (pkg / "web" / "app.html").read_text(encoding="utf-8")
|
|
8955
|
+
help_md = (pkg / "help" / "economics.md").read_text(encoding="utf-8")
|
|
8956
|
+
c3 = {
|
|
8957
|
+
"api": st_g == 200 and len(eg["wells"]) == 2 and st_vr == 403 and st_or == 200 and st_bad == 400 and st_imp == 200 and imp2["id"] == "midland_wti"
|
|
8958
|
+
and st_rm == 200 and st_rm2 == 404 and st_ac == 404,
|
|
8959
|
+
"cli": cli.returncode == 0 and "USD/ft" in cli.stdout and "incomplete" not in cli.stdout.split("Well B")[1].split("Well C")[0] and "U.S. Energy Information Administration" in cli.stdout,
|
|
8960
|
+
"page": all(x in page_src for x in ("VIEWS.economics", "['#/economics', 'Economics']", "ecChart", "ec-hit", "ArrowLeft", "/api/economics/rate",
|
|
8961
|
+
"/api/economics/import", "/api/economics/refresh", "incomplete", "a gap of more than six days is not bridged")),
|
|
8962
|
+
"help": any(t["topic"] == "economics" for t in H.topics()) and not H.check("economics") and H.VIEW_TOPIC.get("economics") == "economics"
|
|
8963
|
+
and "fewer than ten wells" in help_md.split("will not call a measurement")[1],
|
|
8964
|
+
}
|
|
8965
|
+
failed3 = [k for k, v in c3.items() if not v]
|
|
8966
|
+
ok(not failed3,
|
|
8967
|
+
"CF3 the API (a viewer reads, only an operator declares; a bad number, an unknown action and a second removal of the same rate named), "
|
|
8968
|
+
"the CLI and the Economics tab: one chart per series on its own axis with the wells' drilling windows shaded and a crosshair by pointer or "
|
|
8969
|
+
"arrows, the wells' cost table, the cost-basis forms, refresh and import; the help page"
|
|
8970
|
+
+ (f" [failed: {', '.join(failed3)}]" if failed3 else ""))
|
|
8971
|
+
|
|
8972
|
+
|
|
8616
8973
|
def main() -> int:
|
|
8617
8974
|
print("GEA-Program - ACCEPTANCE SUITE (the product gate)")
|
|
8618
8975
|
# a handle a job subprocess still holds at the end must not turn a finished gate into a traceback on Windows:
|
|
@@ -8698,6 +9055,8 @@ def main() -> int:
|
|
|
8698
9055
|
section_cb_charts(tmp)
|
|
8699
9056
|
section_cc_site_map(tmp)
|
|
8700
9057
|
section_cd_control_room(tmp)
|
|
9058
|
+
section_ce_well3d(tmp)
|
|
9059
|
+
section_cf_economics(tmp)
|
|
8701
9060
|
if _FAILS:
|
|
8702
9061
|
print(f"[ACCEPTANCE] {len(_FAILS)} FAILURES ({_PASS} passed):")
|
|
8703
9062
|
for f in _FAILS:
|