VAFT API
This page is the map of the vaft package: what each subpackage is for, the entry points you are
expected to call, and where the detailed guide for each area lives. The signatures shown here are the
real ones — copy them.
import vaft
vaft.__version__ # '0.5.0' on the inspected develop baseline
Importing vaft is cheap. The top-level package exposes its subpackages lazily (__getattr__),
so import vaft does not drag in omas, imas or matplotlib until you actually touch
vaft.omas, vaft.imas or vaft.plot. Two compatibility shims are applied at import time —
vaft.apply_runtime_compat_patches() and vaft.apply_omfit_compat_patches() — which restore
NumPy/SciPy APIs that recent releases removed (trapz, cumtrapz, interp2d) so that OMFIT-derived
code keeps working. You never need to call them yourself.
Package map
flowchart TD
RAW["VEST raw DAQ / SQL"] --> MM["vaft.machine_mapping<br/>raw signals to IMAS IDSs"]
MM --> DB[("vaft.database<br/>load / save")]
DB --> ODS["ODS / IDS<br/>(IMAS data model)"]
ODS --> OMASL["vaft.omas<br/>ODS-aware find_ / compute_ / update_"]
OMASL --> PROC["vaft.process<br/>signal and EM math"]
OMASL --> FORM["vaft.formula<br/>closed-form physics"]
ODS --> PLOT["vaft.plot<br/>figures"]
ODS --> CODE["vaft.code<br/>EFIT / CHEASE / GPEC / TES"]
CODE --> ODS
ODS --> DATA["vaft.data<br/>GEQDSK I/O"]
ODS --> IMASP["vaft.imas<br/>OMAS to IMAS Access Layer"]
| Subpackage | Purpose | Deep dive |
|---|---|---|
vaft.database |
Load/save VEST shots (ODS, native IDS) and reach the raw SQL DAQ archive | Data structures |
vaft.omas |
The ODS-aware API: find_*, compute_*, update_*, sample data |
this page |
vaft.process |
Array-in / array-out signal processing, EM response, magnetics chains | Signal processing and EM modeling |
vaft.formula |
Pure physics functions: equilibrium, stability, Green’s functions, constants | Formula reference |
vaft.machine_mapping |
Raw VEST DAQ to IMAS IDS mapping, plus uncertainty defaults | this page |
vaft.plot |
Matplotlib figures straight from an ODS/ODC | this page |
vaft.code |
Adapters for external codes (EFIT, CHEASE, GPEC, TES) | this page |
vaft.data |
GEQDSK read/write and packaged sample files | this page |
vaft.imas |
OMAS to IMAS Access Layer bridge | Data structures |
Everything in an ODS is in IMAS SI units: seconds, amperes, tesla, weber, m$^{-3}$, and eV or J
where the Data Dictionary says so. The plotting layer is the only place that rescales (for example A
to kA) and it does so through an explicit yunit argument.
vaft.database
The historical, and still the shortest, way to get a shot:
import vaft
ods = vaft.database.load(39915, source="public") # eager omas.ODS
with vaft.database.open(39915, source="public") as remote:
times = remote["equilibrium.time"] # lazy, read-only
load() returns an ODS unless representation="imas" is requested. paths scopes OMAS requests to
IDS roots or leaves; native requests accept top-level IDS names:
eq = vaft.database.load(
39915, source="public", representation="imas", paths="equilibrium"
)
Full signatures:
vaft.database.load(shot, source="public", *, representation="omas", paths=None,
occurrence=None, imas_version=None, cache="auto", transport="auto")
vaft.database.open(shot, *, source="public", representation="omas", paths=None,
occurrence=None, imas_version=None)
vaft.database.save(data, shot, *, target="public", representation=None,
occurrence=None, imas_version=None, derived_cache="auto")
For local files use vaft.omas.load/save or vaft.imas.load/save; use
vaft.database.filedb.FileDB to resolve canonical archive paths. Remote save access is restricted and
is never required by the documentation examples.
Raw DAQ access
vaft.database.raw talks to the VEST SQL archive directly:
from vaft.database import raw
time, data = raw.vest_load(39915, field=109) # (time_array, data_array), or None on failure
time, data = raw.load_raw(39915, fields=109) # the loader vest_load delegates to
label = raw.name(109) # field code to human-readable name
shots = raw.vest_shots("2024-08-08") # shots taken on a date
date = raw.vest_date(39915)
latest = raw.last_shot()
Other members worth knowing: raw.load_raw(shot, fields=None, daq_type=None, sample_opt=False)
(sample_opt points it at an archived dump instead of the live DB),
raw.vest_load_by_name(shot, name), raw.get_all_field_codes_for_shot(shot),
raw.dump_all_raw_signals_for_shot(shot, output_path=...) (offline cache) and its counterpart
raw.compare_db_and_dumped_raw_signals_for_shot(...). raw.sql_loading_available() and
raw.raw_offline_only() report whether the SQL driver and credentials are usable at all — this is
how the test suite decides to skip online tests. Credentials are managed by raw.setup_raw_db() and
raw.configuration() (backed by SecureConfigManager).
Connection and inventory helpers for the HSDS side live in vaft.database.utils:
from vaft.database import utils
utils.is_connect() # is HSDS reachable?
utils.exist_shot(username=None, shot=None, data_filter=None, sort=-1)
vaft.omas
This is the layer you actually call to do physics on a shot. Everything here takes an ODS (or an
ODC) and reads/writes IMAS paths. It is a flat namespace: vaft.omas.<name> resolves into the
general, process_wrapper, formula_wrapper, update and sample modules.
Sample data (works offline)
ods = vaft.omas.sample_ods() # packaged shot 39915
odc = vaft.omas.sample_odc() # 39915, 41524, 41672
geq = vaft.omas.sample_gfile() # packaged g-file as a GEQDSK object
Shot introspection — general
vaft.omas.find_shotnumber(ods)
vaft.omas.find_shotclass(ods, plot_opt=0)
vaft.omas.find_chamber_boundary(ods)
vaft.omas.find_breakdown_onset(ods)
vaft.omas.find_vloop_onset(ods)
vaft.omas.find_ip_onset(ods)
vaft.omas.find_pf_active_onset(ods)
vaft.omas.find_pulse_duration(ods)
vaft.omas.find_max_ip(ods)
vaft.omas.find_bt(ods)
vaft.omas.find_major_radius(ods)
vaft.omas.classify_shot(ods, pressure_threshold=0.01, halpha_threshold=0.01)
vaft.omas.print_info(ods, key_name=None)
vaft.omas.find_matching_time_indices(ods, time_slice=None, atol=1e-6)
Time-base bookkeeping (DAQ time versus breakdown-referenced time) and container plumbing:
vaft.omas.shift_time(one_ods, time_shift)
vaft.omas.change_time_convention(odc_or_ods, convention="vloop")
vaft.omas.odc_or_ods_check(odc_or_ods) # returns an ODC either way
vaft.omas.combine_ods(ods_list)
Derived quantities — process_wrapper
These pull geometry and signals out of the ODS, call into vaft.process, and hand back arrays.
# Electromagnetic response and eddy currents
vaft.omas.compute_grid_ods(ods, xvar, zvar)
vaft.omas.compute_point_response_ods(ods, rz, plasma=None)
vaft.omas.compute_grid_response_ods(ods, plasma=None)
vaft.omas.compute_impedance_matrices_ods(ods, plasma)
vaft.omas.compute_eddy_currents(ods, plasma, ip, dt_sub=...)
vaft.omas.compute_point_vacuum_fields_ods(ods, rz=[(0.4, 0.0)], plot_opt=False, mode="vacuum")
vaft.omas.compute_null_ods(ods, time)
# Equilibrium, profiles, energy
vaft.omas.compute_core_profile_psi(ods, option="n_e", time_slice=None)
vaft.omas.compute_core_profile_2d(ods, option="n_e", time_slice=None)
vaft.omas.compute_magnetic_energy(ods, time_slice=None)
vaft.omas.compute_virial_equilibrium_quantities_ods(ods, time_slice=None)
vaft.omas.compute_volume_averaged_pressure(ods, time_slice=None, option="equilibrium")
vaft.omas.compute_reconstructed_diamagnetic_flux(ods, time_index=0)
vaft.omas.compute_diamagnetic_flux_measured_vs_computed(ods, time_slice=None)
vaft.omas.compute_diamagnetism(ods, time_index=0)
vaft.omas.compute_ohmic_heating_power_from_core_profiles(
ods, time_slice=None, Z_eff=2.0, ln_Lambda=17.0)
Physics scalings — formula_wrapper
vaft.omas.compute_tau_E_exp(ods, time_slice, Z_eff=2.0)
vaft.omas.compute_tau_E_scaling(ods, time_slice, scaling="IBP98y2", Z_eff=2.0, M=1.0,
eng_params=None)
vaft.omas.compute_tau_E_engineering_parameters(ods, time_slice, Z_eff=2.0, M=1.0)
vaft.omas.compute_confiment_time_paramters(ods, time_slice, Z_eff=2.0, M=1.0)
vaft.omas.compute_power_balance(ods, include_line_radiation=True, line_radiation_species=None,
impurity_fractions=None, Z_eff=2.0)
vaft.omas.compute_bremsstrahlung_power(ods, time_slice=None, Z_eff=2.0)
vaft.omas.compute_voltage_consumption(ods, time_slice=None)
vaft.omas.compute_magnetic_shear(ods, time_slice)
In-place enrichment — update
update_* functions mutate the ODS: they fill IMAS fields that the reconstruction itself did not
provide. Run them once after loading, then plot.
vaft.omas.update_equilibrium_profiles_1d_normalized_psi(ods, time_slice=None)
vaft.omas.update_equilibrium_profiles_1d_radial_coordinates(ods, time_slice=None, plot_opt=0)
vaft.omas.update_equilibrium_boundary(ods, time_slice=None)
vaft.omas.update_equilibrium_coordinates(ods, time_slice=None, plot_opt=0)
vaft.omas.update_equilibrium_global_quantities_q_min(ods, time_slice=None)
vaft.omas.update_equilibrium_global_quantities_volume(ods, time_slice=None)
vaft.omas.update_equilibrium_profiles_2d_j_tor(ods, time_slice=None)
vaft.omas.update_equilibrium_profiles_2d_sfl_coordinates(
ods, time_slice=None, profiles_2d_idx=1, convention="sfl", n_theta=129, plot_opt=0)
vaft.omas.update_equilibrium_stored_energy(ods, time_slice=None)
vaft.omas.update_equilibrium_constraints_diamagnetic_flux(ods, time_slice=None)
vaft.omas.update_core_profiles_global_quantities_volume_average(ods, time_slice=None)
vaft.process and vaft.formula
vaft.process is the numerical layer (NumPy in, NumPy out) and vaft.formula is the closed-form
physics layer. Neither one touches an ODS. Modules at a glance:
| Module | Contents |
|---|---|
vaft.process.signal_processing |
smooth, define_baseline, subtract_baseline, signal_on_offset, is_signal_active, process_signal |
vaft.process.numerical |
time_derivative(time, data) on a non-uniform grid |
vaft.process.electromagnetics |
compute_br_bz_phi, calc_grid, compute_response_matrix, compute_impedance_matrices, solve_eddy_currents, compute_vacuum_fields_1d |
vaft.process.magnetics |
rogowski_coil_ip, flux_loop_flux, b_field_pol_probe_field, mirnov_spectrogram, toroidal_mode_analysis, toroidal_phase_fit_at_time |
vaft.process.equilibrium |
psi_to_rz, psi_to_rho, volume_average, shafranov_integrals, efit_virial_volume_integrals, calculate_diamagnetism |
vaft.process.profile |
Thomson and charge-exchange mapping/fitting, core_profiles, core_profiles_from_eq |
vaft.process.statistical_analysis |
generate_core_profiles_history_dataframe, perform_ols_regression, compute_metrics |
vaft.formula.equilibrium |
flux, q and shear; geometry from an $(R,Z)$ boundary; virial (Shafranov) relations; power balance; confinement scalings |
vaft.formula.stability |
beta_N_from_beta_a_B0_Ip, greenwald_density, greenwald_fraction, ballooning/kink/sawtooth criteria |
vaft.formula.green |
greens_function_2d, green_br_bz, complete elliptic integrals |
vaft.formula.utils |
gradient, trapz_integral, fit_profile, make_fit_function |
vaft.formula.atomic |
interpolate_adf11, fractional_abundances, line_cooling_coefficient on OPEN-ADAS ADF11 tables |
vaft.formula.statistics |
residual, goodness-of-fit and convergence statistics (rms, chi_squared, runs_test_z, log10_decay_rate, …) |
vaft.formula.constants |
Physical constants used by the formula layer |
Both are documented in full on the Signal processing and EM modeling and Formula reference pages; the latter is generated per function from the standardized docstrings.
vaft.machine_mapping
This is how a raw VEST shot becomes IMAS. Each time-dependent diagnostic has a canonical function
with a uniform signature — (ods, shot, tstart, tend, dt) — that fills the corresponding IDS in
place:
import vaft
from omas import ODS
ods = ODS()
shot, tstart, tend, dt = 39915, 0.24, 0.36, 1e-4
vaft.machine_mapping.dataset_description(ods, shot)
vaft.machine_mapping.pf_active(ods, shot, tstart, tend, dt)
vaft.machine_mapping.tf(ods, shot, tstart, tend, dt)
vaft.machine_mapping.magnetics(ods, shot, tstart, tend, dt)
vaft.machine_mapping.barometry(ods, shot, tstart, tend, dt)
vaft.machine_mapping.spectrometer_uv(ods, shot, tstart, tend, dt)
Geometry, coupling and model IDSs take a source instead of a time window:
vaft.machine_mapping.pf_passive(ods) # source=None uses the packaged geometry
vaft.machine_mapping.em_coupling(ods) # packaged mutual-inductance matrices
vaft.machine_mapping.mhd_linear(ods, source) # source = a linear-MHD output file
vaft.machine_mapping.summary(ods, source, options)
Two names in this namespace are reserved but not wired up yet:
vaft.machine_mapping.equilibrium and vaft.machine_mapping.pf_plasma raise
NotImplementedError. Equilibria enter the ODS through vaft.code (EFIT/CHEASE) instead.
Kinetic and imaging diagnostics take a shot number plus an optional data root:
vaft.machine_mapping.thomson_scattering(ods, shotnumber, data_root=None, mat_file=None)
vaft.machine_mapping.charge_exchange(ods, shotnumber, options="ces", data_root=None)
vaft.machine_mapping.soft_x_rays(ods, shot, daq_label, data_root=None, digitizer_file=None)
The *_from_raw_database variants — magnetics_from_raw_database, pf_active_from_raw_database,
tf_from_raw_database, barometry_from_raw_database, filterscope_from_raw_database,
dataset_description_from_raw_database, calculate_em_coupling_from_raw_database — are the
pipeline-facing entry points: they take an options dict and reach into the SQL DAQ themselves.
Per-channel entry points at that level (flux_loop_from_raw_database,
b_field_pol_probe_from_raw_database, rogowski_coil_and_ip_from_raw_database) exist as names but
raise NotImplementedError — use the IDS-level magnetics(...) mapping instead.
The vfit_* names are the legacy VEST-fit API, kept as aliases and split into
vfit_<diag>_static (geometry) and vfit_<diag>_dynamic (signals) — for example
vfit_magnetics_static / vfit_magnetics_dynamic, vfit_pf_active_static /
vfit_pf_active_dynamic, vfit_tf_static / vfit_tf_dynamic.
Uncertainties for equilibrium reconstruction are not hard-coded in the mapping; they come from a central table:
vaft.machine_mapping.DEFAULT_CONSTRAINT_UNCERTAINTIES # dict
vaft.machine_mapping.DEFAULT_CONSTRAINT_UNCERTAINTY_VECTOR # ordered vector
vaft.machine_mapping.apply_default_constraint_uncertainties(ods, uncertainty=None,
fl_correct_coeff=None)
vaft.machine_mapping.apply_magnetics_uncertainties(ods, ip_relative_error=None)
vaft.machine_mapping.apply_pf_active_current_uncertainties(ods, relative_error=None)
vaft.machine_mapping.apply_tf_uncertainties(ods, relative_error=None)
vaft.machine_mapping.normalize_constraint_uncertainties(uncertainty)
Geometry assets shipped with the package are resolved with
vaft.machine_mapping.resolve_geometry_asset(filename, geometry_root=None), and DAQ field metadata
with vaft.machine_mapping.raw_database_info(file, shot, key) and
vaft.machine_mapping.get_metadata(source).
vaft.plot
Every plotting function takes an ODS or an ODC (a multi-shot container) — pass an ODC and the shots
are overlaid. The common keywords are label ('shot', 'pulse', 'run', 'key', or an explicit
list), xunit ('s' or 'ms'), yunit, and xlim ('plasma', 'coil', 'none', or
[t0, t1]).
import vaft
ods = vaft.omas.sample_ods()
vaft.plot.time_magnetics_ip(ods, yunit="kA")
vaft.plot.time_pf_active_current(ods, indices="used", yunit="kA")
vaft.plot.time_magnetics_flux_loop_flux(ods, indices="all")
vaft.plot.time_magnetics_b_field_pol_probe_field(ods)
vaft.plot.time_tf_b_field_tor(ods)
vaft.plot.time_barometry_pressure(ods)
vaft.plot.time_spectrometer_uv_intensity(ods)
| Group | Functions |
|---|---|
| Time traces | time_magnetics_ip, time_magnetics_diamagnetic_flux, time_magnetics_flux_loop_flux, time_magnetics_flux_loop_voltage, time_magnetics_b_field_pol_probe_field, time_pf_active_current, time_pf_active_current_turns, time_tf_coil_current, time_tf_b_field_tor, time_tf_b_field_tor_vacuum_r, time_barometry_pressure, time_spectrometer_uv_intensity, time_impurity_effect, time_electromagnetics_current |
| Equilibrium scalars vs. time | time_equilibrium_plasma_current, time_equilibrium_li, time_equilibrium_beta_pol, time_equilibrium_beta_tor, time_equilibrium_beta_n, time_equilibrium_w_mhd, time_equilibrium_w_mag, time_equilibrium_w_tot, time_equilibrium_q0, time_equilibrium_q95, time_equilibrium_qa, time_equilibrium_major_radius |
| Energy and power | time_energy, time_beta, time_power_balance, time_voltage_consumption, time_virial_equilibrium_quantities |
| Profiles (1-D) | equilibrium_1d_radial, plot_onedim_profile, plot_onedim_profile_interactive |
| Geometry and 2-D | twodim_geometry_all, overlay_all, pf_passive_overlay, vacuum_psi_contour, overlay_all_with_vacuum_psi_contour, equilibrium_2d_profiles |
| Kinetic diagnostics | thomson_scattering_radial, thomson_scattering_time, thomson_scattering_radial_profiles, charge_exchange_radial, charge_exchange_time, charge_exchange_rho_profiles, plot_electron_psi_profile, plot_electron_2d_profile |
| Fluctuations | mirnov_signal, mirnov_spectrogram, toroidal_mode_spectrum, toroidal_phase_mode_fit |
| Soft X-rays | plot_soft_x_ray_los, plot_soft_x_ray_signal, plot_soft_x_ray_spectrogram, plot_soft_x_ray_pattern, plot_soft_x_ray_overview |
| Overviews | analysis_diagnostics, analysis_electromagnetics, time_equilibrium_analysis |
| Multi-shot history | plot_scaling_fit, plot_correlation_heatmap, plot_regression_summary, plot_tauE_exp_vs_scaling_loglog, plot_H_factor_distribution, plot_H_factor_vs_greenwald_fraction, confinement_time_exp_vs_scaling |
The Magnetics page shows several of these traces rendered from real shots.

vaft.code
Adapters for the external codes that VAFT drives. They all share one shape: a *Config (paths,
executable, run options), an *Inputs object produced by prepare_*, a run_* that executes the
code, and a collect_* / *Result pair that reads the outputs back.
When a configuration does not supply an executable explicitly, adapters follow the documented
installation roots: EFITHOME, CHEASEHOME, GPECHOME, and TESHOME. Missing binaries are a
readiness state, not a failure of deterministic input preparation.
from vaft.code import EFITConfig, prepare_efit_inputs, run_efit, collect_efit_outputs
cfg = EFITConfig(...)
inputs = prepare_efit_inputs(ods, cfg) # k-file and constraint set
result = run_efit(inputs, cfg) # EFITResult
outputs = collect_efit_outputs(workdir, cfg)
| Code | Entry points |
|---|---|
| EFIT (equilibrium reconstruction) | EFITConfig, EFITInputs, EFITResult, prepare_efit_inputs, run_efit, collect_efit_outputs, generate_kfile, generate_constraints_ods, gfile_to_omas |
| CHEASE (fixed-boundary refinement) | CHEASEConfig, CHEASEInputs, CHEASEResult, find_chease_executable, prepare_chease_inputs, run_chease, refine_equilibrium |
| GPEC (perturbed equilibrium, 3-D response) | GPECSuiteConfig, GPECCaseInputs, GPECModuleRun, GPECSuiteResult, prepare_gpec_suite_case, run_gpec_suite_case, run_gpec, collect_gpec_suite_outputs, format_gfile_header_for_gpec |
| TES (forward equilibrium) | TESConfig, TESInputs, TESResult, prepare_tes_inputs, run_tes, collect_tes_outputs, scan_tes, parse_result_scalars, parse_result_coils |
| Base classes | CodeConfig, CodeInputs, CodeResult, CodeRunner |
refine_equilibrium(source, config=None) is the one-shot CHEASE convenience: g-file or ODS in,
refined equilibrium out. scan_tes(ods, base_config, values, param="ip0_kA") sweeps a single TES
parameter and collects every result. Snakemake rules should start with
vaft.code.init_snakemake_logger(snakemake) so that stdout and stderr land in the rule’s log file.
vaft.data
Portable file formats and the packaged sample files.
from vaft.data import read_geqdsk, write_geqdsk, to_omas, from_omas, sample_geqdsk, data_path
geq = read_geqdsk("g039915.00319") # GEQDSK
ods = to_omas(geq) # GEQDSK to ODS (equilibrium IDS)
geq = from_omas(ods, time_index=0) # ODS to GEQDSK
write_geqdsk(geq, "g039915.00319.copy")
geq = sample_geqdsk() # packaged default: efit/g039915.00319
root = data_path() # the packaged vaft/data directory
from_imas and to_imas perform the same round trip against a native IMAS entry instead of an ODS.
vaft.imas
The OMAS to IMAS Access Layer bridge: load_omas_imas, save_omas_imas, imas_open,
imas_open_uri, imas_get, imas_set, plus IMAS_DD_VERSION_CONVERSION (the Data Dictionary
version used for conversion, overridable through the environment variable of the same name).
vaft.database uses this under the hood; call it directly only when you need to talk to an Access
Layer entry that is not a VEST shot. See
Data structures for worked examples.
Notebooks and workflows
Each subpackage has at least one notebook that exercises it end to end:
| Area | Notebook |
|---|---|
vaft.database |
database_initialization_and_load.ipynb |
vaft.database.raw |
vest_raw_signal_sql_database.ipynb |
vaft.data, vaft.imas |
imas_omas_data_conversion.ipynb, read_and_convert_data_structure.ipynb |
vaft.machine_mapping, vaft.process.magnetics |
magnetic_diagnostics_processing.ipynb |
vaft.process.electromagnetics |
electromagnetic_response_modeling_with_efund.ipynb, eddy_current_calculation_and_startup_analysis.ipynb |
vaft.code |
magnetic_equilibrium_reconstruction_with_efit.ipynb, equilibrium_refinement_using_chease.ipynb, perturbed_equilibrium_and_3d_response_with_gpec.ipynb, forward_equilibrium_using_TES.ipynb |
vaft.formula, vaft.process.statistical_analysis |
confinement_time_scaling.ipynb, tokamak_power_balance.ipynb |
vaft.plot |
plotting_sample_using_vaft_plot_module.ipynb |
The Snakemake pipelines that stitch these together live under
workflow/:
automatic_pipeline_1_routine_data_processing (raw DAQ to diagnostics ODS to constraints to EFIT and
CHEASE), automatic_pipeline_2_corrective_data_update (re-run equilibrium and profile updates on
existing shots), and automatic_pipeline_3_data_summary (multi-shot history tables and figures).
Source
Browse the package on GitHub: vaft/ —
database/,
omas/,
process/,
formula/,
machine_mapping/,
plot/,
code/,
data/,
imas/.
See also
- Quick start guide — install, connect, load a shot.
- Data structures — ODS, IDS and the IMAS data model.
- Signal processing and EM modeling —
vaft.processin depth. - Formula reference — every
vaft.formulafunction with definition, units, conventions and references. - Magnetics — plotting the magnetics IDS.
- Examples — the notebook index.