from pytao import Tao
tao = Tao("-init $ACC_ROOT_DIR/bmad-doc/tao_examples/cesr/tao.init -noplot")
Send a command¶
Anything that you would normally type at a Tao> prompt can be sent as a string. The return is a list of output strings. To send a command:
tao.cmd("show lat 1:10")
['# Values shown are for the Downstream End of each Element (Girder at ref point):', '# Index name key s l beta phi_a eta orbit beta phi_b eta orbit Track', '# a [2pi] x x [mm] b [2pi] y y [mm] State', ' 1 IP_L0 Marker 0.000 0.000 0.95 0.000 -0.00 -0.017 0.02 0.000 0.00 0.001 Alive', ' 2 CLEO_SOL#3 Solenoid 0.622 0.622 1.34 0.093 -0.02 1.470 21.81 0.244 0.00 0.041 Alive', ' 3 DET_00W Marker 0.622 0.000 1.34 0.093 -0.02 1.470 21.81 0.244 0.00 0.041 Alive', ' 4 CLEO_SOL#4 Solenoid 0.638 0.016 1.36 0.094 -0.02 1.507 22.92 0.244 0.00 0.043 Alive', ' 5 Q00W\\CLEO_SOL Sol_Quad 1.755 1.117 7.73 0.160 -0.09 5.505 88.01 0.247 -0.01 0.486 Alive', ' 6 Q00W#1 Quadrupole 2.163 0.408 15.96 0.166 -0.13 8.151 76.38 0.248 -0.01 0.717 Alive', ' 7 D003 Drift 2.493 0.331 27.02 0.169 -0.17 10.705 60.25 0.249 -0.02 0.931 Alive', ' 8 DET_01W Marker 2.493 0.000 27.02 0.169 -0.17 10.705 60.25 0.249 -0.02 0.931 Alive', ' 9 D004 Drift 2.924 0.431 45.79 0.171 -0.22 14.030 42.12 0.250 -0.02 1.209 Alive', ' 10 Q01W Quadrupole 3.874 0.950 66.94 0.173 -0.26 16.851 28.95 0.255 -0.02 1.213 Alive', '# Index name key s l beta phi_a eta orbit beta phi_b eta orbit Track', '# a [2pi] x x [mm] b [2pi] y y [mm] State', '# Values shown are for the Downstream End of each Element (Girder at ref point):']
Send a list of commands. This returns the corresponding list of outputs:
tao.cmds(["set lattice model=design", "set ele Q00W x_offset = 1e-6"])
[[], []]
Jupyter magic %%tao¶
This is an alternative way to send commands to Tao directly in the jupyter notebook, using the %%tao magic. Multiple lines can be executed.
%%tao
sho lat 1:10
sho ele 4
----------------- Tao> sho lat 1:10 # Values shown are for the Downstream End of each Element (Girder at ref point): # Index name key s l beta phi_a eta orbit beta phi_b eta orbit Track # a [2pi] x x [mm] b [2pi] y y [mm] State 1 IP_L0 Marker 0.000 0.000 0.95 0.000 -0.00 -0.018 0.02 0.000 0.00 0.001 Alive 2 CLEO_SOL#3 Solenoid 0.622 0.622 1.34 0.093 -0.02 1.469 21.81 0.244 0.00 0.042 Alive 3 DET_00W Marker 0.622 0.000 1.34 0.093 -0.02 1.469 21.81 0.244 0.00 0.042 Alive 4 CLEO_SOL#4 Solenoid 0.638 0.016 1.36 0.094 -0.02 1.507 22.92 0.244 0.00 0.044 Alive 5 Q00W\CLEO_SOL Sol_Quad 1.755 1.117 7.73 0.160 -0.09 5.505 88.01 0.247 -0.01 0.487 Alive 6 Q00W#1 Quadrupole 2.163 0.408 15.96 0.166 -0.13 8.151 76.38 0.248 -0.01 0.719 Alive 7 D003 Drift 2.493 0.331 27.02 0.169 -0.17 10.705 60.25 0.249 -0.02 0.932 Alive 8 DET_01W Marker 2.493 0.000 27.02 0.169 -0.17 10.705 60.25 0.249 -0.02 0.932 Alive 9 D004 Drift 2.924 0.431 45.79 0.171 -0.22 14.030 42.12 0.250 -0.02 1.210 Alive 10 Q01W Quadrupole 3.874 0.950 66.94 0.173 -0.26 16.851 28.95 0.255 -0.02 1.213 Alive # Index name key s l beta phi_a eta orbit beta phi_b eta orbit Track # a [2pi] x x [mm] b [2pi] y y [mm] State # Values shown are for the Downstream End of each Element (Girder at ref point): -------------- Tao> sho ele 4 Element # 4 Element Name: CLEO_SOL#4 Key: Solenoid S_start, S: 0.622301, 0.637956 Ref_time_start, Ref_time: 2.075773E-09, 2.127992E-09 Attribute values [Only non-zero values shown]: 1 L = 1.5655000E-02 m 31 L_SOFT_EDGE = 0.0000000E+00 m 5 KS = -8.5023386E-02 1/m 49 BS_FIELD = 1.5000000E+00 T 10 FRINGE_TYPE = None (1) 11 FRINGE_AT = No_End (4) 13 SPIN_FRINGE_ON = T (1) 17 STATIC_LINEAR_MAP = F (0) 47 PTC_CANONICAL_COORDS = T (1) 53 P0C = 5.2890000E+09 eV BETA = 1.0000000E+00 54 E_TOT = 5.2890000E+09 eV GAMMA = 1.0350315E+04 64 REF_TIME_START = 2.0757727E-09 sec 50 DELTA_REF_TIME = 5.2219459E-11 sec 67 DS_STEP = 2.0000000E-01 m 66 NUM_STEPS = 1 TRACKING_METHOD = Bmad_Standard APERTURE_AT = No_Aperture MAT6_CALC_METHOD = Auto APERTURE_TYPE = Rectangular SPIN_TRACKING_METHOD = Tracking OFFSET_MOVES_APERTURE = F PTC_INTEGRATION_TYPE = Matrix_Kick SYMPLECTIFY = F CSR_METHOD = Off FIELD_MASTER = F SPACE_CHARGE_METHOD = Off LONGITUDINAL ORIENTATION = 1 FIELD_CALC = Refer_to_Lords. REF_SPECIES = Electron Slave_status: Super_Slave Associated Super_Lord(s): Index Name Type 872 CLEO_SOL Solenoid Lord_status: Not_a_Lord Twiss at end of element: A B Cbar C_mat Beta (m) 1.36491295 22.91993494 | -0.11412808 0.00652694 -0.08500116 0.03650637 Alpha -0.65684269 -35.88090507 | -0.16215590 0.00350748 -0.09239803 0.03194118 Gamma (1/m) 1.04874257 56.21479084 | Gamma_c = 0.99967091 Mode_Flip = F Phi (rad) 0.59356744 1.53299607 X Y Z Eta (m) -0.02444701 0.00048948 -0.02453413 0.00007931 0.00000000 Etap -0.03262121 -0.00146703 -0.03270256 -0.00198053 1.00000000 dEta/ds -0.03501130 -0.00147091 -0.03509156 -0.00204828 1.00000000 Sigma 0.00052596 0.00002030 0.00000000 0.00000000 Tracking: Electron, State: Alive Position[mm] Momentum[1E-3] Spin | X: 1.50654353 2.38873277 | t_particle [sec]: 2.12933095E-09 E_tot: 5.28896E+09 Y: 0.04371849 0.06773493 | t_part-t_ref [sec]: 1.33877913E-12 PC: 5.28896E+09 Z: -0.40135588 -0.00717214 | (t_ref-t_part)*Vel [m]: -4.01355883E-04 Beta: 0.999999995
Interface commands¶
Output above from the show
command is designed to be human-readable. In general you should not try to parse these strings for data. For data, Tao has a special set of commands to send back data suitable for parsing in Python (or other software).
Below are the raw commands.
%%tao
help python
---------------- Tao> help python "Python" is the old name for the "pipe" command. For backwards compatibility, the old name is still accepted.
This data is returned as specially formatted lists
tao.cmd("python orbit_at_s end")
['x;REAL;F; -1.77254138865939E-05', 'px;REAL;F; 2.39054981358054E-03', 'y;REAL;F; 9.76400538042761E-07', 'py;REAL;F; 2.93567715470513E-06', 'z;REAL;F; -3.99533038463946E-04', 'pz;REAL;F; -7.17213865221257E-06', 'spin;REAL_ARR;F; 0.00000000000000E+00; 0.00000000000000E+00; 0.00000000000000E+00', 'field;REAL_ARR;F; 0.00000000000000E+00; 0.00000000000000E+00', 'phase;REAL_ARR;F; 0.00000000000000E+00; 0.00000000000000E+00', 's;REAL;F; 7.68426421416168E+02', 't;REAL;F; 2.56319600137482E-06', 'charge;REAL;F; 0.00000000000000E+00', 'dt_ref;REAL;F; 0.00000000000000E+00', 'p0c;REAL;F; 5.28899997531481E+09', 'beta;REAL;F; 9.99999995332664E-01', 'ix_ele;INT;F;868', 'state;STR;F;Alive', 'direction;INT;F;1', 'species;SPECIES;F;Electron', 'location;STR;F;Downstream_End']
Some commands have 'array_out' options. For example, this seems to return nothing:
tao.cmd("python lat_list -array_out 1@0>>Q*|model orbit.floor.x")
[]
But calling .cmd_real
on the same command will get the data from an internal pointer:
tao.cmd_real("python lat_list -array_out 1@0>>Q*|model orbit.floor.x")
array([ 0.00000000e+00, 5.50519658e-03, 8.15062520e-03, 1.68506858e-02, 1.30498325e-02, -1.28475441e-01, -6.17368446e-01, -1.63573127e+00, -3.15361610e+00, -4.96008216e+00, -8.44394575e+00, -1.25353213e+01, -1.53643077e+01, -1.93160719e+01, -2.35334256e+01, -2.86596036e+01, -3.40012341e+01, -4.11157702e+01, -4.73379418e+01, -5.39309791e+01, -6.08761235e+01, -6.66395259e+01, -7.38887343e+01, -8.14004767e+01, -8.91380421e+01, -9.70602503e+01, -1.07067453e+02, -1.15219118e+02, -1.23415239e+02, -1.31835984e+02, -1.39984608e+02, -1.48267474e+02, -1.57243533e+02, -1.65204340e+02, -1.72728163e+02, -1.80184446e+02, -1.85357654e+02, -1.92035945e+02, -2.00803297e+02, -2.06870811e+02, -2.12665465e+02, -2.18176442e+02, -2.23048894e+02, -2.27424214e+02, -2.31268351e+02, -2.34552350e+02, -2.35722776e+02, -2.38140768e+02, -2.39786174e+02, -2.41460795e+02, -2.42244506e+02, -2.42601932e+02, -2.42642705e+02, -2.42650645e+02, -2.42653709e+02, -2.42654860e+02, -2.42659283e+02, -2.42665715e+02, -2.42598206e+02, -2.42184674e+02, -2.41366882e+02, -2.39606403e+02, -2.37970183e+02, -2.35481963e+02, -2.34317756e+02, -2.30993039e+02, -2.27125231e+02, -2.22727551e+02, -2.17816004e+02, -2.12280141e+02, -2.06437555e+02, -2.00332917e+02, -1.91531576e+02, -1.84820355e+02, -1.79653411e+02, -1.72176809e+02, -1.64644374e+02, -1.56674250e+02, -1.47685973e+02, -1.39396225e+02, -1.31238484e+02, -1.22815238e+02, -1.14621366e+02, -1.06478606e+02, -9.64783788e+01, -8.85696847e+01, -8.08418091e+01, -7.33402427e+01, -6.61118703e+01, -6.03400294e+01, -5.34245487e+01, -4.68515948e+01, -4.06754335e+01, -3.36162850e+01, -2.82979320e+01, -2.32077253e+01, -1.88775830e+01, -1.50782620e+01, -1.22578107e+01, -8.16225874e+00, -4.68317227e+00, -2.92445680e+00, -1.48689125e+00, -5.48022494e-01, -1.20827640e-01, -1.39453465e-02, -1.41528754e-02, 1.91718062e-06, -5.55949922e-03, -1.54020758e-03])
Tao method commands¶
For convenience, all of these commands are available as methods of the Tao class, and automatically parse the output.
For example, to get the orbit at an s
position:
tao.orbit_at_s(s_offset=1.2)
{'x': 0.00310868994973794, 'px': 0.00344601640995948, 'y': 0.000183211752399112, 'py': 0.000248952325118116, 'z': -0.00040368403866468, 'pz': -7.17213865126014e-06, 'spin': array([0., 0., 0.]), 'field': array([0., 0.]), 'phase': array([0., 0.]), 's': 1.2, 't': 4.00411570607714e-09, 'charge': 0.0, 'dt_ref': 0.0, 'p0c': 5288999975.31481, 'beta': 0.999999995332663, 'ix_ele': 5, 'state': 'Alive', 'direction': 1, 'species': 'Electron', 'location': 'Inside'}
Some commands return arrays:
tao.evaluate("data::cbar.11[1:10]|model")
array([ 2.81129919e-03, -1.06243941e-03, 1.37712949e-04, 3.08052132e-04, -3.66410982e-04, -3.42737812e-04, -9.80307123e-06, 1.28290108e-03, 2.66261121e-03, 2.68374458e-03])
lat_list¶
lat_list
can be used to efficiently extract array data. By default this returns an array of floats:
s = tao.lat_list("*", "ele.s", verbose=True)
s[0:5]
pipe lat_list -array_out -track_only @>>*|model ele.s
array([0. , 0. , 0.622301, 0.622301, 0.637956])
These particulars keys will return integers:
state = tao.lat_list("*", "orbit.state")
ix = tao.lat_list("*", "ele.ix_ele")
state.dtype, ix.dtype
(dtype('<i4'), dtype('<i4'))
And this one will return a list of strings:
names = tao.lat_list("*", "ele.name")
names[0:5]
['BEGINNING', 'IP_L0', 'CLEO_SOL#3', 'DET_00W', 'CLEO_SOL#4']
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import numpy as np
Simple plot of this data
plt.plot(tao.lat_list("*", "ele.s"), tao.lat_list("*", "orbit.vec.1"), marker=".");
Be careful with the flags. The defaults are:
plt.plot(tao.lat_list("*", "ele.s", flags="-array_out -track_only"));
-index_order
will return
plt.plot(tao.lat_list("*", "ele.s", flags="-array_out -index_order"));
All method commands¶
There are many method commands. Please explore the documentation to find the ones that will best suit your needs.
from pytao import interface_commands
all_cmds = [name for name in dir(Tao) if not name.startswith("_")]
for cmd in all_cmds:
print(cmd)
beam beam_init bmad_com bokeh branch1 building_wall_graph building_wall_list building_wall_point building_wall_section bunch1 bunch_comb bunch_data bunch_params cmd cmd_integer cmd_real cmds constraints da_aperture da_params data data_d1_array data_d2 data_d2_array data_d2_create data_d2_destroy data_d_array data_parameter data_set_design_value datum_create datum_has_ele derivative ele_ac_kicker ele_cartesian_map ele_chamber_wall ele_control_var ele_cylindrical_map ele_elec_multipoles ele_floor ele_gen_attribs ele_gen_grad_map ele_grid_field ele_head ele_lord_slave ele_mat6 ele_methods ele_multipoles ele_orbit ele_param ele_photon ele_spin_taylor ele_taylor ele_twiss ele_wake ele_wall3d em_field enum evaluate floor_orbit floor_plan get_output global_opti_de global_optimization help init init_output inum last_output lat_branch_list lat_calc_done lat_ele_list lat_list lat_param_units matplotlib matrix merit orbit_at_s place_buffer plot plot1 plot_curve plot_curve_manage plot_field plot_graph plot_graph_manage plot_histogram plot_lat_layout plot_line plot_list plot_manager plot_page plot_symbol plot_template_manage plot_transfer ptc_com register_cell_magic reset_output ring_general shape_list shape_manage shape_pattern_list shape_pattern_manage shape_pattern_point_manage shape_set show space_charge_com species_to_int species_to_str spin_invariant spin_polarization spin_resonance super_universe tao_global taylor_map twiss_at_s universe update_plot_shapes var var_create var_general var_v1_array var_v1_create var_v1_destroy var_v_array version wall3d_radius wave
There are many commands:
len(all_cmds)
127
Each has documentation and an example associated with it:
tao.data_d2?
Other Tao instances¶
Unfortunately there can only be one Tao instance per process, because the internal structures are held in memory and accessed via ctypes. So this will replace the current Tao session in memory.
This looks like a new object:
tao2 = Tao(
"-init $ACC_ROOT_DIR/bmad-doc/tao_examples/csr_beam_tracking/tao.init -noplot"
)
but internally connects to:
tao.lat_list("*", "ele.name")
['BEGINNING', 'MAR.CSR', 'FF.PIP00B', 'FF.BEN01', 'FF.PIP01', 'FF.BEN02', 'FF.PIP02A', 'MAR.END', 'END']
Bunch data¶
This example has bunch data. There are three methods:
tao.bunch_params
to get a dict of statistical datatao.bunch1
to get coordinate data arraystao.bunch_data
to get a dict of many coordinate arrays. These can be used to instantiate a ParticleGroup object from the openPMD-beamphysics package.
Statistical data:
stats = tao.bunch_params("end")
stats.keys()
dict_keys(['twiss_beta_x', 'twiss_alpha_x', 'twiss_gamma_x', 'twiss_phi_x', 'twiss_eta_x', 'twiss_etap_x', 'twiss_sigma_x', 'twiss_sigma_p_x', 'twiss_emit_x', 'twiss_norm_emit_x', 'twiss_beta_y', 'twiss_alpha_y', 'twiss_gamma_y', 'twiss_phi_y', 'twiss_eta_y', 'twiss_etap_y', 'twiss_sigma_y', 'twiss_sigma_p_y', 'twiss_emit_y', 'twiss_norm_emit_y', 'twiss_beta_z', 'twiss_alpha_z', 'twiss_gamma_z', 'twiss_phi_z', 'twiss_eta_z', 'twiss_etap_z', 'twiss_sigma_z', 'twiss_sigma_p_z', 'twiss_emit_z', 'twiss_norm_emit_z', 'twiss_beta_a', 'twiss_alpha_a', 'twiss_gamma_a', 'twiss_phi_a', 'twiss_eta_a', 'twiss_etap_a', 'twiss_sigma_a', 'twiss_sigma_p_a', 'twiss_emit_a', 'twiss_norm_emit_a', 'twiss_beta_b', 'twiss_alpha_b', 'twiss_gamma_b', 'twiss_phi_b', 'twiss_eta_b', 'twiss_etap_b', 'twiss_sigma_b', 'twiss_sigma_p_b', 'twiss_emit_b', 'twiss_norm_emit_b', 'twiss_beta_c', 'twiss_alpha_c', 'twiss_gamma_c', 'twiss_phi_c', 'twiss_eta_c', 'twiss_etap_c', 'twiss_sigma_c', 'twiss_sigma_p_c', 'twiss_emit_c', 'twiss_norm_emit_c', 'sigma_11', 'sigma_12', 'sigma_13', 'sigma_14', 'sigma_15', 'sigma_16', 'sigma_21', 'sigma_22', 'sigma_23', 'sigma_24', 'sigma_25', 'sigma_26', 'sigma_31', 'sigma_32', 'sigma_33', 'sigma_34', 'sigma_35', 'sigma_36', 'sigma_41', 'sigma_42', 'sigma_43', 'sigma_44', 'sigma_45', 'sigma_46', 'sigma_51', 'sigma_52', 'sigma_53', 'sigma_54', 'sigma_55', 'sigma_56', 'sigma_61', 'sigma_62', 'sigma_63', 'sigma_64', 'sigma_65', 'sigma_66', 'rel_min_1', 'rel_max_1', 'centroid_vec_1', 'rel_min_2', 'rel_max_2', 'centroid_vec_2', 'rel_min_3', 'rel_max_3', 'centroid_vec_3', 'rel_min_4', 'rel_max_4', 'centroid_vec_4', 'rel_min_5', 'rel_max_5', 'centroid_vec_5', 'rel_min_6', 'rel_max_6', 'centroid_vec_6', 'centroid_t', 'centroid_p0c', 'centroid_beta', 'ix_ele', 'direction', 'species', 'location', 's', 't', 'sigma_t', 'charge_live', 'n_particle_tot', 'n_particle_live', 'n_particle_lost_in_ele', 'beam_saved'])
Array data:
x = tao.bunch1("end", coordinate="x")
px = tao.bunch1("end", coordinate="px")
plt.scatter(x, px);
The state will be returned as an integer array.
state = tao.bunch1("end", coordinate="state")
state.dtype
dtype('<i4')
ParticleGroup from openPMD-beamphysics¶
openPMD-beamphysics is an external package that can be useful for further bunch analysis, plotting, and conversion.
https://github.com/ChristopherMayes/openPMD-beamphysics
Here is example usage to extract bunch data and instantiate as a ParticleGroup
object.
Note that the momentum units in openPMD-beamphysics are in eV/c, whereas Bmad's momenta are normalized by a refrence momentum.
from pmd_beamphysics import ParticleGroup
This data is suitable for the ParticleGroup
class
data = tao.bunch_data("end")
data.keys()
dict_keys(['x', 'px', 'y', 'py', 't', 'pz', 'status', 'weight', 'z', 'species'])
P = ParticleGroup(data=data)
P.plot("x", "px")
Tao's write format is already in the openPMD-beamphysics, so particles can be written and read in this way as well.
tao.cmd("write beam -at end test.h5")
['[INFO] tao_write_cmd:', ' Written: test.h5']
P2 = ParticleGroup("test.h5")
P2.plot("x", "px")
# Cleanup
!rm test.h5
Error handling and Debugging¶
All methods have a raises=True
option. This will raise a RuntimeError if any errors are seen from Tao.
tao.lat_list("*", "ele.s")
array([0. , 0. , 0.06 , 0.193, 0.263, 0.385, 0.445, 0.445, 0.445])
try:
tao.var("foobar")
except Exception as ex:
print("Exception handled:", ex)
Exception handled: Command: python var foobar causes error: ERROR detected: [ERROR | 2025-JAN-12 01:24:31] tao_pipe_cmd: "pipe var foobar": Not a valid variable name INVALID
This suppresses the exceptions, returning the error text:
tao.cmd("invalid_command", raises=False)
['[ERROR | 2025-JAN-12 01:24:31] tao_command:', ' UNRECOGNIZED COMMAND: invalid_command']
Logging¶
All input commands are recorded as debug messages using standard Python logging.
Enable stdout to see the log messages:
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
tao.cmd("sho ele 2");
DEBUG:pytao.tao_ctypes.core:Tao> sho ele 2
Cleanup¶
!rm csr_wake.dat
rm: cannot remove 'csr_wake.dat': No such file or directory