From 89e869b365806bd3145a593d10eafc6d09699615 Mon Sep 17 00:00:00 2001 From: Sofia Kostoglou Date: Mon, 11 Jul 2022 17:50:32 +0200 Subject: [PATCH 1/4] flat optics example with vertical external crossing angle in IP8 --- .../000_pymask.py | 587 ++++++++++++++++++ .../checks_and_doc/crab_crossing.pptx | Bin 0 -> 72915 bytes .../checks_and_doc/footprint.py | 72 +++ .../fort_parts/fort_beginning.3 | 33 + .../checks_and_doc/fort_parts/fort_end.3 | 57 ++ .../checks_and_doc/helpers.py | 222 +++++++ .../checks_and_doc/install_miniconda.sh | 22 + .../checks_and_doc/t000_plots_from_twiss.py | 38 ++ .../t001_check_b4_against_b2.py | 44 ++ .../checks_and_doc/t003_fc_to_fort.py | 31 + .../t004_check_output_consistency.py | 312 ++++++++++ .../checks_and_doc/t005_check_crabs.py | 242 ++++++++ .../t006_calc_and_plot_footprint.py | 113 ++++ .../t007_check_orbit_and_lin_normal_form.py | 59 ++ .../t008_check_against_sixtrack.py | 51 ++ .../checks_and_doc/tests.md | 272 ++++++++ .../hl_lhc_collisions_flat_python/config.yaml | 206 ++++++ .../customization.bash.example | 5 + .../optics_specific_tools.py | 250 ++++++++ 19 files changed, 2616 insertions(+) create mode 100644 python_examples/hl_lhc_collisions_flat_python/000_pymask.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/crab_crossing.pptx create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py create mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md create mode 100644 python_examples/hl_lhc_collisions_flat_python/config.yaml create mode 100644 python_examples/hl_lhc_collisions_flat_python/customization.bash.example create mode 100644 python_examples/hl_lhc_collisions_flat_python/optics_specific_tools.py diff --git a/python_examples/hl_lhc_collisions_flat_python/000_pymask.py b/python_examples/hl_lhc_collisions_flat_python/000_pymask.py new file mode 100644 index 0000000..cb2f307 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/000_pymask.py @@ -0,0 +1,587 @@ +import os +import sys +import json +import yaml +import numpy as np + +##################################################### +# Read general configurations and setup envirnoment # +##################################################### + +assert not(os.path.isfile('config.yaml') + and os.path.isfile('config.py')), ( + "Please specify only a config file (yaml or py)") + +try: + with open('config.yaml','r') as fid: + configuration = yaml.safe_load(fid) +except: + from config import configuration + +# Start tree_maker logging if log_file is present in config +try: + import tree_maker + if 'log_file' not in configuration.keys(): + tree_maker=None +except: + tree_maker=None + +if tree_maker is not None: + tree_maker.tag_json.tag_it(configuration['log_file'], 'started') + +mode = configuration['mode'] +tol_beta = configuration['tol_beta'] +tol_sep = configuration['tol_sep'] +flat_tol = configuration['tol_co_flatness'] +links = configuration['links'] +optics_file = configuration['optics_file'] +check_betas_at_ips = configuration['check_betas_at_ips'] +check_separations_at_ips = configuration['check_separations_at_ips'] +save_intermediate_twiss = configuration['save_intermediate_twiss'] +enable_lumi_control = configuration['enable_lumi_control'] +enable_imperfections = configuration['enable_imperfections'] +enable_crabs = configuration['enable_crabs'] +match_q_dq_with_bb = configuration['match_q_dq_with_bb'] +knob_settings = configuration['knob_settings'] +knob_names = configuration['knob_names'] + +# Make links +if links['tracking_tools'] == 'auto': + import pymask as pm + links['tracking_tools'] = str( + pm._pkg_root.parent.parent.absolute()) + +for kk in links.keys(): + if os.path.exists(kk): + os.remove(kk) + os.symlink(os.path.abspath(links[kk]), kk) + +# Create empty temp folder +os.system('rm -r temp') +os.system('mkdir temp') + +# Execute customization script if present +os.system('bash customization.bash') + +# Import pymask +sys.path.append('./modules') +import pymask as pm + +# Import user-defined optics-specific tools +import optics_specific_tools as ost + +###################################### +# Check parameters and activate mode # +###################################### + +# Define configuration +(beam_to_configure, sequences_to_check, sequence_to_track, generate_b4_from_b2, + track_from_b4_mad_instance, enable_bb_python, enable_bb_legacy, + force_disable_check_separations_at_ips, + ) = pm.get_pymask_configuration(mode) + +if force_disable_check_separations_at_ips: + check_separations_at_ips = False + +if not(enable_crabs): + knob_settings['par_crab1'] = 0. + knob_settings['par_crab5'] = 0. + +######################## +# Build MAD-X instance # +######################## + +# Start mad +Madx = pm.Madxp +mad = Madx(command_log="mad_collider.log") + +# Set verbose flag +mad.globals.par_verbose = int(configuration['verbose_mad_parts']) + +# Build sequence (alse creates link to optics_toolkit and calls it) +ost.build_sequence(mad, beam=beam_to_configure, + configuration=configuration) + +# Set twiss formats for MAD-X parts (macro from opt. toolkit) +mad.input('exec, twiss_opt;') + +# Apply optics +ost.apply_optics(mad, optics_file=optics_file) + +# Attach beam to sequences +mad.globals.nrj = configuration['beam_energy_tot'] +particle_type = 'proton' + +if 'particle_mass' in configuration.keys(): + particle_mass = configuration['particle_mass'] + particle_type = 'ion' +else: + particle_mass = mad.globals.pmass # proton mass + +if 'particle_charge' in configuration.keys(): + particle_charge = configuration['particle_charge'] + particle_type = 'ion' +else: + particle_charge = 1. + +gamma_rel = (particle_charge*configuration['beam_energy_tot'])/particle_mass +for ss in mad.sequence.keys(): + # bv and bv_aux flags + if ss == 'lhcb1': + ss_beam_bv, ss_bv_aux = 1, 1 + elif ss == 'lhcb2': + if int(beam_to_configure) == 4: + ss_beam_bv, ss_bv_aux = 1, -1 + else: + ss_beam_bv, ss_bv_aux = -1, 1 + + mad.globals['bv_aux'] = ss_bv_aux + mad.input(f''' + beam, particle={particle_type},sequence={ss}, + energy={configuration['beam_energy_tot']*particle_charge}, + sigt={configuration['beam_sigt']}, + bv={ss_beam_bv}, + npart={configuration['beam_npart']}, + sige={configuration['beam_sige']}, + ex={configuration['beam_norm_emit_x'] * 1e-6 / gamma_rel}, + ey={configuration['beam_norm_emit_y'] * 1e-6 / gamma_rel}, + mass={particle_mass}, + charge={particle_charge}; + ''') + + +# Test machine before any change +twiss_dfs, other_data = ost.twiss_and_check(mad, sequences_to_check, + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_from_optics', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, + check_separations_at_ips=check_separations_at_ips) + +# Set IP1-IP5 phase and store corresponding reference +mad.input("call, file='modules/submodule_01c_phase.madx';") + +# Set optics-specific knobs +ost.set_optics_specific_knobs(mad, knob_settings, mode) + +# Crossing-save and some reference measurements +mad.input('exec, crossing_save;') +mad.input("call, file='modules/submodule_01e_final.madx';") + + +################################# +# Check bahavior of orbit knobs # +################################# + +# Check flat machine +mad.input('exec, crossing_disable;') +twiss_dfs, other_data = ost.twiss_and_check(mad, sequences_to_check, + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_no_crossing', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, check_separations_at_ips=check_separations_at_ips) + +# Check orbit flatness +for ss in twiss_dfs.keys(): + tt = twiss_dfs[ss] + assert np.max(np.abs(tt.x)) < flat_tol + assert np.max(np.abs(tt.y)) < flat_tol + +# Check machine after crossing restore +mad.input('exec, crossing_restore;') +twiss_dfs, other_data = ost.twiss_and_check(mad, sequences_to_check, + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_with_crossing', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, check_separations_at_ips=check_separations_at_ips) + + +################################# +# Set luminosity in IP2 and IP8 # +################################# + +if len(sequences_to_check) == 2: + print('Luminosities before leveling (crab cavities are not considered):') + pm.print_luminosity(mad, twiss_dfs, + configuration['nco_IP1'], configuration['nco_IP2'], + configuration['nco_IP5'], configuration['nco_IP8']) +else: + print('Warning: Luminosity computation requires two beams') + + +if not enable_lumi_control: + print('Separations in IP2 and IP8 are left untouched') +elif enable_bb_legacy or mode=='b4_without_bb': + mad.use(f'lhcb{beam_to_configure}') + if mode=='b4_without_bb': + print('Leveling not working in this mode!') + else: + if particle_type == 'ion': # the legacy macro for BB have been checked but not maintained + raise ValueError + # Luminosity levelling + vars_for_legacy_level = ['lumi_ip8', + 'nco_IP1', 'nco_IP2', 'nco_IP5', 'nco_IP8'] + mad.set_variables_from_dict({ + 'par_'+kk: configuration[kk] for kk in vars_for_legacy_level}) + mad.input("call, file='modules/module_02_lumilevel.madx';") +else: + print('Start pythonic leveling:') + ost.lumi_control(mad, twiss_dfs, configuration, knob_names) + +# Force leveling +if 'force_leveling' in configuration.keys(): + force_leveling = configuration['force_leveling'] + if force_leveling is not None: + for kk in force_leveling.keys(): + mad.globals[kk] = force_leveling[kk] + +# Re-save knobs (for the last time!) +mad.input('exec, crossing_save;') + +# Check machine after leveling +mad.input('exec, crossing_restore;') +twiss_dfs, other_data = ost.twiss_and_check(mad, sequences_to_check, + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_after_leveling', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, + check_separations_at_ips=check_separations_at_ips) + +if len(sequences_to_check) == 2: + print('Luminosities after leveling (crab cavities are not considered):') + pm.print_luminosity(mad, twiss_dfs, + configuration['nco_IP1'], configuration['nco_IP2'], + configuration['nco_IP5'], configuration['nco_IP8']) +else: + print('Luminosity computation requires two beams') + +qqqqqqqqqqqqqqqqqqqq +##################### +# Force on_disp = 0 # +##################### + +mad.globals.on_disp = 0. +# will be restored later + + +################################### +# Compute beam-beam configuration # +################################### + +# Prepare bb dataframes +if enable_bb_python: + bbconfig = configuration['beambeam_config'] + bb_dfs = pm.generate_bb_dataframes(mad, + ip_names=['ip1', 'ip2', 'ip5', 'ip8'], + harmonic_number=35640, + numberOfLRPerIRSide=bbconfig['numberOfLRPerIRSide'], + bunch_spacing_buckets=bbconfig['bunch_spacing_buckets'], + numberOfHOSlices=bbconfig['numberOfHOSlices'], + bunch_num_particles = bbconfig['bunch_num_particles'], + bunch_particle_charge = bbconfig['bunch_particle_charge'], + sigmaz_m=bbconfig['sigmaz_m'], + z_crab_twiss=bbconfig['z_crab_twiss']*float(enable_crabs), + remove_dummy_lenses=True) + + # Here the datafremes can be edited, e.g. to set bbb intensity + +################### +# Generate beam 4 # +################### + +if generate_b4_from_b2: + mad_b4 = Madx(command_log="mad_b4.log") + ost.build_sequence(mad_b4, beam=4,configuration=configuration) + + pm.configure_b4_from_b2(mad_b4, mad) + + twiss_dfs_b2, other_data_b2 = ost.twiss_and_check(mad, + sequences_to_check=['lhcb2'], + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_b2_for_b4check', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, check_separations_at_ips=False) + + twiss_dfs_b4, other_data_b4 = ost.twiss_and_check(mad_b4, + sequences_to_check=['lhcb2'], + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_b4_for_b4check', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, check_separations_at_ips=False) + +# For B1, to be generalized for B4 +if 'filling_scheme_json' in configuration['beambeam_config'].keys(): + assert 'b4' not in mode + filling_scheme_json = configuration['beambeam_config']['filling_scheme_json'] + bunch_to_track = configuration['beambeam_config']['bunch_to_track'] + bb_schedule_to_track_b1 = ost.create_bb_shedule_to_track( + filling_scheme_json,bunch_to_track, beam=1) + bb_dfs['b1']=ost.filter_bb_df(bb_dfs['b1'],bb_schedule_to_track_b1) + +################################################## +# Select mad instance for tracking configuration # +################################################## + +# We will be working exclusively on the sequence to track +# Select mad object +if track_from_b4_mad_instance: + mad_track = mad_b4 +else: + mad_track = mad + +mad_collider = mad +del(mad) + +# Twiss machine to track +twiss_dfs, other_data = ost.twiss_and_check(mad_track, sequences_to_check, + tol_beta=tol_beta, tol_sep=tol_sep, + twiss_fname='twiss_track_intermediate', + save_twiss_files=save_intermediate_twiss, + check_betas_at_ips=check_betas_at_ips, check_separations_at_ips=False) + + +##################### +# Install bb lenses # +##################### + +# Python approach +if enable_bb_python: + if track_from_b4_mad_instance: + bb_df_track = bb_dfs['b4'] + assert(sequence_to_track=='lhcb2') + else: + bb_df_track = bb_dfs['b1'] + assert(sequence_to_track=='lhcb1') + + pm.install_lenses_in_sequence(mad_track, bb_df_track, sequence_to_track) + + # Disable bb (to be activated later) + mad_track.globals.on_bb_charge = 0 +else: + bb_df_track = None + +# Legacy bb macros +if enable_bb_legacy: + bbconfig = configuration['beambeam_config'] + assert(beam_to_configure == 1) + assert(not(track_from_b4_mad_instance)) + assert(not(enable_bb_python)) + mad_track.globals['par_on_bb_switch'] = 1 + mad_track.set_variables_from_dict( + params=configuration['pars_for_legacy_bb_macros']) + mad_track.set_variables_from_dict( + params={f'par_nho_ir{ir}': bbconfig['numberOfHOSlices'] + for ir in [1,2,5,8]}) + mad_track.input("call, file='modules/module_03_beambeam.madx';") + + +######################### +# Install crab cavities # +######################### + +if enable_crabs: + mad_track.input("call, file='optics_toolkit/enable_crabcavities.madx';") + # They are left off, they will be swiched on at the end: + mad_track.globals.on_crab1 = 0 + mad_track.globals.on_crab5 = 0 + + +############################################## +# Save references for tuning and corrections # +############################################## + +mad_track.input("call, file='modules/submodule_04_1b_save_references.madx';") + + +##################### +# Force on_disp = 0 # +##################### + +mad_track.globals.on_disp = 0. +# will be restored later + + +############# +# Final use # +############# + +mad_track.use(sequence_to_track) +# Disable use +mad_track._use = mad_track.use +mad_track.use = None + + +############################## +# Install and correct errors # +############################## + +if enable_imperfections: + mad_track.set_variables_from_dict( + configuration['pars_for_imperfections']) + mad_track.input("call, file='modules/module_04_errors.madx';") +else: + # Synthesize knobs + mad_track.input('call, file="modules/submodule_04a_s1_prepare_nom_twiss_table.madx";') + if configuration['enable_knob_synthesis']: + mad_track.input('exec, crossing_disable;') + mad_track.input("call, file='modules/submodule_04e_s1_synthesize_knobs.madx';") + mad_track.input('exec, crossing_restore;') + + +################## +# Machine tuning # +################## + +# Enable bb for matchings +if match_q_dq_with_bb: + mad_track.globals['on_bb_charge'] = 1 +else: + mad_track.globals['on_bb_charge'] = 0 + +# Switch on octupoles +brho = mad_track.globals.nrj*1e9/mad_track.globals.clight +i_oct = configuration['oct_current'] +beam_str = {'lhcb1':'b1', 'lhcb2':'b2'}[sequence_to_track] +for ss in '12 23 34 45 56 67 78 81'.split(): + mad_track.input(f'kof.a{ss}{beam_str} = kmax_mo*({i_oct})/imax_mo/({brho});') + mad_track.input(f'kod.a{ss}{beam_str} = kmax_mo*({i_oct})/imax_mo/({brho});') + + +# Correct linear coupling +qx_fractional, qx_integer = np.modf(configuration['qx0']) +qy_fractional, qy_integer = np.modf(configuration['qy0']) +coupl_corr_info = pm.coupling_correction(mad_track, + n_iterations=configuration['N_iter_coupling'], + qx_integer=qx_integer, qy_integer=qy_integer, + qx_fractional=qx_fractional, qy_fractional=qy_fractional, + tune_knob1_name=knob_names['qknob_1'][sequence_to_track], + tune_knob2_name=knob_names['qknob_2'][sequence_to_track], + cmr_knob_name=knob_names['cmrknob'][sequence_to_track], + cmi_knob_name=knob_names['cmiknob'][sequence_to_track], + sequence_name=sequence_to_track, skip_use=True) + +# Add custom values to coupling knobs +mad_track.globals[knob_names['cmrknob'][sequence_to_track]] += configuration['delta_cmr'] +mad_track.globals[knob_names['cmiknob'][sequence_to_track]] += configuration['delta_cmi'] + +# Check strength limits +if enable_imperfections: + mad_track.input('call, file="errors/HL-LHC/corr_limit.madx";') + +# Rematch the orbit at IPs +mad_track.input("call, file='tools/rematchCOIP.madx';") + +# Rematch the CO in the arc for dispersion correction +if mad_track.globals.on_disp != 0: + mad_track.input("call, file='tools/rematchCOarc.madx';") + +# Match tunes and chromaticities +pm.match_tune_and_chromaticity(mad_track, + q1=configuration['qx0'], + q2=configuration['qy0'], + dq1=configuration['chromaticity_x'], + dq2=configuration['chromaticity_y'], + tune_knob1_name=knob_names['qknob_1'][sequence_to_track], + tune_knob2_name=knob_names['qknob_2'][sequence_to_track], + chromaticity_knob1_name=knob_names['chromknob_1'][sequence_to_track], + chromaticity_knob2_name=knob_names['chromknob_2'][sequence_to_track], + sequence_name=sequence_to_track, skip_use=True) + +# Check strength limits +if enable_imperfections: + mad_track.input("call, file='errors/HL-LHC/corr_value_limit.madx';") + +# Switch on bb lenses +mad_track.globals.on_bb_charge = 1. + +# Switch on RF cavities +mad_track.globals['vrf400'] = configuration['vrf_total'] +if sequence_to_track == 'lhcb1': + mad_track.globals['lagrf400.b1'] = 0.5 +elif sequence_to_track == 'lhcb2': + mad_track.globals['lagrf400.b2'] = 0. + +# Switch on crab cavities +if enable_crabs: + mad_track.globals.on_crab1 = knob_settings['on_crab1'] + mad_track.globals.on_crab5 = knob_settings['on_crab5'] + + +##################### +# Generate sixtrack # +##################### + +if enable_bb_legacy: + mad_track.input("call, file='modules/module_06_generate.madx'") +else: + pm.generate_sixtrack_input(mad_track, + seq_name=sequence_to_track, + bb_df=bb_df_track, + output_folder='./', + reference_num_particles_sixtrack=( + mad_track.sequence[sequence_to_track].beam.npart), + reference_particle_charge_sixtrack=mad_track.sequence[sequence_to_track].beam.charge, + emitnx_sixtrack_um=( + mad_track.sequence[sequence_to_track].beam.exn), + emitny_sixtrack_um=( + mad_track.sequence[sequence_to_track].beam.eyn), + sigz_sixtrack_m=( + mad_track.sequence[sequence_to_track].beam.sigt), + sige_sixtrack=( + mad_track.sequence[sequence_to_track].beam.sige), + ibeco_sixtrack=1, + ibtyp_sixtrack=0, + lhc_sixtrack=2, + ibbc_sixtrack=0, + radius_sixtrack_multip_conversion_mad=0.017, + skip_mad_use=True) + + +####################################### +# Save optics and orbit at start ring # +####################################### + +optics_and_co_at_start_ring_from_madx = pm.get_optics_and_orbit_at_start_ring( + mad_track, sequence_to_track, skip_mad_use=True) +with open('./optics_orbit_at_start_ring_from_madx.json', 'w') as fid: + json.dump(optics_and_co_at_start_ring_from_madx, fid, cls=pm.JEncoder) + +######################## +# Generate xtrack line # +######################## +if enable_bb_legacy: + print('xtrack line is not generated with bb legacy macros') +else: + pm.generate_xsuite_line(mad_track, sequence_to_track, bb_df_track, + optics_and_co_at_start_ring_from_madx, + folder_name = './xsuite_lines', + skip_mad_use=True, + prepare_line_for_xtrack=True) + +################################### +# Save final twiss # +################################### + +mad_track.globals.on_bb_charge = 0 +mad_track.twiss() +tdf = mad_track.get_twiss_df('twiss') +sdf = mad_track.get_summ_df('summ') +tdf.to_parquet('final_twiss_BBOFF.parquet') +sdf.to_parquet('final_summ_BBOFF.parquet') + + +mad_track.globals.on_bb_charge = 1 +mad_track.twiss() +tdf = mad_track.get_twiss_df('twiss') +sdf = mad_track.get_summ_df('summ') +tdf.to_parquet('final_twiss_BBON.parquet') +sdf.to_parquet('final_summ_BBON.parquet') + +############################# +# Save sequence and errors # +############################# +# N.B. this erases the errors in the mad_track instance +# pm.save_mad_sequence_and_error(mad_track, sequence_to_track, filename='final') + +if tree_maker is not None: + tree_maker.tag_json.tag_it(configuration['log_file'], 'completed') diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/crab_crossing.pptx b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/crab_crossing.pptx new file mode 100644 index 0000000000000000000000000000000000000000..3efe63a921be43069db79e5d0efbb91be8807394 GIT binary patch literal 72915 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 literal 0 HcmV?d00001 diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py new file mode 100644 index 0000000..bf6c24c --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py @@ -0,0 +1,72 @@ +import numpy as np +import matplotlib.patches +import matplotlib.collections +import matplotlib.pyplot as plt +#plt.style.use('kostas') + +def initial_xy_polar(r_min, r_max, r_N, theta_min, theta_max, theta_N): + return np.array([[(r*np.cos(theta),r*np.sin(theta)) for r in np.linspace(r_min,r_max,r_N)] for theta in np.linspace(theta_min,theta_max,theta_N)]) + +def initial_xy_cartesian(x_min, x_max, x_N, y_min, y_max, y_N): + return np.array([[(x,y) for x in np.linspace(x_min,x_max,x_N)] for y in np.linspace(y_min,y_max,y_N)]) + + +def draw_footprint(A, axis_object=None, figure_object=None, axis=0, linewidth=4): + ''' + Input A should be a 3-D numpy array with shape (Nx,Ny,2) + representing a 2-D array of (x,y) points. This function + will draw lines between adjacent points in the 2-D array. + ''' + if len(A.shape) != 3: + print('ERROR: Invalid input matrix') + return None + if A.shape[2] != 2: + print('ERROR: Points are not defined in 2D space') + return None + + sx = A.shape[0]-1 + sy = A.shape[1]-1 + + p1 = A[:-1,:-1,:].reshape(sx*sy,2)[:,:] + p2 = A[1:,:-1,:].reshape(sx*sy,2)[:] + p3 = A[1:,1:,:].reshape(sx*sy,2)[:] + p4 = A[:-1,1:,:].reshape(sx*sy,2)[:] + + #Stack endpoints to form polygons + Polygons = np.stack((p1,p2,p3,p4)) + #transpose polygons + Polygons = np.transpose(Polygons,(1,0,2)) + patches = list(map(matplotlib.patches.Polygon,Polygons)) + + #assign colors + patch_colors = [(0,0,0) for a in Polygons] + patch_colors[(sx-1)*sy:] = [(0,1,0)]*sy + patch_colors[(sy-1)::sy] = [(0,0,1)]*sx + + p_collection = matplotlib.collections.PatchCollection(patches,facecolors=[],linewidth=linewidth,edgecolor=patch_colors) + + if axis_object is None: + if figure_object: + fig = figure_object + else: + fig = plt.figure() + if len(fig.axes) == 0: + plt.subplot(1,1,1) + if axis >= len(fig.axes) or axis < 0: + i = 0 + else: + i = axis + ax = fig.axes[i] + else: + ax = axis_object + fig = None + + ax.add_collection(p_collection) + + return fig + +def example(): + coords_matrix = initial_xy_polar(0.01,5,11,0.,np.pi/2.,10) + fig = draw_footprint(coords_matrix) + fig.axes[0].set_xlim(-0.05,5.1) + fig.axes[0].set_ylim(-0.05,5.1) diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 new file mode 100644 index 0000000..e804b14 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 @@ -0,0 +1,33 @@ +GEOME-STRENG TITLE:ts_collisions_ats30_en20_IMO380_C7_X160_I1.2_62.3100_60.3200%1%s%62.31_60.32%8_10%5%80 +PRINTOUT OF INPUT PARAMETERS-------------------------------------------- +NEXT +TRACKING PARAMETERS----------------------------------------------------- +1 0 1 0.00 0.00 0 1 +1 1 0 1 2 +0 0 1 1 1000 50000 2 +NEXT +INITIAL COORDINATES----------------------------------------------------- + 2 0. 0. 0.999999 0 + 0. + 0. + 0. + 0. + 0. + 0. + .0 + 0.01 + 0.003 + 0. + 0. + 0.000 + 7000000. + 7000000. + 7000000. +NEXT +SYNCHROTRON OSCILLATIONS--------'PLACE AFTER TRACKING PARAMETERS'------- + 35640 .000347 16. 0. 26658.883200 938.272013 1 + 1. 1. +NEXT +DUMP +ALL 1 664 101 dump3.dat +NEXT diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 new file mode 100644 index 0000000..64968ba --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 @@ -0,0 +1,57 @@ +FLUCTUATION +100000 1 7 3 +NEXT +/TUNE VARIATION---------------------------------------------------------- +/ mq.7r3.b1 62.31 +/ mq.8r3.b1 60.32 +// 0. +// +/NEXT +/CHROMATICITY CORRECTION------------------------------------------------ +/ ms.11r3.b1 2. +/ ms.12r3.b1 2. +/NEXT +POSTPROCESSING---------------------------------------------------------- +LHC Dynamic Aperture at Injection Version 5 (1998) +20 0 0 1 .08 .08 1 0 0 1. 1. +62. 60. 1 1 10 .005 1 .050 +0 1 0 1 1 1 1 1 30 +NEXT +/DIFFERENTIAL ALGEBRA--------------------------------------------------- +/%NO %NV 1E-38 %nsix 0 +/NEXT +ENDE==================================================================== +ORGA-------------------------------------------------------------------- +NEXT +DIFFERENTIAL ALGEBRA--------------------------------------------------- +4 5 1E-38 1 1 +NEXT +COMBINATION OF ELEMENTS------------------------------------------------- +CODSDO 1. CODSDE +CODSFO 1. CODSFE +NEXT +SUBRESONANCE CALCULATION------------------------------------------------ +3 3 62.31 60.32 1. 1. 0 26658.883200 +NEXT +RESONANCE COMPENSATION ------------------------------------------------- + 1 3 0 0 0 0 0 0 + 0 0 0 0 + 26658.883200 62.31 60.32 1. 1. + MSCBH MSCBV + 0 MSCBH MSCBV + 0 QF QD 62.31 60.32 +NEXT +RIPPLE OF POWER SUPPLIES------------------------------------------------ + Q413R -1.54D-4 4340.580 + Q414R -3.70D-4 4340.580 +NEXT +LIMITATION OF APERTURE-------------------------------------------------- + AP1 EL 10. 10. +NEXT +SEARCH FOR THE BEST POSITIONS FOR COMPENSATION-ELEMENTS----------------- + 62.31 60.32 1. 1. 26658.883200 + 2 3 0 2 -2 0 0 0 + MSCBH MSCBV +NEXT +ORBIT ADJUSTMENT-------------------------------------------------------- +NEXT diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py new file mode 100644 index 0000000..171c093 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py @@ -0,0 +1,222 @@ +import numpy as np +import os +import sixtracktools +import time + +import xpart as xp +import xtrack as xt + +def vectorize_all_coords(Dx_wrt_CO_m, Dpx_wrt_CO_rad, + Dy_wrt_CO_m, Dpy_wrt_CO_rad, + Dzeta_wrt_CO_m, Ddelta_wrt_CO): + + n_part = 1 + for var in [Dx_wrt_CO_m, Dpx_wrt_CO_rad, + Dy_wrt_CO_m, Dpy_wrt_CO_rad, + Dzeta_wrt_CO_m, Ddelta_wrt_CO]: + if hasattr(var, '__iter__'): + if n_part == 1: + n_part = len(var) + assert len(var) == n_part + + Dx_wrt_CO_m = Dx_wrt_CO_m + np.zeros(n_part) + Dpx_wrt_CO_rad = Dpx_wrt_CO_rad + np.zeros(n_part) + Dy_wrt_CO_m = Dy_wrt_CO_m + np.zeros(n_part) + Dpy_wrt_CO_rad = Dpy_wrt_CO_rad + np.zeros(n_part) + Dzeta_wrt_CO_m = Dzeta_wrt_CO_m + np.zeros(n_part) + Ddelta_wrt_CO = Ddelta_wrt_CO + np.zeros(n_part) + + return Dx_wrt_CO_m, Dpx_wrt_CO_rad,\ + Dy_wrt_CO_m, Dpy_wrt_CO_rad,\ + Dzeta_wrt_CO_m, Ddelta_wrt_CO + + +def track_particle_sixtrack( + partCO, Dx_wrt_CO_m, Dpx_wrt_CO_rad, + Dy_wrt_CO_m, Dpy_wrt_CO_rad, + Dzeta_wrt_CO_m, Ddelta_wrt_CO, n_turns, + input_folder='./'): + + Dx_wrt_CO_m, Dpx_wrt_CO_rad,\ + Dy_wrt_CO_m, Dpy_wrt_CO_rad,\ + Dzeta_wrt_CO_m, Ddelta_wrt_CO = vectorize_all_coords( + Dx_wrt_CO_m, Dpx_wrt_CO_rad, + Dy_wrt_CO_m, Dpy_wrt_CO_rad, + Dzeta_wrt_CO_m, Ddelta_wrt_CO) + + n_part = len(Dx_wrt_CO_m) + + wfold = 'temp_trackfun' + + if not os.path.exists(wfold): + os.mkdir(wfold) + + os.system(f'cp {input_folder}/fort.* {wfold}') + + with open(f'{wfold}/fort.3', 'r') as fid: + lines_f3 = fid.readlines() + + # Set initial coordinates + i_start_ini = None + for ii, ll in enumerate(lines_f3): + if ll.startswith('INITIAL COO'): + i_start_ini = ii + break + + lines_f3[i_start_ini + 2] = ' 0.\n' + lines_f3[i_start_ini + 3] = ' 0.\n' + lines_f3[i_start_ini + 4] = ' 0.\n' + lines_f3[i_start_ini + 5] = ' 0.\n' + lines_f3[i_start_ini + 6] = ' 0.\n' + lines_f3[i_start_ini + 7] = ' 0.\n' + + lines_f3[i_start_ini + 2 + 6] = ' 0.\n' + lines_f3[i_start_ini + 3 + 6] = ' 0.\n' + lines_f3[i_start_ini + 4 + 6] = ' 0.\n' + lines_f3[i_start_ini + 5 + 6] = ' 0.\n' + lines_f3[i_start_ini + 6 + 6] = ' 0.\n' + lines_f3[i_start_ini + 7 + 6] = ' 0.\n' + + lines_f13 = [] + + for i_part in range(n_part): + if type(partCO) == xp.Particles: + temp_part = partCO.copy() + else: + temp_part = xp.Particles(**partCO) + temp_part.x += Dx_wrt_CO_m[i_part] + temp_part.px += Dpx_wrt_CO_rad[i_part] + temp_part.y += Dy_wrt_CO_m[i_part] + temp_part.py += Dpy_wrt_CO_rad[i_part] + temp_part.zeta += Dzeta_wrt_CO_m[i_part] + temp_part.update_delta(temp_part.delta + Ddelta_wrt_CO[i_part]) + + lines_f13.append('%.10e\n' % ((temp_part.x) * 1e3)) + lines_f13.append('%.10e\n' % ((temp_part.px) * temp_part.rpp * 1e3)) + lines_f13.append('%.10e\n' % ((temp_part.y) * 1e3)) + lines_f13.append('%.10e\n' % ((temp_part.py) * temp_part.rpp * 1e3)) + lines_f13.append('%.10e\n' % ((temp_part.zeta) * 1e3)) + lines_f13.append('%.10e\n' % ((temp_part.delta))) + if i_part % 2 == 1: + lines_f13.append('%.10e\n' % (temp_part.energy0 * 1e-6)) + lines_f13.append('%.10e\n' % (prev_part.energy * 1e-6)) + lines_f13.append('%.10e\n' % (temp_part.energy * 1e-6)) + prev_part = temp_part + + with open(wfold + '/fort.13', 'w') as fid: + fid.writelines(lines_f13) + + if np.mod(n_part, 2) != 0: + raise ValueError('SixTrack does not like this!') + + i_start_tk = None + for ii, ll in enumerate(lines_f3): + if ll.startswith('TRACKING PAR'): + i_start_tk = ii + break + # Set number of turns and number of particles + temp_list = lines_f3[i_start_tk + 1].split(' ') + temp_list[0] = '%d' % n_turns + temp_list[2] = '%d' % (n_part / 2) + lines_f3[i_start_tk + 1] = ' '.join(temp_list) + # Set number of idfor = 2 + temp_list = lines_f3[i_start_tk + 2].split(' ') + temp_list[2] = '2' + lines_f3[i_start_tk + 2] = ' '.join(temp_list) + + # Setup turn-by-turn dump + i_start_dp = None + for ii, ll in enumerate(lines_f3): + if ll.startswith('DUMP'): + i_start_dp = ii + break + + lines_f3[i_start_dp + 1] = 'StartDUMP 1 664 101 dumtemp.dat\n' + + with open(wfold + '/fort.3', 'w') as fid: + fid.writelines(lines_f3) + + os.system('(cd temp_trackfun; sixtrack >fort.6)') + + # Load sixtrack tracking data + sixdump_all = sixtracktools.SixDump101('%s/dumtemp.dat' % wfold) + + x_tbt = np.zeros((n_turns, n_part)) + px_tbt = np.zeros((n_turns, n_part)) + y_tbt = np.zeros((n_turns, n_part)) + py_tbt = np.zeros((n_turns, n_part)) + zeta_tbt = np.zeros((n_turns, n_part)) + delta_tbt = np.zeros((n_turns, n_part)) + + for i_part in range(n_part): + sixdump_part = sixdump_all[i_part::n_part] + x_tbt[:, i_part] = sixdump_part.x + px_tbt[:, i_part] = sixdump_part.px + y_tbt[:, i_part] = sixdump_part.y + py_tbt[:, i_part] = sixdump_part.py + zeta_tbt[:, i_part] = sixdump_part.tau * sixdump_part.beta0 + delta_tbt[:, i_part] = sixdump_part.delta + + extra = {} + + return x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra + + + +def track_particle_xtrack( + line, partCO, Dx_wrt_CO_m, Dpx_wrt_CO_rad, + Dy_wrt_CO_m, Dpy_wrt_CO_rad, + Dzeta_wrt_CO_m, Ddelta_wrt_CO, n_turns, + _context=None): + + Dx_wrt_CO_m, Dpx_wrt_CO_rad,\ + Dy_wrt_CO_m, Dpy_wrt_CO_rad,\ + Dzeta_wrt_CO_m, Ddelta_wrt_CO = vectorize_all_coords( + Dx_wrt_CO_m, Dpx_wrt_CO_rad, + Dy_wrt_CO_m, Dpy_wrt_CO_rad, + Dzeta_wrt_CO_m, Ddelta_wrt_CO) + + tracker = xt.Tracker(_context=_context, line=line) + particles = xp.Particles(_context=_context, + p0c = partCO.p0c, + x = partCO.x + Dx_wrt_CO_m, + px = partCO.px + Dpx_wrt_CO_rad, + y = partCO.y + Dy_wrt_CO_m, + py = partCO.py + Dpy_wrt_CO_rad, + zeta = partCO.zeta + Dzeta_wrt_CO_m, + delta = partCO.delta + Ddelta_wrt_CO) + + print('Start track') + tracker.track(particles, num_turns=n_turns, turn_by_turn_monitor=True) + print('Done track') + + #print(res.particles[0]) + x_tbt = tracker.record_last_track.x.copy().T + px_tbt = tracker.record_last_track.px.copy().T + y_tbt = tracker.record_last_track.y.copy().T + py_tbt = tracker.record_last_track.py.copy().T + zeta_tbt = tracker.record_last_track.zeta.copy().T + delta_tbt = tracker.record_last_track.delta.copy().T + + print('Done loading!') + + extra = {'particles': particles, 'tracker': tracker} + + return x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra + + +def betafun_from_ellip(x_tbt, px_tbt): + + x_max = np.max(x_tbt) + mask = np.logical_and(np.abs(x_tbt) < x_max / 5., px_tbt > 0) + x_masked = x_tbt[mask] + px_masked = px_tbt[mask] + ind_sorted = np.argsort(x_masked) + x_sorted = np.take(x_masked, ind_sorted) + px_sorted = np.take(px_masked, ind_sorted) + + px_cut = np.interp(0, x_sorted, px_sorted) + + beta_x = x_max / px_cut + + return beta_x, x_max, px_cut diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh new file mode 100644 index 0000000..bc282b9 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh @@ -0,0 +1,22 @@ +wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh +bash Miniconda3-latest-Linux-x86_64.sh -b -p ./miniconda -f +source miniconda/bin/activate +pip install ipython jupyterlab numpy scipy pandas awkward matplotlib +pip install pyarrow pyyaml pytest +pip install cpymad +pip install xsuite +git clone git@github.com:lhcopt/lhcerrors.git +git clone git@github.com:lhcopt/lhctoolkit.git +git clone git@github.com:lhcopt/lhcmask.git +git clone git@github.com:lhcopt/beambeam_macros.git +cd beambeam_macros +gfortran headonslice.f -o headonslice +cd .. +cd lhcmask +git checkout release/v1.3.1 +pip install -e . +cd ../ +git clone https://github.com/PyCOMPLETE/FillingPatterns.git +pip install ./FillingPatterns +python -m pip install sixtracktools +python -m pip install NAFFlib diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py new file mode 100644 index 0000000..d935183 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py @@ -0,0 +1,38 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + +twiss_df = pd.read_parquet('../twiss_with_crossing_seq_lhcb1.parquet') + + +plt.close('all') + +# %% +fig = plt.figure(1, figsize=(6.4*1.6, 4.8*1.5)) +axbetx = fig.add_subplot(2,2,1) +axbetx.plot(twiss_df['s'], twiss_df['betx']) +axbetx.set_xlabel('s') +axbetx.set_ylabel(r'$\beta_x$ [m]') +axbetx.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') + +# %% +axbety = fig.add_subplot(2,2,2, sharex=axbetx, sharey=axbetx) +axbety.plot(twiss_df['s'], twiss_df['bety']) +axbety.set_xlabel('s') +axbety.set_ylabel(r'$\beta_y$ [m]') +axbety.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') +axbety.legend() +# %% + +axcox = fig.add_subplot(2,2,3, sharex=axbetx) +axcox.plot(twiss_df['s'], twiss_df['x']) +axcox.set_xlabel('s') +axcox.set_ylabel('x [m]') +#plt.xlim(5500,7500) +# %% +axcoy = fig.add_subplot(2,2,4, sharex=axbetx, sharey=axcox) +axcoy.plot(twiss_df['s'], twiss_df['y']) +axcoy.set_xlabel('s') +axcoy.set_ylabel('y [m]') + +plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py new file mode 100644 index 0000000..6bf143e --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py @@ -0,0 +1,44 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + +twiss_df_b2 = pd.read_parquet('../twiss_b2_for_b4check_seq_lhcb2.parquet') +twiss_df_b4 = pd.read_parquet('../twiss_b4_for_b4check_seq_lhcb2.parquet') + + +plt.close('all') + +# %% +fig = plt.figure(1, figsize=(6.4*1.6, 4.8*1.5)) +axbetx = fig.add_subplot(2,2,1) +axbetx.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],twiss_df_b4['betx'],'b') +axbetx.plot(twiss_df_b2['s'], twiss_df_b2['betx'],'--r') +axbetx.set_xlabel('s') +axbetx.set_ylabel(r'$\beta_x$ [m]') +axbetx.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') + +# %% +axbety = fig.add_subplot(2,2,2, sharex=axbetx, sharey=axbetx) +axbety.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],twiss_df_b4['bety'],'b', + label='b4') +axbety.plot(twiss_df_b2['s'], twiss_df_b2['bety'],'--r', label='b2') +axbety.set_xlabel('s') +axbety.set_ylabel(r'$\beta_y$ [m]') +axbety.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') +axbety.legend() +# %% + +axcox = fig.add_subplot(2,2,3, sharex=axbetx) +axcox.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],-twiss_df_b4['x'],'b') +axcox.plot(twiss_df_b2['s'], twiss_df_b2['x'], '--r') +axcox.set_xlabel('s') +axcox.set_ylabel('x [m]') +#plt.xlim(5500,7500) +# %% +axcoy = fig.add_subplot(2,2,4, sharex=axbetx, sharey=axcox) +axcoy.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],twiss_df_b4['y'],'b') +axcoy.plot(twiss_df_b2['s'], twiss_df_b2['y'], '--r') +axcoy.set_xlabel('s') +axcoy.set_ylabel('y [m]') + +plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py new file mode 100644 index 0000000..6829953 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py @@ -0,0 +1,31 @@ +import os +import shutil + +folder_list = [ + '../', + '../../../examples/hl_lhc_collision/', + '../../../examples/hl_lhc_collision_nobb_b4'] + +for sixtrack_input_folder in folder_list: + print(sixtrack_input_folder) + try: + for iff in [2,8,16,34]: + os.system(f"rm {sixtrack_input_folder}/fort.{iff}") + try: + shutil.copy(sixtrack_input_folder + f"/fc.{iff}", + sixtrack_input_folder + f"/fort.{iff}") + except Exception: + print(f"/fc.{iff} not found!") + + with open(sixtrack_input_folder + "/fort.3", "w") as fout: + with open("fort_parts/fort_beginning.3", "r") as fid_fort3b: + fout.write(fid_fort3b.read()) + with open(sixtrack_input_folder + "/fc.3", "r") as fid_fc3: + fout.write(fid_fc3.read()) + with open("fort_parts/fort_end.3", "r") as fid_fort3e: + fout.write(fid_fort3e.read()) + except Exception as e: + print('Skipped...') + print(e) + +print('Ended.') diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py new file mode 100644 index 0000000..6dcd74b --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py @@ -0,0 +1,312 @@ +import time +import shutil +import pickle +import json +import numpy as np + +import sixtracktools +import xtrack as xt +import xfields as xf + + + + +# Tests b1 with bb +tests = [ + { + 'test_name': 'B1 - pymask sixtrack input vs madx mask', + 'path_test': '../', + 'type_test': 'sixtrack', + 'path_ref': '../../../examples/hl_lhc_collision', + 'type_ref': 'sixtrack', + 'rtol': 3e-5, + 'atol': 1e-12, + 'strict': False, + }, + { + 'test_name': 'B1 - pymask xsuite vs pymask sixtrack input', + 'path_test': '../xsuite_lines/line_bb_dipole_not_cancelled.json', + 'type_test': 'xsuite', + 'path_ref': '../', + 'type_ref': 'sixtrack', + 'rtol': 4e-7, + 'atol': 1e-13, + 'strict': True, + } +] + +# # Tests b4 no bb +# tests = [ +# { +# 'test_name': 'B4 - pymask sixtrack input vs madx mask', +# 'path_test': '../', +# 'type_test': 'sixtrack', +# 'path_ref': '../../../examples/hl_lhc_collision_nobb_b4', +# 'type_ref': 'sixtrack', +# 'rtol': 8e-5, +# 'atol': 1e-12, +# 'strict': False, +# }, +# { +# 'test_name': 'B4 - pymask xsuite vs pymask sixtrack input', +# 'path_test': '../xsuite/line_bb_dipole_not_cancelled.json', +# 'type_test': 'xsuite', +# 'path_ref': '../', +# 'type_ref': 'sixtrack', +# 'rtol': 4e-7, +# 'atol': 1e-100, +# 'strict': True, +# } +# ] + +def norm(x): + return np.sqrt(np.sum(np.array(x) ** 2)) + +def prepare_line(path, input_type): + + if input_type == 'xsuite': + # Load machine + with open(path, 'r') as fid: + ltest = xt.Line.from_dict(json.load(fid)) + elif input_type == 'sixtrack': + print('Build xsuite from sixtrack input:') + sixinput_test = sixtracktools.sixinput.SixInput(path) + ltest = xt.Line.from_sixinput(sixinput_test) + print('Done') + else: + raise ValueError('What?!') + + return ltest + +for tt in tests: + test_name = tt['test_name'] + path_test = tt['path_test'] + type_test = tt['type_test'] + path_ref = tt['path_ref'] + type_ref = tt['type_ref'] + rtol = tt['rtol'] + atol = tt['atol'] + strict = tt['strict'] + + # Load + ltest = prepare_line(path_test, type_test) + lref = prepare_line(path_ref, type_ref) + original_length = ltest.get_length() + assert (lref.get_length() - original_length) < 1e-6 + + # Simplify the two machines + for ll in (ltest, lref): + ll._var_management = None + ll.remove_inactive_multipoles(inplace=True) + ll.remove_zero_length_drifts(inplace=True) + ll.merge_consecutive_drifts(inplace=True) + ll.merge_consecutive_multipoles(inplace=True) + + # Remove inactive RFMultipoles and normalize phase + for ii, ee in enumerate(ll.elements): + if ee.__class__.__name__ == 'RFMultipole': + if ( np.max(np.abs(ee.knl)) < 1e-20 and + np.max(np.abs(ee.ksl)) < 1e-20 and + abs(ee.voltage) < 1e-20): + ll.element_names[ii] = None + # # Normalize phase + # for kkll, pp in [[ee.knl, ee.pn], + # [ee.ksl, ee.ps]]: + # for ii, vv in enumerate(kkll): + # if vv < 0: + # kkll[ii] = -vv + # pp[ii] += 180 + # if pp[ii]>180: + # pp[ii] -= 360 + + while None in ll.element_names: + ll.element_names.remove(None) + + # Check that the two machines are identical + assert len(ltest) == len(lref) + + assert (ltest.get_length() - original_length) < 1e-6 + assert (lref.get_length() - original_length) < 1e-6 + + for ii, (ee_test, ee_six, nn_test, nn_six) in enumerate( + zip(ltest.elements, lref.elements, ltest.element_names, lref.element_names) + ): + assert type(ee_test) == type(ee_six) + + dtest = ee_test.to_dict() + dref = ee_six.to_dict() + + for kk in dtest.keys(): + + # Check if they are identical + if np.isscalar(dref[kk]) and dtest[kk] == dref[kk]: + continue + + if isinstance(dref[kk], dict): + if kk=='fieldmap': + continue + if kk=='boost_parameters': + continue + if kk=='Sigmas_0_star': + continue + + # Check if the relative error is small + val_test = dtest[kk] + val_ref = dref[kk] + if not np.isscalar(val_ref): + if len(val_ref) != len(val_test): + diff_rel = 100 + else: + for iiii, (vvr, vvt) in enumerate(list(zip(val_ref, val_test))): + if not np.isclose(vvr, vvt, atol=atol, rtol=rtol): + print(f'Issue found on `{kk}[{iiii}]`') + diff_rel = 1000 + else: + diff_rel = 0 + else: + if val_ref > 0: + diff_rel = np.abs((val_test - val_ref)/val_ref) + else: + diff_rel = 100 + + if diff_rel < rtol: + continue + + # Check if absolute error is small + + if not np.isscalar(val_ref) and len(val_ref) != len(val_test): + diff_abs = 1000 + else: + diff_abs = norm(np.array(val_test) - np.array(val_ref)) + if diff_abs > 0: + print(f"{nn_test}[{kk}] - test:{dtest[kk]} six:{dref[kk]}") + if diff_abs < atol: + continue + + # Exception: drift length (100 um tolerance) + if not(strict) and isinstance(ee_test, xt.Drift): + if kk == "length": + if diff_abs < 1e-4: + continue + + # Exception: multipole lrad is not passed to sixtraxk + if isinstance(ee_test, xt.Multipole): + if kk == "length": + if np.abs(ee_test.hxl) + np.abs(ee_test.hyl) == 0.0: + continue + if kk == "order": + # Checked through bal + continue + if kk == 'knl' or kk == 'ksl' or kk == 'bal': + if len(val_ref) != len(val_test): + lmin = min(len(val_ref), len(val_test)) + for vv in [val_ref,val_test]: + if len(vv)> lmin: + for oo in range(lmin, len(vv)): + # we do not care about errors above 10 + if vv[oo] != 0 and oo < {'knl':10, + 'ksl':10, 'bal':20}[kk]: + raise ValueError( + 'Missing significant multipole strength') + + val_ref = val_ref[:lmin] + val_test = val_test[:lmin] + + if len(val_ref) == 0 and len(val_test) == 0: + continue + else: + for iiii, (vvr, vvt) in enumerate(list(zip(val_ref, val_test))): + if not np.isclose(vvr, vvt, atol=atol, rtol=rtol): + print(f'Issue found on `{kk}[{iiii}]`') + diff_rel = 1000 + break + else: + diff_rel = 0 + if diff_rel < rtol: + continue + + # Exception: correctors involved in lumi leveling + passed_corr = False + for nn_corr in [ + 'mcbcv.5l8.b1', 'mcbyv.a4l8.b1', 'mcbxv.3l8', + 'mcbyv.4r8.b1', 'mcbyv.b5r8.b1', + 'mcbyh.b5l2.b1', 'mcbyh.4l2.b1', 'mcbxh.3l2', 'mcbyh.a4r2.b1', + 'mcbch.5r2.b1', + 'mcbcv.5l8.b2', 'mcbyv.a4l8.b2', 'mcbxv.3l8', + 'mcbyv.4r8.b2', 'mcbyv.b5r8.b2', + 'mcbyh.b5l2.b2', 'mcbyh.4l2.b2', 'mcbxh.3l2', 'mcbyh.a4r2.b2', + 'mcbch.5r2.b2', 'mcbch.a5r2.b2', 'mcbyh.4r2.b2', 'mcbxh.3r2', + 'mcbyh.a4l2.b2', 'mcbyh.5l2.b2', 'mcbyv.5r8.b2', 'mcbyv.a4r8.b2', + 'mcbxv.3r8', 'mcbyv.4l8.b2', 'mcbcv.b5l8.b2']: + if nn_corr in nn_test and kk in ['knl','ksl','bal']: + assert len(val_ref)<=2 + assert len(val_test)<=2 + diff_rel = norm(val_test-val_ref)/norm(val_ref) + passed_corr = (diff_rel < 1e-2) + if passed_corr: + break + if not(strict) and passed_corr: + continue + + # Exceptions BB4D (separations are recalculated) + if not(strict) and isinstance(ee_test, xf.BeamBeamBiGaussian2D): + if kk == "x_bb": + if diff_abs / dtest["sigma_x"] < 0.01: # This is neede to accommodate different leveling routines (1% difference) + continue + if kk == "y_bb": + if diff_abs / dtest["sigma_y"] < 0.01: + continue + if kk == "sigma_x": + if diff_rel < 1e-5: + continue + if kk == "sigma_y": + if diff_rel < 1e-5: + continue + if isinstance(ee_test, xf.BeamBeamBiGaussian2D): + if kk == 'q0' or kk == 'n_particles': + # ambiguity due to old interface + if np.abs(ee_test.n_particles*ee_test.q0 - + ee_six.n_particles*ee_six.q0 ) < 1.: # charges + continue + + # Exceptions BB6D (angles and separations are recalculated) + if not(strict) and isinstance(ee_test, xf.BeamBeamBiGaussian3D): + if kk == "alpha": + if diff_abs < 10e-6: + continue + if kk == "x_co" or kk == "x_bb_co" or kk == 'delta_x': + if diff_abs / np.sqrt(dtest["sigma_11"]) < 0.015: + continue + if kk == "y_co" or kk == "y_bb_co" or kk == 'delta_y': + if diff_abs / np.sqrt(dtest["sigma_33"]) < 0.015: + continue + if kk == "zeta_co": + if diff_abs <1e-5: + continue + if kk == "delta_co": + if diff_abs <1e-5: + continue + if kk == "px_co" or kk == 'py_co': + if diff_abs <30e-9: + continue + if isinstance(ee_test, xt.XYShift): + if kk in ['dx','dy']: + if diff_abs <1e-9: + continue + if isinstance(ee_test, xt.SRotation): + if kk in ['sin_z', 'cos_z', 'angle']: + if diff_abs <1e-9: + continue + # If it got here it means that no condition above is met + raise ValueError("Too large discrepancy!") + print( + f""" + ******************************************************************* + test: {test_name} + + The line from test seq. and the line from reference are identical! + ******************************************************************* + """ + ) + + time.sleep(1) diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py new file mode 100644 index 0000000..0e55115 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py @@ -0,0 +1,242 @@ +import shutil +import numpy as np +import json + +import xtrack as xt +import xpart as xp +import xfields as xf +import sixtracktools + +L_lhc = 27e3 +h_cc = 35640 + +# Mad-X knobs +Phi = 250e-6 +Phi_c = -190e-6 + +# B1 ip5 +beam_track = 'b1' +ip_choice = 5 +plane = 'y' +phi_weak = Phi +phi_c_weak = Phi_c + +# B1 ip1 +beam_track = 'b1' +ip_choice = 1 +plane = 'x' +phi_weak = Phi +phi_c_weak = Phi_c + +# B4 ip5 +beam_track = 'b4' +ip_choice = 5 +plane = 'y' +phi_weak = Phi +phi_c_weak = Phi_c + +# B4 ip1 +beam_track = 'b4' +ip_choice = 1 +plane = 'x' +phi_weak = -Phi +phi_c_weak = -Phi_c + +phi_strong = -phi_weak +phi_c_strong = -phi_c_weak + +path_test = '../' +type_test = 'sixtrack' + +#path_test = '../xsuite_lines/line_bb_dipole_not_cancelled.json' +#type_test = 'xsuite' + +def prepare_line(path, input_type): + + if input_type == 'xsuite': + # Load xsuite machine + with open(path, 'r') as fid: + ltest = xt.Line.from_dict(json.load(fid)) + elif input_type == 'sixtrack': + print('Build xsuite from sixtrack input:') + sixinput_test = sixtracktools.sixinput.SixInput(path) + ltest = sixinput_test.generate_xtrack_line() + print('Done') + else: + raise ValueError('What?!') + + return ltest + +# Load +ltest = prepare_line(path_test, type_test) + + +s_all_elems = ltest.get_s_elements() +names_all_elems = ltest.element_names +all_elems = ltest.elements + +bb_names = [] +bb_s = [] +bb_elems = [] +bb_index = [] +for ii, (nn, ss, ee) in enumerate(zip(names_all_elems, s_all_elems, all_elems)): + if nn.startswith('bb_ho'): + if f'{ip_choice}b' in nn: + bb_names.append(nn) + bb_s.append(ss) + bb_elems.append(ee) + bb_index.append(ii) + +iho = np.where(['.c' in nn for nn in bb_names])[0][0] +sho = bb_s[iho] +s_rel = np.array(bb_s) - sho + +import matplotlib.pyplot as plt +plt.close('all') +fig1 = plt.figure(1) +axcrab = fig1.add_subplot(111) + + +# For B1 as weak beam (and we plot B2 as stron beam) +# phi_c_strong = dR_crab / dz +# phi_strong = dR_crab / ds +R_crab = phi_c_strong * L_lhc / (2*np.pi*h_cc) *np.sin(2*np.pi*h_cc/L_lhc*2*s_rel) +R_no_crab = phi_strong * s_rel +R1_orbit = phi_weak * s_rel + +axcrab.plot(s_rel, np.array( + [getattr(bb, f'delta_{plane}') for bb in bb_elems])+R1_orbit, + 'o', color = 'r', alpha=.5, label='strong xsuite') +plt.plot(s_rel, R_no_crab + R_crab, '*', color='darkred', label='strong formula') + +#axcrab.plot(s_rel, np.array([bb.y_bb_co for bb in bb_elems]), 'o', color='r', alpha=.5) +#plt.plot(s_rel, Y_no_crab + Y_crab - Y1_orbit, '*', color='darkred') + +#plt.plot(s_rel, Y_no_crab, 'xr') +#plt.plot(s_rel, Y_crab, 'xb') +#plt.figure() +#plt.plot(s_rel, np.array([bb.y_bb_co for bb in bb_elems]) +# /(2*s_rel), 'o') + + +# Chek crabs in weak beam + +# Switch off all beam-beam lenses +bb_all, _ = ltest.get_elements_of_type([xf.BeamBeamBiGaussian2D, + xf.BeamBeamBiGaussian3D]) +for bb in bb_all: + bb.q0 = 0 + +# # Switch off all beam-beam lenses +crabs, crab_names = ltest.get_elements_of_type([xt.RFMultipole]) +#for cc in crabs: +# cc.pn = [-90] +# cc.ps = [-90] + +# for cc in crabs: +# cc.knl[0] *= -1 + +with open('../optics_orbit_at_start_ring_from_madx.json', 'r') as fid: + ddd = json.load(fid) +partco = xp.Particles.from_dict(ddd['particle_on_madx_co']) +z_slices = s_rel * 2.0 +partco = xp.build_particles(particle_on_co=partco, mode='shift', zeta=z_slices) + +tracker=xt.Tracker(line=ltest) +tracker.track(partco, turn_by_turn_monitor='ONE_TURN_EBE') +first_turn = tracker.record_last_track + +tracker.track(partco, turn_by_turn_monitor='ONE_TURN_EBE') +second_turn = tracker.record_last_track + +plt.figure(2) +axcox = plt.subplot(2,1,1) +axcoy = plt.subplot(2,1,2, sharex=axcox) +plt.suptitle('Check closed orbit 2 turns') +axcox.plot(first_turn.s[iho,:] , first_turn.x[iho,:]) +axcox.plot(second_turn.s[iho,:] , second_turn.x[iho,:]) + +axcoy.plot(first_turn.s[iho,:] , first_turn.y[iho,:]) +axcoy.plot(second_turn.s[iho,:] , second_turn.y[iho,:]) + +plt.figure(20) +axcopx = plt.subplot(2,1,1, sharex=axcox) +axcopy = plt.subplot(2,1,2, sharex=axcox) +plt.suptitle('Check closed orbit 2 turns') +axcopx.plot(first_turn.s[iho,:] , first_turn.px[iho,:]) +axcopx.plot(second_turn.s[iho,:] , second_turn.px[iho,:]) + +axcopy.plot(first_turn.s[iho,:] , first_turn.py[iho,:]) +axcopy.plot(second_turn.s[iho,:] , second_turn.py[iho,:]) + +plt.figure(3) +axcox = plt.subplot(2,1,1) +axcoy = plt.subplot(2,1,2, sharex=axcox) +plt.suptitle('Check crab') +for iz, zz in enumerate(z_slices): + axcox.plot(first_turn.s[iz,:] , first_turn.x[iz,:]) + axcoy.plot(first_turn.s[iz,:] , first_turn.y[iz,:]) + +# For each s_lens, we find the transverse position of the weak beam slice +# that collides with the sycnhronous particle of the strong +r_lenses = [] +for ibb, bb in enumerate(bb_elems): + #r_lenses.append(getattr(list_co[bb_index[ibb]], plane)[ibb]) + r_lenses.append(getattr(first_turn, plane)[ibb, bb_index[ibb]]) + +axcrab.plot(s_rel, r_lenses, 'o', color='b', alpha=.5, label= 'weak xsuite') +Rw_crab = phi_c_weak * L_lhc / (2*np.pi*h_cc) *np.sin(2*np.pi*h_cc/L_lhc*2*s_rel) +Rw_no_crab = phi_weak * s_rel +axcrab.plot(s_rel, Rw_crab + Rw_no_crab, '*', color='darkblue', + label='weak formula') +axcrab.legend(loc='best') + +# Check crab bump +import pandas as pd +z_crab_twiss = 0.075 + +if beam_track == 'b1': + crab_df = pd.read_parquet(f'../twiss_z_crab_{z_crab_twiss:.5f}_seq_lhcb1.parquet') + s_twiss = crab_df.s.values + x_twiss = crab_df.x.values + y_twiss = crab_df.y.values + px_twiss = crab_df.px.values + py_twiss = crab_df.py.values + z_crab_track = z_crab_twiss +elif beam_track == 'b4': + crab_df = pd.read_parquet(f'../twiss_z_crab_{z_crab_twiss:.5f}_seq_lhcb2.parquet') + s_twiss = -crab_df.s.values[::-1] + s_twiss -= s_twiss[0] + x_twiss = -crab_df.x.values[::-1] + y_twiss = crab_df.y.values[::-1] + px_twiss = crab_df.px.values[::-1] + py_twiss = -crab_df.py.values[::-1] + z_crab_track = -z_crab_twiss +else: + raise ValueError('????!!!!!') + +figcb = plt.figure(4) +axcbx = figcb.add_subplot(2,1,1) +axcby = figcb.add_subplot(2,1,2, sharex=axcbx) + +axcbx.plot(s_twiss, x_twiss) +axcby.plot(s_twiss, y_twiss) + +part = xp.Particles.from_dict(ddd['particle_on_madx_co']) + +z_test = np.array([0, z_crab_track]) +part = xp.build_particles(particle_on_co=part, + mode='shift', + zeta = z_test, + x = 0*z_test + np.array([0, x_twiss[0]]), + y = 0*z_test + np.array([0, y_twiss[0]]), + px = 0*z_test + np.array([0, px_twiss[0]]), + py = 0*z_test + np.array([0, py_twiss[0]]), + delta = 0*z_test) +tracker.track(part, turn_by_turn_monitor='ONE_TURN_EBE') +mon = tracker.record_last_track + +axcbx.plot(mon.s[0, :], mon.x[1, :] - mon.x[0, :]) +axcby.plot(mon.s[0, :], mon.y[1, :] - mon.y[0, :]) + +plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py new file mode 100644 index 0000000..90aa300 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py @@ -0,0 +1,113 @@ +import json +import numpy as np +import NAFFlib +import helpers as hp +import footprint +import matplotlib.pyplot as plt + +import sixtracktools +import xtrack as xt +import xpart as xp + +track_with = 'xtrack' +track_with = 'sixtrack' + +epsn_x = 2.5e-6 +epsn_y = 2.5e-6 +r_max_sigma = 5. +N_r_footp = 20 +N_theta_footp = 10 + +n_turns = 100 + +def prepare_line(path, input_type): + + if input_type == 'xsuite': + # Load xsuite machine + with open(path, 'r') as fid: + ltest = xt.Line.from_dict(json.load(fid)) + elif input_type == 'sixtrack': + print('Build xsuite from sixtrack input:') + sixinput_test = sixtracktools.sixinput.SixInput(path) + ltest = xt.Line.from_sixinput(sixinput_test) + print('Done') + else: + raise ValueError('What?!') + + return ltest + +line = prepare_line('../xsuite_lines/line_bb_for_tracking.json', input_type='xsuite') + + +with open('../xsuite_lines/line_bb_for_tracking.json', 'r') as fid: + partCO = xp.Particles.from_dict( + json.load(fid)['particle_on_tracker_co']) + +with open('../optics_orbit_at_start_ring_from_madx.json', 'r') as fid: + ddd = json.load(fid) + +part = partCO.copy() + +beta_x = ddd['betx'] +beta_y = ddd['bety'] + +sigmax = np.sqrt(beta_x * epsn_x / part.beta0 / part.gamma0) +sigmay = np.sqrt(beta_y * epsn_y / part.beta0 / part.gamma0) + +xy_norm = footprint.initial_xy_polar(r_min=1e-2, r_max=r_max_sigma, r_N=N_r_footp + 1, + theta_min=np.pi / 100, theta_max=np.pi / 2 - np.pi / 100, + theta_N=N_theta_footp) + +DpxDpy_wrt_CO = np.zeros_like(xy_norm) + +for ii in range(xy_norm.shape[0]): + for jj in range(xy_norm.shape[1]): + + DpxDpy_wrt_CO[ii, jj, 0] = xy_norm[ii, jj, 0] * np.sqrt(epsn_x / part.beta0 / part.gamma0 / beta_x) + DpxDpy_wrt_CO[ii, jj, 1] = xy_norm[ii, jj, 1] * np.sqrt(epsn_y / part.beta0 / part.gamma0 / beta_y) + + +if track_with == 'sixtrack': + x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra = hp.track_particle_sixtrack( + partCO=partCO, Dx_wrt_CO_m=0., Dpx_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 0].flatten(), + Dy_wrt_CO_m=0, Dpy_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 1].flatten(), + Dzeta_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=n_turns, + input_folder='../') + info = track_with +elif track_with == 'xtrack': + x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra = hp.track_particle_xtrack( + line=line, partCO=partCO, Dx_wrt_CO_m=0., Dpx_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 0].flatten(), + Dy_wrt_CO_m=0., Dpy_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 1].flatten(), + Dzeta_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=n_turns) + info = track_with +else: + raise ValueError('What?!') + +n_part = x_tbt.shape[1] +Qx = np.zeros(n_part) +Qy = np.zeros(n_part) + +for i_part in range(n_part): + Qx[i_part] = NAFFlib.get_tune(x_tbt[:, i_part]) + Qy[i_part] = NAFFlib.get_tune(y_tbt[:, i_part]) + +Qxy_fp = np.zeros_like(xy_norm) + +Qxy_fp[:, :, 0] = np.reshape(Qx, Qxy_fp[:, :, 0].shape) +Qxy_fp[:, :, 1] = np.reshape(Qy, Qxy_fp[:, :, 1].shape) + +plt.close('all') + +fig3 = plt.figure(3) +axcoord = fig3.add_subplot(1, 1, 1) +footprint.draw_footprint(xy_norm, axis_object=axcoord, linewidth = 1) +axcoord.set_xlim(right=np.max(xy_norm[:, :, 0])) +axcoord.set_ylim(top=np.max(xy_norm[:, :, 1])) + +fig4 = plt.figure(4) +axFP = fig4.add_subplot(1, 1, 1) +footprint.draw_footprint(Qxy_fp, axis_object=axFP, linewidth = 1) +# axFP.set_xlim(right=np.max(Qxy_fp[:, :, 0])) +# axFP.set_ylim(top=np.max(Qxy_fp[:, :, 1])) +fig4.suptitle(info) +plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py new file mode 100644 index 0000000..32679ad --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py @@ -0,0 +1,59 @@ +import json + +import numpy as np + +import xtrack as xt +import xpart as xp + +with open('../xsuite_lines/line_bb_for_tracking.json', 'r') as fid: + line_dict = json.load(fid) + +line = xt.Line.from_dict(line_dict) + +partCO = xp.Particles.from_dict(line_dict['particle_on_tracker_co']) + +tracker = xt.Tracker(line=line) +particles = xp.Particles(**partCO.to_dict()) + +for _ in range(10): + print(particles.at_turn[0], particles.x[0], particles.y[0], + particles.zeta[0]) + tracker.track(particles) + +for nn in 'x px y py zeta delta'.split(): + assert np.abs(getattr(particles, nn) - getattr(partCO, nn)) < 3e-11 + +WW = np.array(line_dict['WW_finite_diffs']) +WWinv = np.array(line_dict['WWInv_finite_diffs']) +assert np.max(np.abs(np.dot(WW, WWinv) - np.eye(6)))<1e-10 + + +ampl_sigmas = 0.2 +norm_emit_x = 2.5e-6 +geom_emit_x = norm_emit_x / particles.beta0 / particles.gamma0 + +n_part = 100 +theta = np.linspace(0, 2*np.pi, n_part) +x_norm = ampl_sigmas * np.sqrt(geom_emit_x) * np.cos(theta) +px_norm = ampl_sigmas * np.sqrt(geom_emit_x) * np.sin(theta) + +particles_matched = xp.build_particles(particle_on_co=partCO, + x_norm=x_norm, px_norm=px_norm, + R_matrix=np.array(line_dict['RR_finite_diffs']), + symplectify=True) +particles_test = particles_matched.copy() +tracker.track(particles_test, num_turns=10) + +i_matched = np.argmax(particles_matched.x) +i_test = np.argmax(particles_test.x) +assert np.abs(particles_test.x[i_test] - particles_matched.x[i_matched]) < 2e-6 +assert np.abs(particles_test.px[i_test] - particles_matched.px[i_matched]) < 1e-7 + + +import matplotlib.pyplot as plt +plt.close('all') +plt.figure(1) +plt.plot(particles_matched.x, particles_matched.px) +plt.plot(particles_test.x, particles_test.px) + +plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py new file mode 100644 index 0000000..6b63ca0 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py @@ -0,0 +1,51 @@ +import json +import numpy as np +import NAFFlib +import helpers as hp +import matplotlib.pyplot as plt + +import xtrack as xt +import xpart as xp +import sixtracktools + + +displace_x = [-5e-4, 5e-4] +displace_y = [3e-4, -3e-4] +num_turns = 5 + + +with open('../xsuite_lines/line_bb_for_tracking.json', 'r') as fid: + dict_line_xtrack = json.load(fid) + +line = xt.Line.from_dict(dict_line_xtrack) + +partCO = xp.Particles.from_dict(dict_line_xtrack['particle_on_tracker_co']) + +(x_tbt_sixtrack, px_tbt_sixtrack, y_tbt_sixtrack, py_tbt_sixtrack, + zeta_tbt_sixtrack, delta_tbt_sixtrack, extra) = hp.track_particle_sixtrack( + partCO=partCO, Dx_wrt_CO_m=np.array(displace_x), + Dpx_wrt_CO_rad=0, + Dy_wrt_CO_m=np.array(displace_y), Dpy_wrt_CO_rad=0., + Dzeta_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=num_turns, + input_folder='../') + +tracker = xt.Tracker(line=line) + +particles = xp.build_particles(particle_on_co=partCO, + mode='shift', + x=np.array(displace_x), + y=np.array(displace_y)) + +tracker.track(particles, turn_by_turn_monitor=True, num_turns=num_turns) + +print('Xtrack') +print(tracker.record_last_track.x.transpose()) +print('Sixtrack') +print(x_tbt_sixtrack) + +assert np.allclose(tracker.record_last_track.x[0, :], x_tbt_sixtrack[:,0], + rtol=1e-15, atol=9e-11) +assert np.allclose(tracker.record_last_track.y[0, :], y_tbt_sixtrack[:,0], + rtol=1e-15, atol=9e-11) +assert np.allclose(tracker.record_last_track.delta[0, :], delta_tbt_sixtrack[:,0], + rtol=1e-15, atol=5e-11) diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md new file mode 100644 index 0000000..3e635ef --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md @@ -0,0 +1,272 @@ +# Tests performed on pymask + +N.B. For the tests to be successful make sure that cpymad and madx correspond to the same (updated!) version. + + +## Test 1 - b1 with bb legacy macros + +We execute the reference: +```bash + cd ../../examples/hl_lhc_collision + python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad +``` + +We setup the python version: +```bash +cd ../../python_examples/hl_lhc_collisions_python + +``` +In config.yaml we select: +```yaml +mode : b1_with_bb_legacy_macros +``` +We run the python version: +``` +python 000_pymask.py +``` + +We check that the output is identical +```bash +diff fc.2 ../../examples/hl_lhc_collision/fc.2 +diff fc.3 ../../examples/hl_lhc_collision/fc.3 +diff fc.8 ../../examples/hl_lhc_collision/fc.8 +diff fc.16 ../../examples/hl_lhc_collision/fc.16 +diff fc.34 ../../examples/hl_lhc_collision/fc.34 +``` + + +## Test 2 - b1 with new bb tools +In config.yaml we select: +```yaml +mode : b1_with_bb +``` + +We exeute the python version: +```bash +python 000_pymask.py +``` + +We execute the reference (can be reused from the previous test): +```bash + cd ../../examples/hl_lhc_collision + python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad +``` +In the folder ```checks_and_doc```: +Configure ```t004_check_output_consistency.py``` for the test: +```python +# Test b1 +tests = [ + { + 'test_name': 'B1 - pymask sixtrack input vs madx mask', + 'path_test': '../', + 'type_test': 'sixtrack', + 'path_ref': '../../../examples/hl_lhc_collision', + 'type_ref': 'sixtrack', + 'rtol': 3e-5, + 'atol': 1e-12, + 'strict': False, + }, + { + 'test_name': 'B1 - pymask xsuite vs pymask sixtrack input', + 'path_test': '../xsuite/line_bb_dipole_not_cancelled.json', + 'type_test': 'xsuite', + 'path_ref': '../', + 'type_ref': 'sixtrack', + 'rtol': 4e-7, + 'atol': 1e-100, + 'strict': True, + } +] +``` + +Check using xsuite lines: +```bash +python t003_fc_to_fort.py +python t004_check_output_consistency.py +``` + +**Check crab cavities:** + +In ```t005_check_crabs.py``` set: +```python +# B1 ip5 +beam_track = 'b1' +ip_choice = 5 +plane = 'y' +phi_weak = Phi +phi_c_weak = Phi_c +``` + +Run check: +```bash +python t003_fc_to_fort.py +python t005_check_crabs.py +``` +Minor deviations on crab orbit are observed due to errors and tuning. + +Repeat for IP1. + +## Test 3 - b4 without bb + +**WARNING!!!! In this mode the leveling mask module is not working, values are forced in python and the module is skipped** + +We execute the reference: +```bash + cd ../../examples/hl_lhc_collision_nobb_b4 + python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad +``` + +We setup the python version: +```bash +cd ../../python_examples/hl_lhc_collisions_python + +``` +In config.yaml we select: +```yaml +mode: b4_without_bb, +force_leveling: + on_sep8: -0.03425547139366354 + on_sep2: 0.14471680504084292 +``` +(the separations are forced in order to be consitent with the mad-x test case for which the same values are impored, as the legacy leveling macro does not work in this case). + +We run the python version: +``` +python 000_pymask.py +``` + +We check that the output is identical +```bash +diff fc.2 ../../examples/hl_lhc_collision_nobb_b4/fc.2 +diff fc.3 ../../examples/hl_lhc_collision_nobb_b4/fc.3 +diff fc.8 ../../examples/hl_lhc_collision_nobb_b4/fc.8 +diff fc.16 ../../examples/hl_lhc_collision_nobb_b4/fc.16 +diff fc.34 ../../examples/hl_lhc_collision_nobb_b4/fc.34 +``` + +## Test 4 - b4 from b2 without bb + +We execute the reference: +```bash + cd ../../examples/hl_lhc_collision_nobb_b4 + python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad +``` + +We setup the python version: +```bash +cd ../../python_examples/hl_lhc_collisions_python + +``` +In config.yaml we select: +```yaml +mode: b4_from_b2_without_bb +force_leveling: + on_sep8: -0.03425547139366354 + on_sep2: 0.14471680504084292 +``` +(the separations are forced in order to be consitent with the mad-x test case for which the same values are impored, as the legacy leveling macro does not work in this case). + + +We run the python version: +``` +python 000_pymask.py +``` + +We check that the output is identical +```bash +diff fc.2 ../../examples/hl_lhc_collision_nobb_b4/fc.2 +diff fc.3 ../../examples/hl_lhc_collision_nobb_b4/fc.3 +diff fc.8 ../../examples/hl_lhc_collision_nobb_b4/fc.8 +diff fc.16 ../../examples/hl_lhc_collision_nobb_b4/fc.16 +diff fc.34 ../../examples/hl_lhc_collision_nobb_b4/fc.34 +``` + +## Test 5 - b4 from b2 with bb +We setup the python version: +```bash +cd ../../python_examples/hl_lhc_collisions_python + +``` +In config.yaml we select: +```python +mode: b4_from_b2_with_bb, +``` +We run the python version: +``` +python 000_pymask.py +``` + +**As we don't have a reference we can only perform the check on the crab cavities:** + +In ```t005_check_crabs.py``` set: +```python +# B4 ip1 +beam_track = 'b4' +ip_choice = 1 +plane = 'x' +phi_weak = -Phi +phi_c_weak = -Phi_c +``` + +Run check: +```bash +python t003_fc_to_fort.py +python t005_check_crabs.py +``` +Minor deviations on crab orbit are observed due to errors and tuning. + +Repeat for IP5 using: +```python +# B4 ip5 +beam_track = 'b4' +ip_choice = 5 +plane = 'y' +phi_weak = Phi +phi_c_weak = Phi_c +``` + +## Test 7 and 8 - Check the matching and the tracking + +Select on config.yaml + +```yaml +beam: b1_without_bb +``` +and run + +```bash +python t003_fc_to_fort.py +python t007_check_orbit_and_lin_normal_form.py +python t008_check_against_sixtrack.py +``` + +then repeat for + +```yaml +beam: b1_with_bb +``` + +and + +```yaml +beam: b4_from_b2_without_bb +``` + +and + +```yaml +beam: b4_from_b2_with_bb +``` + + + +## To check a mad test againt its reference + +```bash +diff fc.2 reference/fc.2 +diff fc.3 reference/fc.3 +diff fc.8 reference/fc.8 +diff fc.16 reference/fc.16 +diff fc.34 reference/fc.34 +``` + diff --git a/python_examples/hl_lhc_collisions_flat_python/config.yaml b/python_examples/hl_lhc_collisions_flat_python/config.yaml new file mode 100644 index 0000000..4b22737 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/config.yaml @@ -0,0 +1,206 @@ +# Links to be made for tools and scripts +links: + tracking_tools: auto + modules: tracking_tools/lhcmask + tools: tracking_tools/lhctoolkit + beambeam_macros: tracking_tools/beambeam_macros + errors: tracking_tools/lhcerrors + optics_repository: tracking_tools + +# Mode - choose between: + +# Main modes: +# 'b1_without_bb' +# 'b1_with_bb' +# 'b4_from_b2_without_bb' +# 'b4_from_b2_with_bb' + +# Legacy modes +# 'b1_with_bb_legacy_macros +# 'b4_without_bb' + +mode: b1_with_bb + +# For test against madx mask for modes +# 'b4_from_b2_without_bb' and 'b4_without_bb': +# force_leveling: +# on_sep8: -0.03425547139366354 +# on_sep2: 0.14471680504084292 + +# Optics file +#optics_file: optics_repository/hllhc15/flat/opt_flatvh_75_300_1500_thin.madx +optics_file: optics_repository/hllhc15/flat/opt_flatvh_75_300_1500_thin.madx +# Optics version +optics_version: '1.5' + +# Enable checks +check_betas_at_ips: true +check_separations_at_ips: true +save_intermediate_twiss: true + +# Tolerances for checks [ip1, ip2, ip5, ip8] +tol_beta: +- 0.003 +- 0.2 +- 0.002 +- 0.02 +tol_sep: +- 1.0e-06 +- 1.0e-06 +- 1.0e-06 +- 1.0e-06 + +# Tolerance for check on flat machine +tol_co_flatness: 1.0e-06 + +# Beam parameters +beam_norm_emit_x: 2.5 # [um] +beam_norm_emit_y: 2.5 # [um] +beam_sigt: 0.076 # [m] +beam_sige: 0.00011 +beam_npart: 220000000000.0 +beam_energy_tot: 7000 # [GeV] + +# Tunes and chromaticities +qx0: 62.31 +qy0: 60.32 +chromaticity_x: 5 +chromaticity_y: 5 + +# RF voltage +vrf_total: 16.0 # [MV] + +# Octupole current +oct_current: -235 # [A] + +# Luminosity parameters +enable_lumi_control: true +sep_plane_ip2: x # used by python tools - NOT by legacy macros +sep_plane_ip8: y # used by python tools - NOT by legacy macros, +lumi_ip8: 2.0e+33 # [Hz/cm2] leveled luminosity in IP8 +fullsep_in_sigmas_ip2: 5 +nco_IP1: 2748 # number of Head-On collisions in IP1 +nco_IP2: 2494 # number of Head-On collisions in IP2 +nco_IP5: 2748 # number of Head-On collisions in IP5 +nco_IP8: 2572 # number of Head-On collisions in IP8 + +# Beam-beam parameters (used by python tools - NOT by legacy macros) +beambeam_config: + numberOfLRPerIRSide: + - 25 + - 20 + - 25 + - 20 + bunch_spacing_buckets: 10 + numberOfHOSlices: 11 + bunch_num_particles: null + bunch_particle_charge: null + sigmaz_m: null + z_crab_twiss: 0.075 + +# Match tunes and chromaticities including beam-beam effects +match_q_dq_with_bb: false # should be off at collision + +# Enable crab cavities +enable_crabs: true + +# N. iterations for coupling correction +N_iter_coupling: 2 + +# Value to be added to linear coupling knobs (on sequence_to_track) +delta_cmr: 0.0 +delta_cmi: 0.0 + +# Verbose flag for MAD-X parts +verbose_mad_parts: true + +# Optics-specific knob namings +knob_names: + # Common knobs + sepknob_ip2_mm: on_sep2 + sepknob_ip8_mm: on_sep8 + + # Knobs associated to sequences + qknob_1: + lhcb1: kqtf.b1 + lhcb2: kqtf.b2 + qknob_2: + lhcb1: kqtd.b1 + lhcb2: kqtd.b2 + chromknob_1: + lhcb1: ksf.b1 + lhcb2: ksf.b2 + chromknob_2: + lhcb1: ksd.b1 + lhcb2: ksd.b2 + cmrknob: + lhcb1: cmrskew + lhcb2: cmrskew + cmiknob: + lhcb1: cmiskew + lhcb2: cmiskew + +# Optics specific knob values +# (only on_disp, on_crab1 and on_crab5 are used directly by the mask, +# the other values are used only throught the optics_specific_tools file) +knob_settings: + # IP specific orbit settings + on_x1: 250 # [urad] + on_sep1: 0 # [mm] + on_x2: -170 # [urad] + on_sep2: 0.138 # [mm] + on_x5: 250 # [urad] + on_sep5: 0 # [mm] + on_x8h: 0 # [urad] + on_x8v: 170 # [urad] + on_sep8h: 0 # [mm] + on_sep8v: 0 # [mm] + on_a1: 0 # [urad] + on_o1: 0 # [mm] + on_a2: 0 # [urad] + on_o2: 0 # [mm] + on_a5: 0 # [urad] + on_o5: 0 # [mm] + on_a8: 0 # [urad] + on_o8: 0 # [mm] + on_crab1: -190 # [urad] + on_crab5: -190 # [urad] + + # Dispersion correction knob + on_disp: 1 # Value to choose could be optics-dependent + + # Magnets of the experiments + on_alice_normalized: 1 + on_lhcb_normalized: 1 + on_sol_atlas: 0 + on_sol_cms: 0 + on_sol_alice: 0 + +# Enable machine imperfections +enable_imperfections: true + +# Enable knob synthesis (for coupling correction, if no imperfections) +enable_knob_synthesis: true + +# Parameters for machine imperfections +pars_for_imperfections: + par_myseed: 1 + par_correct_for_D2: 0 + par_correct_for_MCBX: 0 + par_on_errors_LHC: 1 + par_off_errors_Q4_inIP15: 0 + par_off_errors_Q5_inIP15: 0 + par_on_errors_MBH: 1 + par_on_errors_Q4: 1 + par_on_errors_D2: 1 + par_on_errors_D1: 1 + par_on_errors_IT: 1 + par_on_errors_MCBRD: 0 + par_on_errors_MCBXF: 0 + par_on_errors_NLC: 0 + par_write_errortable: 1 + +# Parameters for legacy beam-beam macros (not used in default modes) +pars_for_legacy_bb_macros: + par_b_t_dist: 25.0 # bunch spacing [ns] + par_n_inside_D1: 5 # n. parasitic encounters inside D1 diff --git a/python_examples/hl_lhc_collisions_flat_python/customization.bash.example b/python_examples/hl_lhc_collisions_flat_python/customization.bash.example new file mode 100644 index 0000000..3ef2c56 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/customization.bash.example @@ -0,0 +1,5 @@ +#rm modules +#ln -fns /home/sterbini/test_new_mask/v0.5/lhcmachines machines +#ln -fns /home/sterbini/test_new_mask/v0.5/lhctoolkit tools +ln -fns /home/sterbini/lhcmask modules +#ln -fns /home/sterbini/test_new_mask/v0.5/lhcerrors errors diff --git a/python_examples/hl_lhc_collisions_flat_python/optics_specific_tools.py b/python_examples/hl_lhc_collisions_flat_python/optics_specific_tools.py new file mode 100644 index 0000000..aa29ee5 --- /dev/null +++ b/python_examples/hl_lhc_collisions_flat_python/optics_specific_tools.py @@ -0,0 +1,250 @@ +import numpy as np + +import pymask as pm + +# The parts marked by (*) in following need to be +# adapted according to knob definitions + +def build_sequence(mad, beam, configuration): + + optics_version = configuration['optics_version'] + + # Select beam + mad.input(f'mylhcbeam = {beam}') + + # Make link to optics toolkit + pm.make_links({'optics_toolkit': + f'optics_repository/hllhc{optics_version.replace(".", "")}/toolkit'}, + force=True) + + mad.input(''' + + ! Specify machine version + ver_lhc_run = 0; + ''' + f'''ver_hllhc_optics = {optics_version};''' + f''' + ! Get the toolkit + call,file= + "optics_toolkit/macro.madx"; + ''') + _redefine_crossing_save_disable_restore(mad) + mad.input(''' + ! Build sequence + option, -echo,-warn,-info; + if (mylhcbeam==4){ + call,file="optics_repository/runIII/lhcb4.seq"; + } else { + call,file="optics_repository/runIII/lhc.seq"; + }; + option, -echo, warn,-info; + ''' + f''' + !Install HL-LHC + call, file= + "optics_repository/hllhc{optics_version.replace(".", "")}/hllhc_sequence.madx"; + ''' + ''' + ! Slice nominal sequence + exec, myslice; + + ! Install placeholder elements for errors (set to zero) + call, file="errors/HL-LHC/install_MQXF_fringenl.madx"; ! adding fringe place holder + call, file="errors/HL-LHC/install_MCBXFAB_errors.madx"; ! adding D1 corrector placeholders in IR1/5 (for errors) + call, file="errors/HL-LHC/install_MCBRD_errors.madx"; ! adding D2 corrector placeholders in IR1/5 (for errors) + call, file="errors/HL-LHC/install_NLC_errors.madx"; ! adding non-linear corrector placeholders in IR1/5 (for errors) + + !Cycling w.r.t. to IP3 (mandatory to find closed orbit in collision in the presence of errors) + if (mylhcbeam<3){ + seqedit, sequence=lhcb1; flatten; cycle, start=IP3; flatten; endedit; + }; + seqedit, sequence=lhcb2; flatten; cycle, start=IP3; flatten; endedit; + ''') + +def apply_optics(mad, optics_file): + mad.call(optics_file) + + +def set_optics_specific_knobs(mad, knob_settings, mode=None): + + # Copy knob settings to mad variable space + mad.set_variables_from_dict(params=knob_settings) + + # A check + if mad.globals.nrj < 500: + assert knob_settings['on_disp'] == 0 + + # A knob redefinition + mad.input('on_alice := on_alice_normalized * 7000./nrj;') + mad.input('on_lhcb := on_lhcb_normalized * 7000./nrj;') + + +def twiss_and_check(mad, sequences_to_check, twiss_fname, + tol_beta=1e-3, tol_sep=1e-6, save_twiss_files=True, + check_betas_at_ips=True, check_separations_at_ips=True): + + var_dict = mad.get_variables_dicts() + twiss_dfs = {} + summ_dfs = {} + for ss in sequences_to_check: + mad.use(ss) + mad.twiss() + tdf = mad.get_twiss_df('twiss') + twiss_dfs[ss] = tdf + sdf = mad.get_summ_df('summ') + summ_dfs[ss] = sdf + + if save_twiss_files: + for ss in sequences_to_check: + tt = twiss_dfs[ss] + if twiss_fname is not None: + tt.to_parquet(twiss_fname + f'_seq_{ss}.parquet') + + if check_betas_at_ips: + for ss in sequences_to_check: + tt = twiss_dfs[ss] + _check_beta_at_ips_against_madvars(beam=ss[-1], + twiss_df=tt, + variable_dicts=var_dict, + tol=tol_beta) + print('IP beta test against knobs passed!') + + if check_separations_at_ips: + twiss_df_b1 = twiss_dfs['lhcb1'] + twiss_df_b2 = twiss_dfs['lhcb2'] + _check_separations_at_ips_against_madvars(twiss_df_b1, twiss_df_b2, + var_dict, tol=tol_sep) + print('IP separation test against knobs passed!') + + other_data = {} + other_data.update(var_dict) + other_data['summ_dfs'] = summ_dfs + + return twiss_dfs, other_data + +def lumi_control(mad, twiss_dfs, configuration, knob_names): + from scipy.optimize import least_squares + + # Leveling in IP8 + sep_plane_ip8 = configuration['sep_plane_ip8'] + sep_knobname_ip8 = knob_names['sepknob_ip8_mm'] + + L_target_ip8 = configuration['lumi_ip8'] + def function_to_minimize_ip8(sep8_m): + my_dict_IP8=pm.get_luminosity_dict( + mad, twiss_dfs, 'ip8', configuration['nco_IP8']) + my_dict_IP8[sep_plane_ip8 + '_1']=np.abs(sep8_m) + my_dict_IP8[sep_plane_ip8 + '_2']=-np.abs(sep8_m) + return np.abs(pm.luminosity(**my_dict_IP8) - L_target_ip8) + + def function_to_minimize_ip8_generalized(sep8_r): + my_dict_IP8=pm.get_luminosity_dict( + mad, twiss_dfs, 'ip8', configuration['nco_IP8']) + theta_1 = np.arctan2(my_dict_IP8['py_1'], my_dict_IP8['px_1']) + theta_2 = np.arctan2(my_dict_IP8['py_2'], my_dict_IP8['px_2']) + theta = theta_1 + np.pi/2.0 + + my_dict_IP8['x_1']=np.abs(sep8_r)* np.cos(theta) + my_dict_IP8['x_2']=-np.abs(sep8_r)*np.cos(theta) + my_dict_IP8['y_1']=np.abs(sep8_r)*np.sin(theta) + my_dict_IP8['y_2']=-np.abs(sep8_r)*np.sin(theta) + return np.abs(pm.luminosity(**my_dict_IP8) - L_target_ip8) + + my_dict_IP8=pm.get_luminosity_dict( + mad, twiss_dfs, 'ip8', configuration['nco_IP8']) + theta_1 = np.arctan2(my_dict_IP8['py_1'], my_dict_IP8['px_1']) + theta_2 = np.arctan2(my_dict_IP8['py_2'], my_dict_IP8['px_2']) + theta = theta_1 + np.pi/2.0 + + sigma_sep_b1_ip8=np.sqrt(twiss_dfs['lhcb1'].loc['ip8:1']['bet'+sep_plane_ip8] + * mad.sequence.lhcb1.beam['e'+sep_plane_ip8]) + optres_ip8=least_squares(function_to_minimize_ip8_generalized, sigma_sep_b1_ip8) + mad.globals['on_sep8h'] = (optres_ip8['x'][0])*np.cos(theta)*1e3 + mad.globals['on_sep8v'] = (optres_ip8['x'][0])*np.sin(theta)*1e3 + ##optres_ip8=least_squares(function_to_minimize_ip8, sigma_sep_b1_ip8) + + ##mad.globals[sep_knobname_ip8] = (np.sign(mad.globals[sep_knobname_ip8]) + ## * np.abs(optres_ip8['x'][0])*1e3) + + # Halo collision in IP2 + sep_plane_ip2 = configuration['sep_plane_ip2'] + sep_knobname_ip2 = knob_names['sepknob_ip2_mm'] + sigma_sep_b1_ip2=np.sqrt( + twiss_dfs['lhcb1'].loc['ip2:1']['bet'+sep_plane_ip2] + * mad.sequence.lhcb1.beam['e'+sep_plane_ip2]) + mad.globals[sep_knobname_ip2] = (np.sign(mad.globals[sep_knobname_ip2]) + * configuration['fullsep_in_sigmas_ip2']*sigma_sep_b1_ip2/2*1e3) + +def _redefine_crossing_save_disable_restore(mad): + + mad.input(''' + crossing_save: macro = { + on_x1_aux=on_x1;on_sep1_aux=on_sep1;on_a1_aux=on_a1;on_o1_aux=on_o1; + on_x2_aux=on_x2;on_sep2_aux=on_sep2;on_a2_aux=on_a2;on_o2_aux=on_o2; on_oe2_aux=on_oe2; + on_x5_aux=on_x5;on_sep5_aux=on_sep5;on_a5_aux=on_a5;on_o5_aux=on_o5; + on_x8_aux=on_x8;on_sep8_aux=on_sep8;on_a8_aux=on_a8;on_o8_aux=on_o8; + on_x8h_aux=on_x8h; + on_x8v_aux=on_x8v; + on_sep8h_aux=on_sep8h; + on_sep8v_aux=on_sep8v; + on_disp_aux=on_disp; + on_alice_aux=on_alice; + on_lhcb_aux=on_lhcb; + }; + + crossing_disable: macro={ + on_x1=0;on_sep1=0;on_a1=0;on_o1=0; + on_x2=0;on_sep2=0;on_a2=0;on_o2=0;on_oe2=0; + on_x5=0;on_sep5=0;on_a5=0;on_o5=0; + on_x8=0;on_sep8=0;on_a8=0;on_o8=0; + on_x8h=0; + on_x8v=0; + on_sep8h=0; + on_sep8v=0; + on_disp=0; + on_alice=0; on_lhcb=0; + }; + + crossing_restore: macro={ + on_x1=on_x1_aux;on_sep1=on_sep1_aux;on_a1=on_a1_aux;on_o1=on_o1_aux; + on_x2=on_x2_aux;on_sep2=on_sep2_aux;on_a2=on_a2_aux;on_o2=on_o2_aux; on_oe2=on_oe2_aux; + on_x5=on_x5_aux;on_sep5=on_sep5_aux;on_a5=on_a5_aux;on_o5=on_o5_aux; + on_x8=on_x8_aux;on_sep8=on_sep8_aux;on_a8=on_a8_aux;on_o8=on_o8_aux; + on_x8h=on_x8h_aux; + on_x8v=on_x8v_aux; + on_sep8h=on_sep8h_aux; + on_sep8v=on_sep8v_aux; + on_disp=on_disp_aux; + on_alice=on_alice_aux; on_lhcb=on_lhcb_aux; + }; + ''') + +def _check_beta_at_ips_against_madvars(beam, twiss_df, variable_dicts, tol): + twiss_value_checks=[] + for iip, ip in enumerate([1,2,5,8]): + for plane in ['x', 'y']: + # (*) Adapet based on knob definitions + twiss_value_checks.append({ + 'element_name': f'ip{ip}:1', + 'keyword': f'bet{plane}', + 'varname': f'bet{plane}ip{ip}b{beam}', + 'tol': tol[iip]}) + + pm.check_twiss_against_madvars(twiss_value_checks, twiss_df, variable_dicts) + +def _check_separations_at_ips_against_madvars(twiss_df_b1, twiss_df_b2, + variables_dict, tol): + + separations_to_check = [] + for iip, ip in enumerate([1,2,5,8]): + for plane in ['x', 'y']: + # (*) Adapet based on knob definitions + separations_to_check.append({ + 'element_name': f'ip{ip}:1', + 'scale_factor': -2*1e-3, + 'plane': plane, + # knobs like on_sep1h, onsep8v etc + 'varname': f'on_sep{ip}'+{'x':'h', 'y':'v'}[plane], + 'tol': tol[iip]}) + pm.check_separations_against_madvars(separations_to_check, + twiss_df_b1, twiss_df_b2, variables_dict) From b1c6946869dbc66d0d2fa25f83550b7ad458dfeb Mon Sep 17 00:00:00 2001 From: Sofia Kostoglou Date: Mon, 11 Jul 2022 17:52:27 +0200 Subject: [PATCH 2/4] flat optics example with vertical external crossing angle in IP8 --- python_examples/hl_lhc_collisions_flat_python/000_pymask.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_examples/hl_lhc_collisions_flat_python/000_pymask.py b/python_examples/hl_lhc_collisions_flat_python/000_pymask.py index cb2f307..00b2ee3 100644 --- a/python_examples/hl_lhc_collisions_flat_python/000_pymask.py +++ b/python_examples/hl_lhc_collisions_flat_python/000_pymask.py @@ -255,7 +255,7 @@ else: print('Luminosity computation requires two beams') -qqqqqqqqqqqqqqqqqqqq + ##################### # Force on_disp = 0 # ##################### From 998fe41e651121c4340950fd13980fb59a45d7d8 Mon Sep 17 00:00:00 2001 From: Sofia Kostoglou Date: Mon, 11 Jul 2022 18:11:40 +0200 Subject: [PATCH 3/4] delected duplicate folders --- .../checks_and_doc/crab_crossing.pptx | Bin 72915 -> 0 bytes .../checks_and_doc/footprint.py | 72 ---- .../fort_parts/fort_beginning.3 | 33 -- .../checks_and_doc/fort_parts/fort_end.3 | 57 ---- .../checks_and_doc/helpers.py | 222 ------------- .../checks_and_doc/install_miniconda.sh | 22 -- .../checks_and_doc/t000_plots_from_twiss.py | 38 --- .../t001_check_b4_against_b2.py | 44 --- .../checks_and_doc/t003_fc_to_fort.py | 31 -- .../t004_check_output_consistency.py | 312 ------------------ .../checks_and_doc/t005_check_crabs.py | 242 -------------- .../t006_calc_and_plot_footprint.py | 113 ------- .../t007_check_orbit_and_lin_normal_form.py | 59 ---- .../t008_check_against_sixtrack.py | 51 --- .../checks_and_doc/tests.md | 272 --------------- .../customization.bash.example | 5 - 16 files changed, 1573 deletions(-) delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/crab_crossing.pptx delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py delete mode 100644 python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md delete mode 100644 python_examples/hl_lhc_collisions_flat_python/customization.bash.example diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/crab_crossing.pptx b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/crab_crossing.pptx deleted file mode 100644 index 3efe63a921be43069db79e5d0efbb91be8807394..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72915 zcmeFZV{|QTx2_w`*tTukwr$(aOfq9TnX&C;#1Z-&%X^bN07(JHOAn>qk`^ zt;T58?ix?;PhGt~cgafugCGO^000L703ZPP;xAr$0|Wp#Km-6l0ssfn60);(HnDZq zQ}(boanhl6x3R|00|6q>0RZ~`{{P+n!FQlPWx{ri9(w2s_z^a^N~oe>t<^PjPN*&O zQ9=I#xP&#qJb^^Ou=emmY)xVZRboRH!I^c0ue%n?$YXaWsir2a}ViDib;-? zl0*mj^2Nl`48|BYqz;4$GqN>smd^NQXX>Uk^>bui1Kl#>jL+eEvQ$aXR1~NTk<*@~ zpK&W_Q{FofYPa0ro{}zRRE|Sn)Q@zTYJFN&$U3^G`!=E%8-t$OvB^IpD)bs`Ko2G2gob0&xQ4$I2_o+(~;P+HBoO8V<*O-D-HI~Li76r zl_It{?%`J6cSpY;bJAY4ljKt!Rg}ax(aINyn5u*3+a$34Z^C_i&~Poz-Uj!Vjd?E6 z7;G$d?c0O5i8*@S_CfICk-Vg3?b9;0l^p%+_CbzDAV~M_!B6Z4B+{%Dut2(~)Ohxe zp=RfB4J`?)Gym|4GS~%$fG;9`iW0cP;}Y^)KIgmz+lf#yr6{aZJO^QciegH8K7KI{aQrclcF;_xFJhmNMOVsrB#Wew6+#|FE*60_3ZG*RVi>T< z?@b|OQT3Cju)Eb&f$3@oE2s2mY{*I;>^Yme*pyE@jw2zkC(o|Vfy0pCboZI&o>G>- z7@1fTD_i0F9`0XXzyR|9kDOut?3@eyoild7Qv%d?&d_r-v38=P{cHb!bHzV6LHsY$ zE8^EAff?X~uRym5#=Y6rC<$k+?SzjrhhG7V7~8Nmu*HjJJ3Df>4Gd`fJI2TP@V1CG z>>h2AuM%rvbbD&c1Y(zZ)VHYIZShc>@EP;j*nh}D!X_HbY3o*Gn+wwldDJjO2;OM) zbYou?jC^n+#M3!vTkhPSZVWs*o=A_aOERu_BIf3T!2*2WKaT+oY zle>u5hn7l{HJYH!7@mfqq~zd9ao{1T5CmkPX=bX0hGhP{F(1PCU9J0oS^6bo{^FK9 z?V>v8J<`^F(}-E9CMVfCH z71-Bj7^;A{uMrde_;7*zWqCpBBwead+Fi9u63?)dM`*91ccLC zL-Ea^$8FzJ(tE_I9*kf$(TGxl^TSb3s`8HI06W`mgxX-!U*h3~qf=Znf3 zDZR0#$E6(_F=tn*1lEhhzm{`tGINdSr{cUm5pvz_lW5t4%xI~LqHBLpbiLITMo}Z= zq-joU+VK8c=|Jg-_*!n; zY-Azs%|22@8OS32@Ayf{KbhtNNel`l2?U3rQZ#oZh^u%FR29~yh*4q|cwJs4X=)Jhr^;Szvi$o${{NeL z#(%VW)_=5mhJUnrrhl}0wtuvG_J4qSg8z21XZR;1{r`jfe?s2-Kgg&52Mm7v|90?m z{Qp$FNa={T9v}b!B_sd<9soGtU(fmfdce1LG;#VatQk04*xCM1@!z}*_+4B3{=q+c zYfqYy?4w8iix+eEHw!NsZ4*q|5Dd`#kG*`Cvl<3iAtW*`FJRIW8^;6C2$EmYGrYXW zAav}y)<`-1SsRcei4#(U@f|hh^NjaxzTBIL2{pzHC6rKru4bhEWw*VM zg}F2fTZCpvoa>dtiRy>XSWjkAk{_3Yu4|&+)EMG`t1F01m;yO3e3-DS-nUejROTkq z3SM|!wHt;7+DjEQt1nvI5FsDV9vguo z$^ZqzFo3V>ho5B5*Iouc>yN>Q*I`ee|C9H48_wIB7_2~Try|L+F@~5rHd&Wf@CU6V9k=y7x&;afRAh7=ukY0(8Mo~LB|sTS$icY!dmhf; znk=|1Fm^-1$qUMf`rvu}IMktLWVMr%t>*cCM6`qp8uKdr1bJ)GnhdXHg6(Rs+;w+Z zYe>7F>#CpU7RqR6aA?{@X}jQh;TJuwT{doo>L00Q&YRff!unt|U8k_a{1JOdaJIW; zr?pne$vR7ZD0p|kUg@X)h``mjU9^c z`m25y4oU^XhcyOwY-;Se)+^hFps$t*QY{weVXgzD{&`1%pq;O^Yre$?c7N%=KE_&*s>>beA#a1tLXNLsZd zQa_4_N#;6JLD3Suq=w&xr*L{1Usz-+X{(7u{qd16jId>JVwJBVZG7`=shKUew53v) zbmhZj>=o4Lh44oM^8cB;k0v-G$Bv5(`!w8_10idk$F2ea=f#ZNN5*}_!H@n-P97}h zyt@}tnhos>s|=m943r3(I_U#YP=d}s0wE7?9QFwb?Td^8Ox^%csKA~vBS!f{-W90e z7&_!WE*y}#5rD*j6MI&S@|AS0%dioNgVjxo!;w?$4r{CJMLshDuN>>9x~eJ`+R=RTy?LpV#bt!Nn94zAcJSWMR%#30%A!(B zfNz=2?v1Ny{NTF0&S@%r@xQ5WN>sDZv;P19VELZCQU4=8{-aj&_u*d;Zs!_Wc9=L-}hvehml{S=~j9@s(ZSHC)kk^zGO2$FT{A>ktFjxMj3 z%?9l@X-ttP!b+qjU!&c2IYVnQ&`8qnGwIm+WKVN64OR6=F;2Lo6uGW8KVOE@%hcw8 z0NKiaHeX2>R(NjMX3C8~FOjtqLLUxlkJkiNr8q%tnk|1fZ9b?uEmp-n#%b&K$xWp=P*KxKLW*_zI~;=fKl?J6G}7R+0R zK0KT8dbI$v;q6!!*swu%W=r({4aHg(=?%QF?n-HyfhplPHlmydXcoC24~1NX-B9s_ zy#cRr^~fk&yZqGfqI=vTQFCA)UtoYpGKj!8jJS2DdAY4iV)>GFjiDITDI_I|%@Tct zXJsLdg~%d}(F$ymiq(=S@Ot{ zoRr9T(RqWOPzDu*QYj-4Uva;~4Tj8mX<;eC@}rPS)as5vRH{0k9k)-B zC)i^km&K{kH0yU{gbvWYVd2U&)>YKf223bVV8b)OVqUQ!Oq7Mj$AWOXfKJj&>eJ7> zNf_W?>|P=pQn|8ru3(K9H3Azyd+ex?xs9Gg{eh9a*d%x9Z2)N<^T!{I0@%$-I)B__ zI{2U$YM^5DgDPMK-Ynwc71=^J*AvcCL(g7(_{7BWX#KLucjP0MK#G@xw=6I@KFQp1 z*ALjTdIZk8l4B7h$Uh5MdDM5c(AAXrgYm$|<|*XE_mP#|J7iM&i(K!19vQ74S}Z-7 z4I2#cB@}1djEqsosxy07MUHmT{g1Mj-VOu+N<3^cWNiTbSTXkLfHq_Rh2~hC;e4j# z7a&*>Qlo&dxjg#voDD=wWgY9>^wTagE7)olv$a~J9QQd8U6_w~UOTD+SM2c%SdKaBM+-?C6iQt3 z#n#M))eyta7piz4l=w8w?G!``^JH<)Vo%ku{T6w+fgRJJ{Q&`V@@q?XT75JE{ z*mmY{?XO~G@t!ivc@o8%D$JOwIM>dUbC;bZWvNG)=OI<8H{CkD^1?5x24w8lGGy+T zy4e7Xhc{&2slIosojNGx5MDe`y0;H<`#N@+3xkKYME>d&Pe+IXqdBk55SGB88X9M+ z(ayW66%Ew4fcpxk!=OG{_bigkIRk8$U$rtB4x zzd}YEAW@PLUOI(_k`CmXDbEG&IcS(U+TEN~)h%m^_(5~sS7wd<=Zm-tNVs()B&LSI zLjol9MHvyGISh4o%%&M zC~}PfRDGy=u<6ULv@zTca7j&N5gJi6wyad z{|lG_u7|e9=E2*X3D%wT$9aEfhW-}nJai$!IKdFHi%slT=SDkolPIRe_^=T$Ca$;Y zFBKgXosIX;kTnZK9Wf6CvlDyY&2B%G!hCD&;^e*_yKEqQFja-%6L#rZW=z=2Jj3h0 zwXUy=w-t-HNSaJ88ri-beZ{1@xr78m=CC4z8`5-0OPPB-XXUdJSBZ))dMo!Gz7z ztf~yM4BbGwQd;52?M6RG0y9^7y_YFWS`dQR(S08=I3WB9!{uAtPU% zfh}}V1aZoT%{K*0c9hsKMFm*op08&yntEZIY2;9hnmhAA=ppOC+g(4_`L^bP%4|F=4$GHen~HT|_1bs&tfI_TAa~w;L0TFJnyK zJ!4o}V$pm;=XyHcMJJ1Rspc-k#L9xp83VYBfkr~&ebDW`yoh5P#xosJ)9tH z)XVE7|A^X=gpO+3W!6cR1ZoG=QUc+RBa{VRwMW&KCc->Q&P*G_2yiMLpvhQ3sWAQe ztxv;%3x9n_BPK~Hxu3L&L<(`Rra>dg92HTy{)q3U;*qdIT#=1wQ;c-)a;x&VmL24_ zr`R5)SEiO=kG&AH;?=i?t%dFZ24Qb5Dtl?AYqGy$;c;fX$CYrU{bdJPb@F zEtWuy2Pd}<*BR9T!IR`YGyVc3WBtA}cTsFbdpC6@XPV4+VFL=3)7 zRu^#{HcF|B`FfE=E{e*jhV{^cC+hqtl4AMXVr`x2gtLpVwjKPEB`jB|lA%Oq8S#=+ znG!T(6p>;C!$7Eh<@$P+!g#5YIOC5S%tGS;&IVDM6^-=xh}A^*cGd>n()IbE>U=ep z#@n$|m2~LaeBB4j2g(K;(sKAOYgs>!=+hX_dc4D!)0x)az|Anqry2qxIlGFiu#+x^ ztuwx^2-9NH4+4Oz+zW1LQ)E2NvaVJ)S+snh@DPRCfGM>Uv5Bwz4m$utd&4~$232uAcwa;ymrs(C~C#6w+JR0R_jfYo&!c*4Gqn0Lx(Bbt5! z@&mvSJteN>Q%QV$t56k@b7`Dav%>vR@aXL_CzG>l{B&^|lWvkxtoDMxF~PyW+fCzh z&EHD+0O2+Svuy_9E~Nkc>yboLDL$ZrtYN#&7v;etZG5DKxYlIq7L{e>!2au;I!hXUxz?`aZ#m5U?4jI+tEU#%6ov%tPUYhLpn|CYdcks0FQ8P z%=)i*i+RzMVD^F(HNXRY0R?g#A+Tn*+k@yPg49FC3jQe?Q0_D}T-A*W+#1I`$BKG7 z#TpQU$vguER5*pidHk)@Kjal466;ZN2xGIlBLzmf9O*lKp&JAh$w^OB+JDv)PH^3# zM|~yhwX9O_jfD3_-7oA6)<}7gpu>0ib`vB5V|n88cgnHHgHXDrEP&=AGwU{{+0?U} zQ3>jjui8)e;n;%B&}2M*5*kkbW+X{cWZ``u!6I@`pS?mHmo5x z_bxsYHQwSlz;`nhMh%%REqbCc5y#ix_0J4=-8a#^Mxn~Y<+ojkjHtGxuyM3U$MVfD z?gklB0Xr0)QtEURm^g1k*5GzQG? zEF{=1pVXEQT@e&Rod8X;W>d^A!82Pbjqvyb?4$r#YtPI+lK6%~ZQu%^J9Jge1v!jF zvArHUzqn@7Zfr)$4o76&Civ63*bITL58?Vo3g!eN0R(f$I1X|P!yz3I<13aKVNI-$ zu^!KSUnbbey*L6RnWVcs3uu#UzAxI5-_Jw*P@LVZX(;aXTe2xuWiP}~b%{~D15tJ5bSb(QHVnBecI|Rn z#UQw596hBSwFR)Xd35sT6^ATx_4*)PkYC{1|Uy4Z&s81Tbmm~nr{P2Ck84~>{A}mGk^vfIdNMz z3`(l)4YDiBckc4!eYroyeK>tB)S`rmy;0t1FD>E|fs5}mZaanhLb?NJ??8yri?yE8 zi-{vF>$dJiStKgwZ4ncMB^v;TW$o3y@`u!~wMEf|NeO-?g83{Tb;jBxbkZr$%@UPbOpSvIh?_L?^ZXtJ2aTG? zmksl#uSSqdk=oub4PHkoV3V%Z;*t3R() zgDto}Vli^GG;s9>PwaE*Ow|e<3=$J|wgW)7_(RdFeF#Vwe|n366ruXs!4rjRx@i8w zPR4U4iYj23OtJ!|TcdA2vDHW!JnLL_<=LDp7_^bX-{c?22leAAtF3e`u8|kd5thb;#`)0+m|ArO+UP^JU zb7Hq9iu%>r{RtPyi`>>N%l))dsd#M-L~J8p-0h=9k7$FCD;`{c^E2Y}imPv|l|~Qp zj2w_U-l^-Q!)TKm_SgG~XO_B}g2JE%CvL8qy|Ks%ar)rNfcyR15WsMpimCx8OagV( zV19ZD_Q(C})vq@OKAMEmLaE{d3wGRzGDK$jXOuwBNH{PPB|M7gx{@c zt@nz_x@U1Rlkuf5GZ*V|>Uiw{&WFkW^Wgo>3Ld8CJECva5ltTHfip z3ut6i19Yup*Y~gODd~DVtY@Cwv;AERzvRa2j$3Oy1;6(T1OwADCKT%9K^q43}JNx7D7udTR9V&EN_6 z;v9k@gS=F}Oj8eN6@MIF+M&{Tp5(YX!BksyN`Hek=+A8Q|KAa6@ z>Doqll)LmD;(4sar4I#Im7kTi@g)`)O~=}DqYXQ8Fg8I6VTYYn9h^-gE>&yiP`zzX zjW1S^S=m;E>&5nZdA1xG2`_lZO|)4I@FHaDT+g{ zh~|3iH=*_0Sx0TyVnjWTw$Zc}wO=KjX_H4gL@@~?ARCK>K@pVd=k;KaO6t!?SwpSH zvq!n^jlGs(DgEd;6Q`o(eGrBQngGFROs-9RZLi5-d5MQ|x-eNlu!`x$-12MCEaU^R z!Yag4gQQ}Xo)?g`hsRcUQZ6ZndDf9anC-bdB5WI{3B>zDx)S;~BO*A)n|HMMvsOfD z6{=hHAPSop_0Upl{>_M0e=}mx)h>{!7L67vlZjLU49Lk6LzktbR>|VKOK?)c21kpHM`qmJ-0A8_5nItZ*+)5nrgB(124PGEedc(rA$OXx zNI+>p#iVr4D{1xDF`|KE9IbKq>`6!s_^&w%3G;TAZ${*^dW&7EbcH_01lD%?X9y0UwT#Oda<6EvcO=Si6_ zYYV2+C?34Z#bFz60_R&-HC@)M<<-RcVSYGd9@Hdrkf@HAYC7^1atz?lu@gq2D@B4Y zj!bNz4)}8f5gWnV(MvX_C}EAqcT`feSj&XHX(^>gjoJu3v%0FZsru-)S+4oNo2zxG zw@F^i8nf(Xk5kkPEA%nM;D)^9d(>ON0bT3qYH|h6;iTueG z!mc`ePFTs==@~~wF@LDjNBB*M>7HvE1=9e_1B4c)v77f*ea~s6@0x)x?un*pYpC^x zQCaJq70xI&6EdF@khMPA6~}^Bm}m<&_W}>EtIBim%?xyn$0HXxOzC7}C4DdfuyC&O zSeIFXtk>R$fNRC@?C`6;R3|T;a5|yO0{S8JM$J0>_S6e$LXScDRbJeup2Z$9t%L2j zxTo5rHNL_?*^Xr-xG4OTct8U+m34}KEl%+B2Ei1kkMkr*)}Sup3?*%^jt0eGm?(c5 z|I|8_r-`WM7N5z!b>cvW9VCy{xQkLR2Hp1;xnvA`urrMLroR+ZV`zA2 z%a{tV#~}6Pc|OHVB~vq11-BFUmgczk=_&&E`ET| zRm{WT2m7Ulz48V1zw4Zk(9em#^DJRi4zgI1#Vm8gr#4EyAHK zbkQ^mDB}|RDrrf_X80vO{&n?7*viT=^dSfLJ=S`s`6nisH@S#i1~dyu#tIM^E;K-X z7C^KN$FwUT2z2YX2<(IP6@3I#6l1AoHb5@?tV%Y(fHpeXrW8wpTFRk7^I0E)=lYD_ zZZ!Q4|0uD(AO9;HOg#@Zw{OsOp)*fOtQ-8g!*JjYh*wv0e$0>#p8ND()r|<8{3e6o z1En~dHMb&X9IzMx1o;!Te{;Wj3z%M{0%?C4Q#fM(c-6d=-$wE4MKu^y2bW&&X~(se z9(=CQ!`K>NUnBu^>@T)2sd-!1N6yRE39r200^~`5>zvTh6K{M{ z0GBh*@*28K_V)hemW5{j^nz`Z@&%wX!* z`y&QCdTBC_vsLf0!3Flp$G|Gr$GRlfNAgA99s?kT0z^MYC9JaEyYIIQL_wmwEBadz z57oqWrjRJQt_NVUIU~6f>wwh8f|r#{FL6t;!6$RX^0uDpHBR-=WlOj>W*VP8sA+E; zU-3%u?CeEX6VG+_+z{Q+qW`aD`G8-c*T5wg5;vvM2I+5aw%ojm z+Nn#lB6V1kp^aq>KfgEmcru;1vpSp#u z*LB(PX`a_A_u=~nKM>cTIB|gX6VS#UD&tYqfFy#-D;ncjmrKD%PXH=D=%VSw?C*#R zy!dZzy$!9w9cRagZE8kxVOd6UiK0uKL-^VZ0Se1Q5G(Uu=Rnq=fn$XBoomXIaz6do zIYiI?EsyO_eg^yK0U-dIZZup*nBlc}=WKZa3EM0eW#=m(^nyGDa?`LZ&N{y=PD<>= za{_D-EiqT4%J0YrFxoSGS6B|@p0f-n&=6}4Lb(T?#y)Z|RUj@g0oJ1HYHh56vZ91v z6BBtJ6M}7tUvPy;BV+);?$Qhq=e5C^Mto!WkTG0VR!UD7||S{q!^?Ic#>a#GuREsqOG5;c@#ls=(kTZUe)cnmGZuZ z_AeXc9#l=2b)w#RkRroXY2}Tk%cdc@H17uWM$9Pr>w2PjdOucLyUo7dUsA)zg(|lP!QTdapq39bBsT<~jJSdK;RF6L^na2v>l&e;nKi#b#7RzIJY;D(x zsmE@};mS3hj?ZtEkO-lZTkH56B@pF!-(2P|@_%9!-Mo97`r4X^cp((;#nGnesG3q} zObq5fp?ZN57`A@~rKn%sqadVc0(1&Sl3TKPd{}6AEM+n~4pMtoL$_P^ZJIW#Sa4Pk zS*}fZan=23!try5LUFSEoE^6MqoJehGwY-Mlvg|2{^72|)XmFMQMQX#Y1%r|&{Tcb z{(99gvcjh!)1e`&(|m$acjmC8D)Q5q(`K^qhi8XMwzW<81G0@IP3+pbk2P-7u{vOw zJ7=s{)I%gTG*QkEFas!jz#k;wBe0~#VA7YOAzlESr!g|v=r~Wn-F3ug^MtLMnU}|s zf8k+Hf@qUdmo&G|f=FfiTS$?tPOF7eiz?Aw6Z36^tfxMaR0F2EsNQ3)Q2Fh+O8E~N zjY{#aZ>P!s?#lZR^{yoOUeQGu@b5c`e;aJhbu8@Gn=w9hbG`&p;380Ij5b5s5(JNI zeT~`F&Cotn$(d@!LPZscSN^muf9Wa?x!cB6hb<}qxjV;tjU-R~jF0d9dV1jgQx@aNff*&nroi#~bk_BG@b+FWU6fRnnN+;v z&Rkyhe!pLkF`nn4-Z^?pzVI|zXNar%THnl`in@cgamvln1Am*~fnJ>7=;I*%asJMo zC9KQZk|AcXz^o`Np?;E;T*pq=wspviI3g zRl`-mSdW@bYR+8IBuGTm25c)yWocoI5I4pL4(OboF9&~G%vUf3Xs8$xBt;xAqI)3l z%fOPzhm#^N?ZXb0UeL?v^zf!&c=Wv8lV#+P<_9PFiH1-;X#C0Z6*?z4Aua&jHH%A# z<2HLN5Za3{1sQ)U->>?N8ShJyqzHW#St5B~5RERYrJ_X?jog_#7P^TGa0;wV<2~-mjYthCxxwE>N3lia zbIO!43CqtU4Me#Bc8OD@zKdZQ$TLdm!TeLq1`st#6@!fDlld}R$EfJC(7t=Zu+{jY zg#_F6*QQp9yx>*3nU<`qGuwBo`rZ-YF$1w`S{unstF(yl>&tMPm$ZPRqcn)8()JPs zrODZ~(L45ua2uHQ+7gr+6A z`ez6*!u?h9YlL^r+sD1Mb_)*w%4UNi^gUyh&4O6;+R9U>*d{b$J8PkJThJ)lad$13 zzQ`;7b;QYLdF(w*vrt0v;WQ33LB$^704C|g?h>pO*m@EN%!{Gq2N9%iMoj;U5ouG< zK{UZRt*eolE<6l4tVT)D4wuKu0RLvhfQHqAZ${K=_=^$e<^Ef+<;Ye%>i#9x+{=a6L6XBP$ zFSH?yZJH|xi-|OHjr7I{Gw&4~Ho2>}OQ4O6d$6Eg>rMp)dBT?%JO=UdoqF?m!gtN< zR5ossYC3EqW(H0R?p#nJTgY3;T|*Jq28!WNBU0X0sIvKoj9QoOYDO{mcsYR4G{Yk_ z2QmVaa^+TJ>6W;;+#?ZSovLmo=mJwQx zHEbkm9TX0~>@3@qTCBkJhN>-%4vrqmT|xH)^d1a9C>y5Y7eas47IfDNKMe7BVjqG% znXh32t%FfM6yy-fJ5a2F9I>)&c=Csk9|J0R+xxnvzXl!M3CL9?=xDVULn#0X3sR^7 z6j?(O7xKz$vv=RHJJOr=AMSStk@ta)l{{dgKgq&6(AH>(Fj6*YIA|_3D@sOdLF|Ob zuFkE|5Cmm?c_YS~{y3I7;taJz@1+w-i4n6X3bGj?U)&zK7?Wh1XK(-K&Xg;!YANM``!q;;5c_JdYCu>hTy7e?wV1a}t_o<9~H?7whj zBd=VTzEVhtsA9@TsKJjM4EGh_exah8C^xLg2h0SUKfHQ@HnoNNK#vW(OYR|blSa1X zW7($k<3*@H5lPo6>iMGoFeN?xLPorEt4t20b*WoZQswo?A2V_r76{NobFs3PQHvVy zZUkO-Wu&RRia*suUC*Ng8^*J`Bn3W<348hCNwTn!g8hR#A8nK1DB(93ah z2CHofh3!rc5a|iT&U(QA1`1Dq%!tM@U)QnM+>dFZ*x-l$4R{aYk%l;>E-m3`$4J!> z69M*3>cG0&(T~V(fe>Jdq<~N$v%k!9J`8pMsRQW>&jf_+qAx) zhyw>TG+%OEfem77bdN~X^&Q8h#x3fQGQ%^cIIH{|qP5P}z|G+JJHx+MC)mRqhor{_ z1g+~0&9F|tTZobqaY}Y#H;h@dwQHGhD}Ua*W(Nb-(+5y}LAOoxWFYp_SnB{?LP17Z zcCi0UK!Op|-eM19J0O3z5aDg{wpO#OgN5TKWuZq73GN!x1Tt8j(wFwymZJ1EPWw;38`AjZJN%}?|W^12?6KxUCjm=eFEBEO$5Sth9nn5Act>k`E}g8N3`(U zV^M~=d`Hu1u`M>FazyC#e5HvrpxO|BF% znYPmfL+=l+FLJJ~KdG4yE=*j>u^_a&-FLA-{z_ov12{HE7^EpbAm7A1LxHF^J1Tx! zbs{j84DRb%NUca{i_`Td-%!!6Uf}@NwC~1s$XzNk-VP4LcBnkq(5$q13kQ z7XqxYwu0)hHV}mvj& zLd@(sxtL#3a4YI2B;GnEzZJ<%+h~1xle+^F0Sghp;0Xm4w_pprvDY0aUv?At4`*h2 z&M?}u{$tlt{jj3WOy;e_>;}YlZEXO&WJascI?!!E3)*?jL!hxO?vcU6>u+zZtDy_F z;p_y(BnCN(_P*=i)!{Mq`RTrILwy$8Nqc6lxb9TyArUO)>!C$e1NsQyUu6Xa##I#% zb2lqZ+Ny$+r_gw5ON4_?zwXm^Ombed-s#av**ikUBUilGsT22Pal`iAdpu>ugFB0& zCOxr2MUw0RAp2HJeRz5*M__--K$6@5?eH^|=*1~|6X)q%_)6avKWMM#d^$Yik*|8+kIK_j($rB< zY`rPia%{pkQD??Ok6uN+6B~)9`hJAst;VofZ0e%!DP+jz-?2wgF$E}S1O*L>V(N$l zCCyl4dYj`;q<+Q!MU{SkQKfTb(u~=EQDu1Je^TZAyt%|K4Y8dB`ecSkcGI-_83%^O z;R^P|7j^35fYe>aY(33?Qe}m_rC+od5WnDwPeHx(7CD%EGhjQ%^*fvN|X4PP>+h_IF2LH_{i{q{yIp&ONo8Rc3s0 z0$BAF0XR3yy7KkQXxLZhH4U3MKBl#8Xa4*^ym7{VK|I6Yz!c?&f$dG=2ZEM_kH?W7 z#!Xp|f_(vSp3i3GWaPXA^EGR!yh5E>cy$(2(^N4#`RQ%t%=J`0J}09yKlsyOroPmQ zjaf~Lh4U9!T=sn>7@~&7UtLEpa6HWfvuI5w^HWQq*I(c3PyctWMBCeZo%(hQ*?c=9 z{@n`Izu#HWuRLSBhL7x>UH^-p+a|3f>VjQXxhZ_EIjz3(_pA>?u5mvvz6>?9Kfa+nQR zEI9Hx))hdfrZzh{P?Q!HyK_?0;<2k5Q0MUF7t&mXGwlUZIkX&B5Nv!7bmXOO_48b9 z;DZb>oeK=6_YSPCy&5bjJ$uUEq8T^9;_69O>DA6eO9;~3Wta&{84a*seP>fgDl{7# zbbdF-C9dMEX-ob=UYf=EwW2LGJzt~?Pd}Sl+ue=l&h|9MYK!y^*oD<-+^RD&Sw+fL zGm%U2Iq3szm9GU;wyAzmx$t@lx@Fnn-ou4X3+~f4Z@Q4cB};}m+rp5H<^@H6FU?EA zW8a!A1B*AtNGTe@C{VbPOH7TZOdStFna{2zyyu8JaFQUh=d3Aldis9TqG#_}ps=JI zPt`d~hH}-uEHs7gVZX~YcEKwA%}-Ar3WkRJvrL{3OVxy_0Af?flvH)_0^6hZ z1{wpL#FPyyq+YShj|+kFd&Miw(`BeP2UCl@hJM~3iMeYeATC4?oj3O@&?UhgLagd~ zN4Sq})H#Z;j#|s7Yh67&&|dIohdjf8rxNdIuzX!#@L=NQ$x3GO0}uzvJ3JU^GZ*jT zCNpZtZ}Q9lJ8j*BgY zq(cfcxMjzMjx7<{E&E=%IX+D)G+$Cf%cH2`TcngY+(-?E8r(M8mgiwE(f(}&bnIeM zhJKI!TgHDM0e`=~;#^b9X_Xz>XDjss4z~-5TCaOD(G?M~HPd2MS|Zy`S(5;Y1EI9<)sNDk1gJx{RowmrLma zRIZtdhDiqBmY^0W#nyjWf_Cc*Qh_@(Ji>oo3XksAZ_v&`r0(7Zq6G@9g+@>$T~iKT zNN;nYyGX8e9=WV4WiTy0qJ`WaxLuGBsLL`{N3y>%2dvJ_I)M8e$8`lfQA_mWN zSdn&sPR$Zb%-2!df)JGTn_h`9P*(-neloYA%4P|)YlrMm1P<7&{h%t+TaiXv#o8EB zLCBsIGc3#6PG`DmfqxHr-BNWirp=sJE-GJjzV(Q(=4@Ff+^9mXU;1_hIk8eldV8y^ zJ5!crU`qPk-3W7fPohhZpvg8n&%1UU9k0e7xYBWL6zsjNdLa{5V_CDWUkn@AL_yw^@++AZ_sep8nu(hMuH9L?a8PjZhd9*~s1vyN3N)9ZiM z%7!!xwwS_!=6ZqT(xm2@`fmqR&P1KUjf zi!(;Yhn1gvHF;OeZ-7&7N2s~6y$753p7fc{dYaNTr4LJLu0%e3Te#VEA%%rqbOO$E zAZGo_VrRi>JdehgfD~V|sZwfbS7x>jlt)M0S|vVP+kd%%9$O%5&-9aI+2Q+@|8fJ3 z=AM@5L4Lq_>T{Y}2?m-hsc+Lw3;xNg4Q%Lyt%M0$)TLNpav9eBeZEr8yt(v5i30*b zr&7COQC+cS&F~_Rw__Hz;EDNKg5)+1+eid+hI});LBqdstbtT$Afpu<`pzTIobah0 zeF+FEIC6=y;bVqfo5Uvmsv=(2+)a={UgAX9p~#bklq(DW1F~w!i4a*V?x+i#3^J)- zJ|RzE1RBm%M5~L(we{NSIqqZFTi2)lOsaK9>`ToD`J9U#GMbmelRjS;Zovcl5UZ>X zM)^%P)W4XA5VE}hX~?zClcu1!XPzk@x4y@7+Q@wBF~~9s;6(!2Ie*EEJn$3*97n)6 z%_#R!{kSQQ#~klM;K^L>LmzyAOD;G`W@1LZt_jD7xju$2H~&S^iTR$ZuHe8|WvJ|3Th62Fbc@>$+vzHOjVa+qP}nwr!5G?NOsJ%C>FWRk!9` z=iakd+<9W}-)BYssL0HU%!nR6Tl>DZxA%Dyov3be-=hlToVCn? zWaobf3t&7>?KS$^ztncHNA}GrBlwiw3%RhNQC@9`SpN~P9nt%m zBSbvZF;H~`%DUoW#3!sn))`VwWQK`(K%F+=0zyH82B0r$D$IX*6TMCiHO$BH(bvwr zWKPC0VOyafb=xoTJqr@z56S9g_WQH3=_obWn~mq&g%mZK0)?7E@KP@_6neliEI#DQ7ht}tgDsGPQ5O2VxjB{7|ciQPi#qoU#4HVqx+aiXN1%G28G zsHg&K_e%l?r`B{*b6GL7bc@bKkY`nL{PqM@jo+xLV} zAoVsS6neRAL)rWsEvaP2YHmrM2CRekp`HWnz1GB_reTE+W7RH7!;o zys(HR>|vI6=r(Z`Yu)H1IfUn&#g|5_K6w}U{6~t?6${>BiE`7<#fhtBP^tSv z1P*ms8I&uqvJW9NxBqM}dbDKh=K=))NMZTs*7Y}k<~&QwZjBYie+&Ga55iN9defQV z6fGvp8F-Pzd=;-(vWdpO5Jb`e2j=aHi;!eU0%g!48#X-|Tx$0LJC|;MC*k?BpEPMw z^O$jBVO*p%_Y4B{NTMQ@>FKRCDrwqfG_o>yOgkdOi%RmT|4q-&Lrq#LgI!)LMTyr* zO+`hTEQwJ!B08cIIeCaJ zLq5eu7XWc1eCFU1V|lH~jSRHHR{4XOjyAg5s_|EQ z&#Y`_uX}s!E+$MAmjz>{;h{qH#KgY9gNfHzX;#SODb?|zwJ9ykr_Bdf`P@K>qhS`t z*3UncW3df4r(H$&#TWVwa}?t33QHj=cDh@<%8|{II%=7vV=4#ZPCtdU4^|7bM6xR4 zP&Oj<=TEY>>B!QMrRUG0*}*thZDme6<_S|AwPw6} z43##`Th)6JdWvsbLQgo<+)^5q4>~FXEE_LYQd?KGs>!%Y{9*xrg#o&f15XN;4BB0U zvoV7Ngh{Z^UhB=uvyyWk;+u1{1*75kS-r?M^4(8Rl8&K(E(+IB zLPkTf6A*=>e7v84~phYa@5W|2Ca5O8HUPL)5MZTU3u0~=s`XXKfmo*?g|B&qU|v8 zOE7l7*^Xd-%_prORGx)Rt*E4Z+9HS^Te6l{+YO{nQAmTEXnbVp zZH@6yrSy^mtuEyq`JH!lx==Zj0g$3rOCY2vv>*g=b!98=BJ5Dq23!z= zP_T6(@jJbxwVH{VAYD529^ctcmUGKx~OxaX<{2ZtYmWUk!%iV>V+!;L>}0 zCdhc*@7kxZ#eoDJu)?q)v8e-m*7UreAKl=AfCu{MF(9!y1DfLa`vffywRdQM-6k z@x5QTgxKKEKZ_zr+Vd40l;;os6=Iux@|icCfoL(c;cxMqaU#d5XfESzX$5nf=kEG^ zA7&;B`IQ1nmRI-rd_NFT^2lswh=b#+lXocSn0c`e`{t#arvfA*E*9LxqUw%OPr1|~ zm*ps$ROD0Xm{4v)foPxQC?%S3Sd3Y)z^>m=wCT|@xZ1@+7GY4)Z)fmeH6GSSMQ}<+ zF`>fbF5pHdLDQw|>7QADViVoE#oyI-L+GEY?ccns^X$K??a+6%<$~~Zb0%;+>fiR5EQ8Vd4oy;5y5{gnc<&-Q%-sF)(IN#45qd*G<)joOw&?}Fg z=*F%Z8m^x$(L32{L{eAICp@jx*$Lve(|3IAf+Oz)7uNnmqJ0vCr-&KSUt$ z&^F3!l~?vjbViVkYM%>vMfxAINp^>A<`#)^;6Ia|S%iP+qHC&?hGxy%du%1LX>Ydd zvz~%QSBrYkEjg#Ad}M{s`4nZZmTCN!?@~uwCH{#LCNj#rjG>9jL+@NU+O;9M1g*W6 zy3v|%;;BESKXq4d6M6Xw8{l-rMiLa zU7Z_|ni1(0bE>`BiXn_ppfHdu*n&z}loLuwR&1kcoW^m&NH8<-@D8-{eYWEz8-9l) zBnzqGAlVj*j6p_23v{rzTIRzErVR&%oz8?mD~bKB8uum=Y>LTdo9f4> z+>c_XV|yo8nSLB8JSLgNS5G@|e0%EJ%-3Fc{2_g_(*SsBGW*eTH=8ZT3m!7}rpOo} zZp9JAt#q}1dv-8g9ap;YSrncsgWCE3dJ3p+Sa zZ(gc6^RdJ2%}p}W&5E7Pds&ye<>1x!g`2&>-4(ciDG&8Q5-|1Xy%Jhv5TAxvbHb<; zbkK2D4=dt!ny4;t&$lhIMDC_HvKX2a(m8>6L&fgq+*N$`G?&_3a0rk@!@CA&vlDmN zb7Bq~HVs3hm3cO@G&&CHp!T(+jQMv$4WjfIlZ9x2VZ(?OMs0>4LtC_-ss$PJr7#SSt2;o z3INSs9m@>pM7O3iMs1iPf*z+e{)v7Lt1-j?yw!55zIfx^{#6^VGMpP?O_bD(uXXwK z6jpV$9H2MUKuIq@+yk>x(@l20+gQK0NiFB zrlhNk=MipERh3gr$MR@SPQQrh_3Zhx9$9s8XRd0kpdGRbvmLt*vw!*md4G7~tOcg( zdAGKdH5fa5Gqm{oE9-)C(h&SQe1Co2vorJV3@r+(aMk=gQKvV`Bu#258&%>XX|cFD=YE%^&;~^_o`0Np)BkzCi~__r>^wg4rzOd;uT26NG!Z*57R; zvr+KTGyVeUT7L>?6Vz%FYUNCzMXERG1Ij(Eii66y7JP*k5dM*BTc_c$D!v=n7%QU! zb|uFAVUgwgg9a@o9j#BN*zAR$Vbsi4iK4__xAqvG{E_{*F|_PC{qunAt{qM}gUW&N zv!g%aknP`uyy5x+`{NB>eui(I(dk|f1l39tI}Fcq=b*D0WGd%X(l^|5nQHtPqf=Ek zeOQ6EKr&;$#+OUs(hEJ;@=5MSHC53ogn)cQHhH3I7j`!SHAT)y9&}bX(aNIkbJe}X z`$zqUUbIP&9|QoP5c8kQ`rjCp0Q+X4%YG98IqA7HUghwB1=kD}%( zH1L^8uX+y=2NiVofKk&<_k_9T-~^YaE_1 zT;Ag^CYBfNsW>xmVMa&~yFhHZeqZd6x$+s2Ca@B=^_mG$?_aWcx@Xl1-I{)ynxQ_B zg<+QR0&_XmbX{}DBkRbDpJHaTtLmFm+MFnr4CS~fn`|B_046{(;opJ}fcB!WVwWO) z5XHctG{q@aggE7d3Gf&KnVm@TQ8mN|NuS#Fl8d?JfBsoP0o5rudXyG(NQO(%c|fn{Kk z#uGY|mBdRF0f(-_HMS5X%NJ0IFVsX%T6-w6NLP-88fCg8VS!v?-|u4@XXnVJBjK4$ z3`<26oA8~Hs^jgo{&od-B41-YrL9yv953{q>!L3zzR1^D-@?Wz-`t6^ys*B$4d<+$1mKtDCX?_z2EHN zr9saf!(e!&7L4hn3fOXgUWO#@Pw-s6L0$cc0zT_sUQa86u9rTP=2?k|vWL4B#MK{% z+X}Y;m?SZu=LpY_Yay-_5O?%RU!fN6o;DjQwW{qVd}PVtACSivibm=`kmF^X(QU5# zdbn+cldlx7^!~Brh*1agEqqJP#LWK(1l8a4$aBpx+ckES4S4!5eDf>yc`bnrLWf=1 zfD+hKXC8f$RW!*Y%E2}`u)8ZK3CWg&<;Xj0dKEN8k=t8dcUlkOyNS+&Y)xpw(d@>6 zm{{yJLh1vJ$r9kVvxPGBHB}0RG5UjOm~$+})&C_qw-slA&Q@{%0rny_aag}fJBc25 z@ECyZ%eUzp#FlbLo%1EVmcww7eXH3E{@|F*&>4vm+J7-lEZvVTU2^Ix#uP?~BvIF$ zye+H}qk_Y%Mko$8Xmai^Zr#@4EXnXp9ZbO&PFddVCjT?|b9y2VEv;{qDLl=Ffjg{j z`J)7z# zCLnug!HFrHiwPKGJ9HB}B3iZu#X&2KJ_S$Fs9-EJn+>QwxZl(#mso9xs3@6!fS5!e zvv+8XSmeeyN5$v20?}nw8+?RNP{wsi=)Mz5OwAAH|Z4 zKBO}SQqZBSGPi+BH2w)+N|9gZD)*$?O`7jaqQ0_Ds{XK~kS(oWaY@vbIGxF%1wG~T zXRrov-|^?lJ=-Q$?Hd+CC$yCT^kQI`;Y8+i*K=j8drSwh1M)qm!qIas*AJJ!rqWIZ zYfM%it^SgnJL1%mfjslUQDa5X z^r}{#nkVp5nQc(B=(4+1;e29&@9NZUj3VuVXoM3iVzz8!1UrP#=iG1$e zm=IF9u8NqgFToKPwuMHSDCSEo;^GgGZ}1bT?O}q`7~>-Tpd?<<-bRq!XW>ZDEytaT zkUxIO4Ls}TK#UCPblSpVK4Fzv3%-O>A(s^ciFMIu4alVr3*!xRy-fzai z_JHT*=wSL^iZfTmH*3m5oIRMmO5*oWUuk#8jjh)@-BcmF$UI;hq?S$Gk?Yd`BN@}Pk-{7!K0-g+UQ7ubrB-(@7^s$t-9M4A3Jr>;Cs91FHb z%8*f+1n3NKnZ6tUB2N5=DhNU|BENGK0BhW^eAiR7^};+(qD$xdPMERHI3iCvhijPe zF9to~O1%k6+osQrbB7`0H}TC%Zc?SQ8&bB&4x@(4^eQ8Xu!iE6EyS5>Jfu#e2HU6( zEajRxu!>?X(lh$zv`1#iGpbtpEP4`^kq)$%-p`|ZX2=Z#?^Q05&$6RD5``;c6tHfE z?U&DMW?oFsqpu38reQu=(3w^%zWLVRg4@N3b&p&q-P;Im$(OE<-8H&rIYOx1Gp(NC z@*1eD4Yq6j_FL_bgyp4@1v#6l+uDw$zS%g6F$2d zvW=CV^NH_uir?-S0{xN+62rPLVw`0Xf~^?gpR*eUj@5#orqaIW)Ac6pW?E382)?pR z8#uh*0(HfvzKp3!@NQNpDRQyt6f4c=j;3_UrkVY6(S451%Z_v{{h^7tg9h9=(3pk} zOV1~%i*t4;2bO`9MXoR3QpSTh6~ovOy7tccE@9qbFD`$wo)kTJ^p9oqRHuD*a)|0& zKNKQ9(WCC^SBPfa{a^xa1qA#tXTB*%4?tHqYo|t^0tDB>@UUC;)KOpir~Luxmy>b+ z)>sAg0uhDo&^yoWG$jw&N~;Sciu3!YR zyB7HgMS5FJslhIZFSP7ssF=5(`>{|bdg#yx{x$W)H07|NSKkPbs?{WI!HDjZFOw_% zyz*VT%FiFj#W9nIWtJ;jMf=FiJn|Ao%AnG>d-3ebow<}MTm^|C-S_`2V2d67IYs_% zgm?J=her4}Mf#h?828uV>o<#0DA_G^ovy6uM|y!)`CRPiy~hD5SRtqDDF)cTSd0J$ z{sy!;s+Btd&yJU$u|7z@JX)zEntQuwF)r3FGmNy-O{wXQo9z6$FDnJW42%~p< zy8D_xE-&)@y2`)Dv}v|WT;LxrG7wa1*Qy&aX%&3_P)Zxv_PIxZIKMPyIXOcsO3glC zlbxoP<+tn{m0mZF6c1m6jvbPe2tx0q;{&EHH5OK2Su9Y@QpQ>#6t!vnb?n-}FmljX zW6S=hK?MEEgQcdXJ@d1v?NBvAsLML45UJ15Z6cUJNvA_T<`V9$$)a%6u}|s+WUI<# zD|EYd-%l-e=Vu)wPr8k0`Hc&1$N_1ao%vC_^#JZvr3>wf zfr@73c&#{pId#wcrXDS`i6EgrShF>s+C@+-VIdmjr3_!#<@tUs-rg!ed)@-cvr7_Uf8MH&8+ z%$a5H=$YwCa#_+kWq1Q!C-RAC+w`$O#vVC;^%@o{&x`KIj!w|}w>mVO-BSah1czY}c*0y+p7HF6018p!0-<^5w%6dn*vn^D$f2DK;aM2oq05?*p_!X! zUp!o|0*zNU@AkF^Kor_|GPPcF5;uvKq4#C2-HleaoiC5~**r8f-A0gwC8p#pMm)K-vCC9Zvf*4 zX3jT&5uT>1Wp3K3h{=r308GJ_~nq zX2R}{;@YH2^I4%?$EO`lpw}OB+&|J($_j$|vjUUrxz<3O^u0Ib+JKN^O8`(EW zejlXIEwrb+jS%mgp z*a`~wn#Nb4v_vD%-+qpkl4(VlE9f#rI7QtUYVuD2R9VyZeghZn^fV?j_Sy`LT>={i zV3D30kTviO=Hqh6152?&YkrQ<-3e+3B75+`@rr+$L}(-50i9TKtjw`ji} z{m&B4zq-g@=!{|1uWxe{Z~?qXlwaVt3ZfQHySuXC*4=hW~m&&6m!V}LV4vq_lf3fT4n%tC)LWjYug7wf< z)|9h}Fk-*6!PJih#s^Mo148LQt@~3V7!m?g3&JCR@?~M&+$kU~?SDDIed9AiZ8M`I zyT)rP*owSY|H5Zr{)Nw23H?GBLKhei+71gS-}s5$MqAt~1gJ0yn_w~Zr8%0;>h{Zp zZc-DesmYw8ku???g$f;Prd={=g^vawEi{QS0>kuyAMsAEE`~@7_Sw2&5HV`fOhn@Y z;Yevn7Y5vf3++z8+%nQuW-lvR2keb~;(UuyF3I7$UHBM5{_wKcSrHDdy zl3Xmn>RM6D1ngfSUU<$KvzFX4-&nJC*!r6JN5yLfw#KNBs=P}?8OLI|ja!oNX_ zGuwZG7Ee7}Ky54%2Rm;Lyctp`qBdQMQJ3-#g<*vH|6*t)uH3$tWiy}5T6tX@EkIg< z8^m1oYE}>DSqpV()f2QSsu%bc!YKy!Uk7&_3F_DsU1i$D!?}KY^Crdxp8YL`M4Jd? z$pvEKlLn3_P@4>lW+?|XJ5%r}u{vE0bOlNF!IE7N0Ssd0X}r;o$k!xS81*Q$GOh84 zy(?}T3z~u~C-lgc#iPMN-VMhR5+jk?i=Kid1Yc}bqm+d99LZXFhIL#vomnF?OBYu& zpkGN}INxw4``#QVHV5e*DkYd>vxcnecXVQLqPxOC*jb56pIB;{ZqK_qobGPavaXU| zSP*C`=ErCfz-5zTNIe*^qbmx3D{HEvvl{s@Req&oD|qJ65JxuN43&lZGT6-ya~ki$ zyzdp-^jFn%S2p36`zWKYnvk({by!xXrLUM!eKYXF$@R@l0BuqIl6|<(>#u5dsO6D8KaoE;0rZMEit1GKj%qGBGTm=6jEo#7d zWhfaAW>=6bIc0v67K4ap!~Y^J=B%&VC{U;WO+28zO7z`Hx9AI}>vlA!{(M=zUi42HsHqm2myH!OO@`xv3NPs2qwb=VOAQR>T>MTA)I=r*K2u1J@dy)@)hD#WV;{?PTKl+}4I&bxt zS4c8ZK{vIQ6M9kCh4>kT1CUggEl2?teEoyb0*ic%8sLI*W~M>rez+*pS1ktPFwS{m ziQmF1yT6>tD0ksspGuqRH*)UpQ~p@N99%jn<;G!eEdC+WXP#M{iYN0#_DyI~IooT+ zu%WEp)TP;Rt;~%ZU0(g_2kc$sxZ!8&BA4Msjj#|)KKSVYzyNb0KL15Q{CFiv1*v0h ziVmc~>s1itqgU_tXyRga9G%uY9%1bje_^f7>DN~CdD=UqnL#$px3?T4o6bji_~k@C zvzn;A&s`4cC*EqmPGqy|q5Nl^^wO%A9fpTSXdg&~9W1vPR~|yiU%Ogc64Kan>v$13 z208qv3+4PLC=`KCa|Fd;GKt8}d>f=1ma)mUCjT(_8Q; z_GmbCBJg==r{5aVZU<^Bzyg?q!7GzDVwxji_ph0zRUBfI>oF=F>%sjMmQ8DnYv1yb zpWPFhuOLJ61PK&u1XSlDB84LkfQVkg6G*@7qg4vMm#AfTKXP*W!KrhIm3Szqd!L+# za$i=e1Ah(h2EYgvfMg4SaqBaRd56d&o1Ek?Ue2+Gaj2UUl$%ZarpZ3!*0JoM0oZj) zu?;Nfb54mKEG{S{RK%|>!F~;bGN1-R*<1_6F+H}7U1fSRNk9Zsd%i{z3N!jgs%H^( z94|*9xg?HxD9`Nl$LApAuRV=rqNAEAp*yn4&I*Zh@5x)apGK!&`99aRcR^$Z&b~D| zWHsh`arHs$#02Po`jY%!jKsi5P$2A1&7|5$F2oVlhj~xU6*<>s?{O4dtDO{JTWWicZY6i9L4q z)taTs-D88ejy0uwrrb+7%{YFeDt)QrRUNWJHB?zPV?umF4j?`(b1_Pm+j^y14qOBSG4zPC@41jtBbMk;obNr@Df;!i zCw5=jpU~=RhoD2mLnDFPaKI_iExZu8iQ9EMKeKkCP02f2WYn45aHQxc# zGt#gV=hg+B!8f94Ds0b=rYG`|r3cDTrc61&7U1N!#8);qVf&8Y1=2O{z)g4dmK<~L-iMpJdgH%zF~j%vt#zn9@tS=IT*z3x zn1@$A9doOKMz~JdwRF+ypujCGYugG9;i3`}L}nw?9=(kkW*l$SA5H^8TaSq}Bw<|; zZwRDNdH8bvRHm*!QtBqsz|DjGd{G)~RV|4?zUIn+JV0Ry*SRquyt!WO#evk@HIhxw zk(Xk{rmoVDHmdE@P?-(kK?Ry@Nd?<$wv0|#h~LMg1ed@J-#A!tDrJ=@AY=|^#)Um2 z?CrNjs$WEuMy_#+2#PhLid{I0q63)2oknwGhsDMfHVgVm8<>oycep9v)*=X_i;Ba} zLYygDh0N?5aVXlzd{^#DlYXI7Qb*|!A-FJY#p0ac41XE z)eIn=5WhMKVpCGFfJba~SoWvp9CEsv*4&3|Eax)onPTUT&lF{7ywemfD?%>yq#E5H z()(cVCSmILPQ`wGMk+e({Z!C^18*$LbdIC7+d+fj5v8)lVEnmNkfjPbIkDx%Kt z9j2Mg=@PF=vSz2(xXT))Jy-b)W(oW^%+im5bkfV|8)gajH_Vb#W>r618Bdz1+PPjT zz7)h{` zwX$zlS+wJc=h9|R&P0XZ)=KgnS*6`C+Fv?t8BC^2yM2|BkI>evva7z-^Olh4xUDc8W4V4V!!Q$!A&jI+pw9-k zCzq=sPT+Vco>i=t-P`bUF%6Dbij!#twe=8v7=_h1g~I*jCcS+T3*Md4;3P7W3>lnP zTEsta^J`HaLjtU@nLpon{70VWe_aKd|IsQi{H;^`Q$X~8)-wECcH;kqyWxNKoA|d3 z#sAn<__q+*|JYUdw;@vHD}@#_Dvzli@E zFPomVNJRv`udaE%UFqLJmH+!1TE@W1*~HO_?(eM1e?9rv@s{R??3y^j*H^I@_}J4C zMa-`YVOUgI3HXSXvT5+;<*b1r&kHTP*=ePpykp&i8+-Q>wvSW}{=)M7Kqv$|5ANK# zIa_w7Zs5AVG*SmN^%qf4ah5NshFa(+D!TIL0zO{6=OwHwcft?Dl~Lf&rPi12;Cw$0 zTsG{)KXF;GnV_wwOX9#DqwhMlE?8G!(#kwk7Sj6~B?cu2c2Z?JIfV3jT_rkl_k=f) zQ0Et~8rQn?WJpCqw^UcU29l|WV2}rXf0?Sz$JDEC{n4DT2R7Yb!=lCMeQ#h&j5>3# z@b&9Mt{hH7AQ^HH0Dkys!c9D`iO*O!q_lL2+jVJa!h=M|_Q27v9(}xvQ#p02MEdL6 zz+tqPG`)!?QZl@j9v{z~#X}0+ak~5Vbsk2+kHg?+o1i=OHG+H>%CAnr(6sf@WO^;B z#+4x~=f;&8*j1go0Nb-7L)Fptv$>mb=SgVUIu}X#P&3q#04hdd#ii%Xc`_B(8TF>X z_6Yg1@*3v(_rc~|jrRtyM(y+k&(i|zDx6LR%7irGg3KDckQ#KGKV-6r>#?@Tlg(gdAg)F6ob|={E@#X@HG@2#uPuq&_31fXY}?y2A1k!(*%OUU{P(k?Eql?7@Em@j3hOdAuff zFhyAgya@JCg}9leK%^6^H2$XiPAJk#V88er#hj$f)B>TSZx0a}Dc(SvG6U`G7SLdK z#llu``eWAGg0bFb$k0Bsy?0=$?|4VEn`NR4w%ya7;k4}S4XQ}QIKx4i@VbH><4t~2 zP!czyFa^2SyI-b_Zj8iNR6$p2)SwuPK7cwPU<{STNtiQnBiu{ZchbizcA-b9xL*_) zkHyO5UH-`4m9H(aJM93Yq&j+PYg1~~Lmak>$YRSuUd}_%PCT=v`{PeSRxIbuG?q&j zaX1$bT?|)I8~8Sin1%DPZzC+3zeN}MO36yr>&LayRl?2DT$~GiKG@6g)!U~8wIIy?&~sS4~KMIT-Xn0NY(Ff?eU`C*frHNysfW~i=n44 z-288zCWjxe4}cHPw^_qKqS%7&HdJC&V&i)fWAkx$a)%s>t7sZN&>#~V?xV=AkYpR| zBidkvhJ8{d?>B6X%l9OPk&2Scb7d8!%UGe7njU>+rJyT~wpc4Kig4ZG12$WPOQyfo zA!QD#M`;!!rookhO-v~*_4#_pHLXFiUavX#L-05ImK^8%R8z~YC#vv0z({d za)eazuRBDWEVmbLvgaeR0csAxDfxA-tyu+41O{}0K~`Yu!R{6uS#se82nT`RhMxSA zn?%6rJg7ORZJhJhj``i@1mL<^_7#vMkMaQbca#L*XP|n432F5g!{4k1bl9#%#-HN1 z5FIg_$l%(0HH2`R@WI_fhP;Naz}nxiPdom4sU_9X$$wMm^A;bS-D!W|_usBG9mh3R zgrP02IWGNWvryt^3Yp^Qa&aFAYq@=F078NYWCUYC@s!yU>6!&xGBQXsVv!LC?UluQ z5fFjxu#FEowCOds&cp&5)P}O>;oHqNv_kI6+rV1U#nQKo9CdNZKBqoPS+T`j$+L~; zCB4l>p^;mwhFVrA3$QdH#)d`71)-sYmCRJ(o^s34hgK63$-?_eb&hs?KTFpU?DLKU zrNlt33vY+XcguEa#@C)vR+58?0Ep4>+3yS@g|S%GJfTX^lpjW>d6~tJ&5x?6kqXcm z28NYf+*VaQQ-{=I*~uMyxT5(SRF0OsBd@yhP&7RsI`en0vgw#?Yg_@5*bo=eV-|c( z{o_uhtZkhb<;I*7yCf~fcJd)j%NAPvKb*^Z0?@+UD+oD_w1M`dN!%mC)NA4fd zA)}0R^RfB>mHXrre0g?oM7uPOOq~W zUtqFa(A-&K^r^0=+X>jors27iz8Ate!9zD2Wj!Jmh2rfcPbXmne2BGwSZq?4{y|nX z&P%93n1Bx9MJO-hClz=ATR;Gy3}QhUz2hQ;P`8H>@*tQ`SZ#Lba9Vu+YTEWCd-iTL zR~<_GW_+Q=9C{|`@Ev$x@X%cojNRS)Dc>Z9TQvrK zPe}_JW|nHyEw3NmJ47Q)jhQzCusxgMm>7n_d<{Jx|6O3ZMg6Q z*NTAitlk;Hw=6WSeCS#S&~R}ZlCd*jy{lLQw49fj8D*#IymGPvXt{b1!~%bjDCRZ* zE;l6Q!fvBmc=UnkkKtn17fmM*FVzFO7dVoA8wmYWyYegDuObSMV-uols8X$*#&2#{ zN7JarKx|}|8!CNcs`+UAOc%(kl06t0bWbax+;7ljo6~hU8}}e{SJ)v9z5M5va|eOX zLz6Uq=y>?Kq33CLRzxOFMlOg>uf;H0@+Cu!D9T`vyxUjG?+xF8y%H{16~Vd-=Ac_5 zxn_=6Kq2j^r)(otP`dcrr&AQv6^ zrbKnG(xO&CXRRG< z*Llr^w9{;Lh&&VeT%;LyP^lQv$oso8!AM?QFg5XZFp=Bkyd<&J*62k{R1Q+j6!77D zyOxP8us+dcFZZ_-hZd3$aa4m)l?l!O=G*Cv$r1iq3WxSL%`i{ECDeRg9IYtop{IA> z5)kf_gaH@qR9Gv6ca19L<-(nyMI7A3^#ot!7)iEhWb_i<5M336gwjOe+cl>DAu53u zJ)?kXy=#nOTqLoMRjk3-74G}$K@Lz+j+MD^7E`!{6=XvL8r_T03ohhg-x=70 zmBc9d9N@@=7diuSI5umIy)x~O9E&bhIGlz~qA5@ee1aIF8}nu5c*-&oOrJmCq_S&N zrubuF^wvcPC3`#Nrd>?_ur2zYhlolQ9Yx0`vlG258#f5O4Z7gIa-MxNj=L$!!I1%z z&Jbgj8g^EK4=Qm)9}#A62ESdsxOAk=4@1)?!Rh$2y7Be-`mBGi|B7h5g6X2!98!CQ zQ$OQXdjDZD0hWy^K7}(am``ZvK`Fd?=ZpF7pQrCDc*s(IeedGGPI3ObUHrH9_Fs3g z%m3cRiOc_oUA$s@iY8DHBMxU4dew~?iJrEWSO}X?v2DHkY=#4ln0no$Um0;{kJl*# zP7$LKNnvKZdmipcTY9yn6JB3<0=s$BEE*qF&XCLAzG!&vK{+B3v4Dvl{9E^rNdyb^ z!Kf6;Cs4c8DQJ%D7L~*AY);)Cl3X*5gm|mmDycr_6k()DmTff;0_7x`E|RQxVSyi5 zA6&%U$i1rQnTZ@lT;LjN?SMsB(g8jV#T15ZApPApf{PR9TA%w?n0HIkVQ+zsB(F7YQZ z7n)Y?=lO(k1DYET04_N84QWOVU}}R29c;xvhkfnf2uqj}?pq#Zr(U>J9Ikwf|?V0_#7D{{F#DAJJySz5- zI5!!(vtZ5+oo}%>93!f;1uNE_u?7#;eQk-B6V`v(W07Jo39zB%^>FYq6qa_BM{{3+ zQ!z9M(Z=kdUwn%e_3|fDXZ1_XSOy4pRr4$;YGj6ceeR!4n2~2_%2D6G@9S?Y;D5LK z|JGd3(>EM1_u{romxHx!`in1|s1>`Px* zBOOsG=t`0E?#Sl7=?vSl8IwkXcEMUs8~t(@M2_0g-oUu&>LsM&V3Hd(q=c9uYI#;c zN!ry-2W^e=GBAk%n}@b3^_7=n;#=v1$<8^5n?Qh1WlA`!j0|n=s!K7a`dFHI8dlVF zaAC5&`g5|8+wS1mZPqepy=`q>(OnVkbqQy^o?)7Z1YOl_c}^3Bh3iK_^CyLG^<%XA zA$ruzhh~=a{)fs`C2ppG)%d132)!UK{WohCUcU|}384W0e5nTr{ndB-(o;JjDnJ{~ zqhYoZZ+?M15WFcFxxH}Uo%|lpC`3LWU%eE@pnEFGQptm(P~&|tah*_tv|6q$Sy=rK zLyA&l;ZusKn-BqY`w-Aq3vgqi^f0Uql#!`XZvAr|_oXH-PMBAuQKDlexz;vE^6XCi zg6Np*D-~#vC_Mr`040y+pf(ErQYEU;bc}y}dz3z!sl%v>ibq*|jH_u^K0i&PBlZ8o z+&jld5_Aio6KiAJ=EmG;V<#Kiwr$%sH{RITY;4<3Hn!jFUVY!a-|u(tdw;z%Q$01^ z)jfTBPMxZwIyPh5lWGO9{scHZuPVws^)$pR^x{vY+y^;dxDlf>EhXWD=%;Rk%MM)% z?sJvc%1hel*+6rCPHbdF45Z-Dllv1KZecp|fQkrWvvkc}G z;p+Ligk|wtvqMD(zEVp^iCr{j;>dK>iZN{eRijiWMZc4ZYIS)zo_cHE=EF~A)HwXAh*X#nB{Gx2BBaFOAFDTJ@6`+dL6Gs4e%+BES%W1h5z8d#=J1;$J~ z{rRf{Ul?8N4A0%UTPDKz57f#66)8pTY;H4(F2AV&x;+NH@^cI2*{+&>-WMh}pPxRe za-l+>Tg`Jz?{3acj3sxc=TC!kx)eG$Jf1b>`14CcWthEwC0H2;Jek>B$jKSaPjhV8 z3mTtBV(Yt{xmlUAe%5(~2rYN<-)_N-*=m7`$Gj)u{N%aI6yWX4N&u<4b#dUSQJ|Hq zE<3Qis}Dr7($t25k=Xw(OBz5m5lOF$1>(b zgUv{c$BEFyMJmUQu&**-DOjb#oOvb*U3Ls$x4zp`n-@%Z3g2XZjKy#_ESkBY=UbK` zoVm^6iVz&ZlYX>e_Dm7#XveM*UoV6<&E>EdYcB$0A5QtZy|g&0&cmUtXJmtp?OAEKE;}7{AoaHI3)BSvPgyl*CV#>ND61j?^TYgaob2E7 z#Hg-200Zi}Hq*VKq90bAt!h6@=b^T{*{)D19f46bg{_Yy(7=G6Ih6nznQU&67;{Ka zToCJTox6d$yPIsst_wUVZtvDgY7M0CG!r2)A3MJUgZ4YhmgcA2`N!9m1SJhsfP4&_ zo3Dsp(?y~N{A)$c z)6+f_)Uvw+2wxXVz|;*4JyIW=lXMN2B-bp!Ff=JmQfLfU^Od&$tCo2C&e@JoK{*rj zX>v?6ftp->u9d!SNA|wz87%>wX&pN~lYfdZ6u4%3sp<2OVqwSeSaYYldzYz=PRzsD zy$09@d?7{oKx_LpxHadjq*B1E%(tp;$&Q03v0$uFH|a+iw@hrAu@ZD+sdj&jNlVDH z(S>w2b&vE*1LG(yAxoYsKN`MiTFKv`Oo*o;;J(!C>N06BJ~MWCV$@aFvEq(jBP0ts z)XYhM>&2KOQiUK{oByB+LdR6wapln~D=X>}%&#SAK6j`0eW+hDSJx^iFIN ziyE^&w9b@F-4|`4-7IDK^@Lhv)5VDWJ$+i#1V3E2VPO67`@bMb$tD@629I<>~l3xCL>J1L#c65cApXPHx!c za|2g7&I5QKL4fjgDn?v(hI~&qUm}KT~R8j$e5xFmN;gBmf%NX^8<` z*E8pc!0CoG(*NE`|C;s2(*7!x{Qg~`6wJRGin;mj2X}JTZrwW^B9WYGVAX1i7s(W#qRv1turS#E zMAQ^E^UWwJH6XeQUumIzIF`SoN;JY$)k(FQ4Fp#x`e;eRTszuIvzS&z{;s4l&ChgH z5#w85l~*l`gfG-8L^IzY%vr#}Pm5b)!Z(N3F{S%1=|u?CHg*#1Qk=(mnwoExRVRj3 z3DW++RFF|cuIO$RYR_Ayy6yr+D>kif3ZXt#3Vq%5YRyQ0)zbHD(sliP3KttRL1lDhHfg z)_(Q#=f{?^#i>N>$yvFtsX@lA6oh3n(kQ5SOR0L`@D_#(YWgJY#R(+6`JWT^6$$s6 znRA*5a7uG!uk3OSKJC+&QfnsT!RELf4zY_a1q3%`Kh`IlL)q_yzN1RGf(_d5Zb*D+ zlt>8fCoi+N4sD?a2p2YXg*+5Zb5ns&x}FS{@42&2a&d4x8W^xJ42L+BJzAeV$0x0? za8;AA+;SuEhwKPKaV2NK1rOUEQUI?acJr_;+9y~WJhf+Y4n5HSvEQ4z2!_MaN}#Ehd|l!NEc6c z-W9Y09DhM{DfQkCPo>qLrbpg0=jf1K6+?;eM+ZM4~oBVK$gVktQN z`$iq<(e)MhP4?^?`e*Y99b&RFD#mt`vsb51lH3`ml_ze=k3G9Qz=7UN`q|jSGLqSJ zHlor%2e)jb`qi8#Q;(+ZmU(Mqjhg)@#aL%f3f_@| z_{}2%P=Zgebn*vE@ai9VUIL$k%%mbki0RVx!uf6d^T|@5k%NDVYK0!ozrN-4?`HH% zN}37TOEb%&LJ#`lm%7)jrQ!L>>L=($2{{LJAHx^qgnEBmY~y%#ohM%V0-9GF4R=H^$2Dhv7Loo zUhy?(WJNG}du_9TSt&R#6m2yJ1!SR*PK*!8zZ#~^3v`Lmfky^`Ik`_FqN8Kl7B4UX#VTR%OR)>Z33JHwIl!UeCx04=P$MOgcWwQt_$f+!Cf6x zL&MiTaOTMRTa)^B)FCi4Yyb73I$Ai;V<(C~qDl^#SRF)&aWCBQEhJ&4T&~lE>+SI6 z{W^D{nTT8Zft8t>Z5t#B-N~1EY!=yAvX~1ACRd{- z4{umWE^f@yktt|UWT`M}-IXk zS(b!2=)DX^xYFZ0Oz#j9nes66v%`HdA2Btem;c5Wr0$(p9XO2##gOV%{gonIe0hN~ew4c%7UHiglcy3;4Ly$UJLx130!|Hg1^K&i%m zj44)hdL}Sy(?#X610B7o(4uc%twJ7;qCEg6vfAa{7@C%|3zq&InSh6L_kE|RI4uI6 zoJL2XLPuWufDlB>2Op;s{{@3+cc4eer_3G?Qi0BuKtEe<)&k)(tT~RiD2W)EEX1>) zlprTCcUu(XpNgL$Zdu$}SAnLc=UYgEOyRv! zYjb{il>=ww$^2}+v_w)pmO>p#14QA9U@5k##KCG1%2Ucz~^j2^4ANi z;bj6|#hwz%@1RF;~q1VbI~;Wcs^r8bnkV8L`iHpP90M@ zPF9;&-2Qy`+^CFzNmxq5hk<9!57r1`<#-dL1csC>Ub3u}A_iUzt*T+N$>I$eG7aU_B8xdaZcxoBL zfb-PlSwZc&)tg3VYV%KXVp5I=OC4qAwr^K5TbnCW4T-g=kTLVvP;aX-mY#1{K zFB47XQt4cQ^NnZo7n|=-XD`jZzCtzSQg^9-bij<(J;~RIEpBSx4NOPUh3?A4mkX-z z5JEboZMUJUj-65ZD4% z^p1=92MYocUS#ThRqqP}oELmV^Fa5A^sS-``tW-MC_;X`p&XNA7@de#LBC#S#D~r? ztYtXRY$LKk4>|Fl+nds5c(_>Q6-3I9JbH(8(3XuVafTzE^B#@k9-lL(gYY7aa!_p~ zkwmcAk-8_!=Fl_NAZ2I@KnAM12ouyLhcLoE&jyEPWz*=DoWm2{8U0lJWsiWWWPv3p zfbz>(&}!%DLJJ><=RQTK_!w5{R(V9oC?2+iOmq)A`AgtcX?Y& zbK3W{rXE<0TRy6d86PknD8amxAf>S_B%XUi?z_3g(u<;~-^ja_T#0 z73B_%oUlC&QU)TzOrP|Pm4dP5CT7j#9<{P$?Wb91_TI`N=1B1(snb0a4UTY6>CA9- z<{e02wr4EZ9pfdMEV~Z_MF%}dVbMfOBXAOGwj(r4VKj$#eK%&t^P3_CO(c{>;G7!R}<7cj8V*s|gjYOsRA?RpE>d&K!vzx@5!{2o^?J;W)OPVyLbFH7D55hOpOnyrANZQU?i1g zDQ|*W`{2-{I~Xz2BxP?BD5|Ff`N%E8O)iT4LHv;P8``ZVK}o*)9R%u&NxfvBaU~BI zi;3ORDxFV=Lj8KV*pE$ukROCDIM&^`qSpL2u6-JgSYlO$&ow9K;>60rjwrJgtt5wz zp5(j?Ff*;#%mGgxN@;?$Wv<%{D|3*bZu#7{l@j&k5_Cc9nywW)UvD?c$D(LPudQRy z-O6ZgQg-*#M}DWotMm^AO(*5myZb!(r5-T2`MaVD@JmGV=(^CYC{S{rxQG&mu@gM^ z*%Y3Otse~82A`QYvErPIWe;dHte8v?w^=uOc;39so{i_?o6WQBdk&*;{@|Qqc8=J0 z-yWJ+r)P3fLeYu;GxfqK^hic{|JO22z=@y}u-kRO{U5sBU!R)>741J+nY{mGWx~0r zx3s#10_ri9IvA}Jsf#Yk=?!FzC1_zV+Ui-}G|wiF8IO~rB!tW)Gj99q_A3es>8CeFhjV?ihT~1P^+bSgj@8F1GLS3cuajdo!w4suJet^341(YpH1wbmDpDk1a7`OL_jMRNgD%wW{Rg^c@%9ooj) zqTXV7U5@f(h6{;&t#lE2@w$g4m4)#7 zIQ(ECDEU=hZqW~^&fH)6Xq)picqRFswrpYbz*)+2J_>yUUb+lJd6o7Xh^TxIGmaYk=r$@rQTwmssE4jwf`@26E!(zWi4<{u`^#tpAwQ36E;ke$6NNWoeQQ{jFF>R5yQ z`A9n22Em*7e)$nJj-!Id;;u&cAE7e8H#w$99Ea^BEP82((D_1hRT{x~^nERV?pjCJ zk=ejKr&ZW5oEA^pIw@-)E?8~K#=0&mI#ZnTFf_m8pYMQsxTSJYLwJQ_siV4p7O>pv z{}PdzKlXde1;a2XmiA`e*#iIC#tZxS;$b{>H$rPQl#O8z%G)SHV+QjEgTOkLqV3qA z<-3G$v)D)SF1XCFja0Axin(u}5#+vII$L&GXchO|M)*6o!D|yg!J=%!CF-dnQuP1 zSfPskqp~&tpWD>udfg+6WwJtihML*5f#m3{{K+ma@?XCl%lrNLQ{zd9fVK{x z5jB$&Xhd~2*%a*4X~&9s8k#%)CELMazVm$+N?w3nTF{Jqxv{;T#k*>K@Hp{S92(60 z3ts3lF6pmn#B9Vja-a#ddT2VuE10Xqu^wuPFM3J zsYE>N{ zdvtKZFxqoth1&pe{>#0k%m*ku`YxZcrJ<`up&50(}dCN4>UP z5;?3fQi?SJ7K^u3bDKUtR#$cAqgC@xaGTKADH{X9o%@qT6>lR-1uf?R8QZLlF9x`! zs@$V_k5fOtJYgbjb!5-FUxak^cIa@MTj&4_(y=|(?F5^`nI~cAYGcI z9~A^AqAclRq}%NZM_el+ws-=v?Vs&Q865&@ujj8qg$<|gTS)3zS~F9Ib)vgMY26~s zvr^>C9?)4Z9jG6;rq8U;(_qMr?qH(&o5R^Gj-bHiZQzYg#A0`us0JedPN_%Q}mSy{V=_};! za~@}Oz)NfxLrsf$ap=ltlhPO36JkOc-IWBWESZgKNYL%NuGK!E$P z8vOiASzJNo6+SUp`9-*RDRK*leC{cOxE<^bmrzJr9Gxd0wEPt%>SysIMLG$uUt_$k zrRaoMHsM2}tD_IHc)?e*UDW%xvLpr_ejnN)ZK1jby4-rl+BPRs84>n5;J)-CTw_y$Og09(xR{q@_WB2e4RIOCWhWx6rk z*{}fXE|=wy|6eL|8Yb1STY-Fw40!ZElsPuGPR5S^tjzg`U4N$){~lJ>3k<0G1i96| z#gnvV-HiX$=!}8nH_Z)*WU6z26}>h3!##$J{Z_9O5zGgsBFBY03FD_}&e8lL2&9IQ zy8#P?hjR~3kZ_3=9oY$zPzX|1gYF7j&fz3sd)6-rl}khV(!;sfkruzyMNfEFIVIU9%Ii!dZ{`%@3N*#2r1P@)W`BZ;h;iE zE)HwZX7K41m}GAL`j?)H6dCGvKY>YWZ1Mj~;qnjoIx_ylm%qsWR{O6^2WI?G^KJ5& zxX!Sv;i~|1+__`0DHLnC67lGQ>>^?Ru7!I}?*x3sBrk~+Zguv`GT$X`ZeHHKr^lVv^fn_h5f8M>x*Du&Zc7C(d7*SU~D_oI-MVYsWaZHx$aBnN!k5`EZ6pMm+BCTmubGwc5L;XhW% z+YXYYwFm~cHk&gX25T8@zL_(@*Y0>-XBZV-#I3pyT&G;Z>P;gl@mlc+d83LE;t@Qx zsoxF+nii_9H|QE{(Wyx9a(V3cod8MT2%Wnq9l2dyri_ zYu#|&9P-EtzPzBE(v<_R8|Dzc7aM6WVJC@>Q@IZ4xyg}OGXj^xKpSZfEvv6ExS}x! zC^K!X-Lr=A!Au93_y9AfR@DMvGZ-4PG1wD%@rb)|J;Nao=lJCbYNfqR#BJThN0>$34DiR__B<_XR5q;?g8C9rP!Uv zbiXZP)hz9*L56R?A}T=372lj><}SmBweZskAv5FT$=K)~c{rtJS z+TTEDoi>HaYY_d3!SG}ClcS6>r#wWU5f95gf_!;9@B`0lsL1;MpfDYY3?`yuOKdyP zjfT*Kb(Z$&OCscnSgPxPH9h+&Y-dbs3R!EE(UOUe`Ll!ZT8`PKG(_Y&|jX1=`M{79nck zm^b)I#0x5eq9g3uh}7R0*gRIa_7y&!Lj1s_P5>3-LxiyD%)-SWoob>(SR=b$f$&9% z{+#u;n>CYRHrA$s7tvb^844Ct-e}*>g%2`UjO{uqsSy39>e=^`4uapCC;543X-cd6 z70#W}>hVr?UW#W-Xfpm0?Q1)w{y3@3s@N5kq|i7@@=4rj$TmT=P}0}3Vq_+Sd5ld3 z_BFb?k}Nld_5CX9^`}s#zKk{ z%#jIf`A}|i?mPOis*zfF=kllr`k{thC=T|uV&fOKZeym1oL+0Pkr~gGk{Q=d(TU8M@>u(NkD2ZO6>R}^1C`6kXtWnP#Z9*bqH6? z*XX`K?w_Xe=jLn7#~!o+R4_3H#TO*^kcRA*qF)u5ok9q}17zEOuwaShvmk&4iS`*W z!A^Xs!2N`Hrzm*4Kj6Ng73p>~(Jvlf8ldV$p=1KC97GIoG1}0HY zKS*?!BV&QWj8(8uFdC9Aqjowdn!3!zpc#R06k-_S!m)Ql~%=EMu8P+}`_TF@Wn^#;gAW4m7)};Jl8o2KMINC)eg2j5l~zj|#%8 z&v{s00q|eop~bw=s2~X&dQ0ER`9O>0KLdg_0pvaflP{zKu;^g&4Io1giQ?#;SGYW?s91?Mk>dA+)pE?t#pj8mJ;r}ILwBqN zhrj^3sh8pa0BHZN%yKd_wl-$`yZtwwuPzgb%YoH}`Aqn04L|k_+n4SN($k7k#w9Us zy=aeWa)>2@txzz*bumalLbekP*8I45N)xe|bxJ^(@$EAljP)~V`Lx^bczRQb-6R3S zK(QI#iwdy|TD}=K23@X57*4?`!wjdY74lV3sw;E9#9GUBJ_6+xj4e@}Vh*e(i*3!m7{?XrL#G#lQy#A9YZ6O&SZcs{i&_3J5_E;ArZ0Q%g zX5T?=>uo7u>S5o_k)dr;W!({D35ALg#|u#-P*kQb^_7GDl8@uX!VO|1astwa^;+dK zo&R+~yT9v8_PfACjez@)R)5>&PXF1NOIS4IENPCZrd2DqezVj#81lua%b3$BjXMah z;PR9MHJjWpj>#6RFL_pE`ZWFWTi{{t5foFYW{4=fCRlh|ObN4$!7%%E%hju-?Zm$I zHHDHiHFk)-3CUKT9#d5S_;#Aq@c6z-3-gnh~dzw`O>loZ> z`NTi)d1s4)8^YC$4$ABZ5WT#rjwgncO79^tqz}r13+6#Z0`KjJ0K>ZVEFQbdMu=QWVXyM_5uKmw7kM#>hLlkV#6&@z6?TVBP=n*IaXR%KkQ8vJ!r# zmIv~T*#-skKGRni6ry&qQfTb4bHmF1JEWH=2X5r8FOc40(ZpN>hSuy=QPtaO2gWue zR5w-q2bTm#N(K)60+if})$rod49L?z-Owk%qoSkqrALOqs7O$U3%PC@{Tzu|@JWPv znFO*jt!#d@iavxSVLPrM4)ZR=OK*LLaR*S-lR`P2h%m`UXW649|H!@io>hRfR`OFk z;DnZ}VwAVX$g=H1zo__Sr_oz=B+fm+$kk-5fedMWaW_kvNO7XVvW;MOPbgkD(e&1iZYPvo%9^M{uNT@y?9Q7n+J*J0`$IyfqPcS! zW6b%8j8`w0PXG)Tiemsmi=sON6n2zRVL8?8exEST@J@jeJSKD5h@Zk07sj*#xYDw^ z%pKBo@M&CNl0&OpnMt8-Ygock`hu@~2kG)NUPY`DW`Sflj(7BXG4PA6!JaXRVHGFdgw9^y}> zB^Mg}$voh*M%WhZig5``v@J>G!s@U}X9y?fP!pJrHM-QmpEs$bMq0202=kxaye5|V#`nrVQOlqD7!Pd8g#tGY*h-GCfq8Z zWd(dr|G^^z_WV`t8hEFkX^qr>tT@lEqd9d2#&rXAJ;hBLaQFe9 zHB1Rn^H{;93OcV*HkuA#329#tUOnT|S-+!kS;be$3Nch&v8howZnY_7ce1?C)Ac0_ zBq?Y)IPi|$Q-V)t+!!glkLF;pw_obon{APtH*YDAn$Jwv@P5g}_A@pjU_ULCEu6Jg z;f~%Y+xhf-COK$%Pa9aWh!X{wvw9vypuY9hKU=5IF{96=SpB9WVzzlz8o+_$Nc6uRNB%mQ{8k^20meXfVIJ`yweh4_ z5A#R5foV1+vbReB$C2^)6ti%3Nmg+W*JvVv+&lsQhNEHuTfSSAJns@T>t`ffAA2MX`&OD{{%9R*xATisgv{Qrx4~Sw$zmlXf>w#(; zEV$<7hUsP}TJ95t6bImX56%;o`gpZ>u^U*#TKC%&@YA*@3p!2Dxsc>k!}Co(tOPdt z!s}UJa-2u4?%uEDLT)hy#banm=VqI1Rn?-)D>OV3IEz+=_}4tzo^3(-yt7^6d_(KR ze>Fm}GEf6S7x9I!@8_WjOD!W4}VS?dQwhEVHl(v2W9np5@jMC#W3cYz%GDeY<$oNABt#+K0f7K^L< z)Qk7`W8B+CTmEkEyH0$@ibv$(of_(i_bM=DHn*F;Q+^-cw}V03kJr0oh%ZQP8o{}< zN#eJDTKR*#!M*C-L`qjS7>6(=%8`N?Qd!$;0|5lWujNNjQe(9c9AqYBxv+7txR>WU zt%8qq$Z#U`Juea#068vGVTMY>w%iBh=$|B!XW5Y==nq)CQkt8+DmjCZTx0GQj@V?s z@}9y3M}ST;1@b)@6e$HByrpCpw6MFq-)Jv9hhzatV8wp3++(^z>AMkPFYZ&@)&w5K>gdN9bIH6|>H zJu}q6e5w->#Zc~rTC;>gk;+3~I7e?H8e)Pm0KC+Z)-;1$`8Vb@XbF{QY)f6j)&cDB zX&e+%toN~xWO)2}=HfkS9PtbI@C99vwFHt)^&oksenIwPGb>;5ax`c+`PqAD$~_|0 zNoeYgf8N4f1O3j6-Rca!9rARnZmXDEJReD|+83Ay%I$Z4*oUq=FSphWs)jEEsiDqi z6ziT2xe5ala|!XN-y5}mUKYTxo;?wIR~J}&q^{=V_~5C2G)hBT`;Ij6v2fWLfIP-ACZ2Toy_T`w z^HTZ(u7vA%iLqN})~P?3A63vw! z=K7Foz41mbJJcQb&Gg<@q=;j@*I_M=&yn?AsO8FeACYxq!QV*f5;%|&=6Kz6CxGN@EedyyAKbvZiFl;3%)L3e)6zmy{db<7qeu&-$m@$=b3NEoC zCNP9f+BDw-i7NYcY9{*a8^r@fgr-BIM#O-xNhW#3GH-7Melg7fgjVXJ6V2|FbhDW8 zf@)y;fP2D*=VIrwnH1}T712|*`C=?$^RTL#wbQ1aviL*O!kuK*YO_?wM~HlW)A3kd z3QLpxYO^cerGWgg4IkUX8FJ_be3PklmtfMNr1q*iS8wU0{z2?kOPih~#^lH1WfW49 zFD<5Io~D#@tBM)1*{1vUGZ>@Uv*7sI^KQoC^_s>aVS@$OrY+lmPai&fCL=KwjRdaG z^aFT~>5{|Uxk&Rxhp)t_;eAzHsdM#Yud{XKW;bib%VswbY`puljjTu2My>W~BV=L* z2abznXC7Z=^opPj5rGl%iDs%XJgEw6+*{E9?v}_ zqw0^4_|!$_j*P`C{(q1@fOhl0Kn#FFvIivsu73EZ0OPCxMz)6X4z_lVK;gsL+Q3HN z+={`%&e-&0>0=9kBq=H(3IGA>>hypwz{fVgPsGjK1OSkc0nh-?g$96w-~fQSEC8?x z;0p-uf7V4or~p92A#gv?uo!9%02(Czwvh#X{sBE;`E$;{eS+nI{CjKQzC6(XXbl3$ z1N)CUpa<9*@Nov>EDmSX{R;KzP2 zX}=$Zd_H;r$k2cu;O~Ht06>vJz>q;cegS|3D+mP0U-)|(a03Dg1`YuU1q}lW2W-%Y z1ONpA0|NyIgMj$+Ga%l;;{b4E2o&Paf{>^R`cNeHXw3fc`Ou_7KYGy>r!UD^3>*Sr zU@I%2_p5(kaAH_ZMUUdTYaK*7Pmz@h%&1p?~&2XSO@ z2;$F>D1r)5`u3K}5Lh<=OdSOUe6fe;Y9HwD0$XK_@ul}I+7qkBxVgdi3F#Au$ z{tK^FfG_~`FMxr9f$x61|)D~Fk}Ef;O(^_BF5aXIruPXn^bYkP8NX8i(m#S zNqe}CdSCXId-npTEju`%62H2^piqsWsaN{@17NXo@J&93nE1X40eUw+W7=ol7d+?u zenHzw6Q2D@V~uyn5ZSij#mIrV36Cs(kBGLTmedA>735YMeg6yk zQa(sl`|G?;vaE(oen_5%OQ~ZNN$Y|K@TIb#OSbl{n61i!wtuDr-HBjyjPY}_QCKAv z95YF9{6j(sk&t_QT?^kgryQ)uH}@Si=g6TBLT~hv$Wko}losy`>TvX#6|yC`$x*WJ z@%uxS={5}@x7O&_2dP)H1KhXay3cH*OODZ2Qz8Ex;3^ z2Jq$(@rw6;kvUnR$YpP?aU;3Lg|AsJ?u4{Fh$g*H zs9W%&_N>u-w7IjXq_?t>hA1>{dHEJEMj|lMA0A!QbA!x5kY(hDSYlR_b<4kkFsL%s zq4a&toOwm((M@*PKuXGyY?@FUxch`AEXbWT`c3x3;1X*m>VRuAMoQ{)Xz;@7?}ZiX z?e-VPbxkp5<=pMR6m+J$%dv!iQ2-8 zmQA7w$jw{=*{?H|{WpX{8S%Itn6gr1@) zcyHZprp{h3p6SMh`AaS~!HstC3(n<0cEJb0Qth+?FW`X7$1*6HT#rs!ImIPP6h}Z^ zKvgNn0q`ZJpv&w^_YBvt@KoU#hdpjloIw$lZLVOMA--vA$*&q1nDxH5t*Bko*rjNB z>;d?gdV4JqABnGTCKjYTqP~qPKT!@5#3EhsFyuvU+Ao<{;z9tC2X&dS6^+ zz(^M%JxitfZP=6sKa~b+aX!Hd?@^N>nj>_x4}*XCrnrDrh^WvyKs2R=6v8Vxf++|| z)of65sOv5F9nL~9<)L@QDZdKmS0FTYldCxnOO&1ZD<aZrd+l!0$VurEVSKPu(!? zfgju69_Qn)g3hS!HV>3?QY{(>!zrA~U?2p;!oy5vNqV8UZud2TKA>-zY^(Gf1Km(o zq$!Ji56!fej-OJ0lxwAtHl>EAdrc9m?J5criNL0Tfwjfn`#e&kHKPu0t#cW-Gh-WF zi#{-)!>61M8E{{6VgX)jY{(OYnj1%H0}AH5mc1}g3i{~p4Rl) zdlGydN7KW25!m=x`pHRqNMhgwjqp{$G3f>=IMy95DYXnsJHg=g1)Ay*jI>Zl_s^H&BlrRz&be=baga|;0 z{_IzX0!pZUL(_Bu_g1c?#fk=1H` zzN`-aHl(&fc%ok?S!H>5)6p{5P{m7rHmZZs!mwdcbsHd|fQ6*&P>zX3LA=6d`1uZIJ{Xdp0DTa!vP721`;(L2^+_Q=uHIUSG)H1T)(QZ!!phWc>!i3UxfkP- zQ}`vMydwnHEw;;9Hmi%+7fK5{`2}Vx z4RNF2m-?K^+o8huqSXua9AJN7E9FXlw=c4Qe1{u_*YI>Jq#j%t(Pt+I!L~gXuf)+ zob{r#d&Lk;QNpUyA>Ih7tZz$wy{|XgzOT}2h0yL8bL%O72jyR3*{>^gT$P&L^t7~I z5J58`0K9)N#6JFOKB*Ga!Ph+Tdr#2f)57 zBwNO}Wm8xHivalp5HBb40jQz1eM4)UTIAa%d_ud`eZFq`08C41!voM1ME<#`zz>Gs z!dWXjQFcY&QDS3E=Q8WDpO2F4m67IKft@PAOX;@EWh|^pT+<~>ylDR#;rOgoJ*4D_ z+#a%FNSb9Dbz6xqrCnj}?bLSN^#P!bKSQ3LcXT&nNclApu%FZtX}2MdhXJ<;h#xl) zD>#w+F5~H2DaY1+u69PMwjGS#o}zrC9$J$I)u{br%1MU#WamY;Helu8EOMOAzI>SU z^Dsk?aqAtcWHwcmv8zp_)YHah93|z{LO}#=TK&h&-+vnbd}4sI{SfL@tD$N>EKn}2 zi{BQKR5;caFiM~TjpYQhBX7#CHU9w!Gm2TjzUAZgHe1sxjWZO#Pe!PokvwXLCQV-R1c(>US-nbq z7lT!|Kt4Ln8c`&+{|x>K+#EVuoX4MnV`xzFI0pH6VovGex!Cm)>BMF3D0_j8VUH$8 zf)b$0U*?u6_oF?am!ze$C2Nbm0Y$nT%!+(ZVLwk1!Ig=ZfU5hlB(P!HB!z1z>NOqQ&SlSqq>V=5{&T&n_p^s()dv8i%XH7NnQ`ZPBb@V(n3H25&n?;*17z7#u}S*GCzi)j)#af<_`dJ1atYXhs_ob5N&)k zI<3M>&o)iDn>D@+&l6$z4vf>f4YqJQNHhRbAy{c17Qs9lnktL+$taM?$3dVe2oQeE z0?E2UM}2j3wX=A|HbUCSC~E7lthbW!QTUbT1MsEv{axY%&@T@qBjU(^8T#n@?k)HB zn)m^TD11ly03dt-Ccl3Gx)Xe_zJbgtXidHnzvF)Za+N*+zGNSOow5%=5Px6y&57^D zUhb~4gMsdo`JZcO`u@|6R!5m$nVx(A zn5kc><`Ttbjc%6pkcRCO$>M!ZM9C#>HqxwnPy@4K=5wu!Pui4&YSm7db;W_^#`VzN z!I9!!WrMss`qtJ{g@3-SI-{9aq=K!l0S8iA;Uod-6v^t}x@N-qn~^xNhR?Jv`&#*( zy$1~jCTM0{bv({apKtA#-IWsucpdi%HxBXQ>{GkG%hpI_dg`4IZo^0rxs9Bd~w=q}u^(1;$4mgWVR%G1_ zss;rWBrdx}=bc*VdDWwZ#D)!ROd90DX!Eq|Sp&^=5YrPAbC#C3wZ3pwaocZTDqbg_0Ly;f@0EQ{i-S668*4b>m zcuM)k5)2JCZ0odaXr!Q`i?s~-PoBUHIu3vGe{#a-@6}LrvxW%%l~Oy}WsRlg?G`E+nI%WC!G=F-$#L}ZW9lxuvtSOVnnmL;vuxGPYK{hgI4(@=i83R2* zbIXm9iso`mM{-={lD5}E;Vw+HLgqdqpX+g8F!PFbB}jJV9bfGd6tg3tA6FkB zbmju(0n_$NB<5&Qp77x|yP zwVH4n)eQyOOR2~WQ#MsvhF`l=>geGAY45GWs_L4@??VV8Ac%kX3fmnv-V6xs$NyF zkM2A4P#zTVnQ$G@?F0D)&{_Y5t;-|Y+SL}=5^$fZ5|xQ-;q4_X3*06uk0p#z^f!1_ z^U`}Qx+D^WMoMHohf|`cH0YB84ibAWI7BVI{bd@o)k=4__fE(-8XmI1Z9HU4<(aDZ z!l2?B>VdVY^3g-}&-9B6juml?mRq@oo`B^e@pYr`73Vp`GCErI>Nc%&i)js;bph^_ z>aKgeL^94F4>PHrWfTKD)j>VCHrVc1_;9aFbZ_tok1Fr-#L6|+>b z^{cdeSrDM!sHErZxYIt~Y%KX0(}tpsTs7s%mjx|5(uCY(esKB&ubkeF(|cJMRDEEp z_pP+-jnX-#$M0g|a6Kj&;{E)pa5`N+8#1ZBba1t)ymBkPk@NoYBr(`>7Q%RDjP#S2 zWPXSu30;F-fl4zsVTZPfz;g_t6bM&d`L7%MSl(OA$0 zZG_ggHpH=kFp55G5W{kFC(VZskLNJH9Vt4g9Y^&ZjeTh;5PctExvpU+c*@WI;IuNQ zW>_6cvf!d^^jH=!e^f$rtL?l%0bL7Blt~10e zmHe2D&xt(-{cQ$;2)yU$qXgH^_KQJNWdmxNgU0U(@CpD5JdB zq~*w_cPzeVArC#3iWi)>U9$YrIn}LY{;RN*xtg>zu-`5GIj`i_P@^3rds>;cT^Z{j zS)v;kEv<~mHX-&Bi|!*zP9<=3jsbe^ejKB+;s|ylv0IPSgdlurqDt6TleRV0&zrb` zk}j>xLQyAUnew|#=?8;OXP`H1w$|E~FJnkPAZ(RG?e)1T+=~bAp>;o%FDQsQ6?tt!-*rB~w*m;4v2fLc^)y#7>cu}!xsw`j%?tMPu1%x-jfMj z7!XWk98_>5?W5sxl2C~|;E>iXQZUxW_Y{0W9u9d|fM{8{&n{}SWT#{$b5Yx1{&Ntc zJ}`bBqMHjMG#iW@nDr&L&ZJx7Y3b$}%jJU!M4uIN)1x&!fd@>rkUNE3{X&=|Ap&8! zKtGf^dv=X!xcU-%tqgSv3tf>z5`fxrMEgXc!lb|pF!+0!I~KXd-5uu69itt?>Y?jH zlM^(?G{&pI8cp#F8n#)}-3@Xy=FcxjK^-_Lb7em=$r6yx27VcV>;Tcmhd4nD=%Z zY%0z8wI|BW1<3MtQZ={y?p5S6qnLj;OuLs7YGHgp&H3PzkNL4De{%ugM(U4^EFZG% zBMZrNSgi*)Jp^tBC>&%rVAck~2ECK!h|L@SE;-SaRYN)5pUrO?EAHQ^?1!s`?pN zO?@JVgrXigrx+GSp%oPV1eY#srTSryHDSW=y*#y`2^uVF z&I6nh&d=5~he(rvUyUAr9HffTJ=;BSs`COY^|eQ3fA4YX8HoF6>S(qPSh{~ZZR)T2 z#_4bda#V8JX19#AVi-CK*kzm^oTTk#d$MJ!&{N4jhqWxb%CH$3l~zZEA7dM@7pWyv zdc>EAMbYrGAKHSOW5(3pFhhagMVq4A*z#cqs<%aMU`526_1i3{5MjS5MODP>)^ezA zXmL#ZmY5n`y3I0ib9nwiX8a(IO2XOoye)OnEgJWhhB1R&tLT?eM=HDs(k0=<#ZWN( ztO`7R`NysBWYA%6P33;S5ZvMNs1w)?mkmFOQ^AD0rZfEezCUOn4TdI}l-~|48jSK) zc;J-=($l}wj@Fj-Zw)b$*OBzE&G)~j=J$W}5y*PliJ&y|)&61XAVhSV)p=Yd#g!cBpD1lJ;5Li?+ zW9qGjx@DaDw%mBvz}f{xglf@BxwM0?0As=X2_-=?WJkYDJNbHmfqX2}IMKjvUx~K* z5v#Jhlwp>!WQTsc;E0}&0t~#z2!*kEH+7A(eKRXu)_38IE6I6074U?Thtj3P zTIdnRODPq}8DdfT@a@zF5#)Yd;MORE1^sB|dYXO0%KuPzqN!89M7qg5Y+iyqhC$0h zS;!Q>KKaOQ<2w(Y=a$ z0#!Y!{3v+ONJ2Pf{i^w*5gs{ZMD~gDbd;MlHOIb0vEuH=8shUuY6co% zbni=vcF@AxiQvNOMq#1fs8#WU%y96)mWg05={{?Nv{JVvvHl0rJda)qg!5@qXqPHV z$t0eEA~i83I#Za0-5b;(P2ud^7B>W{G*i@tGgQ0vX9P@XmV`TUne$d-W|IZQ>h-xfSEi#^gZh_^SkW3#} zrSaMlr}T{TtumF1BI9E`o2gc9!Jf8WQQCWG9T$>1w*1V>SvDt}5&DBNe5bNdHJUXN~%}4 z2|vS>=!5d^_7vv%(WIrNqODGE3& ztz&w)NaFsZSC0rr?TQQ({DdDbnYob{3*PsE1g(6^x2Wqtmqhv`XWU$nJW|;+F~tq9 z>|wy${!x;pJ`#EpW0QW+m2GauBz`XM?r_r#(gRlYrW8fxr0>!qJ8iaR-8Ao-W)gFoLE$O_Q6kU9(aG&eWzp79J5o7e!Vq4igdW@71t&Rj9$KXjxRv8G7X+4a#Zl~XGSGt@HogC-IRMR?lOF7Dbqo(7% zb8^yRN!=UksC9~tcvKGZ1@YxLj5NsagJAXKsf8;VZZ#lWw7s0L># zUGkG#(0R0Q=~PQqAfZA+Lf+Kw`uneSvzx0i8>AddYz2Ar4mCYUua7BwAh&%GiKVLR zV?0OK(%4lOoq|bZmb|d;xDWUew>e0^bFlTZ=_Y}P4U%RR&<8NnyGKGog{k)7p!Gw; z$<%0Z5f`0)NP{?1>W3(&1Nx{*@G8?`XKVQ_Mm*nxh=56oh z1*S&)mpJya+(*4;pgetH!YC3Zq{4`&EOn2#_fbKIK#bzAr`mrbiGZ9?&La87e;zuf z3i8Hb+^WQ(bZe?KNGEyEp4uE)k`cLJG#9SBem_!FVxrYIRN#vO3q|RXbG0_m)1&ku6KO&dkzd|vbk%iI`+ ze;YD$n@m0Yi54t2WaCj>kr}cuXg;uL@tK_0oRijELp9MBOy=V-bDaXG0`a#rVH|s! zxbLI|Ra+~BM-}dW&@f}WJ2b=D5(GWoJ{j%E!4@&lCRY0W9%6wlpNZa z=v$*AbI8d}*%CvgwH`6i2?nFhSwkZunI%=a&(t(HNz|zkZaoLGTcC{^mcR%NGFoeK z^jVq_efW%@3UBsF*KT`8LRVcySUb!%a;6w1$Nj^-;iIom-~JQp{TXn~g8|q5{N_`N zAvT&>PRNxO@u-G;l`qEc30P8THS>g`@a9V6&|XZur;ZTSVn237d08Ph z?5Gl#;#g;?Y_!lfgtzk)87xdL(5K{Jra8$iW=}_1oXjkG)o~(u$Yu1|MP+^JMEjBn zHlur5dc!X|5av2$?oh4(ftOaR4S2gTwW=O#K(fAvDyV)dktbd?9%EvbVmTQrPo{j} zZ|3nXW4=%)Sl0R8F-_D=Kwzc;anj z4cKT5f-nr}K-i0n%uH8B!5_{KViB4syd~L7d26a%0J9c!@`N8`*OlwOh1<;WpseRu z*M-!J2HkERFrwj9Ysgr>70KExtje!4p0;97o2xN_(dKAS?Y_@7KT{7R?d7EqgiI)< z~%qF z5AJ71OzKMEW3|doDld|LZzqH5Z1G?d&2y>8&t7@mII5PKOwi$i7BXC7N%t76a}Yxh zry=^dzQrx@9ZMHjcC%XDb1kQGXlpW7$vW8H?41R28H#w63*F5A>GpGGV>KAcp^Wpm zEhG@TMfw#IoIX~ZJE5#Ae~UiaXj0sr{z@slAg^nIH;qc1w3Xu}v2JTidKJhvT)y-5 z&>HUyC88WPltkZB=>YVgk2!QcUz~_)3t=YBeX-Ob4TNaeMQ9XCo1wY&2=gsB7Z0SA zE~E(WrZJ&6$7=;F)TaBr!lf;a+y!mr6YSS z5m0clFKzTnj+RY}?-yH`b+$9Pwzo*ksyBBad5%ux`dKEaeK%&iH(nUoOr3RKe1T~| z58+FVqrbN47L+R&d$I39`^w&jP0;4*DP^BlMOhy;qn|N>VjQf8;<^rPc5ozyH40)n zvCR*ytQ|RrUYZEME*Y-$b9zBL1L3f&Tc?>$a^!9>t+id;d5JiZ6;q9-|pPHM_d zj~_v-G5Wn%#*`afUw<0$czuJgD0EN}P2I4zpoua#`_5;OuDG?~k%*m*VLYs|vVlBG zjo(b93|0?f1^1n90v01m&wv`Mtpci`x6mkvASYsV=3{d(@tliqOmRR4m~~bzbDC^b zJCd^12*SEjUiXf~1~MU$F`XwU_DjGSh^m%TNWOD)!K=j-=~!vNQ9i-`;gT04YM-%o zzRev?F9-WT*jCiBf!GxPx z&GC0>tPhS~%+jpc{U9%CdDzB~B!@9!OB1J%(mS^(bjlr=(+uKpPx4Lt$nU}tZ3`3e zY;>{>!4EpOVvt27I8Yw!Rt)#(P!hHchwdcmSax>G`!Fg{qLFol)o?@yc~oFXe=xEy zC%+3DrNtJf{tTp7TR+2FCW+z3Qn4|jJv&ep@nbS*%Rx5m#oGz-bfU0H!P{}Xu#tUX z)+DoS_mgMD3>O~+5)&d$9zQl6g{>PLm3ZqYpU#`DMBm(<0jr3E7PC+}Jg>2ZK7u@% zOUM?%bGrjcgX8|z%D2(Um{U4U%S;JMr3rYlh7oRp2!%Na*Oxrp@t zqog!WYd<55L5W9|=q=y8uvDO-W81>0{=;pOXCPW>^Eks-0OCjd9k7FX;0&Yk z?KH!TUUDKRog8e_MS=rAulmw)7&9bB=M}+T({$hOd+qQ(335V{LIV?MTcwn=L{M*n zpH0gJphEb^VQ=o5ci%Cv$$Hdx5JvU}6tY!f>F?Nrt+NwI!Qx0W6CFeHUZ^0r7DGU3 zl1}oOkw%#sGikedzj<$oegXoe1Uy(zA|UBgFV$K|4}q@lBm>OTFH*Nl}syY01$5}eS^tMYxoPLE)I?JdgYI80AOt(i*9 zkILiRgzUv_$KXLc9Tloi&bhf=&14QQM}h4Zl(JTx)DDwk#68Kzoku z6aR{*UaMIZUGRlZtg+pr@-oO0U^2B5KGn4lPG)`&R+})0vMD$29$sJc*o9^7Jj>I6Uj;N}r1dhD@qDyg7XEJw(l?{=w-Cw?=PLb&e zLajyykwd>4?w0sqxuf9YD#i~F9@@DdJ>|>6en1654?|U(A3uZV{@t(!*si14SB+%1 zQ;&O}k<+JGY0R+8e$TEGL5Y0*URvo6`gDP}7}K(OIV%5)qZCYBrZZ4x#(#DOT@u)z zyM;K)31ZabWb7ZN>ZtBGXw zLL8?;-7-9V0g4ofr|L-Pl5bEK6F9oMp8P`DxI8_-EpG_%XEa9w@`Omq6yH z8Ef`^G8Qm!qw~kXTxgXDf&vIOO30YowPt_{2)c#Z7nXV+7Ltb!t-#ni4Yce!C!nW$QolNu# zVBH!+Y7yenCeo_jo)qAAwYZg93zVSn>F}rYApN$DXRRe#uX_)n-V8gO$6#`xLixLR zBsm<7K(6T5YBY1n+Y*cBsZ{F&tJJRsAwtDoIK3oKoUw{kaAYJLd}mQ*9{dG=2f@Wa zHfZkD6*hgmci8haPJoxD_^qgC3K^(@l8=20`4NgfiS8`QJhUw5fuE{z;d-576F@n# zVDeU3NUD}25UA`$Q13gShaLEoFxq)|IAL>Lt{t}7Sqj1+FUie?~!F-ytZ%@t3*ulTqd-bPu*eX}UN?lA;t=br_(|rXf(u z87ER;VS*gowYdh(C(I2BH9p;ew=bSbWiywsx6DG7U-_D*|K1EH+0v57+gLEMl>MFJ zx3wQHrN)iR5K@%E4t)_sZL?S&}=RK8pc{pSHd;G4^qH(NVXJwbv8qd)GQ=8Z z(G9zPmd-*Tmhy*`RDsWw!+cG%I0Joa@CmU(CYp{uEPB`WN>=z#xFHmLvLWqrH^fR| z#r741oY*Y#=E%H6EvD_a+cVPdeOX)>C{^fByX0Jjv4hNS1;Q<2n0RQiWAt#7gA=k{ z#75wtTO@gIwJUz$gX74*E2c|x*QyOpQMe>)hG5hSs;bBW^ghieM#}5(6Sq(7PiHwOwy+6Y_3v1Dc5;4 zly1C2T(CaC`BmDjbV~Ih7{167hAH1^;lsF1tJ!m;*tHr+f=%1hqnC&C+Pj+yol?(d zb+Ac@h-`29kmH9Fsjs=|?FedG4^OKT`ZNrSrt@3yw!IBZBW+d-w#AA+Gd;LInSlXo zDIW(Qpd!HhM7}Jfnr3GIzl>hY$yHe?(@|Pv=lH@ijRgY-S0dCNbWC|PwAn-iBA$Jt zPYzL@chKd9s9&_mf>+MXX8sol%TJ z6*oxm2F)YPGttD{2#2QwosPNW1)?-QfJ7yEMudY|)o?|shxl;4*&eV4ZY z>4yoawB6U`eaLWlSSf}(Gfi*W@m3-NG$bhlk=9N5>0{+iM$+dMl5mpYP*dmc4)+!a zyIF8Gkci)3D;48|psKE}Y7)8Mmp7^tsT$zPkd0qOBT6p4{*}HMIkQJ@16y%ZhAiFq zx!fUUr-(LR1JeU>C_nK-{lzmV<&SaVh^_=HDx6^w4O;_x4{eZ_eX**UD?6$E2#wOv z`if97HJrH`?x(01HpF{-GA3At=q5au6;p4t`ut8r>aZTO*SeiptVghTt~W_F5AGSP zT34$tynClR7A~IkC^DtthxyJS4xlUs6H) zS&k_njC{S1Tq&Gg^nB_tsA$es-{DAz-~;qg93jkv9=I0E2i!Y*=)Z8crhLVWt=dF+ zy&?lK539TdN~fX@M!XDFnQ-!Bh~VdLRZ?vys^W5G@OGLtvYIKJLC}%QdQDT+y94z! z$B%xYS%l~hcggyhE6DGkNUp8*X_brhT7N;*0TVI!WeI;}Q9~xAJY`sVg)|bq>5?<9^r0ibG%TR}vU?YD_Vb-G zuc?WZu8EbpjJ=tzrRvXVC@K0oKp$+C2t2z4ngymVklgdq!2mqv_A8_MdD5+`4Ml&j zuT}y7&I~lhyVMK>(g!qvnVA*Mc^O_ynoC*P&ug_^OVd^%EUdkjrVZ3+xt?}jFX?}z zX#)LtJ?;D{`Tvoo4m?eHJ?*?EHEsa03%}E^s1fl@M756uqB;YImr2U$sKNqTiw$QXP zFg5w9E3SogE`P&Y2b9GrFor*q7F`^>Xun`rF;`_)IFFI`DGUh22y_(sMRVXCD&}t& zcdkHwjp0>>NSe9R0U(oljvx^5wU-W8;0iBw&^hB=46Y*93k@h&0K~^X5ZTm!BF-nx zRm6#aQw}lE4+QR4&!V06PsF*tx{7!(H+k3%bf+=UA-`V;fykeTa|L=8Q8oLJ!W(G5 zO?MSRB>pGjTr*uo?7gCEV7&zbMKE4POuhIMaX#g*A_Tjt)KUP%1n*UZxZA3Eip$1pB9frMe{GAdrpgCB#38 z?=P9zUw^t;_gy9fqJHUceDgQtf6;?Cqut&93VpLqyo~<6KYW4d_unahV}IPNA8#fH zzx^BX%|`2Ha-_%KkZ(4wHFRm6R=HGGG+N-Mt zqAzgRvHy;{)`DKG(t3egO#FA;wKn}~J;@84U+TZ(uJtmm7L>cdjb#2i?pnX+Y8kBy z99_;O+&^-6$*x?IzH42>s}-a!$RW9x$k&MAweG;xS}zwkrhk7bzKUtWW>I}omKzBUCIaj&$9Z`pKjJPR})QNkRfZY zAzx))^=+=EExo`&HUGk0O=5bLa5Z7w1wpj)55grO2G%d<$DhfIE(mXWZ%k!$^)#*q z>noz diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py deleted file mode 100644 index bf6c24c..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/footprint.py +++ /dev/null @@ -1,72 +0,0 @@ -import numpy as np -import matplotlib.patches -import matplotlib.collections -import matplotlib.pyplot as plt -#plt.style.use('kostas') - -def initial_xy_polar(r_min, r_max, r_N, theta_min, theta_max, theta_N): - return np.array([[(r*np.cos(theta),r*np.sin(theta)) for r in np.linspace(r_min,r_max,r_N)] for theta in np.linspace(theta_min,theta_max,theta_N)]) - -def initial_xy_cartesian(x_min, x_max, x_N, y_min, y_max, y_N): - return np.array([[(x,y) for x in np.linspace(x_min,x_max,x_N)] for y in np.linspace(y_min,y_max,y_N)]) - - -def draw_footprint(A, axis_object=None, figure_object=None, axis=0, linewidth=4): - ''' - Input A should be a 3-D numpy array with shape (Nx,Ny,2) - representing a 2-D array of (x,y) points. This function - will draw lines between adjacent points in the 2-D array. - ''' - if len(A.shape) != 3: - print('ERROR: Invalid input matrix') - return None - if A.shape[2] != 2: - print('ERROR: Points are not defined in 2D space') - return None - - sx = A.shape[0]-1 - sy = A.shape[1]-1 - - p1 = A[:-1,:-1,:].reshape(sx*sy,2)[:,:] - p2 = A[1:,:-1,:].reshape(sx*sy,2)[:] - p3 = A[1:,1:,:].reshape(sx*sy,2)[:] - p4 = A[:-1,1:,:].reshape(sx*sy,2)[:] - - #Stack endpoints to form polygons - Polygons = np.stack((p1,p2,p3,p4)) - #transpose polygons - Polygons = np.transpose(Polygons,(1,0,2)) - patches = list(map(matplotlib.patches.Polygon,Polygons)) - - #assign colors - patch_colors = [(0,0,0) for a in Polygons] - patch_colors[(sx-1)*sy:] = [(0,1,0)]*sy - patch_colors[(sy-1)::sy] = [(0,0,1)]*sx - - p_collection = matplotlib.collections.PatchCollection(patches,facecolors=[],linewidth=linewidth,edgecolor=patch_colors) - - if axis_object is None: - if figure_object: - fig = figure_object - else: - fig = plt.figure() - if len(fig.axes) == 0: - plt.subplot(1,1,1) - if axis >= len(fig.axes) or axis < 0: - i = 0 - else: - i = axis - ax = fig.axes[i] - else: - ax = axis_object - fig = None - - ax.add_collection(p_collection) - - return fig - -def example(): - coords_matrix = initial_xy_polar(0.01,5,11,0.,np.pi/2.,10) - fig = draw_footprint(coords_matrix) - fig.axes[0].set_xlim(-0.05,5.1) - fig.axes[0].set_ylim(-0.05,5.1) diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 deleted file mode 100644 index e804b14..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_beginning.3 +++ /dev/null @@ -1,33 +0,0 @@ -GEOME-STRENG TITLE:ts_collisions_ats30_en20_IMO380_C7_X160_I1.2_62.3100_60.3200%1%s%62.31_60.32%8_10%5%80 -PRINTOUT OF INPUT PARAMETERS-------------------------------------------- -NEXT -TRACKING PARAMETERS----------------------------------------------------- -1 0 1 0.00 0.00 0 1 -1 1 0 1 2 -0 0 1 1 1000 50000 2 -NEXT -INITIAL COORDINATES----------------------------------------------------- - 2 0. 0. 0.999999 0 - 0. - 0. - 0. - 0. - 0. - 0. - .0 - 0.01 - 0.003 - 0. - 0. - 0.000 - 7000000. - 7000000. - 7000000. -NEXT -SYNCHROTRON OSCILLATIONS--------'PLACE AFTER TRACKING PARAMETERS'------- - 35640 .000347 16. 0. 26658.883200 938.272013 1 - 1. 1. -NEXT -DUMP -ALL 1 664 101 dump3.dat -NEXT diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 deleted file mode 100644 index 64968ba..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/fort_parts/fort_end.3 +++ /dev/null @@ -1,57 +0,0 @@ -FLUCTUATION -100000 1 7 3 -NEXT -/TUNE VARIATION---------------------------------------------------------- -/ mq.7r3.b1 62.31 -/ mq.8r3.b1 60.32 -// 0. -// -/NEXT -/CHROMATICITY CORRECTION------------------------------------------------ -/ ms.11r3.b1 2. -/ ms.12r3.b1 2. -/NEXT -POSTPROCESSING---------------------------------------------------------- -LHC Dynamic Aperture at Injection Version 5 (1998) -20 0 0 1 .08 .08 1 0 0 1. 1. -62. 60. 1 1 10 .005 1 .050 -0 1 0 1 1 1 1 1 30 -NEXT -/DIFFERENTIAL ALGEBRA--------------------------------------------------- -/%NO %NV 1E-38 %nsix 0 -/NEXT -ENDE==================================================================== -ORGA-------------------------------------------------------------------- -NEXT -DIFFERENTIAL ALGEBRA--------------------------------------------------- -4 5 1E-38 1 1 -NEXT -COMBINATION OF ELEMENTS------------------------------------------------- -CODSDO 1. CODSDE -CODSFO 1. CODSFE -NEXT -SUBRESONANCE CALCULATION------------------------------------------------ -3 3 62.31 60.32 1. 1. 0 26658.883200 -NEXT -RESONANCE COMPENSATION ------------------------------------------------- - 1 3 0 0 0 0 0 0 - 0 0 0 0 - 26658.883200 62.31 60.32 1. 1. - MSCBH MSCBV - 0 MSCBH MSCBV - 0 QF QD 62.31 60.32 -NEXT -RIPPLE OF POWER SUPPLIES------------------------------------------------ - Q413R -1.54D-4 4340.580 - Q414R -3.70D-4 4340.580 -NEXT -LIMITATION OF APERTURE-------------------------------------------------- - AP1 EL 10. 10. -NEXT -SEARCH FOR THE BEST POSITIONS FOR COMPENSATION-ELEMENTS----------------- - 62.31 60.32 1. 1. 26658.883200 - 2 3 0 2 -2 0 0 0 - MSCBH MSCBV -NEXT -ORBIT ADJUSTMENT-------------------------------------------------------- -NEXT diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py deleted file mode 100644 index 171c093..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/helpers.py +++ /dev/null @@ -1,222 +0,0 @@ -import numpy as np -import os -import sixtracktools -import time - -import xpart as xp -import xtrack as xt - -def vectorize_all_coords(Dx_wrt_CO_m, Dpx_wrt_CO_rad, - Dy_wrt_CO_m, Dpy_wrt_CO_rad, - Dzeta_wrt_CO_m, Ddelta_wrt_CO): - - n_part = 1 - for var in [Dx_wrt_CO_m, Dpx_wrt_CO_rad, - Dy_wrt_CO_m, Dpy_wrt_CO_rad, - Dzeta_wrt_CO_m, Ddelta_wrt_CO]: - if hasattr(var, '__iter__'): - if n_part == 1: - n_part = len(var) - assert len(var) == n_part - - Dx_wrt_CO_m = Dx_wrt_CO_m + np.zeros(n_part) - Dpx_wrt_CO_rad = Dpx_wrt_CO_rad + np.zeros(n_part) - Dy_wrt_CO_m = Dy_wrt_CO_m + np.zeros(n_part) - Dpy_wrt_CO_rad = Dpy_wrt_CO_rad + np.zeros(n_part) - Dzeta_wrt_CO_m = Dzeta_wrt_CO_m + np.zeros(n_part) - Ddelta_wrt_CO = Ddelta_wrt_CO + np.zeros(n_part) - - return Dx_wrt_CO_m, Dpx_wrt_CO_rad,\ - Dy_wrt_CO_m, Dpy_wrt_CO_rad,\ - Dzeta_wrt_CO_m, Ddelta_wrt_CO - - -def track_particle_sixtrack( - partCO, Dx_wrt_CO_m, Dpx_wrt_CO_rad, - Dy_wrt_CO_m, Dpy_wrt_CO_rad, - Dzeta_wrt_CO_m, Ddelta_wrt_CO, n_turns, - input_folder='./'): - - Dx_wrt_CO_m, Dpx_wrt_CO_rad,\ - Dy_wrt_CO_m, Dpy_wrt_CO_rad,\ - Dzeta_wrt_CO_m, Ddelta_wrt_CO = vectorize_all_coords( - Dx_wrt_CO_m, Dpx_wrt_CO_rad, - Dy_wrt_CO_m, Dpy_wrt_CO_rad, - Dzeta_wrt_CO_m, Ddelta_wrt_CO) - - n_part = len(Dx_wrt_CO_m) - - wfold = 'temp_trackfun' - - if not os.path.exists(wfold): - os.mkdir(wfold) - - os.system(f'cp {input_folder}/fort.* {wfold}') - - with open(f'{wfold}/fort.3', 'r') as fid: - lines_f3 = fid.readlines() - - # Set initial coordinates - i_start_ini = None - for ii, ll in enumerate(lines_f3): - if ll.startswith('INITIAL COO'): - i_start_ini = ii - break - - lines_f3[i_start_ini + 2] = ' 0.\n' - lines_f3[i_start_ini + 3] = ' 0.\n' - lines_f3[i_start_ini + 4] = ' 0.\n' - lines_f3[i_start_ini + 5] = ' 0.\n' - lines_f3[i_start_ini + 6] = ' 0.\n' - lines_f3[i_start_ini + 7] = ' 0.\n' - - lines_f3[i_start_ini + 2 + 6] = ' 0.\n' - lines_f3[i_start_ini + 3 + 6] = ' 0.\n' - lines_f3[i_start_ini + 4 + 6] = ' 0.\n' - lines_f3[i_start_ini + 5 + 6] = ' 0.\n' - lines_f3[i_start_ini + 6 + 6] = ' 0.\n' - lines_f3[i_start_ini + 7 + 6] = ' 0.\n' - - lines_f13 = [] - - for i_part in range(n_part): - if type(partCO) == xp.Particles: - temp_part = partCO.copy() - else: - temp_part = xp.Particles(**partCO) - temp_part.x += Dx_wrt_CO_m[i_part] - temp_part.px += Dpx_wrt_CO_rad[i_part] - temp_part.y += Dy_wrt_CO_m[i_part] - temp_part.py += Dpy_wrt_CO_rad[i_part] - temp_part.zeta += Dzeta_wrt_CO_m[i_part] - temp_part.update_delta(temp_part.delta + Ddelta_wrt_CO[i_part]) - - lines_f13.append('%.10e\n' % ((temp_part.x) * 1e3)) - lines_f13.append('%.10e\n' % ((temp_part.px) * temp_part.rpp * 1e3)) - lines_f13.append('%.10e\n' % ((temp_part.y) * 1e3)) - lines_f13.append('%.10e\n' % ((temp_part.py) * temp_part.rpp * 1e3)) - lines_f13.append('%.10e\n' % ((temp_part.zeta) * 1e3)) - lines_f13.append('%.10e\n' % ((temp_part.delta))) - if i_part % 2 == 1: - lines_f13.append('%.10e\n' % (temp_part.energy0 * 1e-6)) - lines_f13.append('%.10e\n' % (prev_part.energy * 1e-6)) - lines_f13.append('%.10e\n' % (temp_part.energy * 1e-6)) - prev_part = temp_part - - with open(wfold + '/fort.13', 'w') as fid: - fid.writelines(lines_f13) - - if np.mod(n_part, 2) != 0: - raise ValueError('SixTrack does not like this!') - - i_start_tk = None - for ii, ll in enumerate(lines_f3): - if ll.startswith('TRACKING PAR'): - i_start_tk = ii - break - # Set number of turns and number of particles - temp_list = lines_f3[i_start_tk + 1].split(' ') - temp_list[0] = '%d' % n_turns - temp_list[2] = '%d' % (n_part / 2) - lines_f3[i_start_tk + 1] = ' '.join(temp_list) - # Set number of idfor = 2 - temp_list = lines_f3[i_start_tk + 2].split(' ') - temp_list[2] = '2' - lines_f3[i_start_tk + 2] = ' '.join(temp_list) - - # Setup turn-by-turn dump - i_start_dp = None - for ii, ll in enumerate(lines_f3): - if ll.startswith('DUMP'): - i_start_dp = ii - break - - lines_f3[i_start_dp + 1] = 'StartDUMP 1 664 101 dumtemp.dat\n' - - with open(wfold + '/fort.3', 'w') as fid: - fid.writelines(lines_f3) - - os.system('(cd temp_trackfun; sixtrack >fort.6)') - - # Load sixtrack tracking data - sixdump_all = sixtracktools.SixDump101('%s/dumtemp.dat' % wfold) - - x_tbt = np.zeros((n_turns, n_part)) - px_tbt = np.zeros((n_turns, n_part)) - y_tbt = np.zeros((n_turns, n_part)) - py_tbt = np.zeros((n_turns, n_part)) - zeta_tbt = np.zeros((n_turns, n_part)) - delta_tbt = np.zeros((n_turns, n_part)) - - for i_part in range(n_part): - sixdump_part = sixdump_all[i_part::n_part] - x_tbt[:, i_part] = sixdump_part.x - px_tbt[:, i_part] = sixdump_part.px - y_tbt[:, i_part] = sixdump_part.y - py_tbt[:, i_part] = sixdump_part.py - zeta_tbt[:, i_part] = sixdump_part.tau * sixdump_part.beta0 - delta_tbt[:, i_part] = sixdump_part.delta - - extra = {} - - return x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra - - - -def track_particle_xtrack( - line, partCO, Dx_wrt_CO_m, Dpx_wrt_CO_rad, - Dy_wrt_CO_m, Dpy_wrt_CO_rad, - Dzeta_wrt_CO_m, Ddelta_wrt_CO, n_turns, - _context=None): - - Dx_wrt_CO_m, Dpx_wrt_CO_rad,\ - Dy_wrt_CO_m, Dpy_wrt_CO_rad,\ - Dzeta_wrt_CO_m, Ddelta_wrt_CO = vectorize_all_coords( - Dx_wrt_CO_m, Dpx_wrt_CO_rad, - Dy_wrt_CO_m, Dpy_wrt_CO_rad, - Dzeta_wrt_CO_m, Ddelta_wrt_CO) - - tracker = xt.Tracker(_context=_context, line=line) - particles = xp.Particles(_context=_context, - p0c = partCO.p0c, - x = partCO.x + Dx_wrt_CO_m, - px = partCO.px + Dpx_wrt_CO_rad, - y = partCO.y + Dy_wrt_CO_m, - py = partCO.py + Dpy_wrt_CO_rad, - zeta = partCO.zeta + Dzeta_wrt_CO_m, - delta = partCO.delta + Ddelta_wrt_CO) - - print('Start track') - tracker.track(particles, num_turns=n_turns, turn_by_turn_monitor=True) - print('Done track') - - #print(res.particles[0]) - x_tbt = tracker.record_last_track.x.copy().T - px_tbt = tracker.record_last_track.px.copy().T - y_tbt = tracker.record_last_track.y.copy().T - py_tbt = tracker.record_last_track.py.copy().T - zeta_tbt = tracker.record_last_track.zeta.copy().T - delta_tbt = tracker.record_last_track.delta.copy().T - - print('Done loading!') - - extra = {'particles': particles, 'tracker': tracker} - - return x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra - - -def betafun_from_ellip(x_tbt, px_tbt): - - x_max = np.max(x_tbt) - mask = np.logical_and(np.abs(x_tbt) < x_max / 5., px_tbt > 0) - x_masked = x_tbt[mask] - px_masked = px_tbt[mask] - ind_sorted = np.argsort(x_masked) - x_sorted = np.take(x_masked, ind_sorted) - px_sorted = np.take(px_masked, ind_sorted) - - px_cut = np.interp(0, x_sorted, px_sorted) - - beta_x = x_max / px_cut - - return beta_x, x_max, px_cut diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh deleted file mode 100644 index bc282b9..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/install_miniconda.sh +++ /dev/null @@ -1,22 +0,0 @@ -wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -bash Miniconda3-latest-Linux-x86_64.sh -b -p ./miniconda -f -source miniconda/bin/activate -pip install ipython jupyterlab numpy scipy pandas awkward matplotlib -pip install pyarrow pyyaml pytest -pip install cpymad -pip install xsuite -git clone git@github.com:lhcopt/lhcerrors.git -git clone git@github.com:lhcopt/lhctoolkit.git -git clone git@github.com:lhcopt/lhcmask.git -git clone git@github.com:lhcopt/beambeam_macros.git -cd beambeam_macros -gfortran headonslice.f -o headonslice -cd .. -cd lhcmask -git checkout release/v1.3.1 -pip install -e . -cd ../ -git clone https://github.com/PyCOMPLETE/FillingPatterns.git -pip install ./FillingPatterns -python -m pip install sixtracktools -python -m pip install NAFFlib diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py deleted file mode 100644 index d935183..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t000_plots_from_twiss.py +++ /dev/null @@ -1,38 +0,0 @@ -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt - -twiss_df = pd.read_parquet('../twiss_with_crossing_seq_lhcb1.parquet') - - -plt.close('all') - -# %% -fig = plt.figure(1, figsize=(6.4*1.6, 4.8*1.5)) -axbetx = fig.add_subplot(2,2,1) -axbetx.plot(twiss_df['s'], twiss_df['betx']) -axbetx.set_xlabel('s') -axbetx.set_ylabel(r'$\beta_x$ [m]') -axbetx.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') - -# %% -axbety = fig.add_subplot(2,2,2, sharex=axbetx, sharey=axbetx) -axbety.plot(twiss_df['s'], twiss_df['bety']) -axbety.set_xlabel('s') -axbety.set_ylabel(r'$\beta_y$ [m]') -axbety.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') -axbety.legend() -# %% - -axcox = fig.add_subplot(2,2,3, sharex=axbetx) -axcox.plot(twiss_df['s'], twiss_df['x']) -axcox.set_xlabel('s') -axcox.set_ylabel('x [m]') -#plt.xlim(5500,7500) -# %% -axcoy = fig.add_subplot(2,2,4, sharex=axbetx, sharey=axcox) -axcoy.plot(twiss_df['s'], twiss_df['y']) -axcoy.set_xlabel('s') -axcoy.set_ylabel('y [m]') - -plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py deleted file mode 100644 index 6bf143e..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t001_check_b4_against_b2.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt - -twiss_df_b2 = pd.read_parquet('../twiss_b2_for_b4check_seq_lhcb2.parquet') -twiss_df_b4 = pd.read_parquet('../twiss_b4_for_b4check_seq_lhcb2.parquet') - - -plt.close('all') - -# %% -fig = plt.figure(1, figsize=(6.4*1.6, 4.8*1.5)) -axbetx = fig.add_subplot(2,2,1) -axbetx.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],twiss_df_b4['betx'],'b') -axbetx.plot(twiss_df_b2['s'], twiss_df_b2['betx'],'--r') -axbetx.set_xlabel('s') -axbetx.set_ylabel(r'$\beta_x$ [m]') -axbetx.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') - -# %% -axbety = fig.add_subplot(2,2,2, sharex=axbetx, sharey=axbetx) -axbety.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],twiss_df_b4['bety'],'b', - label='b4') -axbety.plot(twiss_df_b2['s'], twiss_df_b2['bety'],'--r', label='b2') -axbety.set_xlabel('s') -axbety.set_ylabel(r'$\beta_y$ [m]') -axbety.ticklabel_format(style='sci', scilimits=(0, 0), axis='y') -axbety.legend() -# %% - -axcox = fig.add_subplot(2,2,3, sharex=axbetx) -axcox.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],-twiss_df_b4['x'],'b') -axcox.plot(twiss_df_b2['s'], twiss_df_b2['x'], '--r') -axcox.set_xlabel('s') -axcox.set_ylabel('x [m]') -#plt.xlim(5500,7500) -# %% -axcoy = fig.add_subplot(2,2,4, sharex=axbetx, sharey=axcox) -axcoy.plot(twiss_df_b4['s'][-1]-twiss_df_b4['s'],twiss_df_b4['y'],'b') -axcoy.plot(twiss_df_b2['s'], twiss_df_b2['y'], '--r') -axcoy.set_xlabel('s') -axcoy.set_ylabel('y [m]') - -plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py deleted file mode 100644 index 6829953..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t003_fc_to_fort.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import shutil - -folder_list = [ - '../', - '../../../examples/hl_lhc_collision/', - '../../../examples/hl_lhc_collision_nobb_b4'] - -for sixtrack_input_folder in folder_list: - print(sixtrack_input_folder) - try: - for iff in [2,8,16,34]: - os.system(f"rm {sixtrack_input_folder}/fort.{iff}") - try: - shutil.copy(sixtrack_input_folder + f"/fc.{iff}", - sixtrack_input_folder + f"/fort.{iff}") - except Exception: - print(f"/fc.{iff} not found!") - - with open(sixtrack_input_folder + "/fort.3", "w") as fout: - with open("fort_parts/fort_beginning.3", "r") as fid_fort3b: - fout.write(fid_fort3b.read()) - with open(sixtrack_input_folder + "/fc.3", "r") as fid_fc3: - fout.write(fid_fc3.read()) - with open("fort_parts/fort_end.3", "r") as fid_fort3e: - fout.write(fid_fort3e.read()) - except Exception as e: - print('Skipped...') - print(e) - -print('Ended.') diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py deleted file mode 100644 index 6dcd74b..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t004_check_output_consistency.py +++ /dev/null @@ -1,312 +0,0 @@ -import time -import shutil -import pickle -import json -import numpy as np - -import sixtracktools -import xtrack as xt -import xfields as xf - - - - -# Tests b1 with bb -tests = [ - { - 'test_name': 'B1 - pymask sixtrack input vs madx mask', - 'path_test': '../', - 'type_test': 'sixtrack', - 'path_ref': '../../../examples/hl_lhc_collision', - 'type_ref': 'sixtrack', - 'rtol': 3e-5, - 'atol': 1e-12, - 'strict': False, - }, - { - 'test_name': 'B1 - pymask xsuite vs pymask sixtrack input', - 'path_test': '../xsuite_lines/line_bb_dipole_not_cancelled.json', - 'type_test': 'xsuite', - 'path_ref': '../', - 'type_ref': 'sixtrack', - 'rtol': 4e-7, - 'atol': 1e-13, - 'strict': True, - } -] - -# # Tests b4 no bb -# tests = [ -# { -# 'test_name': 'B4 - pymask sixtrack input vs madx mask', -# 'path_test': '../', -# 'type_test': 'sixtrack', -# 'path_ref': '../../../examples/hl_lhc_collision_nobb_b4', -# 'type_ref': 'sixtrack', -# 'rtol': 8e-5, -# 'atol': 1e-12, -# 'strict': False, -# }, -# { -# 'test_name': 'B4 - pymask xsuite vs pymask sixtrack input', -# 'path_test': '../xsuite/line_bb_dipole_not_cancelled.json', -# 'type_test': 'xsuite', -# 'path_ref': '../', -# 'type_ref': 'sixtrack', -# 'rtol': 4e-7, -# 'atol': 1e-100, -# 'strict': True, -# } -# ] - -def norm(x): - return np.sqrt(np.sum(np.array(x) ** 2)) - -def prepare_line(path, input_type): - - if input_type == 'xsuite': - # Load machine - with open(path, 'r') as fid: - ltest = xt.Line.from_dict(json.load(fid)) - elif input_type == 'sixtrack': - print('Build xsuite from sixtrack input:') - sixinput_test = sixtracktools.sixinput.SixInput(path) - ltest = xt.Line.from_sixinput(sixinput_test) - print('Done') - else: - raise ValueError('What?!') - - return ltest - -for tt in tests: - test_name = tt['test_name'] - path_test = tt['path_test'] - type_test = tt['type_test'] - path_ref = tt['path_ref'] - type_ref = tt['type_ref'] - rtol = tt['rtol'] - atol = tt['atol'] - strict = tt['strict'] - - # Load - ltest = prepare_line(path_test, type_test) - lref = prepare_line(path_ref, type_ref) - original_length = ltest.get_length() - assert (lref.get_length() - original_length) < 1e-6 - - # Simplify the two machines - for ll in (ltest, lref): - ll._var_management = None - ll.remove_inactive_multipoles(inplace=True) - ll.remove_zero_length_drifts(inplace=True) - ll.merge_consecutive_drifts(inplace=True) - ll.merge_consecutive_multipoles(inplace=True) - - # Remove inactive RFMultipoles and normalize phase - for ii, ee in enumerate(ll.elements): - if ee.__class__.__name__ == 'RFMultipole': - if ( np.max(np.abs(ee.knl)) < 1e-20 and - np.max(np.abs(ee.ksl)) < 1e-20 and - abs(ee.voltage) < 1e-20): - ll.element_names[ii] = None - # # Normalize phase - # for kkll, pp in [[ee.knl, ee.pn], - # [ee.ksl, ee.ps]]: - # for ii, vv in enumerate(kkll): - # if vv < 0: - # kkll[ii] = -vv - # pp[ii] += 180 - # if pp[ii]>180: - # pp[ii] -= 360 - - while None in ll.element_names: - ll.element_names.remove(None) - - # Check that the two machines are identical - assert len(ltest) == len(lref) - - assert (ltest.get_length() - original_length) < 1e-6 - assert (lref.get_length() - original_length) < 1e-6 - - for ii, (ee_test, ee_six, nn_test, nn_six) in enumerate( - zip(ltest.elements, lref.elements, ltest.element_names, lref.element_names) - ): - assert type(ee_test) == type(ee_six) - - dtest = ee_test.to_dict() - dref = ee_six.to_dict() - - for kk in dtest.keys(): - - # Check if they are identical - if np.isscalar(dref[kk]) and dtest[kk] == dref[kk]: - continue - - if isinstance(dref[kk], dict): - if kk=='fieldmap': - continue - if kk=='boost_parameters': - continue - if kk=='Sigmas_0_star': - continue - - # Check if the relative error is small - val_test = dtest[kk] - val_ref = dref[kk] - if not np.isscalar(val_ref): - if len(val_ref) != len(val_test): - diff_rel = 100 - else: - for iiii, (vvr, vvt) in enumerate(list(zip(val_ref, val_test))): - if not np.isclose(vvr, vvt, atol=atol, rtol=rtol): - print(f'Issue found on `{kk}[{iiii}]`') - diff_rel = 1000 - else: - diff_rel = 0 - else: - if val_ref > 0: - diff_rel = np.abs((val_test - val_ref)/val_ref) - else: - diff_rel = 100 - - if diff_rel < rtol: - continue - - # Check if absolute error is small - - if not np.isscalar(val_ref) and len(val_ref) != len(val_test): - diff_abs = 1000 - else: - diff_abs = norm(np.array(val_test) - np.array(val_ref)) - if diff_abs > 0: - print(f"{nn_test}[{kk}] - test:{dtest[kk]} six:{dref[kk]}") - if diff_abs < atol: - continue - - # Exception: drift length (100 um tolerance) - if not(strict) and isinstance(ee_test, xt.Drift): - if kk == "length": - if diff_abs < 1e-4: - continue - - # Exception: multipole lrad is not passed to sixtraxk - if isinstance(ee_test, xt.Multipole): - if kk == "length": - if np.abs(ee_test.hxl) + np.abs(ee_test.hyl) == 0.0: - continue - if kk == "order": - # Checked through bal - continue - if kk == 'knl' or kk == 'ksl' or kk == 'bal': - if len(val_ref) != len(val_test): - lmin = min(len(val_ref), len(val_test)) - for vv in [val_ref,val_test]: - if len(vv)> lmin: - for oo in range(lmin, len(vv)): - # we do not care about errors above 10 - if vv[oo] != 0 and oo < {'knl':10, - 'ksl':10, 'bal':20}[kk]: - raise ValueError( - 'Missing significant multipole strength') - - val_ref = val_ref[:lmin] - val_test = val_test[:lmin] - - if len(val_ref) == 0 and len(val_test) == 0: - continue - else: - for iiii, (vvr, vvt) in enumerate(list(zip(val_ref, val_test))): - if not np.isclose(vvr, vvt, atol=atol, rtol=rtol): - print(f'Issue found on `{kk}[{iiii}]`') - diff_rel = 1000 - break - else: - diff_rel = 0 - if diff_rel < rtol: - continue - - # Exception: correctors involved in lumi leveling - passed_corr = False - for nn_corr in [ - 'mcbcv.5l8.b1', 'mcbyv.a4l8.b1', 'mcbxv.3l8', - 'mcbyv.4r8.b1', 'mcbyv.b5r8.b1', - 'mcbyh.b5l2.b1', 'mcbyh.4l2.b1', 'mcbxh.3l2', 'mcbyh.a4r2.b1', - 'mcbch.5r2.b1', - 'mcbcv.5l8.b2', 'mcbyv.a4l8.b2', 'mcbxv.3l8', - 'mcbyv.4r8.b2', 'mcbyv.b5r8.b2', - 'mcbyh.b5l2.b2', 'mcbyh.4l2.b2', 'mcbxh.3l2', 'mcbyh.a4r2.b2', - 'mcbch.5r2.b2', 'mcbch.a5r2.b2', 'mcbyh.4r2.b2', 'mcbxh.3r2', - 'mcbyh.a4l2.b2', 'mcbyh.5l2.b2', 'mcbyv.5r8.b2', 'mcbyv.a4r8.b2', - 'mcbxv.3r8', 'mcbyv.4l8.b2', 'mcbcv.b5l8.b2']: - if nn_corr in nn_test and kk in ['knl','ksl','bal']: - assert len(val_ref)<=2 - assert len(val_test)<=2 - diff_rel = norm(val_test-val_ref)/norm(val_ref) - passed_corr = (diff_rel < 1e-2) - if passed_corr: - break - if not(strict) and passed_corr: - continue - - # Exceptions BB4D (separations are recalculated) - if not(strict) and isinstance(ee_test, xf.BeamBeamBiGaussian2D): - if kk == "x_bb": - if diff_abs / dtest["sigma_x"] < 0.01: # This is neede to accommodate different leveling routines (1% difference) - continue - if kk == "y_bb": - if diff_abs / dtest["sigma_y"] < 0.01: - continue - if kk == "sigma_x": - if diff_rel < 1e-5: - continue - if kk == "sigma_y": - if diff_rel < 1e-5: - continue - if isinstance(ee_test, xf.BeamBeamBiGaussian2D): - if kk == 'q0' or kk == 'n_particles': - # ambiguity due to old interface - if np.abs(ee_test.n_particles*ee_test.q0 - - ee_six.n_particles*ee_six.q0 ) < 1.: # charges - continue - - # Exceptions BB6D (angles and separations are recalculated) - if not(strict) and isinstance(ee_test, xf.BeamBeamBiGaussian3D): - if kk == "alpha": - if diff_abs < 10e-6: - continue - if kk == "x_co" or kk == "x_bb_co" or kk == 'delta_x': - if diff_abs / np.sqrt(dtest["sigma_11"]) < 0.015: - continue - if kk == "y_co" or kk == "y_bb_co" or kk == 'delta_y': - if diff_abs / np.sqrt(dtest["sigma_33"]) < 0.015: - continue - if kk == "zeta_co": - if diff_abs <1e-5: - continue - if kk == "delta_co": - if diff_abs <1e-5: - continue - if kk == "px_co" or kk == 'py_co': - if diff_abs <30e-9: - continue - if isinstance(ee_test, xt.XYShift): - if kk in ['dx','dy']: - if diff_abs <1e-9: - continue - if isinstance(ee_test, xt.SRotation): - if kk in ['sin_z', 'cos_z', 'angle']: - if diff_abs <1e-9: - continue - # If it got here it means that no condition above is met - raise ValueError("Too large discrepancy!") - print( - f""" - ******************************************************************* - test: {test_name} - - The line from test seq. and the line from reference are identical! - ******************************************************************* - """ - ) - - time.sleep(1) diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py deleted file mode 100644 index 0e55115..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t005_check_crabs.py +++ /dev/null @@ -1,242 +0,0 @@ -import shutil -import numpy as np -import json - -import xtrack as xt -import xpart as xp -import xfields as xf -import sixtracktools - -L_lhc = 27e3 -h_cc = 35640 - -# Mad-X knobs -Phi = 250e-6 -Phi_c = -190e-6 - -# B1 ip5 -beam_track = 'b1' -ip_choice = 5 -plane = 'y' -phi_weak = Phi -phi_c_weak = Phi_c - -# B1 ip1 -beam_track = 'b1' -ip_choice = 1 -plane = 'x' -phi_weak = Phi -phi_c_weak = Phi_c - -# B4 ip5 -beam_track = 'b4' -ip_choice = 5 -plane = 'y' -phi_weak = Phi -phi_c_weak = Phi_c - -# B4 ip1 -beam_track = 'b4' -ip_choice = 1 -plane = 'x' -phi_weak = -Phi -phi_c_weak = -Phi_c - -phi_strong = -phi_weak -phi_c_strong = -phi_c_weak - -path_test = '../' -type_test = 'sixtrack' - -#path_test = '../xsuite_lines/line_bb_dipole_not_cancelled.json' -#type_test = 'xsuite' - -def prepare_line(path, input_type): - - if input_type == 'xsuite': - # Load xsuite machine - with open(path, 'r') as fid: - ltest = xt.Line.from_dict(json.load(fid)) - elif input_type == 'sixtrack': - print('Build xsuite from sixtrack input:') - sixinput_test = sixtracktools.sixinput.SixInput(path) - ltest = sixinput_test.generate_xtrack_line() - print('Done') - else: - raise ValueError('What?!') - - return ltest - -# Load -ltest = prepare_line(path_test, type_test) - - -s_all_elems = ltest.get_s_elements() -names_all_elems = ltest.element_names -all_elems = ltest.elements - -bb_names = [] -bb_s = [] -bb_elems = [] -bb_index = [] -for ii, (nn, ss, ee) in enumerate(zip(names_all_elems, s_all_elems, all_elems)): - if nn.startswith('bb_ho'): - if f'{ip_choice}b' in nn: - bb_names.append(nn) - bb_s.append(ss) - bb_elems.append(ee) - bb_index.append(ii) - -iho = np.where(['.c' in nn for nn in bb_names])[0][0] -sho = bb_s[iho] -s_rel = np.array(bb_s) - sho - -import matplotlib.pyplot as plt -plt.close('all') -fig1 = plt.figure(1) -axcrab = fig1.add_subplot(111) - - -# For B1 as weak beam (and we plot B2 as stron beam) -# phi_c_strong = dR_crab / dz -# phi_strong = dR_crab / ds -R_crab = phi_c_strong * L_lhc / (2*np.pi*h_cc) *np.sin(2*np.pi*h_cc/L_lhc*2*s_rel) -R_no_crab = phi_strong * s_rel -R1_orbit = phi_weak * s_rel - -axcrab.plot(s_rel, np.array( - [getattr(bb, f'delta_{plane}') for bb in bb_elems])+R1_orbit, - 'o', color = 'r', alpha=.5, label='strong xsuite') -plt.plot(s_rel, R_no_crab + R_crab, '*', color='darkred', label='strong formula') - -#axcrab.plot(s_rel, np.array([bb.y_bb_co for bb in bb_elems]), 'o', color='r', alpha=.5) -#plt.plot(s_rel, Y_no_crab + Y_crab - Y1_orbit, '*', color='darkred') - -#plt.plot(s_rel, Y_no_crab, 'xr') -#plt.plot(s_rel, Y_crab, 'xb') -#plt.figure() -#plt.plot(s_rel, np.array([bb.y_bb_co for bb in bb_elems]) -# /(2*s_rel), 'o') - - -# Chek crabs in weak beam - -# Switch off all beam-beam lenses -bb_all, _ = ltest.get_elements_of_type([xf.BeamBeamBiGaussian2D, - xf.BeamBeamBiGaussian3D]) -for bb in bb_all: - bb.q0 = 0 - -# # Switch off all beam-beam lenses -crabs, crab_names = ltest.get_elements_of_type([xt.RFMultipole]) -#for cc in crabs: -# cc.pn = [-90] -# cc.ps = [-90] - -# for cc in crabs: -# cc.knl[0] *= -1 - -with open('../optics_orbit_at_start_ring_from_madx.json', 'r') as fid: - ddd = json.load(fid) -partco = xp.Particles.from_dict(ddd['particle_on_madx_co']) -z_slices = s_rel * 2.0 -partco = xp.build_particles(particle_on_co=partco, mode='shift', zeta=z_slices) - -tracker=xt.Tracker(line=ltest) -tracker.track(partco, turn_by_turn_monitor='ONE_TURN_EBE') -first_turn = tracker.record_last_track - -tracker.track(partco, turn_by_turn_monitor='ONE_TURN_EBE') -second_turn = tracker.record_last_track - -plt.figure(2) -axcox = plt.subplot(2,1,1) -axcoy = plt.subplot(2,1,2, sharex=axcox) -plt.suptitle('Check closed orbit 2 turns') -axcox.plot(first_turn.s[iho,:] , first_turn.x[iho,:]) -axcox.plot(second_turn.s[iho,:] , second_turn.x[iho,:]) - -axcoy.plot(first_turn.s[iho,:] , first_turn.y[iho,:]) -axcoy.plot(second_turn.s[iho,:] , second_turn.y[iho,:]) - -plt.figure(20) -axcopx = plt.subplot(2,1,1, sharex=axcox) -axcopy = plt.subplot(2,1,2, sharex=axcox) -plt.suptitle('Check closed orbit 2 turns') -axcopx.plot(first_turn.s[iho,:] , first_turn.px[iho,:]) -axcopx.plot(second_turn.s[iho,:] , second_turn.px[iho,:]) - -axcopy.plot(first_turn.s[iho,:] , first_turn.py[iho,:]) -axcopy.plot(second_turn.s[iho,:] , second_turn.py[iho,:]) - -plt.figure(3) -axcox = plt.subplot(2,1,1) -axcoy = plt.subplot(2,1,2, sharex=axcox) -plt.suptitle('Check crab') -for iz, zz in enumerate(z_slices): - axcox.plot(first_turn.s[iz,:] , first_turn.x[iz,:]) - axcoy.plot(first_turn.s[iz,:] , first_turn.y[iz,:]) - -# For each s_lens, we find the transverse position of the weak beam slice -# that collides with the sycnhronous particle of the strong -r_lenses = [] -for ibb, bb in enumerate(bb_elems): - #r_lenses.append(getattr(list_co[bb_index[ibb]], plane)[ibb]) - r_lenses.append(getattr(first_turn, plane)[ibb, bb_index[ibb]]) - -axcrab.plot(s_rel, r_lenses, 'o', color='b', alpha=.5, label= 'weak xsuite') -Rw_crab = phi_c_weak * L_lhc / (2*np.pi*h_cc) *np.sin(2*np.pi*h_cc/L_lhc*2*s_rel) -Rw_no_crab = phi_weak * s_rel -axcrab.plot(s_rel, Rw_crab + Rw_no_crab, '*', color='darkblue', - label='weak formula') -axcrab.legend(loc='best') - -# Check crab bump -import pandas as pd -z_crab_twiss = 0.075 - -if beam_track == 'b1': - crab_df = pd.read_parquet(f'../twiss_z_crab_{z_crab_twiss:.5f}_seq_lhcb1.parquet') - s_twiss = crab_df.s.values - x_twiss = crab_df.x.values - y_twiss = crab_df.y.values - px_twiss = crab_df.px.values - py_twiss = crab_df.py.values - z_crab_track = z_crab_twiss -elif beam_track == 'b4': - crab_df = pd.read_parquet(f'../twiss_z_crab_{z_crab_twiss:.5f}_seq_lhcb2.parquet') - s_twiss = -crab_df.s.values[::-1] - s_twiss -= s_twiss[0] - x_twiss = -crab_df.x.values[::-1] - y_twiss = crab_df.y.values[::-1] - px_twiss = crab_df.px.values[::-1] - py_twiss = -crab_df.py.values[::-1] - z_crab_track = -z_crab_twiss -else: - raise ValueError('????!!!!!') - -figcb = plt.figure(4) -axcbx = figcb.add_subplot(2,1,1) -axcby = figcb.add_subplot(2,1,2, sharex=axcbx) - -axcbx.plot(s_twiss, x_twiss) -axcby.plot(s_twiss, y_twiss) - -part = xp.Particles.from_dict(ddd['particle_on_madx_co']) - -z_test = np.array([0, z_crab_track]) -part = xp.build_particles(particle_on_co=part, - mode='shift', - zeta = z_test, - x = 0*z_test + np.array([0, x_twiss[0]]), - y = 0*z_test + np.array([0, y_twiss[0]]), - px = 0*z_test + np.array([0, px_twiss[0]]), - py = 0*z_test + np.array([0, py_twiss[0]]), - delta = 0*z_test) -tracker.track(part, turn_by_turn_monitor='ONE_TURN_EBE') -mon = tracker.record_last_track - -axcbx.plot(mon.s[0, :], mon.x[1, :] - mon.x[0, :]) -axcby.plot(mon.s[0, :], mon.y[1, :] - mon.y[0, :]) - -plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py deleted file mode 100644 index 90aa300..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t006_calc_and_plot_footprint.py +++ /dev/null @@ -1,113 +0,0 @@ -import json -import numpy as np -import NAFFlib -import helpers as hp -import footprint -import matplotlib.pyplot as plt - -import sixtracktools -import xtrack as xt -import xpart as xp - -track_with = 'xtrack' -track_with = 'sixtrack' - -epsn_x = 2.5e-6 -epsn_y = 2.5e-6 -r_max_sigma = 5. -N_r_footp = 20 -N_theta_footp = 10 - -n_turns = 100 - -def prepare_line(path, input_type): - - if input_type == 'xsuite': - # Load xsuite machine - with open(path, 'r') as fid: - ltest = xt.Line.from_dict(json.load(fid)) - elif input_type == 'sixtrack': - print('Build xsuite from sixtrack input:') - sixinput_test = sixtracktools.sixinput.SixInput(path) - ltest = xt.Line.from_sixinput(sixinput_test) - print('Done') - else: - raise ValueError('What?!') - - return ltest - -line = prepare_line('../xsuite_lines/line_bb_for_tracking.json', input_type='xsuite') - - -with open('../xsuite_lines/line_bb_for_tracking.json', 'r') as fid: - partCO = xp.Particles.from_dict( - json.load(fid)['particle_on_tracker_co']) - -with open('../optics_orbit_at_start_ring_from_madx.json', 'r') as fid: - ddd = json.load(fid) - -part = partCO.copy() - -beta_x = ddd['betx'] -beta_y = ddd['bety'] - -sigmax = np.sqrt(beta_x * epsn_x / part.beta0 / part.gamma0) -sigmay = np.sqrt(beta_y * epsn_y / part.beta0 / part.gamma0) - -xy_norm = footprint.initial_xy_polar(r_min=1e-2, r_max=r_max_sigma, r_N=N_r_footp + 1, - theta_min=np.pi / 100, theta_max=np.pi / 2 - np.pi / 100, - theta_N=N_theta_footp) - -DpxDpy_wrt_CO = np.zeros_like(xy_norm) - -for ii in range(xy_norm.shape[0]): - for jj in range(xy_norm.shape[1]): - - DpxDpy_wrt_CO[ii, jj, 0] = xy_norm[ii, jj, 0] * np.sqrt(epsn_x / part.beta0 / part.gamma0 / beta_x) - DpxDpy_wrt_CO[ii, jj, 1] = xy_norm[ii, jj, 1] * np.sqrt(epsn_y / part.beta0 / part.gamma0 / beta_y) - - -if track_with == 'sixtrack': - x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra = hp.track_particle_sixtrack( - partCO=partCO, Dx_wrt_CO_m=0., Dpx_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 0].flatten(), - Dy_wrt_CO_m=0, Dpy_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 1].flatten(), - Dzeta_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=n_turns, - input_folder='../') - info = track_with -elif track_with == 'xtrack': - x_tbt, px_tbt, y_tbt, py_tbt, zeta_tbt, delta_tbt, extra = hp.track_particle_xtrack( - line=line, partCO=partCO, Dx_wrt_CO_m=0., Dpx_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 0].flatten(), - Dy_wrt_CO_m=0., Dpy_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 1].flatten(), - Dzeta_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=n_turns) - info = track_with -else: - raise ValueError('What?!') - -n_part = x_tbt.shape[1] -Qx = np.zeros(n_part) -Qy = np.zeros(n_part) - -for i_part in range(n_part): - Qx[i_part] = NAFFlib.get_tune(x_tbt[:, i_part]) - Qy[i_part] = NAFFlib.get_tune(y_tbt[:, i_part]) - -Qxy_fp = np.zeros_like(xy_norm) - -Qxy_fp[:, :, 0] = np.reshape(Qx, Qxy_fp[:, :, 0].shape) -Qxy_fp[:, :, 1] = np.reshape(Qy, Qxy_fp[:, :, 1].shape) - -plt.close('all') - -fig3 = plt.figure(3) -axcoord = fig3.add_subplot(1, 1, 1) -footprint.draw_footprint(xy_norm, axis_object=axcoord, linewidth = 1) -axcoord.set_xlim(right=np.max(xy_norm[:, :, 0])) -axcoord.set_ylim(top=np.max(xy_norm[:, :, 1])) - -fig4 = plt.figure(4) -axFP = fig4.add_subplot(1, 1, 1) -footprint.draw_footprint(Qxy_fp, axis_object=axFP, linewidth = 1) -# axFP.set_xlim(right=np.max(Qxy_fp[:, :, 0])) -# axFP.set_ylim(top=np.max(Qxy_fp[:, :, 1])) -fig4.suptitle(info) -plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py deleted file mode 100644 index 32679ad..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t007_check_orbit_and_lin_normal_form.py +++ /dev/null @@ -1,59 +0,0 @@ -import json - -import numpy as np - -import xtrack as xt -import xpart as xp - -with open('../xsuite_lines/line_bb_for_tracking.json', 'r') as fid: - line_dict = json.load(fid) - -line = xt.Line.from_dict(line_dict) - -partCO = xp.Particles.from_dict(line_dict['particle_on_tracker_co']) - -tracker = xt.Tracker(line=line) -particles = xp.Particles(**partCO.to_dict()) - -for _ in range(10): - print(particles.at_turn[0], particles.x[0], particles.y[0], - particles.zeta[0]) - tracker.track(particles) - -for nn in 'x px y py zeta delta'.split(): - assert np.abs(getattr(particles, nn) - getattr(partCO, nn)) < 3e-11 - -WW = np.array(line_dict['WW_finite_diffs']) -WWinv = np.array(line_dict['WWInv_finite_diffs']) -assert np.max(np.abs(np.dot(WW, WWinv) - np.eye(6)))<1e-10 - - -ampl_sigmas = 0.2 -norm_emit_x = 2.5e-6 -geom_emit_x = norm_emit_x / particles.beta0 / particles.gamma0 - -n_part = 100 -theta = np.linspace(0, 2*np.pi, n_part) -x_norm = ampl_sigmas * np.sqrt(geom_emit_x) * np.cos(theta) -px_norm = ampl_sigmas * np.sqrt(geom_emit_x) * np.sin(theta) - -particles_matched = xp.build_particles(particle_on_co=partCO, - x_norm=x_norm, px_norm=px_norm, - R_matrix=np.array(line_dict['RR_finite_diffs']), - symplectify=True) -particles_test = particles_matched.copy() -tracker.track(particles_test, num_turns=10) - -i_matched = np.argmax(particles_matched.x) -i_test = np.argmax(particles_test.x) -assert np.abs(particles_test.x[i_test] - particles_matched.x[i_matched]) < 2e-6 -assert np.abs(particles_test.px[i_test] - particles_matched.px[i_matched]) < 1e-7 - - -import matplotlib.pyplot as plt -plt.close('all') -plt.figure(1) -plt.plot(particles_matched.x, particles_matched.px) -plt.plot(particles_test.x, particles_test.px) - -plt.show() diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py deleted file mode 100644 index 6b63ca0..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/t008_check_against_sixtrack.py +++ /dev/null @@ -1,51 +0,0 @@ -import json -import numpy as np -import NAFFlib -import helpers as hp -import matplotlib.pyplot as plt - -import xtrack as xt -import xpart as xp -import sixtracktools - - -displace_x = [-5e-4, 5e-4] -displace_y = [3e-4, -3e-4] -num_turns = 5 - - -with open('../xsuite_lines/line_bb_for_tracking.json', 'r') as fid: - dict_line_xtrack = json.load(fid) - -line = xt.Line.from_dict(dict_line_xtrack) - -partCO = xp.Particles.from_dict(dict_line_xtrack['particle_on_tracker_co']) - -(x_tbt_sixtrack, px_tbt_sixtrack, y_tbt_sixtrack, py_tbt_sixtrack, - zeta_tbt_sixtrack, delta_tbt_sixtrack, extra) = hp.track_particle_sixtrack( - partCO=partCO, Dx_wrt_CO_m=np.array(displace_x), - Dpx_wrt_CO_rad=0, - Dy_wrt_CO_m=np.array(displace_y), Dpy_wrt_CO_rad=0., - Dzeta_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=num_turns, - input_folder='../') - -tracker = xt.Tracker(line=line) - -particles = xp.build_particles(particle_on_co=partCO, - mode='shift', - x=np.array(displace_x), - y=np.array(displace_y)) - -tracker.track(particles, turn_by_turn_monitor=True, num_turns=num_turns) - -print('Xtrack') -print(tracker.record_last_track.x.transpose()) -print('Sixtrack') -print(x_tbt_sixtrack) - -assert np.allclose(tracker.record_last_track.x[0, :], x_tbt_sixtrack[:,0], - rtol=1e-15, atol=9e-11) -assert np.allclose(tracker.record_last_track.y[0, :], y_tbt_sixtrack[:,0], - rtol=1e-15, atol=9e-11) -assert np.allclose(tracker.record_last_track.delta[0, :], delta_tbt_sixtrack[:,0], - rtol=1e-15, atol=5e-11) diff --git a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md b/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md deleted file mode 100644 index 3e635ef..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/checks_and_doc/tests.md +++ /dev/null @@ -1,272 +0,0 @@ -# Tests performed on pymask - -N.B. For the tests to be successful make sure that cpymad and madx correspond to the same (updated!) version. - - -## Test 1 - b1 with bb legacy macros - -We execute the reference: -```bash - cd ../../examples/hl_lhc_collision - python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad -``` - -We setup the python version: -```bash -cd ../../python_examples/hl_lhc_collisions_python - -``` -In config.yaml we select: -```yaml -mode : b1_with_bb_legacy_macros -``` -We run the python version: -``` -python 000_pymask.py -``` - -We check that the output is identical -```bash -diff fc.2 ../../examples/hl_lhc_collision/fc.2 -diff fc.3 ../../examples/hl_lhc_collision/fc.3 -diff fc.8 ../../examples/hl_lhc_collision/fc.8 -diff fc.16 ../../examples/hl_lhc_collision/fc.16 -diff fc.34 ../../examples/hl_lhc_collision/fc.34 -``` - - -## Test 2 - b1 with new bb tools -In config.yaml we select: -```yaml -mode : b1_with_bb -``` - -We exeute the python version: -```bash -python 000_pymask.py -``` - -We execute the reference (can be reused from the previous test): -```bash - cd ../../examples/hl_lhc_collision - python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad -``` -In the folder ```checks_and_doc```: -Configure ```t004_check_output_consistency.py``` for the test: -```python -# Test b1 -tests = [ - { - 'test_name': 'B1 - pymask sixtrack input vs madx mask', - 'path_test': '../', - 'type_test': 'sixtrack', - 'path_ref': '../../../examples/hl_lhc_collision', - 'type_ref': 'sixtrack', - 'rtol': 3e-5, - 'atol': 1e-12, - 'strict': False, - }, - { - 'test_name': 'B1 - pymask xsuite vs pymask sixtrack input', - 'path_test': '../xsuite/line_bb_dipole_not_cancelled.json', - 'type_test': 'xsuite', - 'path_ref': '../', - 'type_ref': 'sixtrack', - 'rtol': 4e-7, - 'atol': 1e-100, - 'strict': True, - } -] -``` - -Check using xsuite lines: -```bash -python t003_fc_to_fort.py -python t004_check_output_consistency.py -``` - -**Check crab cavities:** - -In ```t005_check_crabs.py``` set: -```python -# B1 ip5 -beam_track = 'b1' -ip_choice = 5 -plane = 'y' -phi_weak = Phi -phi_c_weak = Phi_c -``` - -Run check: -```bash -python t003_fc_to_fort.py -python t005_check_crabs.py -``` -Minor deviations on crab orbit are observed due to errors and tuning. - -Repeat for IP1. - -## Test 3 - b4 without bb - -**WARNING!!!! In this mode the leveling mask module is not working, values are forced in python and the module is skipped** - -We execute the reference: -```bash - cd ../../examples/hl_lhc_collision_nobb_b4 - python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad -``` - -We setup the python version: -```bash -cd ../../python_examples/hl_lhc_collisions_python - -``` -In config.yaml we select: -```yaml -mode: b4_without_bb, -force_leveling: - on_sep8: -0.03425547139366354 - on_sep2: 0.14471680504084292 -``` -(the separations are forced in order to be consitent with the mad-x test case for which the same values are impored, as the legacy leveling macro does not work in this case). - -We run the python version: -``` -python 000_pymask.py -``` - -We check that the output is identical -```bash -diff fc.2 ../../examples/hl_lhc_collision_nobb_b4/fc.2 -diff fc.3 ../../examples/hl_lhc_collision_nobb_b4/fc.3 -diff fc.8 ../../examples/hl_lhc_collision_nobb_b4/fc.8 -diff fc.16 ../../examples/hl_lhc_collision_nobb_b4/fc.16 -diff fc.34 ../../examples/hl_lhc_collision_nobb_b4/fc.34 -``` - -## Test 4 - b4 from b2 without bb - -We execute the reference: -```bash - cd ../../examples/hl_lhc_collision_nobb_b4 - python ../../unmask.py main.mask parameters_for_unmask.txt --run-cpymad -``` - -We setup the python version: -```bash -cd ../../python_examples/hl_lhc_collisions_python - -``` -In config.yaml we select: -```yaml -mode: b4_from_b2_without_bb -force_leveling: - on_sep8: -0.03425547139366354 - on_sep2: 0.14471680504084292 -``` -(the separations are forced in order to be consitent with the mad-x test case for which the same values are impored, as the legacy leveling macro does not work in this case). - - -We run the python version: -``` -python 000_pymask.py -``` - -We check that the output is identical -```bash -diff fc.2 ../../examples/hl_lhc_collision_nobb_b4/fc.2 -diff fc.3 ../../examples/hl_lhc_collision_nobb_b4/fc.3 -diff fc.8 ../../examples/hl_lhc_collision_nobb_b4/fc.8 -diff fc.16 ../../examples/hl_lhc_collision_nobb_b4/fc.16 -diff fc.34 ../../examples/hl_lhc_collision_nobb_b4/fc.34 -``` - -## Test 5 - b4 from b2 with bb -We setup the python version: -```bash -cd ../../python_examples/hl_lhc_collisions_python - -``` -In config.yaml we select: -```python -mode: b4_from_b2_with_bb, -``` -We run the python version: -``` -python 000_pymask.py -``` - -**As we don't have a reference we can only perform the check on the crab cavities:** - -In ```t005_check_crabs.py``` set: -```python -# B4 ip1 -beam_track = 'b4' -ip_choice = 1 -plane = 'x' -phi_weak = -Phi -phi_c_weak = -Phi_c -``` - -Run check: -```bash -python t003_fc_to_fort.py -python t005_check_crabs.py -``` -Minor deviations on crab orbit are observed due to errors and tuning. - -Repeat for IP5 using: -```python -# B4 ip5 -beam_track = 'b4' -ip_choice = 5 -plane = 'y' -phi_weak = Phi -phi_c_weak = Phi_c -``` - -## Test 7 and 8 - Check the matching and the tracking - -Select on config.yaml - -```yaml -beam: b1_without_bb -``` -and run - -```bash -python t003_fc_to_fort.py -python t007_check_orbit_and_lin_normal_form.py -python t008_check_against_sixtrack.py -``` - -then repeat for - -```yaml -beam: b1_with_bb -``` - -and - -```yaml -beam: b4_from_b2_without_bb -``` - -and - -```yaml -beam: b4_from_b2_with_bb -``` - - - -## To check a mad test againt its reference - -```bash -diff fc.2 reference/fc.2 -diff fc.3 reference/fc.3 -diff fc.8 reference/fc.8 -diff fc.16 reference/fc.16 -diff fc.34 reference/fc.34 -``` - diff --git a/python_examples/hl_lhc_collisions_flat_python/customization.bash.example b/python_examples/hl_lhc_collisions_flat_python/customization.bash.example deleted file mode 100644 index 3ef2c56..0000000 --- a/python_examples/hl_lhc_collisions_flat_python/customization.bash.example +++ /dev/null @@ -1,5 +0,0 @@ -#rm modules -#ln -fns /home/sterbini/test_new_mask/v0.5/lhcmachines machines -#ln -fns /home/sterbini/test_new_mask/v0.5/lhctoolkit tools -ln -fns /home/sterbini/lhcmask modules -#ln -fns /home/sterbini/test_new_mask/v0.5/lhcerrors errors From 51c7a46d80efb2f3cf0eef23301f5040dbd9f27e Mon Sep 17 00:00:00 2001 From: Sofia Kostoglou Date: Wed, 30 Nov 2022 12:22:13 +0100 Subject: [PATCH 4/4] fixed bug with filling scheme and mode=b1_without_bb --- python_examples/ions_python/000_pymask.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_examples/ions_python/000_pymask.py b/python_examples/ions_python/000_pymask.py index 00b2ee3..6830f41 100644 --- a/python_examples/ions_python/000_pymask.py +++ b/python_examples/ions_python/000_pymask.py @@ -310,7 +310,7 @@ check_betas_at_ips=check_betas_at_ips, check_separations_at_ips=False) # For B1, to be generalized for B4 -if 'filling_scheme_json' in configuration['beambeam_config'].keys(): +if ('filling_scheme_json') in configuration['beambeam_config'].keys() and ("with_bb" in mode): assert 'b4' not in mode filling_scheme_json = configuration['beambeam_config']['filling_scheme_json'] bunch_to_track = configuration['beambeam_config']['bunch_to_track']