diff --git a/.github/workflows/README.md b/.github/workflows/README.md index ee6aff73..eeab1cb5 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,4 +1,4 @@ -# Continous Integration Workflows +# Continuous Integration Workflows This package implements different workflows for CI. They are organised as follows. diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5ff90eff..f964a49e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -34,7 +34,7 @@ jobs: run: python -m pip install ".[test]" - name: Get acc-models-lhc - run: git clone https://gitlab.cern.ch/acc-models/acc-models-lhc.git --depth 1 + run: git clone -b 2022 https://gitlab.cern.ch/acc-models/acc-models-lhc.git --depth 1 - name: Run all tests run: make alltests diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 8cbdaf91..b8b28500 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, ubuntu-22.04, macos-latest, windows-latest] - python-version: [3.8, 3.9, "3.10", 3.x] # crons should always run latest python hence 3.x + python-version: [3.8, 3.9, "3.10", "3.11", 3.x] # crons should always run latest python hence 3.x # exclude: # - os: windows-latest # scipy deps issues # python-version: "3.10" @@ -42,7 +42,7 @@ jobs: run: python -m pip install ".[test]" - name: Get acc-models-lhc - run: git clone https://gitlab.cern.ch/acc-models/acc-models-lhc.git --depth 1 + run: git clone -b 2022 https://gitlab.cern.ch/acc-models/acc-models-lhc.git --depth 1 - name: Run Tests run: make alltests diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 3cba8cf5..152af3f4 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -56,7 +56,7 @@ jobs: # Upload artifacts if in PR so reviewers can have a quick look without building documentation from the branch locally - name: Upload build artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: success() && github.event_name == 'pull_request' # only for pushes in PR with: name: site-build diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a35e6018..ff0fe0b0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, ubuntu-22.04, macos-latest, windows-latest] - python-version: [3.8, 3.9, "3.10"] + python-version: [3.8, 3.9, "3.10", "3.11"] fail-fast: false steps: @@ -42,7 +42,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, ubuntu-22.04, macos-latest, windows-latest] - python-version: [3.8, 3.9, "3.10"] + python-version: [3.8, 3.9, "3.10", "3.11"] fail-fast: false steps: @@ -59,7 +59,7 @@ jobs: run: python -m pip install ".[test]" - name: Get acc-models-lhc - run: git clone https://gitlab.cern.ch/acc-models/acc-models-lhc.git --depth 1 + run: git clone -b 2022 https://gitlab.cern.ch/acc-models/acc-models-lhc.git --depth 1 - name: Run Tests run: make slowtests diff --git a/docs/conf.py b/docs/conf.py index 2e9750c7..44f89ecf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,6 +36,10 @@ import pyhdtoolkit +# This is to tell Sphinx how to print some specific type annotations +# See: https://stackoverflow.com/a/67483317 +# See: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases +autodoc_type_aliases = {"ArrayLike": "ArrayLike"} # To use SVG outputs when scraping matplotlib figures for the sphinx-gallery class matplotlib_svg_scraper(object): @@ -144,6 +148,7 @@ def __call__(self, *args, **kwargs): "sphinx_panels", # Create panels in a grid layout or as drop-downs "matplotlib.sphinxext.plot_directive", # Include a Matplotlib plot in a Sphinx document "sphinx-prompt", # prompt symbols will not be copy-pastable + "sphinx_codeautolink", # Automatically link example code to documentation source ] # Config for autosectionlabel extension diff --git a/docs/release.rst b/docs/release.rst index 428dd095..45a1e137 100644 --- a/docs/release.rst +++ b/docs/release.rst @@ -3,6 +3,14 @@ Release Notes The full list of releases can be found in the GitHub repository's `releases page `_. +Version 1.2.0 +------------- + +.. toctree:: + :maxdepth: 2 + + releases/v1.2.0 + Version 1.1.1 ------------- @@ -10,7 +18,7 @@ Version 1.1.1 :maxdepth: 2 releases/v1.1.1 - + Version 1.1.0 ------------- @@ -18,7 +26,7 @@ Version 1.1.0 :maxdepth: 2 releases/v1.1.0 - + Version 1.0.0 ------------- diff --git a/docs/releases/v1.1.1.rst b/docs/releases/v1.1.1.rst index db46c0a4..59927d44 100644 --- a/docs/releases/v1.1.1.rst +++ b/docs/releases/v1.1.1.rst @@ -13,4 +13,4 @@ Bug Fixes * A few values in the available plotting settings were changed. -See `v1.1.1 release notes on GitHub `_ and the `full changes since v1.0.0 `_. +See `v1.1.1 release notes on GitHub `_ and the `full changes since v1.1.0 `_. diff --git a/docs/releases/v1.2.0.rst b/docs/releases/v1.2.0.rst new file mode 100644 index 00000000..4e2637b3 --- /dev/null +++ b/docs/releases/v1.2.0.rst @@ -0,0 +1,45 @@ +.. _release_1.2.0: + +1.2.0 +----- + +Release `1.2.0` brings some new features and dependency changes. + +Changes +~~~~~~~ + +* The `pyhdtoolkit.plotting.envelope` functions `plot_envelope` and `plot_stay_clear` have been removed. They are replaced by a new one, `plot_beam_envelope`, which handles all enveloppe calculations directly from the `MAD-X` instance. Documentation and examples have been updated. + +Enhancements +~~~~~~~~~~~~ + +* The `pyhdtoolkit.plotting.aperture` module has a new function, `~pyhdtoolkit.plotting.aperture.plot_physical_apertures`, to try and plot the physical apertures of elements on the given axis. +* The `pyhdtoolkit.plotting.utils` module has a new function, `~pyhdtoolkit.plotting.utils.draw_confidence_ellipse`, to plot the covariance ellipse of two arrays. +* The `pyhdtoolkit.plotting.layout.plot_machine_layout` function (and those relying on it) can now plot octupole patches. +* The `pyhdtoolkit.plotting.layout.plot_machine_layout` function (and those relying on it) can now handle single values given for ylimits of different elements in the machine layout. For instance now `k1l_lim=5e-2` is valid and equivalent to `k1l_lim=(-5e-2, 5e-2)`. +* An experimental utility function was added to split a complex-valued columns from a dataframe into two real-valued ones. It might join the public API in a future release, and is for now available as `~pyhdtoolkit.utils._misc.split_complex_columns`. +* An experimental utility function was added to add noise to the ``LHC`` *IR* BPMs in a given dataframe column. It might join the public API in a future release, and is for now available as `~pyhdtoolkit.utils._misc.add_noise_to_ir_bpms`. +* An experimental utility function was added to add noise to the ``LHC`` *arc* BPMs in a given dataframe column. It might join the public API in a future release, and is for now available as `~pyhdtoolkit.utils._misc.add_noise_to_arc_bpms`. +* The functions in the `pyhdtoolkit.cpymadtools.ptc` module can now be given values for the ``PTC`` universe creation and called commands parameters through keyword arguments. The documentation has been updated with information on the available parameters. + +Bug Fixes +~~~~~~~~~ + +* The `pyhdtoolkit.plotting.layout.plot_machine_layout` function now only adds a legend for element patches of a certain type if at least one element was drawn. +* The `pyhdtoolkit.plotting.layout.plot_machine_layout` function now tries to properly determine legend locations based on which elements were drawn. + +Documentation +~~~~~~~~~~~~~ + +* The documentation generated by `Sphinx` should now properly display short type hints instead of the full expanded ones. +* Some example pages have been updated to use the minimal requirement of provided **args** for the functions in `~pyhdtoolkit.cpymadtools.matching`. +* Some typos that were left in a few docstrings have been corrected. + +Maintenance +~~~~~~~~~~~ + +* The `madx` argument is now positional only in all APIs involving it (APIs in `~pyhdtoolkit.cpymadtools` and `~pyhdtoolkit.plotting`). +* The `~numpy` dependency is now capped at version `<1.24.0`, as when using this new version some `~matplotlib` functions are broken. +* Various type hints from the `typing` module, such as `Dict` or `List`, have been superseeded by the default, now-standard builtins (such as `dict` or `list`). + +See `v1.2.0 release notes on GitHub `_ and the `full changes since v1.1.1 `_. diff --git a/examples/acc-models-lhc/operation/optics/R2022a_A11mC11mA10mL10m.madx b/examples/acc-models-lhc/operation/optics/R2022a_A11mC11mA10mL10m.madx new file mode 100644 index 00000000..0af60c45 --- /dev/null +++ b/examples/acc-models-lhc/operation/optics/R2022a_A11mC11mA10mL10m.madx @@ -0,0 +1,10 @@ +! JMad export of model LHC 2022 optic R2022a_A11mC11mA10mL10m + + +call, file="acc-models-lhc/toolkit/zero-strengths.madx"; +call, file="acc-models-lhc/strengths/ATS_Nominal/2022/ramp/ats_11m.madx"; +call, file="acc-models-lhc/toolkit/reset-bump-flags.madx"; +call, file="acc-models-lhc/toolkit/match-lumiknobs.madx"; +call, file="acc-models-lhc/toolkit/generate-op-tune-knobs-ats.madx"; +call, file="acc-models-lhc/toolkit/generate-op-chroma-knobs-ats.madx"; +call, file="acc-models-lhc/toolkit/generate-op-coupling-knobs-ats.madx"; diff --git a/examples/acc-models-lhc/strengths/ATS_Nominal/2022/ramp/ats_11m.madx b/examples/acc-models-lhc/strengths/ATS_Nominal/2022/ramp/ats_11m.madx new file mode 100644 index 00000000..5f4e630d --- /dev/null +++ b/examples/acc-models-lhc/strengths/ATS_Nominal/2022/ramp/ats_11m.madx @@ -0,0 +1,1562 @@ +NRJ := 450.000000 ; +ARC_SQUEEZE := 0.000000 ; + + !***BETAS in IR1 and IR5*** +betx_IP1 := 11.000000 ; +bety_IP1 := 11.000000 ; +betx_IP5 := 11.000000 ; +bety_IP5 := 11.000000 ; + + !***TELE-INDEX in IR1 and IR5*** + rx_IP1 := 1.000000 ; + ry_IP1 := 1.000000 ; + rx_IP5 := 1.000000 ; + ry_IP5 := 1.000000 ; + + !***Exp. configuration in IR1, IR2, IR5 and IR8*** + + !** IR1 +on_x1 := 170.000 ; +on_sep1 := -2.000 ; + on_oh1 := 0.000 ; + on_ov1 := 0.000 ; + phi_IR1:= 90.000 ; +on_sep1_h :=on_sep1 ; +on_sep1_v := 0 ; +on_x1_h := 0 ; +on_x1_v := on_x1 ; +!*on_ssep1_h:=on_sep1_h; +!*on_ssep1_v:=on_sep1_v; +!*on_xx1_h:=on_x1_h ; +!*on_xx1_v:=on_x1_v ; + + !** IR5 +on_x5 := 170.000 ; +on_sep5 := 2.000 ; + on_oh5 := 0.000 ; + on_ov5 := 0.000 ; + phi_IR5:= 0.000 ; +on_sep5_h := 0 ; +on_sep5_v :=on_sep5 ; +on_x5_h := on_x5 ; +on_x5_v := 0 ; +!*on_ssep5_h:=on_sep5_h; +!*on_ssep5_v:=on_sep5_v; +!*on_xx5_h:=on_x5_h ; +!*on_xx5_v:=on_x5_v ; + + !**Spurious dispersion correction +on_disp := 0.000 ; + + !** IR2 + on_x2h := 0.000 ; + on_sep2h:= 3.500 ; + on_x2v := 170.000 ; + on_sep2v:= 0.000 ; + on_a2 := -40.000 ; + on_o2 := 0.000 ; + on_oh2 := 0.000 ; + on_ov2 := 0.000 ; + phi_IR2 := 90.000 ; + + !** IR8 + on_x8h := -170.000 ; + on_sep8h:= 0.000 ; + on_x8v := 0.000 ; + on_sep8v:= -3.500 ; + on_a8 := -40.000 ; + on_o8 := 0.000 ; + on_oh8 := 0.000 ; + on_ov8 := 0.000 ; + phi_IR8 := 180.000 ; + + !** Experimental solenoids +abas:= 12.00/ 6.0*clight/(7E12)*on_sol_atlas; +abls:= 6.05/12.1*clight/(7E12)*on_sol_alice ; +abcs:= 52.00/13.0*clight/(7E12)*on_sol_cms ; + + !***Ring Geometry*** + + !Separation/recombination dipoles +kd1.lr1 := ad1.lr1/l.mbxw; +kd2.l1 := ad2.l5/l.mbrc ; +kd2.r1 := ad2.r5/l.mbrc ; +kd1.l2 := ad1.l2/l.mbx ; +kd1.r2 := ad1.r2/l.mbx ; +kd2.l2 := ad2.l2/l.mbrc ; +kd2.r2 := ad2.r2/l.mbrc ; +kd3.lr3 := ad3.lr3/l.mbw ; +kd4.lr3 := ad4.lr3/l.mbw ; +kd3.l4 := ad3.l4/l.mbrs ; +kd3.r4 := ad3.r4/l.mbrs ; +kd4.l4 := ad4.l4/l.mbrb ; +kd4.r4 := ad4.r4/l.mbrb ; +kd34.lr3 := ad3.lr3/l.mbw ; +kd34.lr7 := ad3.lr7/l.mbw ; +kd1.lr5 := ad1.lr5/l.mbxw; +kd2.l5 := ad2.l5/l.mbrc ; +kd2.r5 := ad2.r5/l.mbrc ; +kd3.lr7 := ad3.lr7/l.mbw ; +kd4.lr7 := ad4.lr7/l.mbw ; +kd1.l8 := ad1.l8/l.mbx ; +kd1.r8 := ad1.r8/l.mbx ; +kd2.l8 := ad2.l8/l.mbrc ; +kd2.r8 := ad2.r8/l.mbrc ; +ksumd2.l1b2 := kd2.l1 ; +ksumd2.l2b2 := kd2.l2 ; +ksumd2.l5b2 := kd2.l5 ; +ksumd2.l8b2 := kd2.l8 ; +ksumd2.r1b2 := kd2.l1 ; +ksumd2.r2b2 := kd2.l2 ; +ksumd2.r5b2 := kd2.l5 ; +ksumd2.r8b2 := kd2.l8 ; + + !Main dipoles +kb.a12 := ab.a12/l.mb ; +kb.a23 := ab.a23/l.mb ; +kb.a34 := ab.a34/l.mb ; +kb.a45 := ab.a45/l.mb ; +kb.a56 := ab.a56/l.mb ; +kb.a67 := ab.a67/l.mb ; +kb.a78 := ab.a78/l.mb ; +kb.a81 := ab.a81/l.mb ; + + !***IR1 Optics*** +KQX.L1 := -0.854562504357E-02 ; +KTQX1.L1 := 0.000000000000E+00 ; +KTQX2.L1 := 0.000000000000E+00 ; +KQX.R1 := -KQX.L1 ; +KTQX1.R1 := -KTQX1.L1 ; +KTQX2.R1 := -KTQX2.L1 ; + + !Beam1 +KQ4.L1B1 := 0.532939746961E-02 ; +KQ4.R1B1 := -0.532939746961E-02 ; +KQ5.L1B1 := -0.549310859055E-02 ; +KQ5.R1B1 := 0.549310859055E-02 ; +KQ6.L1B1 := 0.499494557346E-02 ; +KQ6.R1B1 := -0.499494557346E-02 ; +KQ7.L1B1 := -0.456683783331E-02 ; +KQ7.R1B1 := 0.448901787695E-02 ; +KQ8.L1B1 := 0.732209287546E-02 ; +KQ8.R1B1 := -0.733784873620E-02 ; +KQ9.L1B1 := -0.680559079530E-02 ; +KQ9.R1B1 := 0.686826741479E-02 ; +KQ10.L1B1 := 0.723117774947E-02 ; +KQ10.R1B1 := -0.717547782475E-02 ; +KQTL11.L1B1 := -0.568441367868E-03 ; +KQTL11.R1B1 := 0.447635694271E-03 ; +KQT12.L1B1 := 0.875025120228E-04 ; +KQT12.R1B1 := 0.251274473750E-02 ; +KQT13.L1B1 := -0.242130986821E-02 ; +KQT13.R1B1 := 0.719121815526E-04 ; + + !Beam2 +KQ4.L1B2 := -0.480712170430E-02 ; +KQ4.R1B2 := 0.480712170430E-02 ; +KQ5.L1B2 := 0.509850806769E-02 ; +KQ5.R1B2 := -0.509850806769E-02 ; +KQ6.L1B2 := -0.501561197551E-02 ; +KQ6.R1B2 := 0.501561197551E-02 ; +KQ7.L1B2 := 0.477695999326E-02 ; +KQ7.R1B2 := -0.472762660695E-02 ; +KQ8.L1B2 := -0.710402639207E-02 ; +KQ8.R1B2 := 0.709847080621E-02 ; +KQ9.L1B2 := 0.709604793831E-02 ; +KQ9.R1B2 := -0.712819245801E-02 ; +KQ10.L1B2 := -0.733748351116E-02 ; +KQ10.R1B2 := 0.731101565147E-02 ; +KQTL11.L1B2 := 0.598240054096E-03 ; +KQTL11.R1B2 := -0.479983721888E-03 ; +KQT12.L1B2 := -0.902980664653E-03 ; +KQT12.R1B2 := -0.117280120507E-02 ; +KQT13.L1B2 := 0.171100605794E-02 ; +KQT13.R1B2 := -0.259385575933E-03 ; + + !***IR1 X-scheme*** +ACBXV1.L1x := 0.549019607843E-07 ; +ACBXV1.L1s := -0.800000000000E-05 ; +ACBXV1.L1 := ( 0.549019607843E-07 )*on_x1_v +( -0.800000000000E-05 )*on_sep1_v ; +ACBXV1.R1x := -0.549019607843E-07 ; +ACBXV1.R1s := -0.800000000000E-05 ; +ACBXV1.R1 := ( -0.549019607843E-07 )*on_x1_v +( -0.800000000000E-05 )*on_sep1_v ; +ACBXH1.L1x := 0.549019607843E-07 ; +ACBXH1.L1s := 0.800000000000E-05 ; +ACBXH1.L1 := ( 0.549019607843E-07 )*on_x1_h +( 0.800000000000E-05 )*on_sep1_h ; +ACBXH1.R1x := -0.549019607843E-07 ; +ACBXH1.R1s := 0.800000000000E-05 ; +ACBXH1.R1 := ( -0.549019607843E-07 )*on_x1_h +( 0.800000000000E-05 )*on_sep1_h ; +ACBXV2.L1x := 0.549019607843E-07 ; +ACBXV2.L1s := -0.800000000000E-05 ; +ACBXV2.L1 := ( 0.549019607843E-07 )*on_x1_v +( -0.800000000000E-05 )*on_sep1_v ; +ACBXV2.R1x := -0.549019607843E-07 ; +ACBXV2.R1s := -0.800000000000E-05 ; +ACBXV2.R1 := ( -0.549019607843E-07 )*on_x1_v +( -0.800000000000E-05 )*on_sep1_v ; +ACBXH2.L1x := 0.549019607843E-07 ; +ACBXH2.L1s := 0.800000000000E-05 ; +ACBXH2.L1 := ( 0.549019607843E-07 )*on_x1_h +( 0.800000000000E-05 )*on_sep1_h ; +ACBXH2.R1x := -0.549019607843E-07 ; +ACBXH2.R1s := 0.800000000000E-05 ; +ACBXH2.R1 := ( -0.549019607843E-07 )*on_x1_h +( 0.800000000000E-05 )*on_sep1_h ; +ACBXV3.L1x := 0.549019607843E-07 ; +ACBXV3.L1s := -0.800000000000E-05 ; +ACBXV3.L1 := ( 0.549019607843E-07 )*on_x1_v +( -0.800000000000E-05 )*on_sep1_v ; +ACBXV3.R1x := -0.549019607843E-07 ; +ACBXV3.R1s := -0.800000000000E-05 ; +ACBXV3.R1 := ( -0.549019607843E-07 )*on_x1_v +( -0.800000000000E-05 )*on_sep1_v ; +ACBXH3.L1x := 0.549019607843E-07 ; +ACBXH3.L1s := 0.800000000000E-05 ; +ACBXH3.L1 := ( 0.549019607843E-07 )*on_x1_h +( 0.800000000000E-05 )*on_sep1_h ; +ACBXH3.R1x := -0.549019607843E-07 ; +ACBXH3.R1s := 0.800000000000E-05 ; +ACBXH3.R1 := ( -0.549019607843E-07 )*on_x1_h +( 0.800000000000E-05 )*on_sep1_h ; + + !Beam1 +ACBYVS4.L1B1x := -0.238960627295E-06 ; +ACBYVS4.L1B1s := -0.213926221358E-04 ; +ACBYVS4.L1B1ov:= 0.243888055356E-04 ; +ACBYVS4.L1B1 := ( -0.238960627295E-06 )*on_x1_v+( -0.213926221358E-04 )*on_sep1_v+( 0.243888055356E-04 )*on_ov1; +ACBYVS4.R1B1x := 0.983050840700E-07 ; +ACBYVS4.R1B1s := 0.313262793246E-05 ; +ACBYVS4.R1B1ov:= 0.103683643244E-04 ; +ACBYVS4.R1B1 := ( 0.983050840700E-07 )*on_x1_v+( 0.313262793246E-05 )*on_sep1_v+( 0.103683643244E-04 )*on_ov1; +ACBCV5.L1B1x := -0.897503400093E-07 ; +ACBCV5.L1B1s := 0.906000825746E-05 ; +ACBCV5.L1B1ov := 0.227603525803E-04 ; +ACBCV5.L1B1 := ( -0.897503400093E-07 )*on_x1_v+( 0.906000825746E-05 )*on_sep1_v+( 0.227603525803E-04 )*on_ov1; +ACBCV6.R1B1x := 0.168417771323E-06 ; +ACBCV6.R1B1s := -0.104145657263E-04 ; +ACBCV6.R1B1ov := 0.967606336277E-05 ; +ACBCV6.R1B1 := ( 0.168417771323E-06 )*on_x1_v+( -0.104145657263E-04 )*on_sep1_v+( 0.967606336277E-05 )*on_ov1; +ACBYHS4.L1B1x := -0.110276141294E-06 ; +ACBYHS4.L1B1s := -0.239228162795E-05 ; +ACBYHS4.L1B1oh:= 0.109403349767E-04 ; +ACBYHS4.L1B1 := ( -0.110276141294E-06 )*on_x1_h+( -0.239228162795E-05 )*on_sep1_h+( 0.109403349767E-04 )*on_oh1; +ACBYHS4.R1B1x := 0.220934724398E-06 ; +ACBYHS4.R1B1s := 0.232106850028E-04 ; +ACBYHS4.R1B1oh:= 0.220185968638E-04 ; +ACBYHS4.R1B1 := ( 0.220934724398E-06 )*on_x1_h+( 0.232106850028E-04 )*on_sep1_h+( 0.220185968638E-04 )*on_oh1; +ACBCH6.L1B1x := -0.159508355581E-06 ; +ACBCH6.L1B1s := 0.986356371258E-05 ; +ACBCH6.L1B1oh := 0.102098432436E-04 ; +ACBCH6.L1B1 := ( -0.159508355581E-06 )*on_x1_h+( 0.986356371258E-05 )*on_sep1_h+( 0.102098432436E-04 )*on_oh1; +ACBCH5.R1B1x := 0.101918482920E-06 ; +ACBCH5.R1B1s := -0.102874530490E-04 ; +ACBCH5.R1B1oh := 0.205484039475E-04 ; +ACBCH5.R1B1 := ( 0.101918482920E-06 )*on_x1_h+( -0.102874530490E-04 )*on_sep1_h+( 0.205484039475E-04 )*on_oh1; +ACBCH8.L1B1oh := -0.223268262538E-04 ; +ACBCH8.L1B1 := ( -0.223268262538E-04)*on_oh1; +ACBCV7.L1B1ov := -0.393902876746E-04 ; +ACBCV7.L1B1 := ( -0.393902876746E-04)*on_ov1; +ACBCH7.R1B1oh := -0.377177579090E-04 ; +ACBCH7.R1B1 := ( -0.377177579090E-04)*on_oh1; +ACBCV8.R1B1ov := -0.225071599802E-04 ; +ACBCV8.R1B1 := ( -0.225071599802E-04)*on_ov1; + + !Beam2 +ACBYVS4.L1B2x := 0.924432833657E-07 ; +ACBYVS4.L1B2s := -0.349510819116E-05 ; +ACBYVS4.L1B2ov:= 0.123686048725E-04 ; +ACBYVS4.L1B2 := ( 0.924432833657E-07 )*on_x1_v+( -0.349510819116E-05 )*on_sep1_v+( 0.123686048725E-04 )*on_ov1; +ACBYVS4.R1B2x := -0.224563718073E-06 ; +ACBYVS4.R1B2s := 0.228459436339E-04 ; +ACBYVS4.R1B2ov:= 0.234429686230E-04 ; +ACBYVS4.R1B2 := ( -0.224563718073E-06 )*on_x1_v+( 0.228459436339E-04 )*on_sep1_v+( 0.234429686230E-04 )*on_ov1; +ACBCV6.L1B2x := 0.161872533892E-06 ; +ACBCV6.L1B2s := 0.100098234838E-04 ; +ACBCV6.L1B2ov := 0.115427468317E-04 ; +ACBCV6.L1B2 := ( 0.161872533892E-06 )*on_x1_v+( 0.100098234838E-04 )*on_sep1_v+( 0.115427468317E-04 )*on_ov1; +ACBCV5.R1B2x := -0.102478345969E-06 ; +ACBCV5.R1B2s := -0.103448593934E-04 ; +ACBCV5.R1B2ov := 0.218776696796E-04 ; +ACBCV5.R1B2 := ( -0.102478345969E-06 )*on_x1_v+( -0.103448593934E-04 )*on_sep1_v+( 0.218776696796E-04 )*on_ov1; +ACBYHS4.L1B2x := 0.242711053793E-06 ; +ACBYHS4.L1B2s := -0.210126288317E-04 ; +ACBYHS4.L1B2oh:= 0.257613904862E-04 ; +ACBYHS4.L1B2 := ( 0.242711053793E-06 )*on_x1_h+( -0.210126288317E-04 )*on_sep1_h+( 0.257613904862E-04 )*on_oh1; +ACBYHS4.R1B2x := -0.801291730920E-07 ; +ACBYHS4.R1B2s := 0.425649177212E-05 ; +ACBYHS4.R1B2oh:= 0.117943365273E-04 ; +ACBYHS4.R1B2 := ( -0.801291730920E-07 )*on_x1_h+( 0.425649177212E-05 )*on_sep1_h+( 0.117943365273E-04 )*on_oh1; +ACBCH5.L1B2x := 0.897888250373E-07 ; +ACBCH5.L1B2s := 0.906311195827E-05 ; +ACBCH5.L1B2oh := 0.240412893353E-04 ; +ACBCH5.L1B2 := ( 0.897888250373E-07 )*on_x1_h+( 0.906311195827E-05 )*on_sep1_h+( 0.240412893353E-04 )*on_oh1; +ACBCH6.R1B2x := -0.170602962675E-06 ; +ACBCH6.R1B2s := -0.105496309899E-04 ; +ACBCH6.R1B2oh := 0.110068226761E-04 ; +ACBCH6.R1B2 := ( -0.170602962675E-06 )*on_x1_h+( -0.105496309899E-04 )*on_sep1_h+( 0.110068226761E-04 )*on_oh1; +ACBCV8.L1B2ov := -0.231366909020E-04 ; +ACBCV8.L1B2 := ( -0.231366909020E-04)*on_ov1; +ACBCH7.L1B2oh := -0.376095212107E-04 ; +ACBCH7.L1B2 := ( -0.376095212107E-04)*on_oh1; +ACBCV7.R1B2ov := -0.361136185547E-04 ; +ACBCV7.R1B2 := ( -0.361136185547E-04)*on_ov1; +ACBCH8.R1B2oh := -0.231010415515E-04 ; +ACBCH8.R1B2 := ( -0.231010415515E-04)*on_oh1; + + !***IR5 Optics*** +KQX.L5 := -0.854562504357E-02 ; +KTQX1.L5 := 0.000000000000E+00 ; +KTQX2.L5 := 0.000000000000E+00 ; +KQX.R5 := -KQX.L5 ; +KTQX1.R5 := -KTQX1.L5 ; +KTQX2.R5 := -KTQX2.L5 ; + + !Beam1 +KQ4.L5B1 := 0.532939746961E-02 ; +KQ4.R5B1 := -0.532939746961E-02 ; +KQ5.L5B1 := -0.549310859055E-02 ; +KQ5.R5B1 := 0.549310859055E-02 ; +KQ6.L5B1 := 0.499494557346E-02 ; +KQ6.R5B1 := -0.499494557346E-02 ; +KQ7.L5B1 := -0.456683783331E-02 ; +KQ7.R5B1 := 0.448901787695E-02 ; +KQ8.L5B1 := 0.732209287546E-02 ; +KQ8.R5B1 := -0.733784873620E-02 ; +KQ9.L5B1 := -0.680559079530E-02 ; +KQ9.R5B1 := 0.686826741479E-02 ; +KQ10.L5B1 := 0.723117774947E-02 ; +KQ10.R5B1 := -0.717547782475E-02 ; +KQTL11.L5B1 := -0.568441367868E-03 ; +KQTL11.R5B1 := 0.447635694271E-03 ; +KQT12.L5B1 := 0.875025120228E-04 ; +KQT12.R5B1 := 0.251274473750E-02 ; +KQT13.L5B1 := -0.242130986821E-02 ; +KQT13.R5B1 := 0.719121815526E-04 ; + + !Beam2 +KQ4.L5B2 := -0.480712170430E-02 ; +KQ4.R5B2 := 0.480712170430E-02 ; +KQ5.L5B2 := 0.509850806769E-02 ; +KQ5.R5B2 := -0.509850806769E-02 ; +KQ6.L5B2 := -0.501561197551E-02 ; +KQ6.R5B2 := 0.501561197551E-02 ; +KQ7.L5B2 := 0.477695999326E-02 ; +KQ7.R5B2 := -0.472762660695E-02 ; +KQ8.L5B2 := -0.710402639207E-02 ; +KQ8.R5B2 := 0.709847080621E-02 ; +KQ9.L5B2 := 0.709604793831E-02 ; +KQ9.R5B2 := -0.712819245801E-02 ; +KQ10.L5B2 := -0.733748351116E-02 ; +KQ10.R5B2 := 0.731101565147E-02 ; +KQTL11.L5B2 := 0.598240054096E-03 ; +KQTL11.R5B2 := -0.479983721888E-03 ; +KQT12.L5B2 := -0.902980664653E-03 ; +KQT12.R5B2 := -0.117280120507E-02 ; +KQT13.L5B2 := 0.171100605794E-02 ; +KQT13.R5B2 := -0.259385575933E-03 ; + + !***IR5 X-scheme*** +ACBXV1.L5x := 0.549019607843E-07 ; +ACBXV1.L5s := 0.800000000000E-05 ; +ACBXV1.L5 := ( 0.549019607843E-07 )*on_x5_v +( 0.800000000000E-05 )*on_sep5_v ; +ACBXV1.R5x := -0.549019607843E-07 ; +ACBXV1.R5s := 0.800000000000E-05 ; +ACBXV1.R5 := ( -0.549019607843E-07 )*on_x5_v +( 0.800000000000E-05 )*on_sep5_v ; +ACBXH1.L5x := 0.549019607843E-07 ; +ACBXH1.L5s := -0.800000000000E-05 ; +ACBXH1.L5 := ( 0.549019607843E-07 )*on_x5_h +( -0.800000000000E-05 )*on_sep5_h ; +ACBXH1.R5x := -0.549019607843E-07 ; +ACBXH1.R5s := -0.800000000000E-05 ; +ACBXH1.R5 := ( -0.549019607843E-07 )*on_x5_h +( -0.800000000000E-05 )*on_sep5_h ; +ACBXV2.L5x := 0.549019607843E-07 ; +ACBXV2.L5s := 0.800000000000E-05 ; +ACBXV2.L5 := ( 0.549019607843E-07 )*on_x5_v +( 0.800000000000E-05 )*on_sep5_v ; +ACBXV2.R5x := -0.549019607843E-07 ; +ACBXV2.R5s := 0.800000000000E-05 ; +ACBXV2.R5 := ( -0.549019607843E-07 )*on_x5_v +( 0.800000000000E-05 )*on_sep5_v ; +ACBXH2.L5x := 0.549019607843E-07 ; +ACBXH2.L5s := -0.800000000000E-05 ; +ACBXH2.L5 := ( 0.549019607843E-07 )*on_x5_h +( -0.800000000000E-05 )*on_sep5_h ; +ACBXH2.R5x := -0.549019607843E-07 ; +ACBXH2.R5s := -0.800000000000E-05 ; +ACBXH2.R5 := ( -0.549019607843E-07 )*on_x5_h +( -0.800000000000E-05 )*on_sep5_h ; +ACBXV3.L5x := 0.549019607843E-07 ; +ACBXV3.L5s := 0.800000000000E-05 ; +ACBXV3.L5 := ( 0.549019607843E-07 )*on_x5_v +( 0.800000000000E-05 )*on_sep5_v ; +ACBXV3.R5x := -0.549019607843E-07 ; +ACBXV3.R5s := 0.800000000000E-05 ; +ACBXV3.R5 := ( -0.549019607843E-07 )*on_x5_v +( 0.800000000000E-05 )*on_sep5_v ; +ACBXH3.L5x := 0.549019607843E-07 ; +ACBXH3.L5s := -0.800000000000E-05 ; +ACBXH3.L5 := ( 0.549019607843E-07 )*on_x5_h +( -0.800000000000E-05 )*on_sep5_h ; +ACBXH3.R5x := -0.549019607843E-07 ; +ACBXH3.R5s := -0.800000000000E-05 ; +ACBXH3.R5 := ( -0.549019607843E-07 )*on_x5_h +( -0.800000000000E-05 )*on_sep5_h ; + + !Beam1 +ACBYVS4.L5B1x := -0.220949195645E-06 ; +ACBYVS4.L5B1s := 0.232108182498E-04 ; +ACBYVS4.L5B1ov:= 0.238291621199E-04 ; +ACBYVS4.L5B1 := ( -0.220949195645E-06 )*on_x5_v+( 0.232108182498E-04 )*on_sep5_v+( 0.238291621199E-04 )*on_ov5; +ACBYVS4.R5B1x := 0.110280617994E-06 ; +ACBYVS4.R5B1s := -0.239208859279E-05 ; +ACBYVS4.R5B1ov:= 0.105572715976E-04 ; +ACBYVS4.R5B1 := ( 0.110280617994E-06 )*on_x5_v+( -0.239208859279E-05 )*on_sep5_v+( 0.105572715976E-04 )*on_ov5; +ACBCV5.L5B1x := -0.101910593064E-06 ; +ACBCV5.L5B1s := -0.102875467053E-04 ; +ACBCV5.L5B1ov := 0.222380768403E-04 ; +ACBCV5.L5B1 := ( -0.101910593064E-06 )*on_x5_v+( -0.102875467053E-04 )*on_sep5_v+( 0.222380768403E-04 )*on_ov5; +ACBCV6.R5B1x := 0.159506191946E-06 ; +ACBCV6.R5B1s := 0.986349425437E-05 ; +ACBCV6.R5B1ov := 0.985235720125E-05 ; +ACBCV6.R5B1 := ( 0.159506191946E-06 )*on_x5_v+( 0.986349425437E-05 )*on_sep5_v+( 0.985235720125E-05 )*on_ov5; +ACBYHS4.L5B1x := -0.983004449266E-07 ; +ACBYHS4.L5B1s := 0.313282618249E-05 ; +ACBYHS4.L5B1oh:= 0.107380079522E-04 ; +ACBYHS4.L5B1 := ( -0.983004449266E-07 )*on_x5_h+( 0.313282618249E-05 )*on_sep5_h+( 0.107380079522E-04 )*on_oh5; +ACBYHS4.R5B1x := 0.238947550477E-06 ; +ACBYHS4.R5B1s := -0.213925054414E-04 ; +ACBYHS4.R5B1oh:= 0.224745235292E-04 ; +ACBYHS4.R5B1 := ( 0.238947550477E-06 )*on_x5_h+( -0.213925054414E-04 )*on_sep5_h+( 0.224745235292E-04 )*on_oh5; +ACBCH6.L5B1x := -0.168420055840E-06 ; +ACBCH6.L5B1s := -0.104146390652E-04 ; +ACBCH6.L5B1oh := 0.100210256974E-04 ; +ACBCH6.L5B1 := ( -0.168420055840E-06 )*on_x5_h+( -0.104146390652E-04 )*on_sep5_h+( 0.100210256974E-04 )*on_oh5; +ACBCH5.R5B1x := 0.897572884285E-07 ; +ACBCH5.R5B1s := 0.905992577644E-05 ; +ACBCH5.R5B1oh := 0.209738881576E-04 ; +ACBCH5.R5B1 := ( 0.897572884285E-07 )*on_x5_h+( 0.905992577644E-05 )*on_sep5_h+( 0.209738881576E-04 )*on_oh5; +ACBCH8.L5B1oh := -0.219370052312E-04 ; +ACBCH8.L5B1 := ( -0.219370052312E-04)*on_oh5; +ACBCV7.L5B1ov := -0.380276570706E-04 ; +ACBCV7.L5B1 := ( -0.380276570706E-04)*on_ov5; +ACBCH7.R5B1oh := -0.389631993797E-04 ; +ACBCH7.R5B1 := ( -0.389631993797E-04)*on_oh5; +ACBCV8.R5B1ov := -0.228931104417E-04 ; +ACBCV8.R5B1 := ( -0.228931104417E-04)*on_ov5; + + !Beam2 +ACBYVS4.L5B2x := 0.801340596828E-07 ; +ACBYVS4.L5B2s := 0.425628214283E-05 ; +ACBYVS4.L5B2ov:= 0.121329380580E-04 ; +ACBYVS4.L5B2 := ( 0.801340596828E-07 )*on_x5_v+( 0.425628214283E-05 )*on_sep5_v+( 0.121329380580E-04 )*on_ov5; +ACBYVS4.R5B2x := -0.242723837542E-06 ; +ACBYVS4.R5B2s := -0.210127379873E-04 ; +ACBYVS4.R5B2ov:= 0.239826987798E-04 ; +ACBYVS4.R5B2 := ( -0.242723837542E-06 )*on_x5_v+( -0.210127379873E-04 )*on_sep5_v+( 0.239826987798E-04 )*on_ov5; +ACBCV6.L5B2x := 0.170600646628E-06 ; +ACBCV6.L5B2s := -0.105495498088E-04 ; +ACBCV6.L5B2ov := 0.113228156103E-04 ; +ACBCV6.L5B2 := ( 0.170600646628E-06 )*on_x5_v+( -0.105495498088E-04 )*on_sep5_v+( 0.113228156103E-04 )*on_ov5; +ACBCV5.R5B2x := -0.897818754159E-07 ; +ACBCV5.R5B2s := 0.906319153047E-05 ; +ACBCV5.R5B2ov := 0.223813617792E-04 ; +ACBCV5.R5B2 := ( -0.897818754159E-07 )*on_x5_v+( 0.906319153047E-05 )*on_sep5_v+( 0.223813617792E-04 )*on_ov5; +ACBYHS4.L5B2x := 0.224549528623E-06 ; +ACBYHS4.L5B2s := 0.228458183833E-04 ; +ACBYHS4.L5B2oh:= 0.251161661279E-04 ; +ACBYHS4.L5B2 := ( 0.224549528623E-06 )*on_x5_h+( 0.228458183833E-04 )*on_sep5_h+( 0.251161661279E-04 )*on_oh5; +ACBYHS4.R5B2x := -0.924385638824E-07 ; +ACBYHS4.R5B2s := -0.349531196308E-05 ; +ACBYHS4.R5B2oh:= 0.120170681991E-04 ; +ACBYHS4.R5B2 := ( -0.924385638824E-07 )*on_x5_h+( -0.349531196308E-05 )*on_sep5_h+( 0.120170681991E-04 )*on_oh5; +ACBCH5.L5B2x := 0.102486278372E-06 ; +ACBCH5.L5B2s := -0.103447685685E-04 ; +ACBCH5.L5B2oh := 0.234391469357E-04 ; +ACBCH5.L5B2 := ( 0.102486278372E-06 )*on_x5_h+( -0.103447685685E-04 )*on_sep5_h+( 0.234391469357E-04 )*on_oh5; +ACBCH6.R5B2x := -0.161874731447E-06 ; +ACBCH6.R5B2s := 0.100099005116E-04 ; +ACBCH6.R5B2oh := 0.112146824409E-04 ; +ACBCH6.R5B2 := ( -0.161874731447E-06 )*on_x5_h+( 0.100099005116E-04 )*on_sep5_h+( 0.112146824409E-04 )*on_oh5; +ACBCV8.L5B2ov := -0.227064267043E-04 ; +ACBCV8.L5B2 := ( -0.227064267043E-04)*on_ov5; +ACBCH7.L5B2oh := -0.361933012237E-04 ; +ACBCH7.L5B2 := ( -0.361933012237E-04)*on_oh5; +ACBCV7.R5B2ov := -0.374291564539E-04 ; +ACBCV7.R5B2 := ( -0.374291564539E-04)*on_ov5; +ACBCH8.R5B2oh := -0.235263402012E-04 ; +ACBCH8.R5B2 := ( -0.235263402012E-04)*on_oh5; + + !***IR2 Optics*** +KQX.L2 := 0.950981581300E-02 ; +KTQX1.L2 := 0.000000000000E+00 ; +KTQX2.L2 := 0.000000000000E+00 ; +KQX.R2 := -KQX.L2 ; +KTQX1.R2 := -KTQX1.L2 ; +KTQX2.R2 := -KTQX2.L2 ; + + !Beam1 +KQ4.L2B1 := -0.549274522900E-02 ; +KQ4.R2B1 := 0.471284923000E-02 ; +KQ5.L2B1 := 0.482678438300E-02 ; +KQ5.R2B1 := -0.461752389200E-02 ; +KQ6.L2B1 := -0.412280426800E-02 ; +KQ6.R2B1 := 0.411759641200E-02 ; +KQ7.L2B1 := 0.567093958700E-02 ; +KQ7.R2B1 := -0.558634402600E-02 ; +KQ8.L2B1 := -0.372181911500E-02 ; +KQ8.R2B1 := 0.549676225900E-02 ; +KQ9.L2B1 := 0.569021737800E-02 ; +KQ9.R2B1 := -0.552491220300E-02 ; +KQ10.L2B1 := -0.546840282100E-02 ; +KQ10.R2B1 := 0.725790465900E-02 ; +KQTL11.L2B1 := 0.243572091600E-03 ; +KQTL11.R2B1 := -0.707734797100E-03 ; +KQT12.L2B1 := 0.247915277100E-02 ; +KQT12.R2B1 := -0.212101584500E-02 ; +KQT13.L2B1 := -0.361161054600E-02 ; +KQT13.R2B1 := -0.565934150300E-03 ; + + !Beam2 +KQ4.L2B2 := 0.452521222318E-02 ; +KQ4.R2B2 := -0.500626968981E-02 ; +KQ5.L2B2 := -0.405061343361E-02 ; +KQ5.R2B2 := 0.467063541927E-02 ; +KQ6.L2B2 := 0.406368933511E-02 ; +KQ6.R2B2 := -0.406381317453E-02 ; +KQ7.L2B2 := -0.640786355563E-02 ; +KQ7.R2B2 := 0.582033421128E-02 ; +KQ8.L2B2 := 0.633750244207E-02 ; +KQ8.R2B2 := -0.396639924046E-02 ; +KQ9.L2B2 := -0.689032817327E-02 ; +KQ9.R2B2 := 0.589585691962E-02 ; +KQ10.L2B2 := 0.725229605511E-02 ; +KQ10.R2B2 := -0.569250285723E-02 ; +KQTL11.L2B2 := 0.174716642135E-02 ; +KQTL11.R2B2 := 0.368388534240E-03 ; +KQT12.L2B2 := -0.461187461690E-02 ; +KQT12.R2B2 := 0.250760353834E-03 ; +KQT13.L2B2 := 0.495111039908E-02 ; +KQT13.R2B2 := -0.212771737980E-02 ; + + !***IR2 X-scheme*** +abxwt.l2 := -0.0000772587268993839836*on_alice ; +abwmd.l2 := +0.0001472587268993839840*on_alice ; +abaw.r2 := -0.0001335474860334838000*on_alice ; +abxwt.r2 := +0.0000635474860334838004*on_alice ; +ACBXV1.L2x := 0.588235294118E-08; +ACBXV1.L2s := 0.900000000000E-05; +ACBXV1.L2 := 0.588235294118E-08 * on_x2v + ( 0.900000000000E-05) * on_sep2v; +ACBXV1.R2x := -0.588235294118E-08; +ACBXV1.R2s := 0.900000000000E-05; +ACBXV1.R2 := -0.588235294118E-08 * on_x2v + ( 0.900000000000E-05) * on_sep2v; +ACBXH1.L2x := 0.000000000000E+00; +ACBXH1.L2s := 0.000000000000E+00; +ACBXH1.L2 := 0.000000000000E+00 * on_x2h + ( 0.000000000000E+00) * on_sep2h; +ACBXH1.R2x := -0.588235294118E-08; +ACBXH1.R2s := 0.900000000000E-05; +ACBXH1.R2 := -0.588235294118E-08 * on_x2h + ( 0.900000000000E-05) * on_sep2h; +ACBXV2.L2x := 0.588235294118E-08; +ACBXV2.L2s := 0.900000000000E-05; +ACBXV2.L2 := 0.588235294118E-08 * on_x2v + ( 0.900000000000E-05) * on_sep2v; +ACBXV2.R2x := -0.588235294118E-08; +ACBXV2.R2s := 0.900000000000E-05; +ACBXV2.R2 := -0.588235294118E-08 * on_x2v + ( 0.900000000000E-05) * on_sep2v; +ACBXH2.L2x := 0.882352941176E-08; +ACBXH2.L2s := 0.135000000000E-04; +ACBXH2.L2 := 0.882352941176E-08 * on_x2h + ( 0.135000000000E-04) * on_sep2h; +ACBXH2.R2x := -0.588235294118E-08; +ACBXH2.R2s := 0.900000000000E-05; +ACBXH2.R2 := -0.588235294118E-08 * on_x2h + ( 0.900000000000E-05) * on_sep2h; +ACBXV3.L2x := 0.588235294118E-08; +ACBXV3.L2s := 0.900000000000E-05; +ACBXV3.L2 := 0.588235294118E-08 * on_x2v + ( 0.900000000000E-05) * on_sep2v; +ACBXV3.R2x := -0.588235294118E-08; +ACBXV3.R2s := 0.900000000000E-05; +ACBXV3.R2 := -0.588235294118E-08 * on_x2v + ( 0.900000000000E-05) * on_sep2v; +ACBXH3.L2x := 0.882352941176E-08; +ACBXH3.L2s := 0.135000000000E-04; +ACBXH3.L2 := 0.882352941176E-08 * on_x2h + ( 0.135000000000E-04) * on_sep2h; +ACBXH3.R2x := -0.588235294118E-08; +ACBXH3.R2s := 0.900000000000E-05; +ACBXH3.R2 := -0.588235294118E-08 * on_x2h + ( 0.900000000000E-05) * on_sep2h; + + !Beam1 + ACBYVS4.L2B1x := -0.359306469082E-06 ; + ACBYVS4.L2B1s := 0.967871365304E-05 ; + ACBYVS4.L2B1o := 0.226681316440E-04 ; + ACBYVS4.L2B1ov:= 0.647912745271E-05 ; + ACBYVS4.L2B1 := -0.359306469082E-06 * on_x2v + ( 0.967871365304E-05) * on_sep2v + ( 0.226681316440E-04) * on_o2 + ( 0.647912745271E-05) * on_ov2; + ACBYV4.L2B1 := 0.000000000000E+00 * on_x2v + ( 0.000000000000E+00) * on_sep2v + ( 0.000000000000E+00) * on_o2 + ( 0.647912745271E-05) * on_ov2; + ACBYVS4.R2B1x := -0.265881050814E-07 ; + ACBYVS4.R2B1s := 0.187114720092E-04 ; + ACBYVS4.R2B1o := 0.121026173328E-03 ; + ACBYVS4.R2B1ov:= 0.421914517202E-04 ; + ACBYVS4.R2B1 := -0.265881050814E-07 * on_x2v + ( 0.187114720092E-04) * on_sep2v + ( 0.121026173328E-03) * on_o2 + ( 0.421914517202E-04) * on_ov2; + ACBYVS5.L2B1x := -0.370036866051E-07 ; + ACBYVS5.L2B1s := 0.484848457040E-05 ; + ACBYVS5.L2B1o := -0.681187797143E-04 ; + ACBYVS5.L2B1ov:= -0.464522709319E-04 ; + ACBYVS5.L2B1 := -0.370036866051E-07 * on_x2v + ( 0.484848457040E-05) * on_sep2v + ( -0.681187797143E-04) * on_o2 + ( -0.464522709319E-04) * on_ov2; + ACBCVS5.R2B1x := 0.263924957448E-06 ; + ACBCVS5.R2B1s := -0.543938795831E-05 ; + ACBCVS5.R2B1o := -0.548545420249E-04 ; + ACBCVS5.R2B1ov:= -0.809924595773E-05 ; + ACBCVS5.R2B1 := 0.263924957448E-06 * on_x2v + ( -0.543938795831E-05) * on_sep2v + ( -0.548545420249E-04) * on_o2 + ( -0.809924595773E-05) * on_ov2; + ACBCV5.R2B1 := 0.000000000000E+00 * on_x2v + ( 0.000000000000E+00) * on_sep2v + ( 0.000000000000E+00) * on_o2 + ( -0.809924595773E-05) * on_ov2; + ACBYHS4.L2B1x := -0.301684625582E-07 ; + ACBYHS4.L2B1s := 0.803040506369E-05 ; + ACBYHS4.L2B1a := 0.368242425339E-07 ; + ACBYHS4.L2B1oh:= 0.442919848516E-04 ; + ACBYHS4.L2B1 := -0.301684625582E-07 * on_x2h + ( 0.803040506369E-05) * on_sep2h + ( 0.368242425339E-07) * on_a2 + ( 0.442919848516E-04) * on_oh2; + ACBYHS4.R2B1x := 0.336005985737E-06 ; + ACBYHS4.R2B1s := 0.662687107340E-05 ; + ACBYHS4.R2B1a := 0.297500680936E-06 ; + ACBYHS4.R2B1oh:= 0.130805280449E-04 ; + ACBYHS4.R2B1 := 0.336005985737E-06 * on_x2h + ( 0.662687107340E-05) * on_sep2h + ( 0.297500680936E-06) * on_a2 + ( 0.130805280449E-04) * on_oh2; + ACBYH4.R2B1 := 0.000000000000E+00 * on_x2h + ( 0.000000000000E+00) * on_sep2h + ( 0.000000000000E+00) * on_a2 + ( 0.130805280449E-04) * on_oh2; + ACBYHS5.L2B1x := -0.209785441294E-06 ; + ACBYHS5.L2B1s := -0.155017932911E-05 ; + ACBYHS5.L2B1a := -0.237517327511E-06 ; + ACBYHS5.L2B1oh:= -0.747106707276E-05 ; + ACBYHS5.L2B1 := -0.209785441294E-06 * on_x2h + ( -0.155017932911E-05) * on_sep2h + ( -0.237517327511E-06) * on_a2 + ( -0.747106707276E-05) * on_oh2; + ACBYH5.L2B1 := 0.000000000000E+00 * on_x2h + ( 0.000000000000E+00) * on_sep2h + ( 0.000000000000E+00) * on_a2 + ( -0.747106707276E-05) * on_oh2; + ACBCHS5.R2B1x := 0.550071248297E-07 ; + ACBCHS5.R2B1s := 0.720563921720E-05 ; + ACBCHS5.R2B1a := 0.125878506890E-06 ; + ACBCHS5.R2B1oh:= -0.316351747110E-04 ; + ACBCHS5.R2B1 := 0.550071248297E-07 * on_x2h + ( 0.720563921720E-05) * on_sep2h + ( 0.125878506890E-06) * on_a2 + ( -0.316351747110E-04) * on_oh2; + ACBCH6.R2B1oh := -0.122071345316E-04 ; + ACBCH6.R2B1 :=( -0.122071345316E-04)*on_oh2; + ACBCV6.L2B1ov := -0.604651281594E-05 ; + ACBCV6.L2B1 :=( -0.604651281594E-05)*on_ov2; + ACBCH7.L2B1oh := -0.212190413889E-04 ; + ACBCH7.L2B1 :=( -0.212190413889E-04)*on_oh2; + ACBCV7.R2B1ov := -0.202127351779E-04 ; + ACBCV7.R2B1 :=( -0.202127351779E-04)*on_ov2; + + !Beam2 + ACBYVS4.L2B2x := 0.577672138807E-07 ; + ACBYVS4.L2B2s := -0.169729431706E-04 ; + ACBYVS4.L2B2o := 0.103493647937E-03 ; + ACBYVS4.L2B2ov:= 0.416028721622E-04 ; + ACBYVS4.L2B2 := 0.577672138807E-07 * on_x2v + ( -0.169729431706E-04) * on_sep2v + ( 0.103493647937E-03) * on_o2 + ( 0.416028721622E-04) * on_ov2; + ACBYVS4.R2B2x := -0.338366891365E-06 ; + ACBYVS4.R2B2s := -0.693506266163E-05 ; + ACBYVS4.R2B2o := 0.612150535242E-04 ; + ACBYVS4.R2B2ov:= 0.121133629350E-04 ; + ACBYVS4.R2B2 := -0.338366891365E-06 * on_x2v + ( -0.693506266163E-05) * on_sep2v + ( 0.612150535242E-04) * on_o2 + ( 0.121133629350E-04) * on_ov2; + ACBYV4.R2B2 := 0.000000000000E+00 * on_x2v + ( 0.000000000000E+00) * on_sep2v + ( 0.000000000000E+00) * on_o2 + ( 0.121133629350E-04) * on_ov2; + ACBYVS5.L2B2x := 0.210669686866E-06 ; + ACBYVS5.L2B2s := 0.434181842841E-05 ; + ACBYVS5.L2B2o := -0.437858929958E-04 ; + ACBYVS5.L2B2ov:= -0.235466128048E-05 ; + ACBYVS5.L2B2 := 0.210669686866E-06 * on_x2v + ( 0.434181842841E-05) * on_sep2v + ( -0.437858929958E-04) * on_o2 + ( -0.235466128048E-05) * on_ov2; + ACBYV5.L2B2 := 0.000000000000E+00 * on_x2v + ( 0.000000000000E+00) * on_sep2v + ( 0.000000000000E+00) * on_o2 + ( -0.235466128048E-05) * on_ov2; + ACBCVS5.R2B2x := -0.556113941340E-07 ; + ACBCVS5.R2B2s := -0.728659552868E-05 ; + ACBCVS5.R2B2o := -0.102373015930E-03 ; + ACBCVS5.R2B2ov:= -0.377186132907E-04 ; + ACBCVS5.R2B2 := -0.556113941340E-07 * on_x2v + ( -0.728659552868E-05) * on_sep2v + ( -0.102373015930E-03) * on_o2 + ( -0.377186132907E-04) * on_ov2; + ACBYHS4.L2B2x := 0.351657604285E-06 ; + ACBYHS4.L2B2s := -0.655793522704E-05 ; + ACBYHS4.L2B2a := -0.330398196602E-06 ; + ACBYHS4.L2B2oh:= 0.112925479721E-04 ; + ACBYHS4.L2B2 := 0.351657604285E-06 * on_x2h + ( -0.655793522704E-05) * on_sep2h + ( -0.330398196602E-06) * on_a2 + ( 0.112925479721E-04) * on_oh2; + ACBYH4.L2B2 := 0.000000000000E+00 * on_x2h + ( 0.000000000000E+00) * on_sep2h + ( 0.000000000000E+00) * on_a2 + ( 0.112925479721E-04) * on_oh2; + ACBYHS4.R2B2x := 0.360918624029E-07 ; + ACBYHS4.R2B2s := -0.189056672398E-04 ; + ACBYHS4.R2B2a := -0.104121190725E-06 ; + ACBYHS4.R2B2oh:= 0.435128290583E-04 ; + ACBYHS4.R2B2 := 0.360918624029E-07 * on_x2h + ( -0.189056672398E-04) * on_sep2h + ( -0.104121190725E-06) * on_a2 + ( 0.435128290583E-04) * on_oh2; + ACBYHS5.L2B2x := 0.348993247816E-07 ; + ACBYHS5.L2B2s := -0.697737704950E-05 ; + ACBYHS5.L2B2a := -0.831782737488E-07 ; + ACBYHS5.L2B2oh:= -0.310788968197E-04 ; + ACBYHS5.L2B2 := 0.348993247816E-07 * on_x2h + ( -0.697737704950E-05) * on_sep2h + ( -0.831782737488E-07) * on_a2 + ( -0.310788968197E-04) * on_oh2; + ACBCHS5.R2B2x := -0.261914932151E-06 ; + ACBCHS5.R2B2s := 0.539723817366E-05 ; + ACBCHS5.R2B2a := 0.293963812481E-06 ; + ACBCHS5.R2B2oh:= -0.808365061049E-05 ; + ACBCHS5.R2B2 := -0.261914932151E-06 * on_x2h + ( 0.539723817366E-05) * on_sep2h + ( 0.293963812481E-06) * on_a2 + ( -0.808365061049E-05) * on_oh2; + ACBCH5.R2B2 := 0.000000000000E+00 * on_x2h + ( 0.000000000000E+00) * on_sep2h + ( 0.000000000000E+00) * on_a2 + ( -0.808365061049E-05) * on_oh2; + ACBCH6.L2B2oh := -0.105385387981E-04 ; + ACBCH6.L2B2 :=( -0.105385387981E-04)*on_oh2; + ACBCV6.R2B2ov := -0.113045475282E-04 ; + ACBCV6.R2B2 :=( -0.113045475282E-04)*on_ov2; + ACBCH7.R2B2oh := -0.208457698120E-04 ; + ACBCH7.R2B2 :=( -0.208457698120E-04)*on_oh2; + ACBCV7.L2B2ov := -0.199307633032E-04 ; + ACBCV7.L2B2 :=( -0.199307633032E-04)*on_ov2; + + !***IR8 Optics*** +KQX.L8 := 0.950981581300E-02 ; +KTQX1.L8 := 0.000000000000E+00 ; +KTQX2.L8 := 0.000000000000E+00 ; +KQX.R8 := -KQX.L8 ; +KTQX1.R8 := -KTQX1.L8 ; +KTQX2.R8 := -KTQX2.L8 ; + + !Beam1 +KQ4.L8B1 := -0.444742916700E-02 ; +KQ4.R8B1 := 0.518334462400E-02 ; +KQ5.L8B1 := 0.436221680400E-02 ; +KQ5.R8B1 := -0.445506455500E-02 ; +KQ6.L8B1 := -0.410379100600E-02 ; +KQ6.R8B1 := 0.363751203300E-02 ; +KQ7.L8B1 := 0.602522217500E-02 ; +KQ7.R8B1 := -0.588377725600E-02 ; +KQ8.L8B1 := -0.463355083200E-02 ; +KQ8.R8B1 := 0.641931511900E-02 ; +KQ9.L8B1 := 0.655889492800E-02 ; +KQ9.R8B1 := -0.665649775100E-02 ; +KQ10.L8B1 := -0.579881572000E-02 ; +KQ10.R8B1 := 0.730644092200E-02 ; +KQTL11.L8B1 := 0.140097521800E-03 ; +KQTL11.R8B1 := 0.279089877900E-03 ; +KQT12.L8B1 := 0.319089331500E-02 ; +KQT12.R8B1 := -0.326612795500E-02 ; +KQT13.L8B1 := 0.638267067900E-03 ; +KQT13.R8B1 := -0.102490097000E-02 ; + + !Beam2 +KQ4.L8B2 := 0.449559181916E-02 ; +KQ4.R8B2 := -0.447368899600E-02 ; +KQ5.L8B2 := -0.538821723331E-02 ; +KQ5.R8B2 := 0.425682473400E-02 ; +KQ6.L8B2 := 0.437836800124E-02 ; +KQ6.R8B2 := -0.357803963762E-02 ; +KQ7.L8B2 := -0.634827888396E-02 ; +KQ7.R8B2 := 0.577974785469E-02 ; +KQ8.L8B2 := 0.591138830612E-02 ; +KQ8.R8B2 := -0.507012392471E-02 ; +KQ9.L8B2 := -0.520380412295E-02 ; +KQ9.R8B2 := 0.687320071310E-02 ; +KQ10.L8B2 := 0.713269884012E-02 ; +KQ10.R8B2 := -0.645711113202E-02 ; +KQTL11.L8B2 := 0.193266159142E-02 ; +KQTL11.R8B2 := 0.513724567865E-03 ; +KQT12.L8B2 := -0.277897753841E-02 ; +KQT12.R8B2 := 0.295012492459E-02 ; +KQT13.L8B2 := 0.462333918060E-02 ; +KQT13.R8B2 := 0.293618183397E-02 ; + + !***IR8 X-scheme*** +abxws.l8 := -0.000045681598453109894*on_lhcb ; +abxwh.l8 := +0.000180681598453109894*on_lhcb ; +ablw.r8 := -0.000180681598453109894*on_lhcb ; +abxws.r8 := +0.000045681598453109894*on_lhcb ; +ACBXV1.L8x := 0.117647058824E-07; +ACBXV1.L8s := 0.900000000000E-05; +ACBXV1.L8 := 0.117647058824E-07 * on_x8v + ( 0.900000000000E-05) * on_sep8v; +ACBXV1.R8x := -0.117647058824E-07; +ACBXV1.R8s := 0.900000000000E-05; +ACBXV1.R8 := -0.117647058824E-07 * on_x8v + ( 0.900000000000E-05) * on_sep8v; +ACBXH1.L8x := 0.117647058824E-07; +ACBXH1.L8s := 0.900000000000E-05; +ACBXH1.L8 := 0.117647058824E-07 * on_x8h + ( 0.900000000000E-05) * on_sep8h; +ACBXH1.R8x := -0.117647058824E-07; +ACBXH1.R8s := 0.900000000000E-05; +ACBXH1.R8 := -0.117647058824E-07 * on_x8h + ( 0.900000000000E-05) * on_sep8h; +ACBXV2.L8x := 0.117647058824E-07; +ACBXV2.L8s := 0.900000000000E-05; +ACBXV2.L8 := 0.117647058824E-07 * on_x8v + ( 0.900000000000E-05) * on_sep8v; +ACBXV2.R8x := -0.117647058824E-07; +ACBXV2.R8s := 0.900000000000E-05; +ACBXV2.R8 := -0.117647058824E-07 * on_x8v + ( 0.900000000000E-05) * on_sep8v; +ACBXH2.L8x := 0.117647058824E-07; +ACBXH2.L8s := 0.900000000000E-05; +ACBXH2.L8 := 0.117647058824E-07 * on_x8h + ( 0.900000000000E-05) * on_sep8h; +ACBXH2.R8x := -0.117647058824E-07; +ACBXH2.R8s := 0.900000000000E-05; +ACBXH2.R8 := -0.117647058824E-07 * on_x8h + ( 0.900000000000E-05) * on_sep8h; +ACBXV3.L8x := 0.117647058824E-07; +ACBXV3.L8s := 0.900000000000E-05; +ACBXV3.L8 := 0.117647058824E-07 * on_x8v + ( 0.900000000000E-05) * on_sep8v; +ACBXV3.R8x := -0.117647058824E-07; +ACBXV3.R8s := 0.900000000000E-05; +ACBXV3.R8 := -0.117647058824E-07 * on_x8v + ( 0.900000000000E-05) * on_sep8v; +ACBXH3.L8x := 0.117647058824E-07; +ACBXH3.L8s := 0.900000000000E-05; +ACBXH3.L8 := 0.117647058824E-07 * on_x8h + ( 0.900000000000E-05) * on_sep8h; +ACBXH3.R8x := -0.117647058824E-07; +ACBXH3.R8s := 0.900000000000E-05; +ACBXH3.R8 := -0.117647058824E-07 * on_x8h + ( 0.900000000000E-05) * on_sep8h; + + !Beam1 + ACBYVS4.L8B1x := -0.370598600532E-06 ; + ACBYVS4.L8B1s := 0.840739730187E-05 ; + ACBYVS4.L8B1a := -0.328608937393E-06 ; + ACBYVS4.L8B1ov:= 0.126145542272E-04 ; + ACBYVS4.L8B1 := -0.370598600532E-06 * on_x8v + ( 0.840739730187E-05) * on_sep8v + ( -0.328608937393E-06) * on_a8 + ( 0.126145542272E-04) * on_ov8; + ACBYV4.L8B1 := 0.000000000000E+00 * on_x8v + ( 0.000000000000E+00) * on_sep8v + ( 0.000000000000E+00) * on_a8 + ( 0.126145542272E-04) * on_ov8; + ACBYVS4.R8B1x := 0.938377208233E-07 ; + ACBYVS4.R8B1s := 0.174538575200E-04 ; + ACBYVS4.R8B1a := -0.249723205992E-07 ; + ACBYVS4.R8B1ov:= 0.389867684856E-04 ; + ACBYVS4.R8B1 := 0.938377208233E-07 * on_x8v + ( 0.174538575200E-04) * on_sep8v + ( -0.249723205992E-07) * on_a8 + ( 0.389867684856E-04) * on_ov8; + ACBCVS5.L8B1x := 0.103695222682E-07 ; + ACBCVS5.L8B1s := 0.470430103501E-05 ; + ACBCVS5.L8B1a := -0.821760660637E-07 ; + ACBCVS5.L8B1ov:= -0.301641088553E-04 ; + ACBCVS5.L8B1 := 0.103695222682E-07 * on_x8v + ( 0.470430103501E-05) * on_sep8v + ( -0.821760660637E-07) * on_a8 + ( -0.301641088553E-04) * on_ov8; + ACBYVS5.R8B1x := 0.184450496490E-06 ; + ACBYVS5.R8B1s := -0.433151486387E-05 ; + ACBYVS5.R8B1a := 0.235889032138E-06 ; + ACBYVS5.R8B1ov:= -0.498374128676E-05 ; + ACBYVS5.R8B1 := 0.184450496490E-06 * on_x8v + ( -0.433151486387E-05) * on_sep8v + ( 0.235889032138E-06) * on_a8 + ( -0.498374128676E-05) * on_ov8; +ACBYV5.R8B1 := 0.000000000000E+00 * on_x8v + ( 0.000000000000E+00) * on_sep8v + ( 0.000000000000E+00) * on_a8 + ( -0.498374128676E-05) * on_ov8; + ACBYHS4.L8B1x := -0.117205865645E-06 ; + ACBYHS4.L8B1s := 0.169035964274E-04 ; + ACBYHS4.L8B1o := 0.102799389258E-03 ; + ACBYHS4.L8B1oh:= 0.447164644971E-04 ; + ACBYHS4.L8B1 := -0.117205865645E-06 * on_x8h + ( 0.169035964274E-04) * on_sep8h + ( 0.102799389258E-03) * on_o8 + ( 0.447164644971E-04) * on_oh8; + ACBYHS4.R8B1x := 0.368622757856E-06 ; + ACBYHS4.R8B1s := 0.930146960875E-05 ; + ACBYHS4.R8B1o := 0.279661736368E-04 ; + ACBYHS4.R8B1oh:= 0.787038416337E-05 ; + ACBYHS4.R8B1 := 0.368622757856E-06 * on_x8h + ( 0.930146960875E-05) * on_sep8h + ( 0.279661736368E-04) * on_o8 + ( 0.787038416337E-05) * on_oh8; + ACBYH4.R8B1 := 0.000000000000E+00 * on_x8h + ( 0.000000000000E+00) * on_sep8h + ( 0.000000000000E+00) * on_o8 + ( 0.787038416337E-05) * on_oh8; + ACBCHS5.L8B1x := -0.187734558528E-06 ; + ACBCHS5.L8B1s := -0.440800786381E-05 ; + ACBCHS5.L8B1o := -0.444555100070E-04 ; + ACBCHS5.L8B1oh:= -0.375977649657E-05 ; + ACBCHS5.L8B1 := -0.187734558528E-06 * on_x8h + ( -0.440800786381E-05) * on_sep8h + ( -0.444555100070E-04) * on_o8 + ( -0.375977649657E-05) * on_oh8; + ACBCH5.L8B1 := 0.000000000000E+00 * on_x8h + ( 0.000000000000E+00) * on_sep8h + ( 0.000000000000E+00) * on_o8 + ( -0.375977649657E-05) * on_oh8; + ACBYHS5.R8B1x := -0.106483961780E-07 ; + ACBYHS5.R8B1s := 0.483652230929E-05 ; + ACBYHS5.R8B1o := -0.679454818244E-04 ; + ACBYHS5.R8B1oh:= -0.402874597858E-04 ; + ACBYHS5.R8B1 := -0.106483961780E-07 * on_x8h + ( 0.483652230929E-05) * on_sep8h + ( -0.679454818244E-04) * on_o8 + ( -0.402874597858E-04) * on_oh8; + ACBCH6.R8B1oh := -0.734487460812E-05 ; + ACBCH6.R8B1 :=( -0.734487460812E-05)*on_oh8; + ACBCV6.L8B1ov := -0.117722740228E-04 ; + ACBCV6.L8B1 :=( -0.117722740228E-04)*on_ov8; + ACBCH7.L8B1oh := -0.214223976213E-04 ; + ACBCH7.L8B1 :=( -0.214223976213E-04)*on_oh8; + ACBCV7.R8B1ov := -0.186774617775E-04 ; + ACBCV7.R8B1 :=( -0.186774617775E-04)*on_ov8; + + !Beam2 + ACBYVS4.L8B2x := 0.117180757512E-06 ; + ACBYVS4.L8B2s := -0.169056856716E-04 ; + ACBYVS4.L8B2a := -0.488045166324E-08 ; + ACBYVS4.L8B2ov:= 0.433275254079E-04 ; + ACBYVS4.L8B2 := 0.117180757512E-06 * on_x8v + ( -0.169056856716E-04) * on_sep8v + ( -0.488045166324E-08) * on_a8 + ( 0.433275254079E-04) * on_ov8; + ACBYVS4.R8B2x := -0.370517978833E-06 ; + ACBYVS4.R8B2s := -0.844397268837E-05 ; + ACBYVS4.R8B2a := 0.329247846628E-06 ; + ACBYVS4.R8B2ov:= 0.114119006058E-04 ; + ACBYVS4.R8B2 := -0.370517978833E-06 * on_x8v + ( -0.844397268837E-05) * on_sep8v + ( 0.329247846628E-06) * on_a8 + ( 0.114119006058E-04) * on_ov8; + ACBYV4.R8B2 := 0.000000000000E+00 * on_x8v + ( 0.000000000000E+00) * on_sep8v + ( 0.000000000000E+00) * on_a8 + ( 0.114119006058E-04) * on_ov8; + ACBCVS5.L8B2x := 0.195659225750E-06 ; + ACBCVS5.L8B2s := 0.459473378142E-05 ; + ACBCVS5.L8B2a := -0.250223614299E-06 ; + ACBCVS5.L8B2ov:= -0.124159489441E-04 ; + ACBCVS5.L8B2 := 0.195659225750E-06 * on_x8v + ( 0.459473378142E-05) * on_sep8v + ( -0.250223614299E-06) * on_a8 + ( -0.124159489441E-04) * on_ov8; + ACBCV5.L8B2 := 0.000000000000E+00 * on_x8v + ( 0.000000000000E+00) * on_sep8v + ( 0.000000000000E+00) * on_a8 + ( -0.124159489441E-04) * on_ov8; + ACBYVS5.R8B2x := 0.104086484620E-07 ; + ACBYVS5.R8B2s := -0.472205870866E-05 ; + ACBYVS5.R8B2a := 0.824862621483E-07 ; + ACBYVS5.R8B2ov:= -0.267165638870E-04 ; + ACBYVS5.R8B2 := 0.104086484620E-07 * on_x8v + ( -0.472205870866E-05) * on_sep8v + ( 0.824862621483E-07) * on_a8 + ( -0.267165638870E-04) * on_ov8; + ACBYHS4.L8B2x := 0.370528835639E-06 ; + ACBYHS4.L8B2s := -0.843572526061E-05 ; + ACBYHS4.L8B2o := 0.401285097500E-04 ; + ACBYHS4.L8B2oh:= 0.126679491018E-04 ; + ACBYHS4.L8B2 := 0.370528835639E-06 * on_x8h + ( -0.843572526061E-05) * on_sep8h + ( 0.401285097500E-04) * on_o8 + ( 0.126679491018E-04) * on_oh8; + ACBYH4.L8B2 := 0.000000000000E+00 * on_x8h + ( 0.000000000000E+00) * on_sep8h + ( 0.000000000000E+00) * on_o8 + ( 0.126679491018E-04) * on_oh8; + ACBYHS4.R8B2x := -0.116250421371E-06 ; + ACBYHS4.R8B2s := -0.169260077017E-04 ; + ACBYHS4.R8B2o := 0.103025638291E-03 ; + ACBYHS4.R8B2oh:= 0.368868985081E-04 ; + ACBYHS4.R8B2 := -0.116250421371E-06 * on_x8h + ( -0.169260077017E-04) * on_sep8h + ( 0.103025638291E-03) * on_o8 + ( 0.368868985081E-04) * on_oh8; + ACBCHS5.L8B2x := -0.100793000386E-07 ; + ACBCHS5.L8B2s := -0.457801904082E-05 ; + ACBCHS5.L8B2o := -0.643141827314E-04 ; + ACBCHS5.L8B2oh:= -0.259949391155E-04 ; + ACBCHS5.L8B2 := -0.100793000386E-07 * on_x8h + ( -0.457801904082E-05) * on_sep8h + ( -0.643141827314E-04) * on_o8 + ( -0.259949391155E-04) * on_oh8; + ACBYHS5.R8B2x := -0.186717054487E-06 ; + ACBYHS5.R8B2s := 0.438410426426E-05 ; + ACBYHS5.R8B2o := -0.442145652312E-04 ; + ACBYHS5.R8B2oh:= -0.473283800199E-05 ; + ACBYHS5.R8B2 := -0.186717054487E-06 * on_x8h + ( 0.438410426426E-05) * on_sep8h + ( -0.442145652312E-04) * on_o8 + ( -0.473283800199E-05) * on_oh8; + ACBYH5.R8B2 := 0.000000000000E+00 * on_x8h + ( 0.000000000000E+00) * on_sep8h + ( 0.000000000000E+00) * on_o8 + ( -0.473283800199E-05) * on_oh8; + ACBCH6.L8B2oh := -0.118221036945E-04 ; + ACBCH6.L8B2 :=( -0.118221036945E-04)*on_oh8; + ACBCV6.R8B2ov := -0.106499221957E-04 ; + ACBCV6.R8B2 :=( -0.106499221957E-04)*on_ov8; + ACBCH7.R8B2oh := -0.176714732648E-04 ; + ACBCH7.R8B2 :=( -0.176714732648E-04)*on_oh8; + ACBCV7.L8B2ov := -0.207569960567E-04 ; + ACBCV7.L8B2 :=( -0.207569960567E-04)*on_ov8; + + !***IR4 Optics*** + + !Beam1 +KQ5.L4B1 := 0.424661437787E-02 ; +KQ5.R4B1 := -0.433498008414E-02 ; +KQ6.L4B1 := -0.505182623195E-02 ; +KQ6.R4B1 := 0.586991737700E-02 ; +KQ7.L4B1 := 0.710885440748E-02 ; +KQ7.R4B1 := -0.803917164603E-02 ; +KQ8.L4B1 := -0.487871574000E-02 ; +KQ8.R4B1 := 0.841787257258E-02 ; +KQ9.L4B1 := 0.666341447294E-02 ; +KQ9.R4B1 := -0.657270822152E-02 ; +KQ10.L4B1 := -0.560598317583E-02 ; +KQ10.R4B1 := 0.712809436385E-02 ; +KQTL11.L4B1 := 0.285697973189E-03 ; +KQTL11.R4B1 := 0.143457787233E-02 ; +KQT12.L4B1 := 0.347281451879E-02 ; +KQT12.R4B1 := -0.164097580184E-02 ; +KQT13.L4B1 := 0.141422735277E-02 ; +KQT13.R4B1 := -0.881864591583E-03 ; + + !Beam2 +KQ5.L4B2 := -0.422607022260E-02 ; +KQ5.R4B2 := 0.458706672627E-02 ; +KQ6.L4B2 := 0.585667110556E-02 ; +KQ6.R4B2 := -0.611835214396E-02 ; +KQ7.L4B2 := -0.504519892340E-02 ; +KQ7.R4B2 := 0.666437961122E-02 ; +KQ8.L4B2 := 0.856569447707E-02 ; +KQ8.R4B2 := -0.733406676228E-02 ; +KQ9.L4B2 := -0.510187013370E-02 ; +KQ9.R4B2 := 0.637625907364E-02 ; +KQ10.L4B2 := 0.714189340172E-02 ; +KQ10.R4B2 := -0.566728474458E-02 ; +KQTL11.L4B2 := 0.124411469642E-02 ; +KQTL11.R4B2 := 0.544319559471E-03 ; +KQT12.L4B2 := 0.823049005124E-03 ; +KQT12.R4B2 := 0.284569448861E-02 ; +KQT13.L4B2 := 0.132911144488E-02 ; +KQT13.R4B2 := -0.929482557175E-03 ; + + !***IR6 Optics*** + + !Beam1 +KQ4.L6B1 := -0.488141473400E-02 ; +KQ4.R6B1 := 0.573212897533E-02 ; +KQ5.L6B1 := 0.646745674089E-02 ; +KQ5.R6B1 := -0.667304786888E-02 ; +KQ8.L6B1 := -0.504073116977E-02 ; +KQ8.R6B1 := 0.762757138243E-02 ; +KQ9.L6B1 := 0.669045344296E-02 ; +KQ9.R6B1 := -0.655757996110E-02 ; +KQ10.L6B1 := -0.770486742073E-02 ; +KQ10.R6B1 := 0.711719158505E-02 ; +KQTL11.L6B1 := 0.874471746678E-03 ; +KQTL11.R6B1 := -0.461116869017E-03 ; +KQT12.L6B1 := -0.319415427295E-02 ; +KQT12.R6B1 := -0.186388189423E-02 ; +KQT13.L6B1 := 0.344900777508E-03 ; +KQT13.R6B1 := 0.102054118252E-02 ; + + !Beam2 +KQ4.L6B2 := 0.575352048600E-02 ; +KQ4.R6B2 := -0.483383773000E-02 ; +KQ5.L6B2 := -0.672026946100E-02 ; +KQ5.R6B2 := 0.643791542900E-02 ; +KQ8.L6B2 := 0.770011925000E-02 ; +KQ8.R6B2 := -0.511750117200E-02 ; +KQ9.L6B2 := -0.711953547200E-02 ; +KQ9.R6B2 := 0.666473261800E-02 ; +KQ10.L6B2 := 0.717256284700E-02 ; +KQ10.R6B2 := -0.740260848400E-02 ; +KQTL11.L6B2 := -0.170815548900E-02 ; +KQTL11.R6B2 := 0.422211660500E-03 ; +KQT12.L6B2 := -0.115079353200E-03 ; +KQT12.R6B2 := 0.415173587700E-02 ; +KQT13.L6B2 := 0.329034593900E-02 ; +KQT13.R6B2 := -0.118956328800E-02 ; + + !***IR3 Optics*** +KQ4.LR3 := 0.124128400000E-02 ; +KQT4.L3 := 0.688713000000E-03 ; +KQT4.R3 := 0.688713000000E-03 ; +KQ5.LR3 := -0.130392400000E-02 ; +KQT5.L3 := 0.972084000000E-03 ; +KQT5.R3 := 0.972084000000E-03 ; + + !Beam1 +KQ6.L3B1 := 0.265206600700E-02 ; +KQ6.R3B1 := -0.248284002200E-02 ; +KQTL7.L3B1 := -0.199490058500E-02 ; +KQTL7.R3B1 := 0.910271209100E-03 ; +KQTL8.L3B1 := 0.129306774100E-03 ; +KQTL8.R3B1 := 0.250745464100E-02 ; +KQTL9.L3B1 := -0.471507917000E-02 ; +KQTL9.R3B1 := -0.746685541000E-04 ; +KQTL10.L3B1 := 0.915285061000E-03 ; +KQTL10.R3B1 := 0.285281176400E-02 ; +KQTL11.L3B1 := -0.338523656500E-03 ; +KQTL11.R3B1 := -0.363123332200E-02 ; +KQT12.L3B1 := 0.284276661600E-02 ; +KQT12.R3B1 := 0.500000000000E-02 ; +KQT13.L3B1 := -0.635371688300E-03 ; +KQT13.R3B1 := -0.456628965700E-02 ; + + !Beam2 +KQ6.L3B2 := -0.247436871800E-02 ; +KQ6.R3B2 := 0.267567475900E-02 ; +KQTL7.L3B2 := 0.766905226600E-03 ; +KQTL7.R3B2 := -0.281553667400E-02 ; +KQTL8.L3B2 := 0.219362332600E-02 ; +KQTL8.R3B2 := -0.148739570200E-03 ; +KQTL9.L3B2 := 0.847198389400E-05 ; +KQTL9.R3B2 := -0.396947896700E-02 ; +KQTL10.L3B2 := 0.214887891600E-02 ; +KQTL10.R3B2 := 0.851839112500E-03 ; +KQTL11.L3B2 := -0.341390900600E-02 ; +KQTL11.R3B2 := 0.110161035700E-02 ; +KQT12.L3B2 := 0.918454053400E-03 ; +KQT12.R3B2 := 0.280357642100E-02 ; +KQT13.L3B2 := -0.508018044000E-02 ; +KQT13.R3B2 := -0.632055499900E-03 ; + + !***IR7 Optics*** +KQ4.LR7 := 0.131382724100E-02 ; +KQT4.L7 := 0.331689344000E-03 ; +KQT4.R7 := 0.331689344000E-03 ; +KQ5.LR7 := -0.133553657300E-02 ; +KQT5.L7 := 0.000000000000E+00 ; +KQT5.R7 := 0.000000000000E+00 ; + + !Beam1 +KQ6.L7B1 := 0.332380383100E-02 ; +KQ6.R7B1 := -0.281821059300E-02 ; +KQTL7.L7B1 := 0.307231360100E-03 ; +KQTL7.R7B1 := 0.411775382800E-02 ; +KQTL8.L7B1 := 0.535631538200E-03 ; +KQTL8.R7B1 := 0.180061251400E-02 ; +KQTL9.L7B1 := 0.104649831600E-03 ; +KQTL9.R7B1 := 0.316515736800E-02 ; +KQTL10.L7B1 := 0.469149843300E-02 ; +KQTL10.R7B1 := 0.234006504200E-03 ; +KQTL11.L7B1 := 0.109300381500E-02 ; +KQTL11.R7B1 := -0.129517571700E-03 ; +KQT12.L7B1 := 0.203869506000E-02 ; +KQT12.R7B1 := 0.414855502900E-03 ; +KQT13.L7B1 := -0.647047560500E-03 ; +KQT13.R7B1 := 0.163470209700E-03 ; + + !Beam2 +KQ6.L7B2 := -0.278052285800E-02 ; +KQ6.R7B2 := 0.330261896100E-02 ; +KQTL7.L7B2 := 0.391109869200E-02 ; +KQTL7.R7B2 := 0.307913213400E-03 ; +KQTL8.L7B2 := 0.141328062600E-02 ; +KQTL8.R7B2 := 0.139274871000E-02 ; +KQTL9.L7B2 := 0.363516060400E-02 ; +KQTL9.R7B2 := 0.692028108000E-04 ; +KQTL10.L7B2 := 0.156243369200E-03 ; +KQTL10.R7B2 := 0.451207010600E-02 ; +KQTL11.L7B2 := 0.360602594900E-03 ; +KQTL11.R7B2 := 0.131920025500E-02 ; +KQT12.L7B2 := -0.705199531300E-03 ; +KQT12.R7B2 := -0.138620184600E-02 ; +KQT13.L7B2 := -0.606647736700E-03 ; +KQT13.R7B2 := -0.585571959400E-03 ; + + !***Arc Optics*** + !QF/QD +KQF.A81 := 0.870329953900E-02 ; +KQF.A12 := 0.870329953900E-02 ; +KQF.A45 := 0.870329953900E-02 ; +KQF.A56 := 0.870329953900E-02 ; +KQD.A81 := -0.870475552100E-02 ; +KQD.A12 := -0.870475552100E-02 ; +KQD.A45 := -0.870475552100E-02 ; +KQD.A56 := -0.870475552100E-02 ; +KQF.A78 := 0.877219370500E-02 ; +KQF.A23 := 0.874003742200E-02 ; +KQF.A34 := 0.874003742200E-02 ; +KQF.A67 := 0.877219370500E-02 ; +KQD.A78 := -0.885967069500E-02 ; +KQD.A23 := -0.855353408600E-02 ; +KQD.A34 := -0.855353408600E-02 ; +KQD.A67 := -0.885967069500E-02 ; + + !QTF/QTD BEAM1 +dQx.b1 := 0.000000000000E+00 ; +dQy.b1 := 0.000000000000E+00 ; +dQx.b1_sq := 0.000000000000E+00 ; +dQy.b1_sq := 0.000000000000E+00 ; + +KQTF.A81B1 := 0.000000000000E+00 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTF.A12B1 := 0.000000000000E+00 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTF.A45B1 := 0.000000000000E+00 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTF.A56B1 := 0.000000000000E+00 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTD.A81B1 := 0.000000000000E+00 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTD.A12B1 := 0.000000000000E+00 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTD.A45B1 := 0.000000000000E+00 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTD.A56B1 := 0.000000000000E+00 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + ( 0.000000000000E+00) * dQx.b1_sq + ( 0.000000000000E+00) * dQy.b1_sq ; +KQTF.A78B1 := 0.761119694300E-03 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.724576651335E-02) * dQx.b1_sq + ( 0.132470834350E-02) * dQy.b1_sq ; +KQTF.A23B1 := -0.771991245000E-03 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.724576651335E-02) * dQx.b1_sq + ( 0.132470834350E-02) * dQy.b1_sq ; +KQTF.A34B1 := -0.771991245000E-03 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.724576651335E-02) * dQx.b1_sq + ( 0.132470834350E-02) * dQy.b1_sq ; +KQTF.A67B1 := 0.761119694300E-03 + ( 0.386676513181E-02) * dQx.b1 + ( 0.714331925873E-03) * dQy.b1 + ( 0.724576651335E-02) * dQx.b1_sq + ( 0.132470834350E-02) * dQy.b1_sq ; +KQTD.A78B1 := -0.933337382400E-03 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + (-0.134605020682E-02) * dQx.b1_sq + (-0.720191761063E-02) * dQy.b1_sq ; +KQTD.A23B1 := 0.915761029700E-03 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + (-0.134605020682E-02) * dQx.b1_sq + (-0.720191761063E-02) * dQy.b1_sq ; +KQTD.A34B1 := 0.915761029700E-03 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + (-0.134605020682E-02) * dQx.b1_sq + (-0.720191761063E-02) * dQy.b1_sq ; +KQTD.A67B1 := -0.933337382400E-03 + (-0.675083603533E-03) * dQx.b1 + (-0.361401832632E-02) * dQy.b1 + (-0.134605020682E-02) * dQx.b1_sq + (-0.720191761063E-02) * dQy.b1_sq ; + + !QTF/QTD BEAM2 +dQx.b2 := 0.000000000000E+00 ; +dQy.b2 := 0.000000000000E+00 ; +dQx.b2_sq := 0.000000000000E+00 ; +dQy.b2_sq := 0.000000000000E+00 ; + +KQTF.A81B2 := 0.000000000000E+00 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTF.A12B2 := 0.000000000000E+00 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTF.A45B2 := 0.000000000000E+00 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTF.A56B2 := 0.000000000000E+00 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTD.A81B2 := 0.000000000000E+00 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTD.A12B2 := 0.000000000000E+00 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTD.A45B2 := 0.000000000000E+00 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTD.A56B2 := 0.000000000000E+00 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + ( 0.000000000000E+00) * dQx.b2_sq + ( 0.000000000000E+00) * dQy.b2_sq ; +KQTF.A78B2 := -0.761119694300E-03 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.724555877649E-02) * dQx.b2_sq + ( 0.132958267416E-02) * dQy.b2_sq ; +KQTF.A23B2 := 0.771991245000E-03 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.724555877649E-02) * dQx.b2_sq + ( 0.132958267416E-02) * dQy.b2_sq ; +KQTF.A34B2 := 0.771991245000E-03 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.724555877649E-02) * dQx.b2_sq + ( 0.132958267416E-02) * dQy.b2_sq ; +KQTF.A67B2 := -0.761119694300E-03 + ( 0.362511545988E-02) * dQx.b2 + ( 0.670934528663E-03) * dQy.b2 + ( 0.724555877649E-02) * dQx.b2_sq + ( 0.132958267416E-02) * dQy.b2_sq ; +KQTD.A78B2 := 0.933337382400E-03 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + (-0.134980561765E-02) * dQx.b2_sq + (-0.723074576787E-02) * dQy.b2_sq ; +KQTD.A23B2 := -0.915761029700E-03 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + (-0.134980561765E-02) * dQx.b2_sq + (-0.723074576787E-02) * dQy.b2_sq ; +KQTD.A34B2 := -0.915761029700E-03 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + (-0.134980561765E-02) * dQx.b2_sq + (-0.723074576787E-02) * dQy.b2_sq ; +KQTD.A67B2 := 0.933337382400E-03 + (-0.676003525867E-03) * dQx.b2 + (-0.362125819951E-02) * dQy.b2 + (-0.134980561765E-02) * dQx.b2_sq + (-0.723074576787E-02) * dQy.b2_sq ; + + !Sextupole BEAM1 +dQpx.b1 := 0.000000000000E+00 ; +dQpy.b1 := 0.000000000000E+00 ; +dQpx.b1_sq := 0.000000000000E+00 ; +dQpy.b1_sq := 0.000000000000E+00 ; + + !Strong sextupoles of sectors 81/12/45/56 +KSF1.A81B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSF1.A12B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSF1.A45B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSF1.A56B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD2.A81B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD2.A12B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD2.A45B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD2.A56B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; + + !Weak sextupoles of sectors 81/12/45/56 +KSF2.A81B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSF2.A12B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSF2.A45B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSF2.A56B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD1.A81B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD1.A12B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD1.A45B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; +KSD1.A56B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + ( 0.000000000000E+00) * dQpx.b1_sq + ( 0.000000000000E+00) * dQpy.b1_sq ; + + !Weak sextupoles of sectors 78/23/34/67 +KSF1.A78B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF2.A78B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF1.A23B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF2.A23B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF1.A34B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF2.A34B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF1.A67B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSF2.A67B1 := 0.588546421584E-01 + ( 0.595401465145E-03) * dQpx.b1 + ( 0.110121796520E-03) * dQpy.b1 + ( 0.120139469137E-02) * dQpx.b1_sq + ( 0.219684419819E-03) * dQpy.b1_sq ; +KSD1.A78B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD2.A78B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD1.A23B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD2.A23B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD1.A34B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD2.A34B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD1.A67B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; +KSD2.A67B1 := -0.970134663097E-01 + (-0.185524728432E-03) * dQpx.b1 + (-0.993072214958E-03) * dQpy.b1 + (-0.376419376007E-03) * dQpx.b1_sq + (-0.200285511737E-02) * dQpy.b1_sq ; + + !Sextupole BEAM2 +dQpx.b2 := 0.000000000000E+00 ; +dQpy.b2 := 0.000000000000E+00 ; +dQpx.b2_sq := 0.000000000000E+00 ; +dQpy.b2_sq := 0.000000000000E+00 ; + + !Strong sextupoles of sectors 81/12/45/56 +KSF2.A81B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSF2.A12B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSF2.A45B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSF2.A56B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD1.A81B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD1.A12B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD1.A45B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD1.A56B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; + + !Weak sextupoles of sectors 81/12/45/56 +KSF1.A81B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSF1.A12B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSF1.A45B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSF1.A56B2 := 0.591774798333E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD2.A81B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD2.A12B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD2.A45B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; +KSD2.A56B2 := -0.976453305953E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + ( 0.000000000000E+00) * dQpx.b2_sq + ( 0.000000000000E+00) * dQpy.b2_sq ; + + !Weak sextupoles of sectors 78/23/34/67 +KSF1.A78B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF2.A78B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF1.A23B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF2.A23B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF1.A34B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF2.A34B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF1.A67B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSF2.A67B2 := 0.591774798332E-01 + ( 0.596514774636E-03) * dQpx.b2 + ( 0.110451575511E-03) * dQpy.b2 + ( 0.119467103401E-02) * dQpx.b2_sq + ( 0.219909921634E-03) * dQpy.b2_sq ; +KSD1.A78B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD2.A78B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD1.A23B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD2.A23B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD1.A34B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD2.A34B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD1.A67B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; +KSD2.A67B2 := -0.976453305964E-01 + (-0.186376785517E-03) * dQpx.b2 + (-0.993784197335E-03) * dQpy.b2 + (-0.374666386809E-03) * dQpx.b2_sq + (-0.199949018947E-02) * dQpy.b2_sq ; + + !MQS BEAM1 +CMRS.b1 := 0.000000000000E+00 ; +CMIS.b1 := 0.000000000000E+00 ; +CMRS.b1_sq := 0.000000000000E+00 ; +CMIS.b1_sq := 0.000000000000E+00 ; +ona2_b1 := 0.000000000000E+00 ; + + KQS.R1B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.280686316043E-01) * CMRS.b1 + (-0.150086236672E-02) * CMIS.b1 + ( 0.000000000000E+00) * CMRS.b1_sq + ( 0.000000000000E+00) * CMIS.b1_sq ; + KQS.L2B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.280686316043E-01) * CMRS.b1 + (-0.150086236672E-02) * CMIS.b1 + ( 0.000000000000E+00) * CMRS.b1_sq + ( 0.000000000000E+00) * CMIS.b1_sq ; +KQS.A23B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.114478730038E-01) * CMRS.b1 + ( 0.120058055868E-01) * CMIS.b1 + ( 0.261719946305E-01) * CMRS.b1_sq + ( 0.187050881463E-01) * CMIS.b1_sq ; + KQS.R3B1 := ( 0.000000000000E+00) * ona2_b1 + (-0.143126970962E-01) * CMRS.b1 + (-0.123848295077E-01) * CMIS.b1 + (-0.365294742709E-01) * CMRS.b1_sq + (-0.187466040969E-01) * CMIS.b1_sq ; + KQS.L4B1 := ( 0.000000000000E+00) * ona2_b1 + (-0.143126970962E-01) * CMRS.b1 + (-0.123848295077E-01) * CMIS.b1 + (-0.365294742709E-01) * CMRS.b1_sq + (-0.187466040969E-01) * CMIS.b1_sq ; +KQS.A45B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.839713242611E-02) * CMRS.b1 + ( 0.122601188299E-01) * CMIS.b1 + ( 0.000000000000E+00) * CMRS.b1_sq + ( 0.000000000000E+00) * CMIS.b1_sq ; + KQS.R5B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.491234725876E-02) * CMRS.b1 + ( 0.118500012957E-01) * CMIS.b1 + ( 0.000000000000E+00) * CMRS.b1_sq + ( 0.000000000000E+00) * CMIS.b1_sq ; + KQS.L6B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.491234725876E-02) * CMRS.b1 + ( 0.118500012957E-01) * CMIS.b1 + ( 0.000000000000E+00) * CMRS.b1_sq + ( 0.000000000000E+00) * CMIS.b1_sq ; +KQS.A67B1 := ( 0.000000000000E+00) * ona2_b1 + (-0.181434492476E-01) * CMRS.b1 + ( 0.598554938318E-02) * CMIS.b1 + (-0.777589377415E-01) * CMRS.b1_sq + ( 0.145560531033E-01) * CMIS.b1_sq ; + KQS.R7B1 := ( 0.000000000000E+00) * ona2_b1 + (-0.445820381483E-02) * CMRS.b1 + (-0.114994329352E-01) * CMIS.b1 + (-0.294734119906E-03) * CMRS.b1_sq + (-0.193431207443E-01) * CMIS.b1_sq ; + KQS.L8B1 := ( 0.000000000000E+00) * ona2_b1 + (-0.445820381483E-02) * CMRS.b1 + (-0.114994329352E-01) * CMIS.b1 + (-0.294734119906E-03) * CMRS.b1_sq + (-0.193431207443E-01) * CMIS.b1_sq ; +KQS.A81B1 := ( 0.000000000000E+00) * ona2_b1 + ( 0.258286623986E-01) * CMRS.b1 + (-0.278051803450E-02) * CMIS.b1 + ( 0.000000000000E+00) * CMRS.b1_sq + ( 0.000000000000E+00) * CMIS.b1_sq ; + + !MQS BEAM2 +CMRS.b2 := 0.000000000000E+00 ; +CMIS.b2 := 0.000000000000E+00 ; +CMRS.b2_sq := 0.000000000000E+00 ; +CMIS.b2_sq := 0.000000000000E+00 ; +ona2_b2 := 0.000000000000E+00 ; + +KQS.A12B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.135037705330E-01) * CMRS.b2 + (-0.200797321792E-01) * CMIS.b2 + ( 0.000000000000E+00) * CMRS.b2_sq + ( 0.000000000000E+00) * CMIS.b2_sq ; + KQS.R2B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.119181969783E-01) * CMRS.b2 + ( 0.460486139466E-02) * CMIS.b2 + ( 0.196812068149E-01) * CMRS.b2_sq + ( 0.817759311982E-02) * CMIS.b2_sq ; + KQS.L3B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.119181969783E-01) * CMRS.b2 + ( 0.460486139466E-02) * CMIS.b2 + ( 0.196812068149E-01) * CMRS.b2_sq + ( 0.817759311982E-02) * CMIS.b2_sq ; +KQS.A34B2 := ( 0.000000000000E+00) * ona2_b2 + (-0.180939204059E-01) * CMRS.b2 + ( 0.277912722934E-02) * CMIS.b2 + (-0.415626926119E-01) * CMRS.b2_sq + ( 0.191210585660E-01) * CMIS.b2_sq ; + KQS.R4B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.126568600221E-01) * CMRS.b2 + ( 0.427311110085E-02) * CMIS.b2 + ( 0.000000000000E+00) * CMRS.b2_sq + ( 0.000000000000E+00) * CMIS.b2_sq ; + KQS.L5B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.126568600221E-01) * CMRS.b2 + ( 0.427311110085E-02) * CMIS.b2 + ( 0.000000000000E+00) * CMRS.b2_sq + ( 0.000000000000E+00) * CMIS.b2_sq ; +KQS.A56B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.147205903153E-01) * CMRS.b2 + ( 0.215362435731E-02) * CMIS.b2 + ( 0.000000000000E+00) * CMRS.b2_sq + ( 0.000000000000E+00) * CMIS.b2_sq ; + KQS.R6B2 := ( 0.000000000000E+00) * ona2_b2 + (-0.707437243226E-02) * CMRS.b2 + ( 0.175564174176E-01) * CMIS.b2 + (-0.359449194435E-01) * CMRS.b2_sq + ( 0.606374570183E-01) * CMIS.b2_sq ; + KQS.L7B2 := ( 0.000000000000E+00) * ona2_b2 + (-0.707437243226E-02) * CMRS.b2 + ( 0.175564174176E-01) * CMIS.b2 + (-0.359449194435E-01) * CMRS.b2_sq + ( 0.606374570183E-01) * CMIS.b2_sq ; +KQS.A78B2 := ( 0.000000000000E+00) * ona2_b2 + (-0.115459135687E-01) * CMRS.b2 + (-0.508410394662E-02) * CMIS.b2 + (-0.183213483542E-01) * CMRS.b2_sq + (-0.993334552106E-02) * CMIS.b2_sq ; + KQS.R8B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.155044851427E-01) * CMRS.b2 + (-0.204151361066E-01) * CMIS.b2 + ( 0.000000000000E+00) * CMRS.b2_sq + ( 0.000000000000E+00) * CMIS.b2_sq ; + KQS.L1B2 := ( 0.000000000000E+00) * ona2_b2 + ( 0.155044851427E-01) * CMRS.b2 + (-0.204151361066E-01) * CMIS.b2 + ( 0.000000000000E+00) * CMRS.b2_sq + ( 0.000000000000E+00) * CMIS.b2_sq ; + + !MSS BEAM1 +ona3_b1 := 0.000000000000E+00 ; + +KSS.A12B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A23B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A34B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A45B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A56B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A67B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A78B1 := ( 0.000000000000E+00) * ona3_b1 ; +KSS.A81B1 := ( 0.000000000000E+00) * ona3_b1 ; + + !MSS BEAM2 +ona3_b2 := 0.000000000000E+00 ; + +KSS.A12B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A23B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A34B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A45B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A56B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A67B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A78B2 := ( 0.000000000000E+00) * ona3_b2 ; +KSS.A81B2 := ( 0.000000000000E+00) * ona3_b2 ; + + !OF/OD BEAM1 +ON_MO.b1 := 0.000000000000E+00 ; +KOF.B1 := 0.000000000000E+00 ; +KOD.B1 := 0.000000000000E+00 ; +KOF.A12B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A23B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A34B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A45B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A56B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A67B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A78B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOF.A81B1 := -0.600000000000E+01 *ON_MO.b1 + KOF.B1; +KOD.A12B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A23B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A34B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A45B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A56B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A67B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A78B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; +KOD.A81B1 := -0.600000000000E+01 *ON_MO.b1 + KOD.B1; + + !OF/OD BEAM2 +ON_MO.b2 := 0.000000000000E+00 ; +KOF.B2 := 0.000000000000E+00 ; +KOD.B2 := 0.000000000000E+00 ; +KOF.A12B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A23B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A34B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A45B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A56B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A67B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A78B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOF.A81B2 := -0.600000000000E+01 *ON_MO.b2 + KOF.B2; +KOD.A12B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A23B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A34B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A45B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A56B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A67B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A78B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; +KOD.A81B2 := -0.600000000000E+01 *ON_MO.b2 + KOD.B2; + + !! MCB-beam1 for spurious dispersion correction + + !! MCB in sector 81 and 12 +acbh14.r8b1x := 0.000000000000E+00 ; +acbh14.r8b1s := -0.000000000000E+00 ; +acbh14.r8b1 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh16.r8b1x := 0.000000000000E+00 ; +acbh16.r8b1s := -0.000000000000E+00 ; +acbh16.r8b1 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh14.l1b1x := 0.000000000000E+00 ; +acbh14.l1b1s := -0.000000000000E+00 ; +acbh14.l1b1 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh12.l1b1x := -0.000000000000E+00 ; +acbh12.l1b1s := 0.000000000000E+00 ; +acbh12.l1b1 := (( -0.000000000000E+00 )*on_xx1_h+( 0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh13.r1b1x := -0.000000000000E+00 ; +acbh13.r1b1s := -0.000000000000E+00 ; +acbh13.r1b1 := (( -0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh15.r1b1x := 0.000000000000E+00 ; +acbh15.r1b1s := -0.000000000000E+00 ; +acbh15.r1b1 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh15.l2b1x := 0.000000000000E+00 ; +acbh15.l2b1s := -0.000000000000E+00 ; +acbh15.l2b1 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh13.l2b1x := 0.000000000000E+00 ; +acbh13.l2b1s := -0.000000000000E+00 ; +acbh13.l2b1 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbv13.r8b1x := 0.000000000000E+00 ; +acbv13.r8b1s := -0.000000000000E+00 ; +acbv13.r8b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv15.r8b1x := 0.000000000000E+00 ; +acbv15.r8b1s := -0.000000000000E+00 ; +acbv15.r8b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv15.l1b1x := 0.000000000000E+00 ; +acbv15.l1b1s := -0.000000000000E+00 ; +acbv15.l1b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv13.l1b1x := 0.000000000000E+00 ; +acbv13.l1b1s := 0.000000000000E+00 ; +acbv13.l1b1 := (( 0.000000000000E+00 )*on_xx1_v+( 0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv12.r1b1x := 0.000000000000E+00 ; +acbv12.r1b1s := -0.000000000000E+00 ; +acbv12.r1b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv14.r1b1x := 0.000000000000E+00 ; +acbv14.r1b1s := -0.000000000000E+00 ; +acbv14.r1b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv16.l2b1x := 0.000000000000E+00 ; +acbv16.l2b1s := -0.000000000000E+00 ; +acbv16.l2b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv14.l2b1x := 0.000000000000E+00 ; +acbv14.l2b1s := -0.000000000000E+00 ; +acbv14.l2b1 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; + + !! MCB in sector 45 and 56 +acbh14.r4b1x := 0.000000000000E+00 ; +acbh14.r4b1s := 0.000000000000E+00 ; +acbh14.r4b1 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh16.r4b1x := 0.000000000000E+00 ; +acbh16.r4b1s := 0.000000000000E+00 ; +acbh16.r4b1 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh14.l5b1x := 0.000000000000E+00 ; +acbh14.l5b1s := 0.000000000000E+00 ; +acbh14.l5b1 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh12.l5b1x := -0.000000000000E+00 ; +acbh12.l5b1s := -0.000000000000E+00 ; +acbh12.l5b1 := (( -0.000000000000E+00 )*on_xx5_h+( -0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh13.r5b1x := -0.000000000000E+00 ; +acbh13.r5b1s := 0.000000000000E+00 ; +acbh13.r5b1 := (( -0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh15.r5b1x := 0.000000000000E+00 ; +acbh15.r5b1s := 0.000000000000E+00 ; +acbh15.r5b1 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh15.l6b1x := 0.000000000000E+00 ; +acbh15.l6b1s := 0.000000000000E+00 ; +acbh15.l6b1 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh13.l6b1x := 0.000000000000E+00 ; +acbh13.l6b1s := 0.000000000000E+00 ; +acbh13.l6b1 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbv13.r4b1x := 0.000000000000E+00 ; +acbv13.r4b1s := 0.000000000000E+00 ; +acbv13.r4b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv15.r4b1x := 0.000000000000E+00 ; +acbv15.r4b1s := 0.000000000000E+00 ; +acbv15.r4b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv15.l5b1x := 0.000000000000E+00 ; +acbv15.l5b1s := 0.000000000000E+00 ; +acbv15.l5b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv13.l5b1x := 0.000000000000E+00 ; +acbv13.l5b1s := -0.000000000000E+00 ; +acbv13.l5b1 := (( 0.000000000000E+00 )*on_xx5_v+( -0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv12.r5b1x := 0.000000000000E+00 ; +acbv12.r5b1s := 0.000000000000E+00 ; +acbv12.r5b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv14.r5b1x := 0.000000000000E+00 ; +acbv14.r5b1s := 0.000000000000E+00 ; +acbv14.r5b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv16.l6b1x := 0.000000000000E+00 ; +acbv16.l6b1s := 0.000000000000E+00 ; +acbv16.l6b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv14.l6b1x := 0.000000000000E+00 ; +acbv14.l6b1s := 0.000000000000E+00 ; +acbv14.l6b1 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; + + !! MCB-beam2 for spurious dispersion correction + + !! MCB in sector 81 and 12 +acbh13.r8b2x := 0.000000000000E+00 ; +acbh13.r8b2s := -0.000000000000E+00 ; +acbh13.r8b2 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh15.r8b2x := 0.000000000000E+00 ; +acbh15.r8b2s := -0.000000000000E+00 ; +acbh15.r8b2 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh15.l1b2x := 0.000000000000E+00 ; +acbh15.l1b2s := -0.000000000000E+00 ; +acbh15.l1b2 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh13.l1b2x := -0.000000000000E+00 ; +acbh13.l1b2s := 0.000000000000E+00 ; +acbh13.l1b2 := (( -0.000000000000E+00 )*on_xx1_h+( 0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh12.r1b2x := -0.000000000000E+00 ; +acbh12.r1b2s := -0.000000000000E+00 ; +acbh12.r1b2 := (( -0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh14.r1b2x := 0.000000000000E+00 ; +acbh14.r1b2s := -0.000000000000E+00 ; +acbh14.r1b2 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh16.l2b2x := 0.000000000000E+00 ; +acbh16.l2b2s := -0.000000000000E+00 ; +acbh16.l2b2 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbh14.l2b2x := 0.000000000000E+00 ; +acbh14.l2b2s := -0.000000000000E+00 ; +acbh14.l2b2 := (( 0.000000000000E+00 )*on_xx1_h+( -0.000000000000E+00 )*on_ssep1_h)*ON_DISP ; +acbv14.r8b2x := 0.000000000000E+00 ; +acbv14.r8b2s := -0.000000000000E+00 ; +acbv14.r8b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv16.r8b2x := 0.000000000000E+00 ; +acbv16.r8b2s := -0.000000000000E+00 ; +acbv16.r8b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv14.l1b2x := 0.000000000000E+00 ; +acbv14.l1b2s := -0.000000000000E+00 ; +acbv14.l1b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv12.l1b2x := 0.000000000000E+00 ; +acbv12.l1b2s := 0.000000000000E+00 ; +acbv12.l1b2 := (( 0.000000000000E+00 )*on_xx1_v+( 0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv13.r1b2x := 0.000000000000E+00 ; +acbv13.r1b2s := -0.000000000000E+00 ; +acbv13.r1b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv15.r1b2x := 0.000000000000E+00 ; +acbv15.r1b2s := -0.000000000000E+00 ; +acbv15.r1b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv15.l2b2x := 0.000000000000E+00 ; +acbv15.l2b2s := -0.000000000000E+00 ; +acbv15.l2b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; +acbv13.l2b2x := 0.000000000000E+00 ; +acbv13.l2b2s := -0.000000000000E+00 ; +acbv13.l2b2 := (( 0.000000000000E+00 )*on_xx1_v+( -0.000000000000E+00 )*on_ssep1_v)*ON_DISP ; + + !! MCB in sector 45 and 56 +acbh13.r4b2x := 0.000000000000E+00 ; +acbh13.r4b2s := 0.000000000000E+00 ; +acbh13.r4b2 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh15.r4b2x := 0.000000000000E+00 ; +acbh15.r4b2s := 0.000000000000E+00 ; +acbh15.r4b2 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh15.l5b2x := 0.000000000000E+00 ; +acbh15.l5b2s := 0.000000000000E+00 ; +acbh15.l5b2 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh13.l5b2x := -0.000000000000E+00 ; +acbh13.l5b2s := -0.000000000000E+00 ; +acbh13.l5b2 := (( -0.000000000000E+00 )*on_xx5_h+( -0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh12.r5b2x := -0.000000000000E+00 ; +acbh12.r5b2s := 0.000000000000E+00 ; +acbh12.r5b2 := (( -0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh14.r5b2x := 0.000000000000E+00 ; +acbh14.r5b2s := 0.000000000000E+00 ; +acbh14.r5b2 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh16.l6b2x := 0.000000000000E+00 ; +acbh16.l6b2s := 0.000000000000E+00 ; +acbh16.l6b2 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbh14.l6b2x := 0.000000000000E+00 ; +acbh14.l6b2s := 0.000000000000E+00 ; +acbh14.l6b2 := (( 0.000000000000E+00 )*on_xx5_h+( 0.000000000000E+00 )*on_ssep5_h)*ON_DISP ; +acbv14.r4b2x := 0.000000000000E+00 ; +acbv14.r4b2s := 0.000000000000E+00 ; +acbv14.r4b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv16.r4b2x := 0.000000000000E+00 ; +acbv16.r4b2s := 0.000000000000E+00 ; +acbv16.r4b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv14.l5b2x := 0.000000000000E+00 ; +acbv14.l5b2s := 0.000000000000E+00 ; +acbv14.l5b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv12.l5b2x := 0.000000000000E+00 ; +acbv12.l5b2s := -0.000000000000E+00 ; +acbv12.l5b2 := (( 0.000000000000E+00 )*on_xx5_v+( -0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv13.r5b2x := 0.000000000000E+00 ; +acbv13.r5b2s := 0.000000000000E+00 ; +acbv13.r5b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv15.r5b2x := 0.000000000000E+00 ; +acbv15.r5b2s := 0.000000000000E+00 ; +acbv15.r5b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv15.l6b2x := 0.000000000000E+00 ; +acbv15.l6b2s := 0.000000000000E+00 ; +acbv15.l6b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; +acbv13.l6b2x := 0.000000000000E+00 ; +acbv13.l6b2s := 0.000000000000E+00 ; +acbv13.l6b2 := (( 0.000000000000E+00 )*on_xx5_v+( 0.000000000000E+00 )*on_ssep5_v)*ON_DISP ; + + /* + !****OPTICS SUMMARY**** + + !Tune and Chroma +Qxb1 = 62.310000; Qyb1 = 60.320000; Qpxb1= -0.000000; Qpyb1= -0.000000; dmux15b1= 30.9765534; dmuy15b1= 29.6485062; +Qxb2 = 62.310000; Qyb2 = 60.320000; Qpxb2= 0.000000; Qpyb2= 0.000000; dmux15b2= 31.0619743; dmuy15b2= 29.7618930; + + !IR Optics summary (phase, twiss param., dispersion) +muxIP1b1= 2.6448000; muyIP1b1= 2.6450000; muxIP1b1_L= 1.2935038; muyIP1b1_L= 1.3627990; muxIP1b1_R= 1.3512962; muyIP1b1_R= 1.2822010; betxIP1b1= 11.000000; betyIP1b1= 11.000000; alfxIP1b1= 0.000000; alfyIP1b1= -0.000000; dxIP1b1= 0.000000; dpxIP1b1= -0.000000; +muxIP1b2= 2.6448000; muyIP1b2= 2.6450000; muxIP1b2_L= 1.3795986; muyIP1b2_L= 1.2742484; muxIP1b2_R= 1.2652014; muyIP1b2_R= 1.3707516; betxIP1b2= 11.000000; betyIP1b2= 11.000000; alfxIP1b2= 0.000000; alfyIP1b2= -0.000000; dxIP1b2= -0.000000; dpxIP1b2= -0.000000; +muxIP5b1= 2.6448000; muyIP5b1= 2.6450000; muxIP5b1_L= 1.2935038; muyIP5b1_L= 1.3627990; muxIP5b1_R= 1.3512962; muyIP5b1_R= 1.2822010; betxIP5b1= 11.000000; betyIP5b1= 11.000000; alfxIP5b1= 0.000000; alfyIP5b1= 0.000000; dxIP5b1= 0.000000; dpxIP5b1= 0.000000; +muxIP5b2= 2.6448000; muyIP5b2= 2.6450000; muxIP5b2_L= 1.3795986; muyIP5b2_L= 1.2742484; muxIP5b2_R= 1.2652014; muyIP5b2_R= 1.3707516; betxIP5b2= 11.000000; betyIP5b2= 11.000000; alfxIP5b2= 0.000000; alfyIP5b2= -0.000000; dxIP5b2= -0.000000; dpxIP5b2= -0.000000; +muxIP2b1= 2.9500000; muyIP2b1= 2.7450000; betxIP2b1= 10.000000; betyIP2b1= 10.000000; alfxIP2b1= -0.000000; alfyIP2b1= -0.000000; dxIP2b1= -0.000000; dpxIP2b1= -0.000000; +muxIP2b2= 2.9500000; muyIP2b2= 2.7450000; betxIP2b2= 10.000000; betyIP2b2= 10.000000; alfxIP2b2= 0.000000; alfyIP2b2= -0.000000; dxIP2b2= -0.000000; dpxIP2b2= -0.000000; +muxIP8b1= 3.0200000; muyIP8b1= 2.8000000; betxIP8b1= 10.000000; betyIP8b1= 10.000000; alfxIP8b1= -0.000000; alfyIP8b1= 0.000000; dxIP8b1= 0.000000; dpxIP8b1= -0.000000; +muxIP8b2= 3.0200000; muyIP8b2= 2.8000000; betxIP8b2= 10.000000; betyIP8b2= 10.000000; alfxIP8b2= -0.000000; alfyIP8b2= 0.000000; dxIP8b2= 0.000000; dpxIP8b2= -0.000000; +muxIP4b1= 2.1600000; muyIP4b1= 1.7200000; betxIP4b1= 239.413644; betyIP4b1= 242.774144; alfxIP4b1= 0.495763; alfyIP4b1= -0.373724; dxIP4b1= 0.000000; dpxIP4b1= -0.000000; +muxIP4b2= 2.1600000; muyIP4b2= 1.7200000; betxIP4b2= 231.105188; betyIP4b2= 303.948682; alfxIP4b2= -0.500292; alfyIP4b2= 0.498879; dxIP4b2= -0.000000; dpxIP4b2= 0.000000; +muxIP6b1= 2.1600000; muyIP6b1= 1.9800000; betxIP6b1= 188.438199; betyIP6b1= 175.629947; alfxIP6b1= -0.553539; alfyIP6b1= 0.667606; dxIP6b1= -0.331638; dpxIP6b1= -0.001250; +muxIP6b2= 2.1600000; muyIP6b2= 1.9800000; betxIP6b2= 189.334229; betyIP6b2= 181.968922; alfxIP6b2= 0.568601; alfyIP6b2= -0.626959; dxIP6b2= -0.304990; dpxIP6b2= 0.000451; +muxIP3b1= 2.2230000; muyIP3b1= 1.9650000; betxIP3b1= 121.566844; betyIP3b1= 218.585060; alfxIP3b1= 2.295731; alfyIP3b1= -2.642890; dxIP3b1= -0.537375; dpxIP3b1= -0.006869; +muxIP3b2= 2.2230000; muyIP3b2= 1.9650000; betxIP3b2= 121.567284; betyIP3b2= 218.584477; alfxIP3b2= -2.295728; alfyIP3b2= 2.642905; dxIP3b2= -0.445260; dpxIP3b2= 0.008749; +muxIP7b1= 2.4640000; muyIP7b1= 2.0000000; betxIP7b1= 120.813252; betyIP7b1= 149.430470; alfxIP7b1= 1.276977; alfyIP7b1= -1.385146; dxIP7b1= -0.169987; dpxIP7b1= -0.000000; +muxIP7b2= 2.4640000; muyIP7b2= 2.0000000; betxIP7b2= 120.813252; betyIP7b2= 149.430471; alfxIP7b2= -1.276977; alfyIP7b2= 1.385146; dxIP7b2= -0.008803; dpxIP7b2= -0.000000; + + !Xscheme summary in IR1, IR2,IR5 and IR8 +xIP1b1=-0.0020000004; yIP1b1=-0.0000000001; pxIP1b1=-0.0000000000; pyIP1b1= 0.0001700000; +xIP1b2= 0.0020000004; yIP1b2=-0.0000000001; pxIP1b2= 0.0000000000; pyIP1b2=-0.0001700000; +xIP2b1= 0.0034999999; yIP2b1= 0.0000000000; pxIP2b1=-0.0000354589; pyIP2b1= 0.0012588794; +xIP2b2=-0.0035000000; yIP2b2=-0.0000000001; pxIP2b2=-0.0000445411; pyIP2b2=-0.0012588794; +xIP5b1= 0.0000000004; yIP5b1= 0.0020000000; pxIP5b1= 0.0001700000; pyIP5b1= 0.0000000000; +xIP5b2=-0.0000000003; yIP5b2=-0.0019999999; pxIP5b2=-0.0001700000; pyIP5b2=-0.0000000000; +xIP8b1=-0.0000000003; yIP8b1=-0.0035000000; pxIP8b1= 0.0019298113; pyIP8b1=-0.0000118486; +xIP8b2=-0.0000000001; yIP8b2= 0.0035000000; pxIP8b2=-0.0019298113; pyIP8b2=-0.0000681514; + + !Arc Optics summary +muxcell81b1=0.24998197; muycell81b1=0.24998197; mux81b1= 5.2178933; muy81b1= 5.2437868; +muxcell45b1=0.24998197; muycell45b1=0.24998197; mux45b1= 5.2178933; muy45b1= 5.2437868; +muxcell12b2=0.24998197; muycell12b2=0.24998197; mux12b2= 5.2178933; muy12b2= 5.2437868; +muxcell56b2=0.24998197; muycell56b2=0.24998197; mux56b2= 5.2178933; muy56b2= 5.2437868; +muxcell12b1=0.25000000; muycell12b1=0.25000000; mux12b1= 5.2441653; muy12b1= 5.2182697; +muxcell56b1=0.25000000; muycell56b1=0.25000000; mux56b1= 5.2441653; muy56b1= 5.2182697; +muxcell81b2=0.25000000; muycell81b2=0.25000000; mux81b2= 5.2441653; muy81b2= 5.2182697; +muxcell45b2=0.25000000; muycell45b2=0.25000000; mux45b2= 5.2441653; muy45b2= 5.2182697; +muxcell23b1=0.25278573; muycell23b1=0.24296472; mux23b1= 5.2552629; muy23b1= 5.0687037; +muxcell78b2=0.25184855; muycell78b2=0.25634235; mux78b2= 5.2352463; muy78b2= 5.3488243; +muxcell34b1=0.25278573; muycell34b1=0.24296472; mux34b1= 5.2814318; muy34b1= 5.0427460; +muxcell67b2=0.25184855; muycell67b2=0.25634235; mux67b2= 5.2619207; muy67b2= 5.3222263; +muxcell67b1=0.25186692; muycell67b1=0.25636098; mux67b1= 5.2775277; muy67b1= 5.4073245; +muxcell34b2=0.25280379; muycell34b2=0.24298226; mux34b2= 5.2973354; muy34b2= 5.1243893; +muxcell78b1=0.25186692; muycell78b1=0.25636098; mux78b1= 5.3050602; muy78b1= 5.3771129; +muxcell23b2=0.25280379; muycell23b2=0.24298226; mux23b2= 5.3247802; muy23b2= 5.1004473; + */ + + + return ; diff --git a/examples/demo_aperture.py b/examples/demo_aperture.py index c84baa55..1cdd2b49 100755 --- a/examples/demo_aperture.py +++ b/examples/demo_aperture.py @@ -15,7 +15,7 @@ from cpymad.madx import Madx from pyhdtoolkit.cpymadtools import lhc -from pyhdtoolkit.plotting.aperture import plot_aperture +from pyhdtoolkit.plotting.aperture import plot_aperture, plot_physical_apertures from pyhdtoolkit.plotting.styles import _SPHINX_GALLERY_PARAMS from pyhdtoolkit.utils import logging @@ -70,6 +70,27 @@ plt.gca().legend() plt.show() + +############################################################################### +# We can also go for a different type of aperture plot, which tries to give +# the elements' real physical apertures, with the `~.plotting.aperture.plot_physical_apertures` +# function: + +plt.figure(figsize=(18, 10)) +plot_physical_apertures(madx, plane="x") +plt.setp(plt.gca(), xlabel="S [m]", ylabel="X [m]") +plt.ylim(-0.035, 0.035) + +############################################################################### +# We can give a ``scale`` argument to change the scale of the Y-axis. Let's make +# it in centimeters here: + +plt.figure(figsize=(18, 10)) +plot_physical_apertures(madx, plane="x", scale=1e2) # just give the scaling factor +plt.setp(plt.gca(), xlabel="S [m]", ylabel="Y [cm]") +plt.ylim(-4, 4) +plt.xlim(9000, 11_000) + ############################################################################### # Let's not forget to close the rpc connection to ``MAD-X``: diff --git a/examples/demo_beam_enveloppe.py b/examples/demo_beam_enveloppe.py index 15e39518..81b24d88 100755 --- a/examples/demo_beam_enveloppe.py +++ b/examples/demo_beam_enveloppe.py @@ -17,37 +17,22 @@ from cpymad.madx import Madx -from pyhdtoolkit.models.beam import BeamParameters -from pyhdtoolkit.plotting.envelope import plot_envelope, plot_stay_clear +from pyhdtoolkit.plotting.envelope import plot_beam_envelope from pyhdtoolkit.plotting.styles import _SPHINX_GALLERY_PARAMS from pyhdtoolkit.utils import logging logging.config_logger(level="error") plt.rcParams.update(_SPHINX_GALLERY_PARAMS) # for readability of this tutorial -############################################################################### -# Define beam parameters for injection and top energy (1.9 GeV -> 19 GeV): - -beam_injection = BeamParameters( - charge=1, - pc_GeV=1.9, - E_0_GeV=0.9382720813, - en_x_m=5e-6, - en_y_m=5e-6, - deltap_p=2e-3, -) -beam_flattop = BeamParameters( - charge=1, - pc_GeV=19, - E_0_GeV=0.9382720813, - en_x_m=5e-6, - en_y_m=5e-6, - deltap_p=2e-4, -) ############################################################################### # Define relevant constants. +pc_GeV = 19 # Beam momentum [GeV] +B_rho_Tm = pc_GeV / 0.3 # Magnetic rigidity [Tm] +E_0_GeV = 0.9382720813 # proton rest mass [GeV] +E_tot_GeV = np.sqrt(pc_GeV**2 + E_0_GeV**2) + circumference = 500.0 # machine circumference [m] n_cells = 25 l_quad = 0.5 # quadrupole length [m] @@ -59,8 +44,8 @@ n_quadrupoles = 2 * n_cells n_dipoles = 4 * n_cells # four dipoles per cell dipole_angle = 2 * np.pi / n_dipoles # [rad] -dipole_field = dipole_angle * beam_flattop.B_rho_Tm / l_bend # [T] -quadrupole_gradient = 1 / f_m * beam_flattop.B_rho_Tm / l_quad # [T/m] +dipole_field = dipole_angle * B_rho_Tm / l_bend # [T] +quadrupole_gradient = 1 / f_m * B_rho_Tm / l_quad # [T/m] r_quadrupole = 0.065 # [m] v_gap_dipole = 0.065 # [m] @@ -120,7 +105,7 @@ option, warn=true; ! DEFINE BEAM PARAMETERS AND PROPERTIES -beam, particle=proton, sequence=cas19, energy={beam_injection.E_tot_GeV}, exn={beam_injection.en_x_m}, eyn={beam_injection.en_y_m}, sige={beam_injection.en_y_m}; +beam, particle=proton, sequence=cas19, energy={E_tot_GeV}, exn=5e-6, eyn=5e-6, sige=5e-6; use, sequence=cas19; select, flag=twiss, column=apertype, aper_1, aper_2; @@ -132,7 +117,7 @@ ############################################################################### # Now let's run an interpolation to be able to see the value of the optics functions -# inside the elements: +# inside the elements, this gives us higher resolution: madx.command.select(flag="interpolate", class_="drift", slice_=4, range_="#s/#e") madx.command.select(flag="interpolate", class_="quadrupole", slice_=8, range_="#s/#e") @@ -140,66 +125,36 @@ madx.command.twiss() ############################################################################### -# We can now plot the beam enveloppe and the stay-clear at injection, for the -# whole machine: - -fig, axes = plt.subplots(3, 1, figsize=(18, 20)) -plot_envelope( - madx, - beam_injection, - ylimits=(-0.12, 0.12), - title=f"Horizontal aperture at {beam_injection.pc_GeV} GeV/c", - axis=axes[0], -) -plot_envelope( - madx, - beam_injection, - ylimits=(-0.12, 0.12), - plane="vertical", - title=f"Vertical aperture at {beam_injection.pc_GeV} GeV/c", - axis=axes[1], -) -plot_stay_clear(madx, beam_injection, title=f"Stay-Clear at {beam_injection.pc_GeV} GeV/c", axis=axes[2]) +# We can now plot the beam enveloppe for the whole machine. Here notice we use +# the *scale* argument to convert the units to mm, this makes for nicer yticks +# values: +figure, axes = plt.subplots(2, 1, sharex=True, figsize=(16, 11)) + +# First let's plot 1 sigma and 2.5 sigma horizontal enveloppes +plot_beam_envelope(madx, "cas19", "x", nsigma=1, scale=1e3, ax=axes[0]) +plot_beam_envelope(madx, "cas19", "horizontal", nsigma=2.5, scale=1e3, ax=axes[0]) +plt.setp(axes[0], ylabel="X [mm]") +axes[0].legend() + +# Then plot 1 sigma and 2.5 sigma vertical enveloppes +plot_beam_envelope(madx, "cas19", "y", nsigma=1, scale=1e3, ax=axes[1]) +plot_beam_envelope(madx, "cas19", "vertical", nsigma=2.5, scale=1e3, ax=axes[1]) +plt.setp(axes[1], xlabel="S [m]", ylabel="Y [mm]") +axes[1].legend() + plt.show() ############################################################################### -# In order to have a look at the enveloppe inside a single cell, we can specify *xlimits*. -# Here we will plot the horizontal enveloppe for the first cell only. +# Notice how the shading automatically adapts based on the *nsigma* argument. +# In order to have a look at the enveloppe in a specific region, we can give the +# *xlimits* argument. Here we will plot the horizontal enveloppe for the first +# 5 cells only. -title = f"First Cell Horizontal Aperture at {beam_injection.pc_GeV} GeV/c" fig, ax = plt.subplots(figsize=(16, 9)) -plot_envelope(madx, beam_injection, ylimits=(-0.12, 0.12), xlimits=(0, l_cell), title=title) -plt.show() - -############################################################################### -# And similarly we can plot for the cell at top energy, only by adapting the -# provided beam parameters: - -fig, axes = plt.subplots(3, 1, figsize=(18, 20)) -plot_envelope( - madx, - beam_injection, - xlimits=(0, l_cell), - ylimits=(-0.12, 0.12), - title=f"Horizontal aperture at {beam_flattop.pc_GeV} GeV/c", - axis=axes[0], -) -plot_envelope( - madx, - beam_injection, - xlimits=(0, l_cell), - ylimits=(-0.12, 0.12), - plane="vertical", - title=f"Vertical aperture at {beam_flattop.pc_GeV} GeV/c", - axis=axes[1], -) -plot_stay_clear( - madx, - beam_injection, - xlimits=(0, l_cell), - title=f"Stay-Clear at {beam_flattop.pc_GeV} GeV/c", - axis=axes[2], -) +plot_beam_envelope(madx, "cas19", "x", nsigma=1, scale=1e3, xlimits=(0, 5 * l_cell)) +plot_beam_envelope(madx, "cas19", "x", nsigma=2.5, scale=1e3, xlimits=(0, 5 * l_cell)) +plt.setp(ax, xlabel="S [m]", ylabel="X [mm]", title="First 5 Cells Horizontal Enveloppe") +ax.legend() plt.show() ############################################################################### @@ -214,5 +169,4 @@ # The use of the following functions, methods, classes and modules is shown # in this example: # -# - `~.plotting.envelope`: `~.plotting.envelope.plot_envelope`, `~.plotting.envelope.plot_stay_clear` -# - `~.models.beam`: `~.models.beam.BeamParameters` +# - `~.plotting.envelope`: `~.plotting.envelope.plot_beam_envelope` diff --git a/examples/demo_lattice.py b/examples/demo_lattice.py index 0a5c5523..5047c3da 100755 --- a/examples/demo_lattice.py +++ b/examples/demo_lattice.py @@ -36,8 +36,7 @@ matching.match_tunes_and_chromaticities( madx, - None, - "CAS3", + sequence="CAS3", q1_target=6.335, q2_target=6.29, dq1_target=100, diff --git a/examples/demo_lhc_rigid_waist_shift.py b/examples/demo_lhc_rigid_waist_shift.py index 2992036a..0114ae1e 100755 --- a/examples/demo_lhc_rigid_waist_shift.py +++ b/examples/demo_lhc_rigid_waist_shift.py @@ -192,9 +192,9 @@ for knobfile in b1_knobs: madx.call(knobfile) - matching.match_tunes(madx, "lhc", f"lhcb1", 62.31, 60.32) - matching.match_chromaticities(madx, "lhc", f"lhcb1", 2.0, 2.0) - matching.match_tunes_and_chromaticities(madx, "lhc", f"lhcb1", 62.31, 60.32, 2.0, 2.0) + matching.match_tunes(madx, "lhc", "lhcb1", 62.31, 60.32) + matching.match_chromaticities(madx, "lhc", "lhcb1", 2.0, 2.0) + matching.match_tunes_and_chromaticities(madx, "lhc", "lhcb1", 62.31, 60.32, 2.0, 2.0) madx.command.twiss() twiss_df = madx.table.twiss.dframe() @@ -297,7 +297,7 @@ # The analytical result (sign will swap depending on if we calculate from left # or right Q1) is then easily calculated. We can then compare this value to the # one found with the markers we previously added, and they are fairly close. -waist = L_star - np.sqrt(beta0 * betaw - betaw ** 2) +waist = L_star - np.sqrt(beta0 * betaw - betaw**2) print(f"Analytical: {waist}") print(f"Markers: {shift}") diff --git a/examples/demo_lhc_setup.py b/examples/demo_lhc_setup.py new file mode 100644 index 00000000..1eab3590 --- /dev/null +++ b/examples/demo_lhc_setup.py @@ -0,0 +1,199 @@ +""" + +.. _demo-lhc-setup: + +=============== +Quick LHC Setup +=============== + +These examples show how to use the functions in `pyhdtoolkit.cpymadtools.lhc._setup` +managers to easily and quickly set up LHC simulations. These are exported and +available at the level of the `pyhdtoolkit.cpymadtools.lhc` module. + +.. note:: + This is obviously very specific to the LHC machine. +""" +# sphinx_gallery_thumbnail_number = 3 +import matplotlib.pyplot as plt +import numpy as np + +from pyhdtoolkit.cpymadtools import coupling, lhc, twiss +from pyhdtoolkit.plotting.aperture import plot_physical_apertures +from pyhdtoolkit.plotting.envelope import plot_beam_envelope +from pyhdtoolkit.plotting.styles import _SPHINX_GALLERY_PARAMS +from pyhdtoolkit.plotting.utils import draw_ip_locations, get_lhc_ips_positions +from pyhdtoolkit.utils import logging + +logging.config_logger(level="error") # keep the output clean +plt.rcParams.update(_SPHINX_GALLERY_PARAMS) # for readability of this tutorial + +############################################################################### +# Preparing the LHC setups +# ------------------------ +# Two functions in `pyhdtoolkit.cpymadtools.lhc._setup` provide functionality to +# set up the LHC simulations quickly and effortlessly: +# `~.cpymadtools.lhc._setup.setup_lhc.prepare_lhc_run2` and +# `~.cpymadtools.lhc._setup.setup_lhc.prepare_lhc_run3`.` +# They return a `cpyamad.Madx` instance with the LHC sequence and optics loaded, +# both beams defined. +# +# .. important:: +# As this is a Run 3 setup, it is assumed that the ``acc-models-lhc`` repo +# is available in the root space, which is needed by the different files in +# ``acc-models-lhc``. + +# We need to give an opticsfile at the very least, other arguments are optional +madx = lhc.prepare_lhc_run3( + opticsfile="acc-models-lhc/operation/optics/R2022a_A30cmC30cmA10mL200cm.madx", + stdout=False, +) +df = twiss.get_twiss_tfs(madx) +ips = get_lhc_ips_positions(df) + +with plt.rc_context(_SPHINX_GALLERY_PARAMS): + fig, ax = plt.subplots(figsize=(18, 10)) + ax.plot(df.S, df.BETX / 1e3, label=r"$\beta_x$") + ax.plot(df.S, df.BETY / 1e3, label=r"$\beta_y$") + draw_ip_locations(ips) + ax.set_xlabel("S [m]") + ax.set_ylabel(r"$\beta_{x,y}$ [km]") + ax.legend() + plt.show() + +############################################################################### +# Let's not forget to close the rpc connection to ``MAD-X``: + +madx.exit() + +############################################################################### +# As a context manager +# -------------------- +# In order to not have to close the ``MAD-X`` instance everytime, we can use the +# context manager options from `cpymad`. To apply this to an LHC setup, there is +# `pyhdtoolkit.cpymadtools.lhc._setup.LHCSetup` to be used as a context manager. +# It calls the functions seen above internally and works on the same logic: +# +# These quick setups, with context manager option, allow to do quick "one-shot" +# simulations with the LHC. For example, one can very quickly compare apertures +# around say IP5 for two optics files as below: + +# One can just give the name of the optics file and it will be found automatically +# assuming the ``acc-models-lhc`` repo structure. Defaults assume Run 3. +with lhc.LHCSetup(run=3, opticsfile="R2022a_A11mC11mA10mL10m.madx", stdout=False) as madx: + df = twiss.get_twiss_tfs(madx) + ips = get_lhc_ips_positions(df) + limits = (ips["IP5"] - 500, ips["IP5"] + 500) + + with plt.rc_context(_SPHINX_GALLERY_PARAMS): + fig, axes = plt.subplots(2, 1, sharex=True, figsize=(18, 10)) + + plot_beam_envelope(madx, "lhcb1", "x", 1, scale=1e3, xlimits=limits, ax=axes[0]) + plot_beam_envelope(madx, "lhcb1", "x", 3, scale=1e3, xlimits=limits, ax=axes[0]) + plot_beam_envelope(madx, "lhcb1", "x", 5, scale=1e3, xlimits=limits, ax=axes[0]) + axes[0].set_ylabel("X [mm]") + + plot_beam_envelope(madx, "lhcb1", "y", 1, scale=1e3, xlimits=limits, ax=axes[1]) + plot_beam_envelope(madx, "lhcb1", "y", 3, scale=1e3, xlimits=limits, ax=axes[1]) + plot_beam_envelope(madx, "lhcb1", "y", 5, scale=1e3, xlimits=limits, ax=axes[1]) + axes[1].set_ylabel("Y [mm]") + axes[1].set_xlabel("S [m]") + + for axis in axes: + axis.legend() + axis.yaxis.set_major_locator(plt.MaxNLocator(5)) + draw_ip_locations(ips, location="inside", ax=axis) + + fig.suptitle("LHC Injection Optics") + fig.align_ylabels(axes) + + plt.tight_layout() + plt.show() + +############################################################################### +# Notice we don't need to call ``madx.exit()`` as the context manager takes care +# of that. Now, the same with betastar = 30cm optics. We can also easily add the +# element apertures on top of the plot: + +with lhc.LHCSetup(opticsfile="R2022a_A30cmC30cmA10mL200cm.madx", stdout=False) as madx: + # We'll need to call these to have aperture limitations + madx.call("lhc/aperture.b1.madx") + madx.call("lhc/aper_tol.b1.madx") + + df = twiss.get_twiss_tfs(madx) + ips = get_lhc_ips_positions(df) + limits = (ips["IP5"] - 350, ips["IP5"] + 350) + + with plt.rc_context(_SPHINX_GALLERY_PARAMS): + fig, axes = plt.subplots(2, 1, sharex=True, figsize=(18, 13)) + + plot_physical_apertures(madx, plane="x", scale=1e3, xlimits=limits, ax=axes[0]) + plot_beam_envelope(madx, "lhcb1", "x", 3, scale=1e3, xlimits=limits, ax=axes[0]) + plot_beam_envelope(madx, "lhcb1", "x", 6, scale=1e3, xlimits=limits, ax=axes[0]) + plot_beam_envelope(madx, "lhcb1", "x", 11, scale=1e3, xlimits=limits, ax=axes[0]) + axes[0].set_ylabel("X [mm]") + + plot_physical_apertures(madx, plane="y", scale=1e3, xlimits=limits, ax=axes[1]) + plot_beam_envelope(madx, "lhcb1", "y", 3, scale=1e3, xlimits=limits, ax=axes[1]) + plot_beam_envelope(madx, "lhcb1", "y", 6, scale=1e3, xlimits=limits, ax=axes[1]) + plot_beam_envelope(madx, "lhcb1", "y", 11, scale=1e3, xlimits=limits, ax=axes[1]) + axes[1].set_ylabel("Y [mm]") + axes[1].set_xlabel("S [m]") + + for axis in axes: + axis.legend() + axis.set_ylim(-45, 45) + axis.yaxis.set_major_locator(plt.MaxNLocator(5)) + draw_ip_locations(ips, ax=axis) + + fig.suptitle(r"LHC $\beta^{\ast} = 30$cm Optics") + fig.align_ylabels(axes) + + plt.tight_layout() + plt.show() + +############################################################################### +# If one wants to have a look at, say, coupling RDTs throughout the machine, for +# both beams when applying the coupling knob: +with lhc.LHCSetup(opticsfile="R2022a_A30cmC30cmA10mL200cm.madx", stdout=False) as madx: + lhc.apply_lhc_colinearity_knob(madx, colinearity_knob_value=3, ir=5) + lhc.apply_lhc_coupling_knob(madx, coupling_knob=5e-3) + rdtsb1 = coupling.get_coupling_rdts(madx) + +with lhc.LHCSetup(opticsfile="R2022a_A30cmC30cmA10mL200cm.madx", beam=2, stdout=False) as madx: + lhc.apply_lhc_colinearity_knob(madx, colinearity_knob_value=3, ir=5) + lhc.apply_lhc_coupling_knob(madx, coupling_knob=5e-3, beam=2) + rdtsb2 = coupling.get_coupling_rdts(madx) + +ipsb1 = get_lhc_ips_positions(rdtsb1) +ipsb2 = get_lhc_ips_positions(rdtsb2) + +with plt.rc_context(_SPHINX_GALLERY_PARAMS): + fig, (b1, b2) = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(15, 10)) + b1.plot(rdtsb1.S, rdtsb1.F1001.abs(), label=r"$f_{1001}$") + b1.plot(rdtsb1.S, rdtsb1.F1010.abs(), label=r"$f_{1010}$") + b1.set_title("Beam 1") + draw_ip_locations(ipsb1, location="inside", ax=b1) + + b2.plot(rdtsb2.S, rdtsb2.F1001.abs(), label=r"$f_{1001}$") + b2.plot(rdtsb2.S, rdtsb2.F1010.abs(), label=r"$f_{1010}$") + draw_ip_locations(ipsb2, location="inside", ax=b2) + b2.set_title("Beam 2") + b2.set_xlabel("S [m]") + + for axis in (b1, b2): + axis.set_ylabel("|RDT|") + axis.legend() + plt.show() + +############################################################################# +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `~.cpymadtools.coupling`: `~.coupling.get_coupling_rdts` +# - `~.cpymadtools.lhc`: `~.lhc.apply_lhc_colinearity_knob`, `~.lhc.apply_lhc_coupling_knob`, `~.lhc._setup.LHCSetup`, `~.lhc._setup.prepare_lhc_run3` +# - `~.plotting.aperture`: `~.plotting.aperture.plot_physical_apertures` +# - `~.plotting.envelope`: `~.plotting.envelope.plot_beam_envelope` +# - `~.plotting.utils`: `~.plotting.utils.draw_ip_locations`, `~.plotting.utils.get_lhc_ips_positions` diff --git a/examples/demo_phase_space.py b/examples/demo_phase_space.py index 7d5b1024..aa31ec22 100755 --- a/examples/demo_phase_space.py +++ b/examples/demo_phase_space.py @@ -50,8 +50,7 @@ madx.input(base_lattice) match_tunes_and_chromaticities( madx, - None, - "CAS3", + sequence="CAS3", q1_target=6.335, q2_target=6.29, dq1_target=100, @@ -112,8 +111,7 @@ match_tunes_and_chromaticities( madx, - None, - "CAS3", + sequence="CAS3", q1_target=6.335, q2_target=6.29, dq1_target=100, diff --git a/pyhdtoolkit/cpymadtools/constants.py b/pyhdtoolkit/cpymadtools/constants.py index 924040b9..c8bbe5ee 100644 --- a/pyhdtoolkit/cpymadtools/constants.py +++ b/pyhdtoolkit/cpymadtools/constants.py @@ -18,6 +18,9 @@ "r11", "r12", "r21", "r22"] # fmt: on +# Needs to be formatted +LHC_IR_BPM_REGEX = r"BPM\S?\S?\.[0-{max_index}][LR][1258]\.*" + # MQX + maybe F (1/3 in HLLHC) + A (1/3) or B (2) + . + maybe A or B (2) + triplet number (1/2/3) + side (R/L) + IP number (1/2/5/8) LHC_TRIPLETS_REGEX = "^MQXF?[AB].[AB]?[123][RL][1258]" @@ -155,34 +158,34 @@ "MQSX1": 0.600 / 0.050, # 0.6 T.m @ 50 mm in IR1&IR5 "MQSX2": 1.360 / 0.017, # 1.36 T @ 17 mm in IR2&IR8 # ------------- # - "MCSX1": 0.050 * 2 / (0.050 ** 2), # 0.050 Tm @ 50 mm in IR1&IR5 - "MCSX2": 0.028 * 2 / (0.017 ** 2), # 0.028 T @ 17 mm in IR2&IR8 + "MCSX1": 0.050 * 2 / (0.050**2), # 0.050 Tm @ 50 mm in IR1&IR5 + "MCSX2": 0.028 * 2 / (0.017**2), # 0.028 T @ 17 mm in IR2&IR8 # ------------- # - "MCSSX1": 0.050 * 2 / (0.050 ** 2), # 0.050 Tm @ 50 mm in IR1&IR5 - "MCSSX2": 0.11 * 2 / (0.017 ** 2), # 0.11 T @ 17 mm in IR2&IR8 + "MCSSX1": 0.050 * 2 / (0.050**2), # 0.050 Tm @ 50 mm in IR1&IR5 + "MCSSX2": 0.11 * 2 / (0.017**2), # 0.11 T @ 17 mm in IR2&IR8 # ------------- # - "MCOX1": 0.030 * 6 / (0.050 ** 3), # 0.030 Tm @ 50 mm in IR1&IR5 - "MCOX2": 0.045 * 6 / (0.017 ** 3), # 0.045 T @ 17 mm in IR2&IR8 + "MCOX1": 0.030 * 6 / (0.050**3), # 0.030 Tm @ 50 mm in IR1&IR5 + "MCOX2": 0.045 * 6 / (0.017**3), # 0.045 T @ 17 mm in IR2&IR8 # ------------- # - "MCOSX1": 0.030 * 6 / (0.050 ** 3), # 0.030 Tm @ 50 mm in IR1&IR5 - "MCOSX2": 0.048 * 6 / (0.017 ** 3), # 0.048 T @ 17 mm in IR2&IR8 + "MCOSX1": 0.030 * 6 / (0.050**3), # 0.030 Tm @ 50 mm in IR1&IR5 + "MCOSX2": 0.048 * 6 / (0.017**3), # 0.048 T @ 17 mm in IR2&IR8 # ------------- # - "MCDX1": 0.030 * 24 / (0.050 ** 4), # 0.030 Tm @ 50 mm in IR1&IR5 + "MCDX1": 0.030 * 24 / (0.050**4), # 0.030 Tm @ 50 mm in IR1&IR5 # ------------- # - "MCDSX1": 0.030 * 24 / (0.050 ** 4), # 0.030 Tm @ 50 mm in IR1&IR5 + "MCDSX1": 0.030 * 24 / (0.050**4), # 0.030 Tm @ 50 mm in IR1&IR5 # ------------- # - "MCTX1": 0.07 * 120 / (0.050 ** 5), # 0.070 Tm @ 50 mm in IR1&IR5 - "MCTX2": 0.01 * 120 / (0.017 ** 5), # 0.010 Tm @ 17 mm in IR1&IR5 + "MCTX1": 0.07 * 120 / (0.050**5), # 0.070 Tm @ 50 mm in IR1&IR5 + "MCTX2": 0.01 * 120 / (0.017**5), # 0.010 Tm @ 17 mm in IR1&IR5 # ------------- # - "MCTSX1": 0.07 * 120 / (0.050 ** 5), # 0.070 Tm @ 50 mm in IR1&IR5 + "MCTSX1": 0.07 * 120 / (0.050**5), # 0.070 Tm @ 50 mm in IR1&IR5 "MQT": 120, # 120 T/m "MQS": 120, # 120 T/m - "MS": 1.280 * 2 / (0.017 ** 2), # 1.28 T @ 17 mm - "MSS": 1.280 * 2 / (0.017 ** 2), # 1.28 T @ 17 mm - "MCS": 0.471 * 2 / (0.017 ** 2), # 0.471 T @ 17 mm - "MCO": 0.040 * 6 / (0.017 ** 3), # 0.04 T @ 17 mm - "MCD": 0.100 * 24 / (0.017 ** 4), # 0.1 T @ 17 mm - "MO": 0.29 * 6 / (0.017 ** 3), # 0.29 T @ 17 mm + "MS": 1.280 * 2 / (0.017**2), # 1.28 T @ 17 mm + "MSS": 1.280 * 2 / (0.017**2), # 1.28 T @ 17 mm + "MCS": 0.471 * 2 / (0.017**2), # 0.471 T @ 17 mm + "MCO": 0.040 * 6 / (0.017**3), # 0.04 T @ 17 mm + "MCD": 0.100 * 24 / (0.017**4), # 0.1 T @ 17 mm + "MO": 0.29 * 6 / (0.017**3), # 0.29 T @ 17 mm } FD_FAMILIES: Set[str] = {"MO", "MS", "MQT"} # Magnets that have F and D families diff --git a/pyhdtoolkit/cpymadtools/coupling.py b/pyhdtoolkit/cpymadtools/coupling.py index c1a69ce5..54abbfae 100644 --- a/pyhdtoolkit/cpymadtools/coupling.py +++ b/pyhdtoolkit/cpymadtools/coupling.py @@ -28,6 +28,7 @@ def get_closest_tune_approach( madx: Madx, + /, accelerator: str = None, sequence: str = None, varied_knobs: Sequence[str] = None, @@ -52,7 +53,7 @@ def get_closest_tune_approach( explicitely given, the appropriate targets will be determined from the ``MAD-X`` internal tables. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. accelerator (Optional[str]): name of the accelerator, used to determmine knobs if *variables* is not given. Automatic determination will only work for `LHC` and `HLLHC`. sequence (str): name of the sequence you want to activate for the tune matching. @@ -143,6 +144,7 @@ def get_closest_tune_approach( def get_cminus_from_coupling_rdts( madx: Madx, + /, patterns: Sequence[str] = [""], method: str = "teapot", qx: float = None, @@ -166,7 +168,7 @@ def get_cminus_from_coupling_rdts( value will be a real number. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. patterns (Sequence[str]): the different patterns (such as ``MQX`` or ``BPM``) of elements to use when computing the coupling RDTs. Defaults to `[""]` which will select and use all elements in the ``TWISS`` outputs. @@ -231,7 +233,7 @@ def get_cminus_from_coupling_rdts( def match_no_coupling_through_ripkens( - madx: Madx, sequence: str = None, location: str = None, vary_knobs: Sequence[str] = None + madx: Madx, /, sequence: str = None, location: str = None, vary_knobs: Sequence[str] = None ) -> None: """ .. versionadded:: 0.16.0 @@ -240,7 +242,7 @@ def match_no_coupling_through_ripkens( to be 0 at a given location. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. sequence (str): name of the sequence to activate for the matching. location (str): the name of the element at which one wants the cross-term Ripkens to be 0. vary_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` routine. @@ -267,15 +269,15 @@ def match_no_coupling_through_ripkens( madx.command.endmatch() -def get_coupling_rdts(madx: Madx, **kwargs) -> tfs.TfsDataFrame: +def get_coupling_rdts(madx: Madx, /, **kwargs) -> tfs.TfsDataFrame: """ .. versionadded:: 0.20.0 - Computed the coupling Resonance Driving Tensors (RDTs) :math:`f_{1001}` and :math:`f_{1010}` + Computed the coupling Resonance Driving Terms (RDTs) :math:`f_{1001}` and :math:`f_{1010}` at all elements in the currently active sequence from a ``TWISS`` call. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. **kwargs: any keyword argument will be transmitted to the ``TWISS`` command in ``MAD-X``. Returns: diff --git a/pyhdtoolkit/cpymadtools/lhc/_coupling.py b/pyhdtoolkit/cpymadtools/lhc/_coupling.py index 0581b0f0..b3c49f16 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_coupling.py +++ b/pyhdtoolkit/cpymadtools/lhc/_coupling.py @@ -15,8 +15,9 @@ from pyhdtoolkit.cpymadtools.constants import MONITOR_TWISS_COLUMNS +# This is a duplicate of the function in _routines.py, merge at some point def correct_lhc_global_coupling( - madx: Madx, beam: int = 1, telescopic_squeeze: bool = True, calls: int = 100, tolerance: float = 1.0e-21 + madx: Madx, /, beam: int = 1, telescopic_squeeze: bool = True, calls: int = 100, tolerance: float = 1.0e-21 ) -> None: """ .. versionadded:: 0.20.0 @@ -30,7 +31,7 @@ def correct_lhc_global_coupling( trick, but it is not a perfect solution. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. beam (int): which beam you want to perform the matching for, should be `1` or `2`. Defaults to `1`. telescopic_squeeze (bool): If set to `True`, uses the coupling knobs @@ -58,7 +59,7 @@ def correct_lhc_global_coupling( madx.command.endmatch() -def get_lhc_bpms_twiss_and_rdts(madx: Madx) -> tfs.TfsDataFrame: +def get_lhc_bpms_twiss_and_rdts(madx: Madx, /) -> tfs.TfsDataFrame: """ .. versionadded:: 0.19.0 @@ -66,7 +67,7 @@ def get_lhc_bpms_twiss_and_rdts(madx: Madx) -> tfs.TfsDataFrame: are also computed through a CMatrix approach via `optics_functions.coupling.coupling_via_cmatrix`. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. Returns: A `~tfs.frame.TfsDataFrame` of the ``TWISS`` table with basic default columns, as well as one diff --git a/pyhdtoolkit/cpymadtools/lhc/_elements.py b/pyhdtoolkit/cpymadtools/lhc/_elements.py index 5e99bf8f..1f20e2d0 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_elements.py +++ b/pyhdtoolkit/cpymadtools/lhc/_elements.py @@ -11,6 +11,7 @@ def install_ac_dipole_as_kicker( madx: Madx, + /, deltaqx: float, deltaqy: float, sigma_x: float, @@ -23,33 +24,56 @@ def install_ac_dipole_as_kicker( """ .. versionadded:: 0.15.0 - Installs an AC dipole as a kicker element in (HL)LHC beam 1 or 2, for tracking. This function - assumes that you have already defined lhcb1/lhcb2 sequence, made a beam for it (``BEAM`` - command or `~lhc.make_lhc_beams` function), matched to your desired working point and made - a ``TWISS`` call. + Installs an AC dipole as a kicker element in (HL)LHC beam 1 or 2, for + tracking. This function assumes that you have already defined lhcb1/lhcb2 + sequence, made a beam for it (``BEAM`` command or `~lhc.make_lhc_beams` + function), matched to your desired working point and made a ``TWISS`` call. .. important:: - In a real machine, the AC Dipole does impact the orbit as well as the betatron - functions when turned on (:cite:t:`Miyamoto:ACD:2008`, part III). In ``MAD-X`` - however, it cannot be modeled to do both at the same time. This routine introduces - an AC Dipole as a kicker element so that its effect can be seen on particle trajectory + In a real machine, the AC Dipole does impact the orbit as well as + the betatron functions when turned on (:cite:t:`Miyamoto:ACD:2008`, + part III). In ``MAD-X`` however, it cannot be modeled to do both at + the same time. This routine introduces an AC Dipole as a kicker + element so that its effect can be seen on particle trajectory in tracking. It **does not** affect ``TWISS`` functions. One can find a full example use of the function for tracking in the :ref:`AC Dipole Tracking ` example gallery. + .. warning:: + Installing the AC Dipole modifies the sequence, which will then be + ``USE``d at the end of this function. This will remove any errors + that were installed in the sequence. + + As the errors impact the optics functions which are used during the + installation of the AC Dipole, it would not be correct to implement + them only after installing the element. + + Therefore, it is recommended to install the errors and save them with + the ``ESAVE`` or ``ETABLE`` command, call this function, then + re-implement the errors with the ``SETERR`` command. + Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - deltaqx (float): the deltaQx (horizontal tune excitation) used by the AC dipole. - deltaqy (float): the deltaQy (vertical tune excitation) used by the AC dipole. - sigma_x (float): the horizontal amplitude to drive the beam to, in bunch sigma. - sigma_y (float): the vertical amplitude to drive the beam to, in bunch sigma. - beam (int): the LHC beam to install the AC Dipole into, either 1 or 2. Defaults to 1. - start_turn (int): the turn at which to start ramping up the AC dipole. Defaults to 100. - ramp_turns (int): the number of turns to use for the ramp-up and the ramp-down of the AC dipole. - This number is important in order to preserve the adiabaticity of the cycle. Defaults to 2000 - as in the LHC. - top_turns (int): the number of turns to drive the beam for. Defaults to 6600 as in the LHC. + Positional only. + deltaqx (float): the deltaQx (horizontal tune excitation) used by the + AC dipole. + deltaqy (float): the deltaQy (vertical tune excitation) used by the + AC dipole. + sigma_x (float): the horizontal amplitude to drive the beam to, in + bunch sigma. + sigma_y (float): the vertical amplitude to drive the beam to, in + bunch sigma. + beam (int): the LHC beam to install the AC Dipole into, either 1 or 2. + Defaults to 1. + start_turn (int): the turn at which to start ramping up the AC dipole. + Defaults to 100. + ramp_turns (int): the number of turns to use for the ramp-up and the + ramp-down of the AC dipole. This number is important in order to + preserve the adiabaticity of the cycle. Defaults to 2000 as in the + LHC. + top_turns (int): the number of turns to drive the beam for. Defaults + to 6600 as in the LHC. Example: .. code-block:: python @@ -118,29 +142,49 @@ def install_ac_dipole_as_kicker( madx.use(sequence=f"lhcb{beam:d}") -def install_ac_dipole_as_matrix(madx: Madx, deltaqx: float, deltaqy: float, beam: int = 1) -> None: +def install_ac_dipole_as_matrix(madx: Madx, /, deltaqx: float, deltaqy: float, beam: int = 1) -> None: """ .. versionadded:: 0.15.0 - Installs an AC dipole as a matrix element in (HL)LHC beam 1 or 2, to see its effect on TWISS functions - This function assumes that you have already defined lhcb1/lhcb2 sequence, made a beam for it (``BEAM`` - command or `~lhc.make_lhc_beams` function), matched to your desired working point and made a ``TWISS`` - call. + Installs an AC dipole as a matrix element in (HL)LHC beam 1 or 2, to + see its effect on TWISS functions This function assumes that you have + already defined lhcb1/lhcb2 sequence, made a beam for it (``BEAM`` + command or `~lhc.make_lhc_beams` function), matched to your desired + working point and made a ``TWISS`` call. - This function's use is very similar to that of `~.lhc.install_ac_dipole_as_kicker`. + This function's use is very similar to that of + `~.lhc.install_ac_dipole_as_kicker`. .. important:: - In a real machine, the AC Dipole does impact the orbit as well as the betatron - functions when turned on (:cite:t:`Miyamoto:ACD:2008`, part III). In ``MAD-X`` - however, it cannot be modeled to do both at the same time. This routine introduces - an AC Dipole as a matrix element so that its effect can be seen on ``TWISS`` functions. - It **does not** affect tracking. + In a real machine, the AC Dipole does impact the orbit as well as the + betatron functions when turned on (:cite:t:`Miyamoto:ACD:2008`, part + III). In ``MAD-X`` however, it cannot be modeled to do both at the + same time. This routine introduces an AC Dipole as a matrix element + so that its effect can be seen on ``TWISS`` functions. It **does not** + affect tracking. + + .. warning:: + Installing the AC Dipole modifies the sequence, which will then be + ``USE``d at the end of this function. This will remove any errors + that were installed in the sequence. + + As the errors impact the optics functions which are used during the + installation of the AC Dipole, it would not be correct to implement + them only after installing the element. + + Therefore, it is recommended to install the errors and save them with + the ``ESAVE`` or ``ETABLE`` command, call this function, then + re-implement the errors with the ``SETERR`` command. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - deltaqx (float): the deltaQx (horizontal tune excitation) used by the AC dipole. - deltaqy (float): the deltaQy (vertical tune excitation) used by the AC dipole. - beam (int): the LHC beam to install the AC Dipole into, either 1 or 2. Defaults to 1. + Positional only. + deltaqx (float): the deltaQx (horizontal tune excitation) used by the + AC dipole. + deltaqy (float): the deltaQy (vertical tune excitation) used by the + AC dipole. + beam (int): the LHC beam to install the AC Dipole into, either 1 or 2. + Defaults to 1. Example: .. code-block:: python @@ -183,30 +227,39 @@ def install_ac_dipole_as_matrix(madx: Madx, deltaqx: float, deltaqy: float, beam madx.use(sequence=f"lhcb{beam:d}") -def add_markers_around_lhc_ip(madx: Madx, sequence: str, ip: int, n_markers: int, interval: float) -> None: +def add_markers_around_lhc_ip(madx: Madx, /, sequence: str, ip: int, n_markers: int, interval: float) -> None: """ .. versionadded:: 1.0.0 - Adds some simple marker elements left and right of an IP point, to increase the granularity of optics - functions returned from a ``TWISS`` call. + Adds some simple marker elements left and right of an IP point, to + increase the granularity of optics functions returned from a ``TWISS`` + call. .. warning:: - You will most likely need to have sliced the sequence before calling this function, - as otherwise there is a risk on getting a negative drift depending on the affected - IP. This would lead to the remote ``MAD-X`` process to crash. + You will most likely need to have sliced the sequence before calling + this function, as otherwise there is a risk on getting a negative + drift depending on the affected IP. This would lead to the remote + ``MAD-X`` process to crash. .. warning:: - After editing the *sequence* to add markers, the ``USE`` command will be run for the changes to apply. - This means the caveats of ``USE`` apply, for instance the erasing of previously defined errors, orbits + After editing the *sequence* to add markers, the ``USE`` command will + be run for the changes to apply. This means the caveats of ``USE`` + apply, for instance the erasing of previously defined errors, orbits corrections etc. + + Therefore, it is recommended to install the errors and save them with + the ``ESAVE`` or ``ETABLE`` command, call this function, then + re-implement the errors with the ``SETERR`` command. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + Positional only. sequence (str): which sequence to use the routine on. ip (int): The interaction point around which to add markers. n_markers (int): how many markers to add on each side of the IP. - interval (float): the distance between markers, in [m]. Giving ``interval=0.05`` will - place a marker every 5cm (starting 5cm away from the IP on each side). + interval (float): the distance between markers, in [m]. Giving + ``interval=0.05`` will place a marker every 5cm (starting 5cm + away from the IP on each side). Example: .. code-block:: python diff --git a/pyhdtoolkit/cpymadtools/lhc/_errors.py b/pyhdtoolkit/cpymadtools/lhc/_errors.py index 12c05731..4615db3a 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_errors.py +++ b/pyhdtoolkit/cpymadtools/lhc/_errors.py @@ -25,7 +25,7 @@ def misalign_lhc_triplets( - madx: Madx, ip: int, sides: Sequence[str] = ("r", "l"), table: str = "triplet_errors", **kwargs + madx: Madx, /, ip: int, sides: Sequence[str] = ("r", "l"), table: str = "triplet_errors", **kwargs ) -> None: """ .. versionadded:: 0.9.0 @@ -36,7 +36,7 @@ def misalign_lhc_triplets( information. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. ip (int): the interaction point around which to apply errors. sides (Sequence[str]): sides of the IP to apply error on the triplets, either L or R or both. Case-insensitive. Defaults to both. @@ -64,6 +64,7 @@ def misalign_lhc_triplets( def misalign_lhc_ir_quadrupoles( madx: Madx, + /, ips: Sequence[int], beam: int, quadrupoles: Sequence[int], @@ -92,7 +93,7 @@ def misalign_lhc_ir_quadrupoles( command, which is guaranteed to work. See the last provided example below. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. ips (Sequence[int]): the interaction point(s) around which to apply errors. beam (int): beam number to apply the errors to. Unlike triplet quadrupoles which are single aperture, Q4 to Q10 are not and will need this information. diff --git a/pyhdtoolkit/cpymadtools/lhc/_misc.py b/pyhdtoolkit/cpymadtools/lhc/_misc.py index d6b6b943..f22f8a83 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_misc.py +++ b/pyhdtoolkit/cpymadtools/lhc/_misc.py @@ -22,7 +22,7 @@ from pyhdtoolkit.optics.ripken import _add_beam_size_to_df -def make_sixtrack_output(madx: Madx, energy: int) -> None: +def make_sixtrack_output(madx: Madx, /, energy: int) -> None: """ .. versionadded:: 0.15.0 @@ -30,7 +30,7 @@ def make_sixtrack_output(madx: Madx, energy: int) -> None: :user:`Joschua Dilly `. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. energy (float): beam energy, in [GeV]. Example: @@ -50,14 +50,14 @@ def make_sixtrack_output(madx: Madx, energy: int) -> None: madx.sixtrack(cavall=True, radius=0.017) # this value is only ok for HL(LHC) magnet radius -def reset_lhc_bump_flags(madx: Madx) -> None: +def reset_lhc_bump_flags(madx: Madx, /) -> None: """ .. versionadded:: 0.15.0 Resets all LHC IP bump flags to 0. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. Example: .. code-block:: python @@ -142,7 +142,7 @@ def get_lhc_tune_and_chroma_knobs( }[accelerator.upper()] -def get_lhc_bpms_list(madx: Madx) -> List[str]: +def get_lhc_bpms_list(madx: Madx, /) -> List[str]: """ .. versionadded:: 0.16.0 @@ -169,14 +169,16 @@ def get_lhc_bpms_list(madx: Madx) -> List[str]: return bpms_df.NAME.tolist() -def get_sizes_at_ip(madx: Madx, ip: int, geom_emit_x: float = None, geom_emit_y: float = None) -> Tuple[float, float]: +def get_sizes_at_ip( + madx: Madx, /, ip: int, geom_emit_x: float = None, geom_emit_y: float = None +) -> Tuple[float, float]: """ .. versionadded:: 1.0.0 Get the Lebedev beam sizes (horizontal and vertical) at the provided LHC *ip*. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. ip (int): the IP to get the sizes at. geom_emit_x (float): the horizontal geometrical emittance to use for the calculation. If not provided, will look for the values of the @@ -193,7 +195,7 @@ def get_sizes_at_ip(madx: Madx, ip: int, geom_emit_x: float = None, geom_emit_y: >>> ip5_x, ip5_y = get_size_at_ip(madx, ip=5) """ - logger.debug(f"Getting horizotnal and vertical sizes at IP{ip:d} through Ripken parameters") + logger.debug(f"Getting horizontal and vertical sizes at IP{ip:d} through Ripken parameters") geom_emit_x = geom_emit_x or madx.globals["geometric_emit_x"] geom_emit_y = geom_emit_y or madx.globals["geometric_emit_y"] diff --git a/pyhdtoolkit/cpymadtools/lhc/_powering.py b/pyhdtoolkit/cpymadtools/lhc/_powering.py index 4a09346d..78d6b3a9 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_powering.py +++ b/pyhdtoolkit/cpymadtools/lhc/_powering.py @@ -11,7 +11,7 @@ from loguru import logger -def apply_lhc_colinearity_knob(madx: Madx, colinearity_knob_value: float = 0, ir: int = None) -> None: +def apply_lhc_colinearity_knob(madx: Madx, /, colinearity_knob_value: float = 0, ir: int = None) -> None: """ .. versionadded:: 0.15.0 @@ -26,7 +26,7 @@ def apply_lhc_colinearity_knob(madx: Madx, colinearity_knob_value: float = 0, ir of the IP, and a powering decrease of the ``MQSX`` *left* of the IP. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. colinearity_knob_value (float): Units of the colinearity knob to apply. Defaults to 0 so users don't mess up local IR coupling by mistake. This should be a positive integer, normally between 1 and 10. @@ -49,7 +49,7 @@ def apply_lhc_colinearity_knob(madx: Madx, colinearity_knob_value: float = 0, ir logger.debug(f"Set '{left_knob}' to {madx.globals[left_knob]}") -def apply_lhc_colinearity_knob_delta(madx: Madx, colinearity_knob_delta: float = 0, ir: int = None) -> None: +def apply_lhc_colinearity_knob_delta(madx: Madx, /, colinearity_knob_delta: float = 0, ir: int = None) -> None: """ .. versionadded:: 0.21.0 @@ -60,7 +60,7 @@ def apply_lhc_colinearity_knob_delta(madx: Madx, colinearity_knob_delta: float = If you don't know what this is, you really should not be using this function. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. colinearity_knob_delta (float): Units of the colinearity knob to vary the existing powerings with. Defaults to 0. ir (int): The Interaction Region to apply the knob to, should be one of [1, 2, 5, 8]. @@ -89,7 +89,7 @@ def apply_lhc_colinearity_knob_delta(madx: Madx, colinearity_knob_delta: float = def apply_lhc_rigidity_waist_shift_knob( - madx: Madx, rigidty_waist_shift_value: float = 0, ir: int = None, side: str = "left" + madx: Madx, /, rigidty_waist_shift_value: float = 0, ir: int = None, side: str = "left" ) -> None: """ .. versionadded:: 0.15.0 @@ -109,7 +109,7 @@ def apply_lhc_rigidity_waist_shift_knob( works well. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. rigidty_waist_shift_value (float): Units of the rigidity waist shift knob (positive values only). ir (int): The Interaction Region to apply the knob to, should be one of [1, 2, 5, 8]. Classically 1 or 5. @@ -148,7 +148,7 @@ def apply_lhc_rigidity_waist_shift_knob( def apply_lhc_coupling_knob( - madx: Madx, coupling_knob: float = 0, beam: int = 1, telescopic_squeeze: bool = True + madx: Madx, /, coupling_knob: float = 0, beam: int = 1, telescopic_squeeze: bool = True ) -> None: """ .. versionadded:: 0.15.0 @@ -156,7 +156,7 @@ def apply_lhc_coupling_knob( Applies a trim of the LHC coupling knob to reach the desired :math:`|C^{-}|` value. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. coupling_knob (float): Desired value for the Cminus, typically a few units of ``1E-3``. Defaults to 0 so users don't mess up coupling by mistake. beam (int): beam to apply the knob to. Defaults to beam 1. @@ -181,7 +181,7 @@ def apply_lhc_coupling_knob( logger.debug(f"Set '{knob_name}' to {madx.globals[knob_name]}") -def carry_colinearity_knob_over(madx: Madx, ir: int, to_left: bool = True) -> None: +def carry_colinearity_knob_over(madx: Madx, /, ir: int, to_left: bool = True) -> None: """ .. versionadded:: 0.20.0 @@ -189,7 +189,7 @@ def carry_colinearity_knob_over(madx: Madx, ir: int, to_left: bool = True) -> No other side. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. ir (int): The Interaction Region around which to apply the change, should be one of [1, 2, 5, 8]. to_left (bool): If `True`, the magnet right of IP is de-powered of and its powering @@ -216,14 +216,14 @@ def carry_colinearity_knob_over(madx: Madx, ir: int, to_left: bool = True) -> No logger.debug("New powerings applied") -def power_landau_octupoles(madx: Madx, beam: int, mo_current: float, defective_arc: bool = False) -> None: +def power_landau_octupoles(madx: Madx, /, beam: int, mo_current: float, defective_arc: bool = False) -> None: """ .. versionadded:: 0.15.0 Powers the Landau octupoles in the (HL)LHC. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. beam (int): beam to use. mo_current (float): `MO` powering, in [A]. defective_arc: If set to `True`, the ``KOD`` in Arc 56 are powered for less ``Imax``. @@ -253,14 +253,14 @@ def power_landau_octupoles(madx: Madx, beam: int, mo_current: float, defective_a madx.globals["KOD.A56B1"] = strength * 4.65 / 6 # defective MO group -def deactivate_lhc_arc_sextupoles(madx: Madx, beam: int) -> None: +def deactivate_lhc_arc_sextupoles(madx: Madx, /, beam: int) -> None: """ .. versionadded:: 0.15.0 Deactivates all arc sextupoles in the (HL)LHC. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. beam (int): beam to use. Example: @@ -283,7 +283,7 @@ def deactivate_lhc_arc_sextupoles(madx: Madx, beam: int) -> None: def vary_independent_ir_quadrupoles( - madx: Madx, quad_numbers: Sequence[int], ip: int, sides: Sequence[str] = ("r", "l"), beam: int = 1 + madx: Madx, /, quad_numbers: Sequence[int], ip: int, sides: Sequence[str] = ("r", "l"), beam: int = 1 ) -> None: """ .. versionadded:: 0.15.0 @@ -297,7 +297,7 @@ def vary_independent_ir_quadrupoles( `make_lhc_beams` to create the beams, this has already been done automatically. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. quad_numbers (Sequence[int]): quadrupoles to be varied, by number (aka position from IP). ip (int): the IP around which to apply the instructions. sides (Sequence[str]): the sides of IP to act on. Should be `R` for right and `L` for left, @@ -345,7 +345,7 @@ def vary_independent_ir_quadrupoles( ) -def switch_magnetic_errors(madx: Madx, **kwargs) -> None: +def switch_magnetic_errors(madx: Madx, /, **kwargs) -> None: """ .. versionadded:: 0.7.0 @@ -353,7 +353,7 @@ def switch_magnetic_errors(madx: Madx, **kwargs) -> None: Initial implementation credits go to :user:`Joschua Dilly `. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. **kwargs: The setting works through keyword arguments, and several specific kwargs are expected. `default` sets global default to this value (defaults to `False`). `AB#` sets the default for all of that order, the order being the `#` number. `A#` or diff --git a/pyhdtoolkit/cpymadtools/lhc/_queries.py b/pyhdtoolkit/cpymadtools/lhc/_queries.py index f0fb2dd6..9f3e5930 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_queries.py +++ b/pyhdtoolkit/cpymadtools/lhc/_queries.py @@ -34,7 +34,7 @@ def get_magnets_powering( - madx: Madx, patterns: Sequence[str] = [r"^mb\.", r"^mq\.", r"^ms\."], brho: Union[str, float] = None, **kwargs + madx: Madx, /, patterns: Sequence[str] = [r"^mb\.", r"^mq\.", r"^ms\."], brho: Union[str, float] = None, **kwargs ) -> tfs.TfsDataFrame: r""" .. versionadded:: 0.17.0 @@ -62,7 +62,7 @@ def get_magnets_powering( The ``TWISS`` flag will be fully cleared after running this function. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. patterns (Sequence[str]): a list of regex patterns to define which elements should be selected and included in the returned table. Defaults to selecting the main bends, quads and sextupoles. See the note admonition above for @@ -89,7 +89,7 @@ def get_magnets_powering( return twiss.get_pattern_twiss(madx, columns=NEW_COLNAMES, patterns=patterns, **kwargs) -def query_arc_correctors_powering(madx: Madx) -> Dict[str, float]: +def query_arc_correctors_powering(madx: Madx, /) -> Dict[str, float]: """ .. versionadded:: 0.15.0 @@ -120,32 +120,32 @@ def query_arc_correctors_powering(madx: Madx) -> Dict[str, float]: result.update({knob: 100 * _knob_value(madx, knob) / k_mqs_max for knob in LHC_KQS_KNOBS}) logger.debug("Querying arc sextupole correctors (MSs) powering") - k_ms_max = 1.280 * 2 / 0.017 ** 2 / madx.globals.brho # 1.28 T @ 17 mm + k_ms_max = 1.280 * 2 / 0.017**2 / madx.globals.brho # 1.28 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_ms_max for knob in LHC_KSF_KNOBS}) logger.debug("Querying arc skew sextupole correctors (MSSs) powering") - k_mss_max = 1.280 * 2 / 0.017 ** 2 / madx.globals.brho # 1.28 T @ 17 mm + k_mss_max = 1.280 * 2 / 0.017**2 / madx.globals.brho # 1.28 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mss_max for knob in LHC_KSS_KNOBS}) logger.debug("Querying arc spool piece (skew) sextupole correctors (MCSs) powering") - k_mcs_max = 0.471 * 2 / 0.017 ** 2 / madx.globals.brho # 0.471 T @ 17 mm + k_mcs_max = 0.471 * 2 / 0.017**2 / madx.globals.brho # 0.471 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mcs_max for knob in LHC_KCS_KNOBS}) logger.debug("Querying arc spool piece (skew) octupole correctors (MCOs) powering") - k_mco_max = 0.040 * 6 / 0.017 ** 3 / madx.globals.brho # 0.04 T @ 17 mm + k_mco_max = 0.040 * 6 / 0.017**3 / madx.globals.brho # 0.04 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mco_max for knob in LHC_KCO_KNOBS}) logger.debug("Querying arc spool piece (skew) decapole correctors (MCDs) powering") - k_mcd_max = 0.100 * 24 / 0.017 ** 4 / madx.globals.brho # 0.1 T @ 17 mm + k_mcd_max = 0.100 * 24 / 0.017**4 / madx.globals.brho # 0.1 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mcd_max for knob in LHC_KCD_KNOBS}) logger.debug("Querying arc short straight sections octupole correctors (MOs) powering") - k_mo_max = 0.29 * 6 / 0.017 ** 3 / madx.globals.brho # 0.29 T @ 17 mm + k_mo_max = 0.29 * 6 / 0.017**3 / madx.globals.brho # 0.29 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mo_max for knob in LHC_KO_KNOBS}) return result -def query_triplet_correctors_powering(madx: Madx) -> Dict[str, float]: +def query_triplet_correctors_powering(madx: Madx, /) -> Dict[str, float]: """ .. versionadded:: 0.15.0 @@ -172,28 +172,28 @@ def query_triplet_correctors_powering(madx: Madx) -> Dict[str, float]: result.update({knob: 100 * _knob_value(madx, knob) / k_mqsx_max for knob in LHC_KQSX_KNOBS}) logger.debug("Querying triplet sextupole correctors (MCSXs) powering") - k_mcsx_max = 0.028 * 2 / 0.017 ** 2 / madx.globals.brho # 0.028 T @ 17 mm + k_mcsx_max = 0.028 * 2 / 0.017**2 / madx.globals.brho # 0.028 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mcsx_max for knob in LHC_KCSX_KNOBS}) logger.debug("Querying triplet skew sextupole correctors (MCSSXs) powering") - k_mcssx_max = 0.11 * 2 / 0.017 ** 2 / madx.globals.brho # 0.11 T @ 17 mm + k_mcssx_max = 0.11 * 2 / 0.017**2 / madx.globals.brho # 0.11 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mcssx_max for knob in LHC_KCSSX_KNOBS}) logger.debug("Querying triplet octupole correctors (MCOXs) powering") - k_mcox_max = 0.045 * 6 / 0.017 ** 3 / madx.globals.brho # 0.045 T @ 17 mm + k_mcox_max = 0.045 * 6 / 0.017**3 / madx.globals.brho # 0.045 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mcox_max for knob in LHC_KCOX_KNOBS}) logger.debug("Querying triplet skew octupole correctors (MCOSXs) powering") - k_mcosx_max = 0.048 * 6 / 0.017 ** 3 / madx.globals.brho # 0.048 T @ 17 mm + k_mcosx_max = 0.048 * 6 / 0.017**3 / madx.globals.brho # 0.048 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mcosx_max for knob in LHC_KCOSX_KNOBS}) logger.debug("Querying triplet decapole correctors (MCTXs) powering") - k_mctx_max = 0.01 * 120 / 0.017 ** 5 / madx.globals.brho # 0.010 T @ 17 mm + k_mctx_max = 0.01 * 120 / 0.017**5 / madx.globals.brho # 0.010 T @ 17 mm result.update({knob: 100 * _knob_value(madx, knob) / k_mctx_max for knob in LHC_KCTX_KNOBS}) return result -def get_current_orbit_setup(madx: Madx) -> Dict[str, float]: +def get_current_orbit_setup(madx: Madx, /) -> Dict[str, float]: """ .. versionadded:: 0.8.0 @@ -201,7 +201,7 @@ def get_current_orbit_setup(madx: Madx) -> Dict[str, float]: :user:`Joschua Dilly `. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. Returns: A `dict` of all orbit variables set, and their values as set in the ``MAD-X`` globals. @@ -219,7 +219,7 @@ def get_current_orbit_setup(madx: Madx) -> Dict[str, float]: # ----- Helpers ----- # -def _list_field_currents(madx: Madx, brho: Union[str, float] = None) -> None: +def _list_field_currents(madx: Madx, /, brho: Union[str, float] = None) -> None: """ Creates additional columns for the ``TWISS`` table with the magnets' total fields and currents, to help later on determine which proportion of their maximum powering @@ -233,7 +233,7 @@ def _list_field_currents(madx: Madx, brho: Union[str, float] = None) -> None: those are defined. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. brho (Union[str, float]): optional, an explicit definition for the magnetic rigidity in :math:`Tm^{-1}`. If not given, it will be assumed that a ``brho`` quantity is defined in the ``MAD-X`` globals and this one will @@ -266,13 +266,13 @@ def _list_field_currents(madx: Madx, brho: Union[str, float] = None) -> None: madx.globals["integrated_field"] = "field * length" -def _knob_value(madx: Madx, knob: str) -> float: +def _knob_value(madx: Madx, /, knob: str) -> float: """ Queryies the current value of a given *knob* name in the ``MAD-X`` process, and defaults to 0 (as ``MAD-X`` does) in case that knob has not been defined in the current process. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. knob (str): the name the knob. Returns: diff --git a/pyhdtoolkit/cpymadtools/lhc/_routines.py b/pyhdtoolkit/cpymadtools/lhc/_routines.py index 808ba46f..0613da0e 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_routines.py +++ b/pyhdtoolkit/cpymadtools/lhc/_routines.py @@ -16,7 +16,7 @@ def do_kmodulation( - madx: Madx, ir: int = 1, side: str = "right", steps: int = 100, stepsize: float = 3e-8, **kwargs + madx: Madx, /, ir: int = 1, side: str = "right", steps: int = 100, stepsize: float = 3e-8, **kwargs ) -> tfs.TfsDataFrame: r""" .. versionadded:: 0.20.0 @@ -35,7 +35,7 @@ def do_kmodulation( :cite:t:`Carlier:AccuracyFeasibilityMeasurement2017`. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. ir (int): the IR in which to perform the modulation. Defaults to 1. side (str): which side of the IP to use the Q1 to perform the modulation. Should be either ``right`` or ``left``, case-insensitive. Defaults to @@ -95,8 +95,9 @@ def do_kmodulation( return results +# This is a duplicate of the function in _coupling.py, merge at some point def correct_lhc_global_coupling( - madx: Madx, beam: int = 1, telescopic_squeeze: bool = True, calls: int = 100, tolerance: float = 1.0e-21 + madx: Madx, /, beam: int = 1, telescopic_squeeze: bool = True, calls: int = 100, tolerance: float = 1.0e-21 ) -> None: """ .. versionadded:: 0.20.0 @@ -110,7 +111,7 @@ def correct_lhc_global_coupling( trick, but it is not a perfect solution. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. beam (int): which beam you want to perform the matching for, should be `1` or `2`. Defaults to `1`. telescopic_squeeze (bool): If set to `True`, uses the coupling knobs @@ -140,6 +141,7 @@ def correct_lhc_global_coupling( def correct_lhc_orbit( madx: Madx, + /, sequence: str, orbit_tolerance: float = 1e-14, iterations: int = 3, @@ -155,7 +157,7 @@ def correct_lhc_orbit( usage information. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. sequence (str): which sequence to use the routine on. orbit_tolerance (float): the tolerance for the correction. Defaults to 1e-14. iterations (int): the number of iterations of the correction to perform. diff --git a/pyhdtoolkit/cpymadtools/lhc/_setup.py b/pyhdtoolkit/cpymadtools/lhc/_setup.py index 8aa30e91..acfc4f77 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_setup.py +++ b/pyhdtoolkit/cpymadtools/lhc/_setup.py @@ -38,8 +38,8 @@ def prepare_lhc_run2( might change that working point. Args: - opticsfile (str): name of the optics file to be used. Can be the string path to the file or only the opticsfile - name itself, which would be looked for at the **acc-models-lhc/operation/optics/** path. + opticsfile (str): the relative string path or a `Path` object to the opticsfile location. This will + be used to determine the location of the sequence file, see the admonition above. beam (int): which beam to set up for. Defaults to beam 1. use_b4 (bool): if `True`, the lhcb4 sequence file will be used. This is the beam 2 sequence but for tracking purposes. Defaults to `False`. @@ -69,6 +69,7 @@ def _run2_sequence_from_opticsfile(opticsfile: Path, use_b4: bool = False) -> Pa seqfile_path = opticsfile.parent.parent / filename if not seqfile_path.is_file(): logger.error(f"Could not find sequence file '{filename}' at expected location '{seqfile_path}'") + raise ValueError(f"No sequence file found at '{seqfile_path}'") return seqfile_path logger.debug("Creating Run 2 setup MAD-X instance") @@ -263,6 +264,7 @@ def __exit__(self, *exc_info): def make_lhc_beams( madx: Madx, + /, energy: float = 7000, emittance_x: float = 3.75e-6, emittance_y: float = 3.75e-6, @@ -275,7 +277,7 @@ def make_lhc_beams( Defines beams with default configuratons for ``LHCB1`` and ``LHCB2`` sequences. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. energy (float): beam energy, in [GeV]. Defaults to 6500. emittance_x (float): horizontal emittance in [m]. Will be used to calculate geometric emittance which is then fed to the ``BEAM`` command. @@ -320,7 +322,7 @@ def make_lhc_beams( ) -def make_lhc_thin(madx: Madx, sequence: str, slicefactor: int = 1, **kwargs) -> None: +def make_lhc_thin(madx: Madx, /, sequence: str, slicefactor: int = 1, **kwargs) -> None: """ .. versionadded:: 0.15.0 @@ -382,7 +384,7 @@ def make_lhc_thin(madx: Madx, sequence: str, slicefactor: int = 1, **kwargs) -> madx.command.makethin(sequence=sequence, style=style, makedipedge=makedipedge) -def re_cycle_sequence(madx: Madx, sequence: str = "lhcb1", start: str = "IP3") -> None: +def re_cycle_sequence(madx: Madx, /, sequence: str = "lhcb1", start: str = "IP3") -> None: """ .. versionadded:: 0.15.0 @@ -469,7 +471,7 @@ def lhc_orbit_variables() -> Tuple[List[str], Dict[str, str]]: return variables, special -def setup_lhc_orbit(madx: Madx, scheme: str = "flat", **kwargs) -> Dict[str, float]: +def setup_lhc_orbit(madx: Madx, /, scheme: str = "flat", **kwargs) -> Dict[str, float]: """ .. versionadded:: 0.8.0 @@ -478,7 +480,7 @@ def setup_lhc_orbit(madx: Madx, scheme: str = "flat", **kwargs) -> Dict[str, flo :user:`Joschua Dilly `. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. scheme (str): the default scheme to apply, as defined in the ``LHC_CROSSING_SCHEMES`` constant. Accepted values are keys of `LHC_CROSSING_SCHEMES`. Defaults to *flat* (every orbit variable to 0). diff --git a/pyhdtoolkit/cpymadtools/lhc/_twiss.py b/pyhdtoolkit/cpymadtools/lhc/_twiss.py index 51da9a38..88cf8384 100644 --- a/pyhdtoolkit/cpymadtools/lhc/_twiss.py +++ b/pyhdtoolkit/cpymadtools/lhc/_twiss.py @@ -16,7 +16,7 @@ from pyhdtoolkit.cpymadtools.constants import DEFAULT_TWISS_COLUMNS -def get_ips_twiss(madx: Madx, columns: Sequence[str] = DEFAULT_TWISS_COLUMNS, **kwargs) -> tfs.TfsDataFrame: +def get_ips_twiss(madx: Madx, /, columns: Sequence[str] = DEFAULT_TWISS_COLUMNS, **kwargs) -> tfs.TfsDataFrame: """ .. versionadded:: 0.9.0 @@ -24,7 +24,7 @@ def get_ips_twiss(madx: Madx, columns: Sequence[str] = DEFAULT_TWISS_COLUMNS, ** included as the `~tfs.frame.TfsDataFrame`'s header dictionary. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. columns (Sequence[str]): the variables to be returned, as columns in the DataFrame. **kwargs: Any keyword argument that can be given to the ``MAD-X`` ``TWISS`` command, such as ``chrom``, ``ripken``, ``centre``; or starting coordinates with ``betx``, ``bety`` etc. @@ -38,10 +38,10 @@ def get_ips_twiss(madx: Madx, columns: Sequence[str] = DEFAULT_TWISS_COLUMNS, ** >>> ips_df = get_ips_twiss(madx, chrom=True, ripken=True) """ logger.debug("Getting Twiss at IPs") - return twiss.get_pattern_twiss(madx=madx, columns=columns, patterns=["IP"], **kwargs) + return twiss.get_pattern_twiss(madx, columns=columns, patterns=["IP"], **kwargs) -def get_ir_twiss(madx: Madx, ir: int, columns: Sequence[str] = DEFAULT_TWISS_COLUMNS, **kwargs) -> tfs.TfsDataFrame: +def get_ir_twiss(madx: Madx, /, ir: int, columns: Sequence[str] = DEFAULT_TWISS_COLUMNS, **kwargs) -> tfs.TfsDataFrame: """ .. versionadded:: 0.9.0 @@ -50,7 +50,7 @@ def get_ir_twiss(madx: Madx, ir: int, columns: Sequence[str] = DEFAULT_TWISS_COL header dictionary. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. ir (int): which interaction region to get the TWISS for. columns (Sequence[str]): the variables to be returned, as columns in the DataFrame. **kwargs: Any keyword argument that can be given to the ``MAD-X`` ``TWISS`` command, such as ``chrom``, @@ -66,7 +66,7 @@ def get_ir_twiss(madx: Madx, ir: int, columns: Sequence[str] = DEFAULT_TWISS_COL """ logger.debug(f"Getting Twiss for IR{ir:d}") return twiss.get_pattern_twiss( - madx=madx, + madx, columns=columns, patterns=[ f"IP{ir:d}", diff --git a/pyhdtoolkit/cpymadtools/matching.py b/pyhdtoolkit/cpymadtools/matching.py index f1fd230b..92251e4b 100644 --- a/pyhdtoolkit/cpymadtools/matching.py +++ b/pyhdtoolkit/cpymadtools/matching.py @@ -18,6 +18,7 @@ def match_tunes_and_chromaticities( madx: Madx, + /, accelerator: str = None, sequence: Optional[str] = None, q1_target: float = None, @@ -45,41 +46,49 @@ def match_tunes_and_chromaticities( galleries. .. important:: - If target tune values only are provided, then tune matching is performed with the provided knobs. - If target chromaticity values only are provided, then chromaticity matching is performed with the - provided knobs. If targets for both types are provided, then both are matched in a single call with - the provided knobs. + If target tune values only are provided, then tune matching is performed with the + provided knobs. If target chromaticity values only are provided, then chromaticity + matching is performed with the provided knobs. If targets for both types are provided, + then both are matched in a single call with the provided knobs. .. note:: - If the user wishes to perform different matching calls for each, then it is recommended to call this - function as many times as necessary, with the appropriate targets. + If the user wishes to perform different matching calls for each, then it is recommended + to call this function as many times as necessary, with the appropriate targets. - For instance, in some cases and machines some prefer to do a tune matching followed by a chromaticity matching, - then followed by a combined matching. In this case the function should be called three times, once with tune - targets and knobs, another time with chromaticity targets and knobs, then a final time with all of the above. - For this, simple wrappers are provided: the :func:`match_tunes` and :func:`match_chromaticities` functions. + For instance, in some cases and machines some prefer to do a tune matching followed by + a chromaticity matching, then followed by a combined matching. In this case the function + should be called three times, once with tune targets and knobs, another time with + chromaticity targets and knobs, then a final time with all of the above. For this, simple + wrappers are provided: the :func:`match_tunes` and :func:`match_chromaticities` functions. .. hint:: - When acting of either the ``LHC`` or ``HLLHC`` machines, the accelerator name can be provided and the vary - knobs will be automatically set accordingly to the provided targets. Note that only the relevant knobs are - set, so if tune targets only are provided, then tune knobs only will be used, and not chromaticity knobs. - If explicit knobs are provided, these will always be used. On other machines the knobs should be provided - explicitly, always. + When acting of either the ``LHC`` or ``HLLHC`` machines, the accelerator name can be + provided and the vary knobs will be automatically set accordingly to the provided targets. + Note that only the relevant knobs are set, so if tune targets only are provided, then tune + knobs only will be used, and not chromaticity knobs. If explicit knobs are provided, these + will always be used. On other machines the knobs should be provided explicitly, always. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - accelerator (Optional[str]): name of the accelerator, used to determmine knobs if *variables* is not given. - Automatic determination will only work for ``LHC`` and ``HLLHC``. - sequence (str): name of the sequence you want to perform the matching for. - q1_target (float): horizontal tune to match to. - q2_target (float): vertical tune to match to. - dq1_target (float): horizontal chromaticity to match to. - dq2_target (float): vertical chromaticity to match to. - varied_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` ``MATCH`` routine. An input - could be ``["kqf", "ksd", "kqf", "kqd"]`` as they are common names used for quadrupole and sextupole - strengths (focusing / defocusing) in most examples. - telescopic_squeeze (bool): ``LHC`` specific. If set to `True`, uses the ``(HL)LHC`` knobs for Telescopic - Squeeze configuration. Defaults to `True` since `v0.9.0`. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. + accelerator (Optional[str]): name of the accelerator, used to determmine knobs if + *variables* is not given. Automatic determination will only work for ``LHC`` and + ``HLLHC``. Defaults to `None`, in which case the knobs must be provided explicitly + through ``varied_knobs``. + sequence (str): name of the sequence you want to perform the matching for. Defaults to + `None`, in which case the currently active sequence will be used for the matching. + q1_target (float): horizontal tune to match to. Defaults to `None`, in which case it will + not be a target and will be excluded from the matching. + q2_target (float): vertical tune to match to. Defaults to `None`, in which case it will + not be a target and will be excluded from the matching. + dq1_target (float): horizontal chromaticity to match to. Defaults to `None`, in which case + it will not be a target and will be excluded from the matching. + dq2_target (float): vertical chromaticity to match to. Defaults to `None`, in which case it + will not be a target and will be excluded from the matching. + varied_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` ``MATCH`` + routine. An example input could be ``["kqf", "ksd", "kqf", "kqd"]`` as they are common + names used for quadrupole and sextupole strengths (focusing / defocusing) in most examples. + telescopic_squeeze (bool): ``LHC`` specific. If set to `True`, uses the ``(HL)LHC`` knobs for + Telescopic Squeeze configuration. Defaults to `True` since `v0.9.0`. run3 (bool): if set to `True`, uses the ``LHC`` Run 3 `*_op` knobs. Defaults to `False`. step (float): step size to use when varying knobs. calls (int): max number of varying calls to perform. Defaults to 100. @@ -101,7 +110,22 @@ def match_tunes_and_chromaticities( ... varied_knobs=["kqf", "kqd", "ksf", "ksd"], ... ) - Matching the LHC lattice: + Note that since the ``accelerator`` and ``sequence`` arguments default to `None`, + they can be omitted. In this case the sequence currently in use will be used for + the matching, and ``varied_knobs`` must be provided. + + .. code-block:: python + + >>> matching.match_tunes_and_chromaticities( + ... madx, + ... q1_target=6.335, + ... q2_target=6.29, + ... dq1_target=100, + ... dq2_target=100, + ... varied_knobs=["kqf", "kqd", "ksf", "ksd"], + ... ) + + Matching the ``lhcb1`` sequence of the ``LHC`` lattice: .. code-block:: python @@ -167,6 +191,7 @@ def match(*args, **kwargs): def match_tunes( madx: Madx, + /, accelerator: str = None, sequence: Optional[str] = None, q1_target: float = None, @@ -181,27 +206,34 @@ def match_tunes( """ .. versionadded:: 0.17.0 - Provided with an active `~cpymad.madx.Madx` object, will run relevant commands to match tunes to - the desired target values. + Provided with an active `~cpymad.madx.Madx` object, will run relevant commands + to match tunes to the desired target values. .. note:: - This is a wrapper around the `~.match_tunes_and_chromaticities` function. Refer to its documentation - for usage details. + This is a wrapper around the `~.match_tunes_and_chromaticities` function. Refer + to its documentation for usage details. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - accelerator (Optional[str]): name of the accelerator, used to determmine knobs if *variables* is not given. - Automatic determination will only work for `LHC` and `HLLHC`. - sequence (str): name of the sequence you want to perform the matching for. - q1_target (float): horizontal tune to match to. - q2_target (float): vertical tune to match to. - varied_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` ``MATCH`` routine. - telescopic_squeeze (bool): ``LHC`` specific. If set to `True`, uses the ``(HL)LHC`` knobs for Telescopic - Squeeze configuration. Defaults to `True` since `v0.9.0`. - run3 (bool): if set to `True`, uses the `LHC` Run 3 `*_op` knobs. Defaults to `False`. - step (float): step size to use when varying knobs. Defaults to `1E-7`. - calls (int): max number of varying calls to perform. Defaults to `100`. - tolerance (float): tolerance for successfull matching. Defaults to `1E-21`. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. + accelerator (Optional[str]): name of the accelerator, used to determmine knobs if + *variables* is not given. Automatic determination will only work for ``LHC`` and + ``HLLHC``. Defaults to `None`, in which case the knobs must be provided explicitly + through ``varied_knobs``. + sequence (str): name of the sequence you want to perform the matching for. Defaults to + `None`, in which case the currently active sequence will be used for the matching. + q1_target (float): horizontal tune to match to. Defaults to `None`, in which case it will + not be a target and will be excluded from the matching. + q2_target (float): vertical tune to match to. Defaults to `None`, in which case it will + not be a target and will be excluded from the matching. + varied_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` ``MATCH`` + routine. An example input could be ``["kqf", "ksd", "kqf", "kqd"]`` as they are common + names used for quadrupole and sextupole strengths (focusing / defocusing) in most examples. + telescopic_squeeze (bool): ``LHC`` specific. If set to `True`, uses the ``(HL)LHC`` knobs for + Telescopic Squeeze configuration. Defaults to `True` since `v0.9.0`. + run3 (bool): if set to `True`, uses the ``LHC`` Run 3 `*_op` knobs. Defaults to `False`. + step (float): step size to use when varying knobs. + calls (int): max number of varying calls to perform. Defaults to 100. + tolerance (float): tolerance for successfull matching. Defaults to :math:`10^{-21}`. Examples: Matching a dummy lattice (not LHC or HLLHC): @@ -217,6 +249,19 @@ def match_tunes( ... varied_knobs=["kqf", "kqd"], # only tune knobs ... ) + Note that since the ``accelerator`` and ``sequence`` arguments default to `None`, + they can be omitted. In this case the sequence currently in use will be used for + the matching, and ``varied_knobs`` must be provided. + + .. code-block:: python + + >>> matching.match_tunes_and_chromaticities( + ... madx, + ... q1_target=6.335, + ... q2_target=6.29, + ... varied_knobs=["kqf", "kqd"], # only tune knobs + ... ) + Matching the LHC lattice: .. code-block:: python @@ -230,7 +275,7 @@ def match_tunes( ... ) """ match_tunes_and_chromaticities( - madx=madx, + madx, accelerator=accelerator, sequence=sequence, q1_target=q1_target, @@ -248,6 +293,7 @@ def match_tunes( def match_chromaticities( madx: Madx, + /, accelerator: str = None, sequence: Optional[str] = None, dq1_target: float = None, @@ -262,27 +308,34 @@ def match_chromaticities( """ .. versionadded:: 0.17.0 - Provided with an active `~cpymad.madx.Madx` object, will run relevant commands to match chromaticities - to the desired target values. + Provided with an active `~cpymad.madx.Madx` object, will run relevant commands + to match chromaticities to the desired target values. .. note:: - This is a wrapper around the `~.match_tunes_and_chromaticities` function. Refer to its documentation - for usage details. + This is a wrapper around the `~.match_tunes_and_chromaticities` function. + Refer to its documentation for usage details. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - accelerator (Optional[str]): name of the accelerator, used to determmine knobs if *variables* is not given. - Automatic determination will only work for `LHC` and `HLLHC`. - sequence (str): name of the sequence you want to perform the matching for. - q1_target (float): horizontal tune to match to. - q2_target (float): vertical tune to match to. - varied_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` ``MATCH`` routine. - telescopic_squeeze (bool): ``LHC`` specific. If set to `True`, uses the ``(HL)LHC`` knobs for Telescopic - Squeeze configuration. Defaults to `True` since `v0.9.0`. - run3 (bool): if set to `True`, uses the `LHC` Run 3 `*_op` knobs. Defaults to `False`. - step (float): step size to use when varying knobs. Defaults to `1E-7`. - calls (int): max number of varying calls to perform. Defaults to `100`. - tolerance (float): tolerance for successfull matching. Defaults to `1E-21`. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. + accelerator (Optional[str]): name of the accelerator, used to determmine knobs if + *variables* is not given. Automatic determination will only work for ``LHC`` and + ``HLLHC``. Defaults to `None`, in which case the knobs must be provided explicitly + through ``varied_knobs``. + sequence (str): name of the sequence you want to perform the matching for. Defaults to + `None`, in which case the currently active sequence will be used for the matching. + dq1_target (float): horizontal chromaticity to match to. Defaults to `None`, in which case + it will not be a target and will be excluded from the matching. + dq2_target (float): vertical chromaticity to match to. Defaults to `None`, in which case it + will not be a target and will be excluded from the matching. + varied_knobs (Sequence[str]): the variables names to ``VARY`` in the ``MAD-X`` ``MATCH`` + routine. An example input could be ``["kqf", "ksd", "kqf", "kqd"]`` as they are common + names used for quadrupole and sextupole strengths (focusing / defocusing) in most examples. + telescopic_squeeze (bool): ``LHC`` specific. If set to `True`, uses the ``(HL)LHC`` knobs for + Telescopic Squeeze configuration. Defaults to `True` since `v0.9.0`. + run3 (bool): if set to `True`, uses the ``LHC`` Run 3 `*_op` knobs. Defaults to `False`. + step (float): step size to use when varying knobs. + calls (int): max number of varying calls to perform. Defaults to 100. + tolerance (float): tolerance for successfull matching. Defaults to :math:`10^{-21}`. Examples: Matching a dummy lattice (not LHC or HLLHC): @@ -298,6 +351,19 @@ def match_chromaticities( ... varied_knobs=["ksf", "ksd"], # only chroma knobs ... ) + Note that since the ``accelerator`` and ``sequence`` arguments default to `None`, + they can be omitted. In this case the sequence currently in use will be used for + the matching, and ``varied_knobs`` must be provided. + + .. code-block:: python + + >>> matching.match_tunes_and_chromaticities( + ... madx, + ... dq1_target=100, + ... dq2_target=100, + ... varied_knobs=["ksf", "ksd"], # only chroma knobs + ... ) + Matching the LHC lattice: .. code-block:: python @@ -311,7 +377,7 @@ def match_chromaticities( ... ) """ match_tunes_and_chromaticities( - madx=madx, + madx, accelerator=accelerator, sequence=sequence, q1_target=None, diff --git a/pyhdtoolkit/cpymadtools/parameters.py b/pyhdtoolkit/cpymadtools/parameters.py index fd7fb20e..077566bb 100644 --- a/pyhdtoolkit/cpymadtools/parameters.py +++ b/pyhdtoolkit/cpymadtools/parameters.py @@ -16,7 +16,7 @@ # ----- Utilities ----- # -def query_beam_attributes(madx: Madx) -> MADXBeam: +def query_beam_attributes(madx: Madx, /) -> MADXBeam: """ .. versionadded:: 0.12.0 @@ -26,7 +26,7 @@ def query_beam_attributes(madx: Madx) -> MADXBeam: for details. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. Returns: A validated `~.models.madx.MADXBeam` object. diff --git a/pyhdtoolkit/cpymadtools/ptc.py b/pyhdtoolkit/cpymadtools/ptc.py index 460de69b..ded118ed 100644 --- a/pyhdtoolkit/cpymadtools/ptc.py +++ b/pyhdtoolkit/cpymadtools/ptc.py @@ -20,36 +20,46 @@ def get_amplitude_detuning( - madx: Madx, order: int = 2, file: Union[Path, str] = None, fringe: bool = False, **kwargs + madx: Madx, /, order: int = 2, file: Union[Path, str] = None, fringe: bool = False, **kwargs ) -> tfs.TfsDataFrame: """ .. versionadded:: 0.7.0 - Calculates amplitude detuning coefficients via ``PTC_NORMAL``, with sensible defaults set for - other relevant ``PTC`` commands used in the process. The result table is returned as a - `~tfs.frame.TfsDataFrame`, the headers of which are the contents of the internal ``SUMM`` table. - This is a heavily refactored version of an initial implementation by :user:`Joschua Dilly `. + Calculates amplitude detuning coefficients via ``PTC_NORMAL``, with sensible + defaults set for other relevant ``PTC`` commands used in the process. The + result table is returned as a `~tfs.frame.TfsDataFrame`, the headers of which + are the contents of the internal ``SUMM`` table. This is a heavily refactored + version of an initial implementation by :user:`Joschua Dilly `. .. important:: - The ``PTC_CREATE_LAYOUT`` command is issued with ``model=3`` (``SixTrack`` model), ``method=4`` - (integration order), ``nst=3`` (number of integration steps, aka body slices for elements) and - ``exact=True`` (use exact Hamiltonian, not an approximated one). - - The ``PTC_NORMAL`` command is explicitely given ``icase=6`` to enforce 6D calculations (see the - `MAD-X manual `_ for details), - ``no=5`` (map order for derivative evaluation of Twiss parameters), ``closedorbit=True`` (triggers - closed orbit calculation) and ``normal=True`` (activate calculation of the Normal Form). + The default values used for the ``PTC_CREATE_LAYOUT`` command are ``model=3`` + (``SixTrack`` model), ``method=4`` (integration order), ``nst=3`` (number of + integration steps, aka body slices for elements) and ``exact=True`` (use exact + Hamiltonian, not an approximated one). These can be provided as keyword + arguments to override them. + + The ``PTC_NORMAL`` command is explicitely given ``icase=6`` by default to + enforce 6D calculations (see the + `MAD-X manual `_ + for details), ``no=5`` (map order for derivative evaluation of Twiss parameters), + ``closedorbit=True`` (triggers closed orbit calculation) and ``normal=True`` + (activate calculation of the Normal Form). Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - order (int): maximum derivative order coefficient (only 0, 1 or 2 implemented in ``PTC``). - Defaults to 2. + Positional only. + order (int): maximum derivative order coefficient (only 0, 1 or 2 + implemented in ``PTC``). Defaults to 2. file (Union[Path, str]): path to output file. Defaults to `None`. - fringe (bool): boolean flag to include fringe field effects in the calculation. Defaults to - ``False``. - **kwargs: any keyword argument is transmitted to the ``PTC_NORMAL`` command. See above which - arguments are already set for ``PTC_NORMAL`` to avoid trying to override them. - + fringe (bool): boolean flag to include fringe field effects in the + calculation. Defaults to ``False``. + **kwargs: Some parameters for the ``PTC`` universe creation can be given as + keyword arguments. They are `model`, `method`, `nst` and `exact`. The + `icase`, `no`, `closed_orbit` and `normal` kwargs can be given for the + ``PTC_NORMAL`` command. Their default values are listed higher up in this + docstring. Any remaining keyword argument is transmitted to the + ``PTC_NORMAL`` command. + Returns: A `~tfs.frame.TfsDataframe` with the calculated coefficients. @@ -57,16 +67,37 @@ def get_amplitude_detuning( .. code-block:: python >>> ampdet_coeffs = get_amplitude_detuning(madx, order=2, closedorbit=True) + + One can also specify parameters for the ``PTC`` universe and the ``PTC_NORMAL`` + command: + + .. code-block:: python + + >>> tracks_dict = get_amplitude_detuning( + ... madx, order=3, model=3, exact=True, icase=5, no=6 + ... ) """ if order >= 3: logger.error(f"Maximum amplitude detuning order in PTC is 2, but {order:d} was requested") raise NotImplementedError("PTC amplitude detuning is not implemented for order > 2") + logger.debug("Looking for PTC universe parameters in keyword arguments") + model = kwargs.pop("model", 3) + method = kwargs.pop("method", 4) + nst = kwargs.pop("nst", 3) + exact = kwargs.pop("exact", True) + + logger.debug("Looking for PTC_NORMAL parameters in keyword arguments") + icase = kwargs.pop("icase", 6) + no = kwargs.pop("no", 5) + closed_orbit = kwargs.pop("closed_orbit", True) + normal = kwargs.pop("normal", True) + logger.debug("Creating PTC universe") madx.ptc_create_universe() logger.trace("Creating PTC layout") - madx.ptc_create_layout(model=3, method=4, nst=3, exact=True) + madx.ptc_create_layout(model=model, method=method, nst=nst, exact=exact) logger.trace("Incorporating MAD-X alignment errors") madx.ptc_align() # use madx alignment errors @@ -99,7 +130,7 @@ def get_amplitude_detuning( madx.select_ptc_normal("anhy=0, 2, 0") # d^2Qy/dey^2 logger.debug("Executing PTC Normal") - madx.ptc_normal(icase=6, no=5, closed_orbit=True, normal=True, **kwargs) + madx.ptc_normal(icase=icase, no=no, closed_orbit=closed_orbit, normal=normal, **kwargs) madx.ptc_end() dframe = get_table_tfs(madx, table_name="normal_results") @@ -113,55 +144,86 @@ def get_amplitude_detuning( def get_rdts( - madx: Madx, order: int = 4, file: Union[Path, str] = None, fringe: bool = False, **kwargs + madx: Madx, /, order: int = 4, file: Union[Path, str] = None, fringe: bool = False, **kwargs ) -> tfs.TfsDataFrame: """ .. versionadded:: 0.7.0 - Calculate the resonance driving terms up to *order* via ``PTC_TWISS``, with sensible defaults - set for other relevant ``PTC`` commands. The result table is returned as a `~tfs.frame.TfsDataFrame`, - the headers of which are the contents of the internal ``SUMM`` table. This is a heavily refactored + Calculate the resonance driving terms up to *order* via ``PTC_TWISS``, + with sensible defaults set for other relevant ``PTC`` commands. The result + table is returned as a `~tfs.frame.TfsDataFrame`, the headers of which are + the contents of the internal ``SUMM`` table. This is a heavily refactored version of an initial implementation by :user:`Joschua Dilly `. .. important:: - The ``PTC_CREATE_LAYOUT`` command is issued with ``model=3`` (``SixTrack`` model), ``method=4`` - (integration order), ``nst=3`` (number of integration steps, aka body slices for elements) and - ``exact=True`` (use exact Hamiltonian, not an approximated one). + The default values used for the ``PTC_CREATE_LAYOUT`` command are ``model=3`` + (``SixTrack`` model), ``method=4`` (integration order), ``nst=3`` (number of + integration steps, aka body slices for elements) and ``exact=True`` (use exact + Hamiltonian, not an approximated one). These can be provided as keyword + arguments to override them. + + The ``PTC_TWISS`` command is given ``icase=6`` by default to enforce 6D + calculations (see the + `MAD-X manual `_ + for details), and ``normal=True`` to trigger saving the normal form analysis + results in a table called ``NONLIN`` which will then be available through the + provided `~cpymad.madx.Madx` instance. - The ``PTC_NORMAL`` command is explicitely given ``icase=6`` to enforce 6D calculations (see the - `MAD-X manual `_ for details), - ``trackrdts=True`` (for this function to fullfill its purpose) and ``normal=True`` to trigger - saving the normal form analysis results in a table called ``NONLIN`` which will then be available - through the provided `~cpymad.madx.Madx` instance. + These default values can be changed through keyword arguments. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - order (int): map order for derivative evaluation of Twiss parameters. Defaults to 4. + Positional only. + order (int): map order for derivative evaluation of Twiss parameters. + Defaults to 4. file (Union[Path, str]): path to output file. Default to `None`. - fringe (bool): boolean flag to include fringe field effects in the calculation. Defaults to `False`. - **kwargs: any keyword argument is transmitted to the ``PTC_TWISS`` command. See above which arguments - are already set for ``PTC_TWISS`` to avoid trying to override them. + fringe (bool): boolean flag to include fringe field effects in the + calculation. Defaults to `False`. + **kwargs: Some parameters for the ``PTC`` universe creation can be given as + keyword arguments. They are `model`, `method`, `nst` and `exact`. The + `icase` and `normal` ones can be given for the ``PTC_TWISS`` command. + Their default values are listed higher up in this docstring. Any remaining + keyword argument is transmitted to the ``PTC_TWISS`` command. Returns: - A `TfsDataframe` with the calculated RDTs. + A `~tfs.frame.TfsDataFrame` with the calculated RDTs. Example: .. code-block:: python >>> rdts_df = get_rdts(madx, order=3, fringe=True) + + One can also specify parameters for the ``PTC`` universe and the ``PTC_TWISS`` + command: + + .. code-block:: python + + >>> tracks_dict = get_rdts( + ... madx, order=3, model=3, method=6, nst=3, exact=True, icase=5 + ... ) """ + logger.debug("Looking for PTC universe parameters in keyword arguments") + model = kwargs.pop("model", 3) + method = kwargs.pop("method", 4) + nst = kwargs.pop("nst", 3) + exact = kwargs.pop("exact", True) + + logger.debug("Looking for PTC_TWISS parameters in keyword arguments") + icase = kwargs.pop("icase", 6) + normal = kwargs.pop("normal", True) + logger.debug("Creating PTC universe") madx.ptc_create_universe() logger.trace("Creating PTC layout") - madx.ptc_create_layout(model=3, method=4, nst=3, exact=True) + madx.ptc_create_layout(model=model, method=method, nst=nst, exact=exact) logger.trace("Incorporating MAD-X alignment errors") madx.ptc_align() # use madx alignment errors madx.ptc_setswitch(fringe=fringe) logger.debug("Executing PTC Twiss") - madx.ptc_twiss(icase=6, no=order, normal=True, trackrdts=True, **kwargs) + madx.ptc_twiss(icase=icase, no=order, normal=normal, trackrdts=True, **kwargs) madx.ptc_end() dframe = get_table_tfs(madx, table_name="twissrdt", headers_table="ptc_twiss_summary") @@ -174,58 +236,97 @@ def get_rdts( def ptc_twiss( - madx: Madx, order: int = 4, file: Union[Path, str] = None, fringe: bool = False, table: str = "ptc_twiss", **kwargs + madx: Madx, + /, + order: int = 4, + file: Union[Path, str] = None, + fringe: bool = False, + table: str = "ptc_twiss", + **kwargs, ) -> tfs.TfsDataFrame: """ .. versionadded:: 0.12.0 - Calculates the ``TWISS`` parameters according to the :cite:t:`Ripken:optics:1989` formalism - via ``PTC_TWISS``, with sensible defaults set for other relevant ``PTC`` commands. The result - table is returned as a `~tfs.frame.TfsDataFrame`, the headers of which are the contents of the - internal ``SUMM`` table. + Calculates the ``TWISS`` parameters according to the :cite:t:`Ripken:optics:1989` + formalism via ``PTC_TWISS``, with sensible defaults set for other relevant ``PTC`` + commands. The result table is returned as a `~tfs.frame.TfsDataFrame`, the headers + of which are the contents of the internal ``SUMM`` table. - This is very similar to the `~.ptc.get_rdts` function as both use ``PTC_TWISS`` internally, - however this function does not track RDTs which makes the calculations significantly faster. + This is very similar to the `~.ptc.get_rdts` function as both use ``PTC_TWISS`` + internally, however this function does not track RDTs which makes the calculations + significantly faster. .. important:: - The ``PTC_CREATE_LAYOUT`` command is issued with ``model=3`` (``SixTrack`` model), ``method=4`` - (integration order), ``nst=3`` (number of integration steps, aka body slices for elements) and - ``exact=True`` (use exact Hamiltonian, not an approximated one). + The default values used for the ``PTC_CREATE_LAYOUT`` command are ``model=3`` + (``SixTrack`` model), ``method=4`` (integration order), ``nst=3`` (number of + integration steps, aka body slices for elements) and ``exact=True`` (use exact + Hamiltonian, not an approximated one). These can be provided as keyword + arguments to override them. + + The ``PTC_TWISS`` command is given ``icase=6`` by default to enforce 6D + calculations (see the + `MAD-X manual `_ + for details), and ``normal=True`` to trigger saving the normal form analysis + results in a table called ``NONLIN`` which will then be available through the + provided `~cpymad.madx.Madx` instance. - The ``PTC_TWISS`` command is explicitely given ``icase=6`` to enforce 6D calculations (see the - `MAD-X manual `_ for details), - ``normal=True`` to trigger saving the normal form analysis results in a table called ``NONLIN`` - which will then be available through the provided `~cpymad.madx.Madx` instance. + These default values can be changed through keyword arguments. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - order (int): map order for derivative evaluation of ``TWISS`` parameters. Defaults to 4. + Positional only. + order (int): map order for derivative evaluation of ``TWISS`` parameters. + Defaults to 4. file (Union[Path, str]): path to output file. Default to `None`. - fringe (bool): boolean flag to include fringe field effects in the calculation. Defaults to `False`. - table (str): the name of the internal table in which to save the results. Defaults to **ptc_twiss**. - **kwargs: Any keyword argument is transmitted to the ``PTC_TWISS`` command. See above which arguments - are already set for `PTC_TWISS` to avoid trying to override them. - + fringe (bool): boolean flag to include fringe field effects in the calculation. + Defaults to `False`. + table (str): the name of the internal table in which to save the results. + Defaults to **ptc_twiss**. + **kwargs: Some parameters for the ``PTC`` universe creation can be given as + keyword arguments. They are `model`, `method`, `nst` and `exact`. The + `icase` and `normal` ones can be given for the ``PTC_TWISS`` command. + Their default values are listed higher up in this docstring. Any remaining + keyword argument is transmitted to the ``PTC_TWISS`` command. + Returns: - A `TfsDataframe` with the calculated ``TWISS`` parameters. + A `~tfs.frame.TfsDataFrame` with the calculated ``TWISS`` parameters. Example: .. code-block:: python >>> twiss_ptc_df = ptc_twiss(madx, order=3) + + One can also specify parameters for the ``PTC`` universe and the ``PTC_TWISS`` + command: + + .. code-block:: python + + >>> tracks_dict = ptc_twiss( + ... madx, order=3, model=3, method=6, nst=3, exact=True, icase=5 + ... ) """ + logger.debug("Looking for PTC universe parameters in keyword arguments") + model = kwargs.pop("model", 3) + method = kwargs.pop("method", 4) + nst = kwargs.pop("nst", 3) + exact = kwargs.pop("exact", True) + + logger.debug("Looking for PTC_TWISS parameters in keyword arguments") + icase = kwargs.pop("icase", 6) + normal = kwargs.pop("normal", True) + logger.debug(f"Creating PTC universe") madx.ptc_create_universe() logger.trace("Creating PTC layout") - madx.ptc_create_layout(model=3, method=4, nst=3, exact=True) + madx.ptc_create_layout(model=model, method=method, nst=nst, exact=exact) logger.trace("Incorporating MAD-X alignment errors") madx.ptc_align() # use madx alignment errors madx.ptc_setswitch(fringe=fringe) logger.debug("Executing PTC Twiss") - madx.ptc_twiss(icase=6, no=order, normal=True, table=table, **kwargs) + madx.ptc_twiss(icase=icase, no=order, normal=normal, table=table, **kwargs) madx.ptc_end() dframe = get_table_tfs(madx, table_name=table, headers_table="ptc_twiss_summary") @@ -239,6 +340,7 @@ def ptc_twiss( def ptc_track_particle( madx: Madx, + /, initial_coordinates: Tuple[float, float, float, float, float, float], nturns: int, sequence: Optional[str] = None, @@ -250,50 +352,67 @@ def ptc_track_particle( """ .. versionadded:: 0.12.0 - Tracks a single particle for *nturns* through ``PTC_TRACK``, based on its initial coordinates. The - use of this function is similar to that of `~.track.track_single_particle`. + Tracks a single particle for *nturns* through ``PTC_TRACK``, based on its initial + coordinates. The use of this function is similar to that of + `~.track.track_single_particle`. .. important:: - The ``PTC_CREATE_LAYOUT`` command is issued with ``model=3`` (``SixTrack`` model), ``method=4`` - (integration order), ``nst=3`` (number of integration steps, aka body slices for elements) and - ``exact=True`` (use exact Hamiltonian, not an approximated one). + The default values used for the ``PTC_CREATE_LAYOUT`` command are ``model=3`` + (``SixTrack`` model), ``method=4`` (integration order), ``nst=3`` (number of + integration steps, aka body slices for elements) and ``exact=True`` (use exact + Hamiltonian, not an approximated one). These can be provided as keyword + arguments to override them. + + The ``PTC_TRACK`` command is given ``ELEMENT_BY_ELEMENT=True`` by default to + force element by element tracking mode. - The ``PTC_TRACK`` command is explicitely given ``ELEMENT_BY_ELEMENT=True`` to force element by - element tracking mode. + These default values can be changed through keyword arguments. .. warning:: - If the *sequence* argument is given a string value, the ``USE`` command will be ran on the provided - sequence name. This means the caveats of ``USE`` apply, for instance the erasing of previously - defined errors, orbits corrections etc. In this case a warning will be logged but the function will - proceed. If `None` is given (by default) then the sequence already in use will be the one tracking - is performed on. + If the *sequence* argument is given a string value, the ``USE`` command will be + ran on the provided sequence name. This means the caveats of ``USE`` apply, for + instance the erasing of previously defined errors, orbits corrections etc. In + this case a warning will be logged but the function will proceed. If `None` is + given (by default) then the sequence already in use will be the one tracking is + performed on. Args: madx (cpymad.madx.Madx): an instantiated cpymad.madx.Madx object. - initial_coordinates (Tuple[float, float, float, float, float, float]): a tuple with the ``X, PX, - Y, PY, T, PT`` starting coordinates of the particle to track. Defaults to all 0 if `None` given. + initial_coordinates (Tuple[float, float, float, float, float, float]): a tuple + with the ``X, PX, Y, PY, T, PT`` starting coordinates of the particle to + track. Defaults to all 0 if `None` given. nturns (int): the number of turns to track for. - sequence (Optional[str]): the sequence to use for tracking. If no value is provided, it is assumed - that a sequence is already defined and in use, and this one will be picked up by ``MAD-X``. - Beware of the dangers of giving a sequence that will be used by ``MAD-X``, see the warning below + sequence (Optional[str]): the sequence to use for tracking. If no value is + provided, it is assumed that a sequence is already defined and in use, + and this one will be picked up by ``MAD-X``. Beware of the dangers of + giving a sequence that will be used by ``MAD-X``, see the warning below for more information. - observation_points (Sequence[str]): sequence of all element names at which to ``OBSERVE`` during the - tracking. - onetable (bool): flag to combine all observation points data into a single table. Defaults to `False`. - fringe (bool): boolean flag to include fringe field effects in the calculation. Defaults to `False`. - **kwargs: Any keyword argument is transmitted to the ``PTC_TRACK`` command such as the ``CLOSED_ORBIT`` - flag to activate closed orbit calculation before tracking. Refer to the - `MAD-X manual `_ for options. + observation_points (Sequence[str]): sequence of all element names at which to + ``OBSERVE`` during the tracking. + onetable (bool): flag to combine all observation points data into a single + table. Defaults to `False`. + fringe (bool): boolean flag to include fringe field effects in the calculation. + Defaults to `False`. + **kwargs: Some parameters for the ``PTC`` universe creation can be given as + keyword arguments. They are `model`, `method`, `nst`, `exact` and + `element_by_element` for the ``PTC_TRACK`` command. Their default values + are listed higher up in this docstring. Any remaining keyword argument is + transmitted to the ``PTC_TRACK`` command such as the `CLOSED_ORBIT` flag + to activate closed orbit calculation before tracking. Refer to the + `MAD-X manual `_ + for options. Returns: - A `dict` with a copy of the track table's dataframe for each defined observation point, - with as columns the coordinates ``x, px, y, py, t, pt, s and e`` (energy). The keys of the dictionary - are simply named ``observation_point_1``, ``observation_point_2`` etc. The first observation point - always corresponds to the start of machine, the others correspond to the ones manually defined, - in the order they are defined in. + A `dict` with a copy of the track table's dataframe for each defined observation + point, with as columns the coordinates ``x, px, y, py, t, pt, s and e`` (energy). + The keys of the dictionary are simply named ``observation_point_1``, + ``observation_point_2`` etc. The first observation point always corresponds to the + start of machine, the others correspond to the ones manually defined, in the order + they are defined in. - If the user has set ``onetable`` to `True`, only one entry is in the dictionary under the key - ``trackone`` and it has the combined table as a pandas DataFrame for value. + If the user has set ``onetable`` to `True`, only one entry is in the dictionary + under the key ``trackone`` and it has the combined table as a `~pandas.DataFrame` + for value. Example: .. code-block:: python @@ -301,11 +420,29 @@ def ptc_track_particle( >>> tracks_dict = ptc_track_particle( ... madx, nturns=1023, initial_coordinates=(2e-4, 0, 1e-4, 0, 0, 0) ... ) + + One can also specify parameters for the ``PTC`` universe: + + .. code-block:: python + + >>> tracks_dict = ptc_track_particle( + ... madx, nturns=10, initial_coordinates=(2e-4, 0, 1e-4, 0, 0, 0), + ... model=3, method=6, nst=3, exact=True + ... ) """ logger.debug("Performing single particle PTC (thick) tracking") start = initial_coordinates if initial_coordinates else [0, 0, 0, 0, 0, 0] observation_points = observation_points if observation_points else [] + logger.debug("Looking for PTC universe parameters in keyword arguments") + model = kwargs.pop("model", 3) + method = kwargs.pop("method", 4) + nst = kwargs.pop("nst", 3) + exact = kwargs.pop("exact", True) + + logger.debug("Looking for PTC_TRACK parameters in keyword arguments") + element_by_element = kwargs.pop("element_by_element", True) + if isinstance(sequence, str): logger.warning(f"Sequence '{sequence}' was provided and will be USEd, beware that this will erase errors etc.") logger.debug(f"Using sequence '{sequence}' for tracking") @@ -315,7 +452,7 @@ def ptc_track_particle( madx.ptc_create_universe() logger.trace("Creating PTC layout") - madx.ptc_create_layout(model=3, method=4, nst=3, exact=True) + madx.ptc_create_layout(model=model, method=method, nst=nst, exact=exact) logger.trace("Incorporating MAD-X alignment errors") madx.ptc_align() # use madx alignment errors @@ -328,7 +465,7 @@ def ptc_track_particle( logger.trace(f"Setting observation point for tracking with OBSERVE at element '{element}'") madx.command.ptc_observe(place=element) - madx.command.ptc_track(turns=nturns, element_by_element=True, onetable=onetable, **kwargs) + madx.command.ptc_track(turns=nturns, element_by_element=element_by_element, onetable=onetable, **kwargs) madx.ptc_end() if onetable: # user asked for ONETABLE, there will only be one table 'trackone' given back by MAD-X diff --git a/pyhdtoolkit/cpymadtools/track.py b/pyhdtoolkit/cpymadtools/track.py index 557b02fa..846df18a 100644 --- a/pyhdtoolkit/cpymadtools/track.py +++ b/pyhdtoolkit/cpymadtools/track.py @@ -19,6 +19,7 @@ def track_single_particle( madx: Madx, + /, initial_coordinates: Tuple[float, float, float, float, float, float], nturns: int, sequence: Optional[str] = None, diff --git a/pyhdtoolkit/cpymadtools/tune.py b/pyhdtoolkit/cpymadtools/tune.py index 11c889dd..9350315a 100644 --- a/pyhdtoolkit/cpymadtools/tune.py +++ b/pyhdtoolkit/cpymadtools/tune.py @@ -23,7 +23,7 @@ def make_footprint_table( - madx: Madx, sigma: float = 5, dense: bool = False, file: str = None, cleanup: bool = True, **kwargs + madx: Madx, /, sigma: float = 5, dense: bool = False, file: str = None, cleanup: bool = True, **kwargs ) -> tfs.TfsDataFrame: """ .. versionadded:: 0.9.0 @@ -37,7 +37,7 @@ def make_footprint_table( this function. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. sigma (float): the maximum amplitude of the tracked particles, in bunch :math:`\\sigma`. Defaults to 5. dense (bool): if set to `True`, an increased number of particles will be tracked. Defaults to `False`. diff --git a/pyhdtoolkit/cpymadtools/twiss.py b/pyhdtoolkit/cpymadtools/twiss.py index 2448c3e1..b4b84c25 100644 --- a/pyhdtoolkit/cpymadtools/twiss.py +++ b/pyhdtoolkit/cpymadtools/twiss.py @@ -21,6 +21,7 @@ def get_pattern_twiss( madx: Madx, + /, columns: Sequence[str] = None, patterns: Sequence[str] = [""], **kwargs, @@ -42,7 +43,7 @@ def get_pattern_twiss( section `Regular Expressions` for details on what is implemented in ``MAD-X`` itself. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. columns (Sequence[str]): the variables to be returned, as columns in the `~tfs.frame.TfsDataFrame`. Defaults to `None`, which will return all available columns. patterns (Sequence[str]): the different element patterns (such as ``MQX`` or ``BPM``) to be applied @@ -95,7 +96,7 @@ def get_pattern_twiss( return twiss_df -def get_twiss_tfs(madx: Madx, **kwargs) -> tfs.TfsDataFrame: +def get_twiss_tfs(madx: Madx, /, **kwargs) -> tfs.TfsDataFrame: """ .. versionadded:: 0.8.3 @@ -105,7 +106,7 @@ def get_twiss_tfs(madx: Madx, **kwargs) -> tfs.TfsDataFrame: returning the dframe to you. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. **kwargs: Any keyword argument that can be given to the ``MAD-X`` ``TWISS`` command, such as ``chrom``, ``ripken``, ``centre``; or starting coordinates with ``betx``, ``bety`` etc. Keyword Args: diff --git a/pyhdtoolkit/cpymadtools/utils.py b/pyhdtoolkit/cpymadtools/utils.py index 570a0a6d..26de7626 100644 --- a/pyhdtoolkit/cpymadtools/utils.py +++ b/pyhdtoolkit/cpymadtools/utils.py @@ -17,7 +17,13 @@ def export_madx_table( - madx: Madx, table_name: str, file_name: Union[Path, str], pattern: str = None, headers_table: str = "SUMM", **kwargs + madx: Madx, + /, + table_name: str, + file_name: Union[Path, str], + pattern: str = None, + headers_table: str = "SUMM", + **kwargs, ) -> None: """ .. versionadded:: 0.17.0 @@ -32,7 +38,7 @@ def export_madx_table( they will be given default values so the **TFS** file can be read by ``MAD-X``. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. table_name (str): the name of the internal table to retrieve. file_name (str): the name of the file to export to. pattern (str): if given, will be used as a regular expression to filter the extracted @@ -63,14 +69,14 @@ def export_madx_table( tfs.write(file_path, dframe, **kwargs) -def get_table_tfs(madx: Madx, table_name: str, headers_table: str = "SUMM") -> tfs.TfsDataFrame: +def get_table_tfs(madx: Madx, /, table_name: str, headers_table: str = "SUMM") -> tfs.TfsDataFrame: """ .. versionadded:: 0.11.0 Turns an internal table from the ``MAD-X`` process into a `~tfs.frame.TfsDataFrame`. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. table_name (str): the name of the internal table to retrieve. headers_table (str): the name of the internal table to use for headers. Defaults to ``SUMM``. diff --git a/pyhdtoolkit/plotting/aperture.py b/pyhdtoolkit/plotting/aperture.py index 0ae213af..166dd324 100644 --- a/pyhdtoolkit/plotting/aperture.py +++ b/pyhdtoolkit/plotting/aperture.py @@ -4,83 +4,122 @@ Aperture Plotters ----------------- -Module with functions to create aperture plots through a `~cpymad.madx.Madx` object. +Module with functions to create aperture plots through a `~cpymad.madx.Madx` +object. """ -from typing import Optional, Tuple +from typing import Optional, Tuple, Union import matplotlib.pyplot as plt +import numpy as np import pandas as pd from cpymad.madx import Madx from loguru import logger from pyhdtoolkit.plotting.layout import plot_machine_layout +from pyhdtoolkit.plotting.utils import maybe_get_ax def plot_aperture( madx: Madx, + /, title: Optional[str] = None, xoffset: float = 0, xlimits: Tuple[float, float] = None, plot_dipoles: bool = True, + plot_dipole_k1: bool = False, plot_quadrupoles: bool = True, plot_bpms: bool = False, aperture_ylim: Tuple[float, float] = None, - k0l_lim: Tuple[float, float] = None, - k1l_lim: Tuple[float, float] = None, - k2l_lim: Tuple[float, float] = None, + k0l_lim: Union[Tuple[float, float], float, int] = None, + k1l_lim: Union[Tuple[float, float], float, int] = None, + k2l_lim: Union[Tuple[float, float], float, int] = None, + k3l_lim: Union[Tuple[float, float], float, int] = None, color: str = None, **kwargs, ) -> None: """ .. versionadded:: 1.0.0 - Creates a plot representing nicely the lattice layout and the aperture tolerance across the machine. - One can find an example use of this function in the :ref:`machine aperture ` - example gallery. + Creates a plot representing the lattice layout and the aperture tolerance + across the machine. The tolerance is based on the ``n1`` values in the + aperture table. One can find an example use of this function in the + :ref:`machine aperture ` example gallery. .. important:: - This function assumes the user has previously made a call to the ``APERTURE`` command in ``MAD-X``, - as it will query relevant values from the ``aperture`` table. + This function assumes the user has previously made a call to the + ``APERTURE`` command in ``MAD-X``, as it will query relevant values + from the ``aperture`` table. .. note:: - This function has some heavy logic behind it, especially in how it needs to order several axes. The - easiest way to go about using it is to manually create and empty figure with the desired properties - (size, etc) then call this function. See the example below or the gallery for more details. + This function has some heavy logic behind it, especially in how it + needs to order several axes. The easiest way to go about using it is + to manually create and empty figure with the desired properties (size, + etc) then call this function. See the example below or the gallery for + more details. .. warning:: - Currently the function tries to plot legends for the different layout patches. The position of the - different legends has been hardcoded in corners and might require users to tweak the axis limits - (through ``k0l_lim``, ``k1l_lim`` and ``k2l_lim``) to ensure legend labels and plotted elements don't - overlap. + Currently the function tries to plot legends for the different layout + patches. The position of the different legends has been hardcoded in + corners and might require users to tweak the axis limits (through + ``k0l_lim``, ``k1l_lim`` and ``k2l_lim``) to ensure legend labels and + plotted elements don't overlap. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + Positional only. title (Optional[str]): title of the figure. - xoffset (float): An offset applied to the ``S`` coordinate before plotting. This is useful if - you want to center a plot around a specific point or element, which would then become located - at :math:`s = 0`. Beware this offset is applied before applying the *xlimits*. Defaults to 0. - xlimits (Tuple[float, float]): will implement xlim (for the ``s`` coordinate) if this is - not ``None``, using the tuple passed. - plot_dipoles (bool): if `True`, dipole patches will be plotted on the layout subplot of - the figure. Defaults to `True`. Dipoles are plotted in blue. - plot_quadrupoles (bool): if `True`, quadrupole patches will be plotted on the layout - subplot of the figure. Defaults to `True`. Quadrupoles are plotted in red. - plot_bpms (bool): if `True`, additional patches will be plotted on the layout subplot to - represent Beam Position Monitors. BPMs are plotted in dark grey. - aperture_ylim (Tuple[float, float]): vertical axis limits for the aperture values. Defaults to - `None`, to be determined by matplotlib based on the provided values. - k0l_lim (Tuple[float, float]): vertical axis limits for the ``k0l`` values used for the - height of dipole patches. Defaults to `None`. - k1l_lim (Tuple[float, float]): vertical axis limits for the ``k1l`` values used for the - height of quadrupole patches. Defaults to `None`. - k2l_lim (Tuple[float, float]): if given, sextupole patches will be plotted on the layout subplot of - the figure, and the provided values act as vertical axis limits for the k2l values used for the - height of sextupole patches. - color (str): the color argument given to the aperture lines. Defaults to `None`, in which case - the first color in your `rcParams`'s cycler will be used. - **kwargs: any keyword argument will be transmitted to `~.plotting.utils.plot_machine_layout`, later on - to `~.plotting.utils._plot_lattice_series`, and then `~matplotlib.patches.Rectangle`, such as ``lw`` etc. + xoffset (float): An offset applied to the ``S`` coordinate before + plotting. This is useful if you want to center a plot around a + specific point or element, which would then become located at + :math:`s = 0`. Beware this offset is applied before applying the + *xlimits*. Defaults to 0. + xlimits (Tuple[float, float]): will implement xlim (for the ``s`` + coordinate) if this is not ``None``, using the tuple passed. + plot_dipoles (bool): if `True`, dipole patches will be plotted on + the layout subplot of the figure. Defaults to `True`. Dipoles + are plotted in blue. + plot_dipole_k1 (bool): if `True`, dipole elements with a quadrupolar + gradient will have this gradient plotted as a quadrupole patch. + Defaults to `False`. + plot_quadrupoles (bool): if `True`, quadrupole patches will be plotted + on the layout subplot of the figure. Defaults to `True`. + Quadrupoles are plotted in red. + plot_bpms (bool): if `True`, additional patches will be plotted on the + layout subplot to represent Beam Position Monitors. BPMs are + plotted in dark grey. + aperture_ylim (Tuple[float, float]): vertical axis limits for the + aperture values. Defaults to `None`, to be determined by matplotlib + based on the provided values. + k0l_lim (Union[Tuple[float, float], float, int]): vertical axis limits + for the ``k0l`` values used for the height of dipole patches. Can + be given as a single value (float, int) or a tuple (in which case + it should be symmetric). If `None` (default) is given, then the + limits will be auto-determined based on the ``k0l`` values of the + dipoles in the plot. + k1l_lim (Union[Tuple[float, float], float, int]): vertical axis limits + for the ``k1l`` values used for the height of quadrupole patches. + Can be given as a single value (float, int) or a tuple (in which + case it should be symmetric). If `None` (default) is given, then + the limits will be auto-determined based on the ``k0l`` values of + the quadrupoles in the plot. + k2l_lim (Union[Tuple[float, float], float, int]): if given, sextupole + patches will be plotted on the layout subplot of the figure. If + given, acts as vertical axis limits for the k2l values used for + the height of sextupole patches. Can be given as a single value + (float, int) or a tuple (in which case it should be symmetric). + k3l_lim (Union[Tuple[float, float], float, int]): if given, octupole + patches will be plotted on the layout subplot of the figure. If + given, acts as vertical axis limits for the k3l values used for + the height of octupole patches. Can be given as a single value + (float, int) or a tuple (in which case it should be symmetric). + color (str): the color argument given to the aperture lines. Defaults + to `None`, in which case the first color in your `rcParams`'s + cycler will be used. + **kwargs: any keyword argument will be transmitted to + `~.plotting.utils.plot_machine_layout`, later on to + `~.plotting.utils._plot_lattice_series`, and then + `~matplotlib.patches.Rectangle`, such as ``lw`` etc. Example: .. code-block:: python @@ -118,18 +157,22 @@ def plot_aperture( xoffset=xoffset, xlimits=xlimits, plot_dipoles=plot_dipoles, + plot_dipole_k1=plot_dipole_k1, plot_quadrupoles=plot_quadrupoles, plot_bpms=plot_bpms, k0l_lim=k0l_lim, k1l_lim=k1l_lim, k2l_lim=k2l_lim, + k3l_lim=k3l_lim, **kwargs, ) # Plotting aperture values on remaining two thirds of the figure logger.debug("Plotting aperture values") aperture_axis = plt.subplot2grid((3, 3), (1, 0), colspan=3, rowspan=2, sharex=quadrupole_patches_axis) - aperture_axis.plot(aperture_df.s, aperture_df.n1, marker=".", ls="-", lw=0.8, color=color, label="Aperture Limits") + aperture_axis.plot( + aperture_df.s, aperture_df.n1, marker=".", ls="-", lw=0.8, color=color, label="Aperture Limits" + ) aperture_axis.fill_between(aperture_df.s, aperture_df.n1, aperture_df.n1.max(), interpolate=True, color=color) aperture_axis.legend() aperture_axis.set_ylabel(r"$n_{1} \ [\sigma]$") @@ -142,3 +185,154 @@ def plot_aperture( if xlimits: logger.debug("Setting xlim for longitudinal coordinate") plt.xlim(xlimits) + + +def plot_physical_apertures( + madx, /, plane: str, scale: float = 1, xlimits: Tuple[float, float] = None, **kwargs +) -> None: + """ + .. versionadded:: 1.2.0 + + Determine and plot the "real" physical apertures of elements in the + sequence. A data point is extrapolated at the beginning and the end + of each element, with values based on the ``aper_1`` and ``aper_2`` + columns in the ``TWISS`` table. One can find an example use of this + function in the :ref:`machine aperture ` + example gallery. Original code from :user:`Elias Waagaard `. + + .. important:: + This function assumes the user has previously made a call to the + ``APERTURE`` command in ``MAD-X``, as it will query relevant values + from the ``aperture`` table. + + Args: + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + Positional only. + plane (str): the physical plane to plot for, should be either `x`, + `horizontal`, `y` or `vertical`, and is case-insensitive. + scale (float): a scaling factor to apply to the beam orbit and beam + enveloppe, for the user to adjust to their wanted scale. Defaults + to 1 (values in [m]). + xlimits (Tuple[float, float]): will implement xlim (for the ``s`` + coordinate) if this is not ``None``, using the tuple passed. + Defaults to ``None``. + **kwargs: any keyword argument that can be given to the ``MAD-X`` + ``TWISS`` command. If either `ax` or `axis` is found in the + kwargs, the corresponding value is used as the axis object to + plot on. + + Raises: + ValueError: if the *plane* argument is not one of `x`, `horizontal`, + `y` or `vertical`. + + Examples: + .. code-block:: python + + >>> fig, ax = plt.subplots(figsize=(10, 9)) + >>> plot_physical_apertures(madx, "x") + >>> plt.show() + + In order to do the same plot but have all values in millimeters: + + .. code-block:: python + + >>> fig, ax = plt.subplots(figsize=(10, 9)) + >>> plot_physical_apertures(madx, "x", scale=1e3) + >>> plt.setp(ax, xlabel="S [m]", ylabel="X [mm]") + >>> plt.show() + """ + # pylint: disable=too-many-arguments + if plane.lower() not in ("x", "y", "horizontal", "vertical"): + logger.error(f"'plane' argument should be 'x', 'horizontal', 'y' or 'vertical' not '{plane}'") + raise ValueError("Invalid 'plane' argument.") + + logger.debug(f"Plotting real element apertures") + axis, kwargs = maybe_get_ax(**kwargs) + + if xlimits is not None: + axis.set_xlim(xlimits) + + positions, apertures = _get_positions_and_real_apertures(madx, plane, xlimits, **kwargs) + apertures = apertures * scale + + logger.trace("Plotting apertures") + # previously drawstyle="steps", but do not use if entry and exit aperture offset differs + axis.fill_between(positions, apertures, 0.2 * scale, color="black", alpha=0.4, label="_nolegend_") + axis.fill_between(positions, -1 * apertures, -0.2 * scale, color="black", alpha=0.4, label="_nolegend_") + axis.plot(positions, apertures, "k", label="_nolegend_") + axis.plot(positions, -1 * apertures, "k", label="_nolegend_") + + +# ----- Helpers ----- # + + +def _get_positions_and_real_apertures( + madx, /, plane: str, xlimits: Tuple[float, float] = None, **kwargs +) -> Tuple[np.ndarray, np.ndarray]: + """ + .. versionadded:: 1.2.0 + + Determines the "real" physical apertures of elements in the sequence. + This is done by extrapolating a data point at the beginning and the end + of each element, with values based on the ``aper_1`` and ``aper_2`` + columns in the ``TWISS`` table. Original code from + :user:`Elias Waagaard `. + + .. important:: + This function assumes the user has previously made a call to the + ``APERTURE`` command in ``MAD-X``, as it will query relevant values + from the ``aperture`` table. + + Args: + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + Positional only. + plane (str): the physical plane to plot for, should be either `x`, + `horizontal`, `y` or `vertical`, and is case-insensitive. + xlimits (Tuple[float, float]): will implement xlim (for the ``s`` + coordinate) if this is not ``None``, using the tuple passed. + Defaults to ``None``. + **kwargs: any keyword argument that can be given to the ``MAD-X`` + ``TWISS`` command. + + Returns: + A `~numpy.ndarray` of the longitudinal positions for the data + points, and another `~numpy.ndarray` with the aperture values + at these positions. + """ + logger.debug("Getting Twiss dframe from MAD-X") + madx.command.select(flag="twiss", column=["aper_1", "aper_2"]) # make sure we to get these two + twiss_df = madx.twiss(**kwargs).dframe() + madx.command.select(flag="twiss", clear=True) # clean up + + logger.trace("Determining aperture column to use") + plane_number = 1 if plane.lower() in ("x", "horizontal") else 2 + apercol = f"aper_{plane_number:d}" + + if xlimits is not None: + twiss_df = twiss_df[twiss_df.s.between(*xlimits)] + + # Initiate arrays for new aperture and positions, + # these need to be lists as we will insert elements + new_aper = twiss_df[apercol].tolist() + new_pos = twiss_df.s.tolist() + indices = [] + + logger.trace("Finding non-zero aperture elements") + for i in range(len(twiss_df[apercol]) - 1, 0, -1): + if twiss_df[apercol][i] != 0: + new_aper.insert(i, twiss_df[apercol][i]) + indices.append(i) + indices = list(reversed(indices)) + + logger.trace("Extrapolating data at beginning of elements") + counter = 0# Keep track of exact position in new array with counter + for j in indices: + new_pos.insert(j + counter, (twiss_df.s[j] - twiss_df.l[j])) + counter += 1 + + # Replace all zeros with Nan + apertures = np.array(new_aper) + apertures[apertures == 0] = np.nan + positions = np.array(new_pos) + + return positions, apertures diff --git a/pyhdtoolkit/plotting/crossing.py b/pyhdtoolkit/plotting/crossing.py index ecfeae1b..0fcf4e3e 100644 --- a/pyhdtoolkit/plotting/crossing.py +++ b/pyhdtoolkit/plotting/crossing.py @@ -19,7 +19,7 @@ def plot_two_lhc_ips_crossings( - madx: Madx, first_ip: int, second_ip: int, ir_limit: float = 275, highlight_mqx_and_mbx: bool = True + madx: Madx, /, first_ip: int, second_ip: int, ir_limit: float = 275, highlight_mqx_and_mbx: bool = True ) -> None: """ .. versionadded:: 1.0.0 @@ -44,7 +44,7 @@ def plot_two_lhc_ips_crossings( before. Please re-``use`` your wanted sequence after calling this function! Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. first_ip (int): the first of the two IPs to plot crossing schemes for. second_ip (int): the second of the two IPs to plot crossing schemes for. ir_limit (float): the amount of meters to keep left and right of the IP point. Will also diff --git a/pyhdtoolkit/plotting/envelope.py b/pyhdtoolkit/plotting/envelope.py index aa10e8f6..77379c87 100644 --- a/pyhdtoolkit/plotting/envelope.py +++ b/pyhdtoolkit/plotting/envelope.py @@ -8,158 +8,119 @@ """ from typing import Tuple -import matplotlib -import matplotlib.axes import numpy as np -import pandas as pd from cpymad.madx import Madx from loguru import logger -from pyhdtoolkit.models.beam import BeamParameters from pyhdtoolkit.plotting.utils import maybe_get_ax -def plot_envelope( +def plot_beam_envelope( madx: Madx, - beam_params: BeamParameters, + /, + sequence: str, + plane: str, + nsigma: float = 1, + scale: float = 1, xlimits: Tuple[float, float] = None, - ylimits: Tuple[float, float] = None, - plane: str = "Horizontal", - title: str = None, **kwargs, -) -> matplotlib.axes.Axes: +) -> None: """ - .. versionadded:: 1.0.0 + .. versionadded:: 1.2.0 - Creates a plot representing an estimation of the beam stay-clear enveloppe through the machine, - as well as an estimation of the aperture limits of elements. One can find an example use of this - function in the :ref:`beam enveloppe ` example gallery. + Draws the beam enveloppe around the beam orbit on the given *axis*. + The enveloppe is determined from the active sequence's beam's parameters. + + One can find an example use of this function in the + :ref:`beam enveloppe ` example gallery. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - beam_params (BeamParameters): a validated `~.models.beam.BeamParameters` object one can - get from `~.optics.beam.compute_beam_parameters`. - xlimits (Tuple[float, float]): will implement xlim (for the ``s`` coordinate) if this is - not ``None``, using the tuple passed. - ylimits (Tuple[float, float]): the y limits for the horizontal plane plot (so - that machine geometry doesn't make the plot look shrinked). Defaults to `None`. - plane (str): the physical plane to plot for, should be either ``Horizontal`` or ``Vertical``, - and is case-insensitive. Defaults to ``Horizontal``. - title (Optional[str]): if provided, is set as title of the plot. Defaults to `None`. - **kwargs: If either `ax` or `axis` is found in the kwargs, the corresponding value is used as the - axis object to plot on. - - Returns: - The `~matplotlib.axes.Axes` on which the beam envelope is drawn. - - Example: + Positional only. + sequence (str): the name of the sequence to plot the beam enveloppe + for, should be the active sequence. Case-insensitive. + plane (str): the physical plane to plot for, should be either `x`, + `horizontal`, `y` or `vertical`, and is case-insensitive. + nsigma (float): the standard deviation to use for the beam enveloppe + calculation. A value of 3 will draw the 3 sigma beam enveloppe. + Defaults to 1. + scale (float): a scaling factor to apply to the beam orbit and beam + enveloppe, for the user to adjust to their wanted scale. Defaults + to 1 (values in [m]). + xlimits (Tuple[float, float]): will implement xlim (for the ``s`` + coordinate) if this is not ``None``, using the tuple passed. + Defaults to ``None``. + **kwargs: any keyword argument that can be given to the ``MAD-X`` + ``TWISS`` command. If either `ax` or `axis` is found in the + kwargs, the corresponding value is used as the axis object to + plot on. + + Raises: + ValueError: if the *plane* argument is not one of `x`, `horizontal`, + `y` or `vertical`. + + Examples: .. code-block:: python - >>> title = f"Horizontal aperture at {beam_injection.pc_GeV} GeV/c" >>> fig, ax = plt.subplots(figsize=(10, 9)) - >>> plot_envelope(madx, beam_injection, title=title) - """ - if plane.lower() not in ("horizontal", "vertical"): - logger.error(f"Plane should be either Horizontal or Vertical but '{plane}' was given") - raise ValueError("Invalid plane value") - # pylint: disable=too-many-arguments - # We need to interpolate in order to get high resolution along the S direction - logger.debug("Plotting estimated machine aperture and beam envelope") + >>> plot_beam_envelope(madx, "lhcb1", "x", nsigma=3) + >>> plt.show() - _interpolate_madx(madx) - twiss_hr = _get_twiss_hr_from_madx(madx, beam_params) - machine = twiss_hr[twiss_hr.apertype == "ellipse"] - - axis, kwargs = maybe_get_ax(**kwargs) + In order to do the same plot but have all values in millimeters: - if plane.lower() == "horizontal": - logger.debug("Plotting the horizontal aperture") - axis.plot(twiss_hr.s, twiss_hr.envelope_x, color="b") - axis.plot(twiss_hr.s, -twiss_hr.envelope_x, color="b") - axis.fill_between(twiss_hr.s, twiss_hr.envelope_x, -twiss_hr.envelope_x, color="b", alpha=0.25) - axis.fill_between(twiss_hr.s, 3 * twiss_hr.envelope_x, -3 * twiss_hr.envelope_x, color="b", alpha=0.25) - axis.fill_between(machine.s, machine.aper_1, machine.aper_1 * 100, color="k", alpha=0.5) - axis.fill_between(machine.s, -machine.aper_1, -machine.aper_1 * 100, color="k", alpha=0.5) - axis.plot(machine.s, machine.aper_1, "k.-") - axis.plot(machine.s, -machine.aper_1, "k.-") - axis.set_ylabel(r"$X \ [m]$") - axis.set_xlabel(r"$S \ [m]$") - else: - logger.debug("Plotting the vertical aperture") - axis.plot(twiss_hr.s, twiss_hr.envelope_y, color="r") - axis.plot(twiss_hr.s, -twiss_hr.envelope_y, color="r") - axis.fill_between(twiss_hr.s, twiss_hr.envelope_y, -twiss_hr.envelope_y, color="r", alpha=0.25) - axis.fill_between(twiss_hr.s, twiss_hr.envelope_y, -twiss_hr.envelope_y, color="r", alpha=0.25) - axis.fill_between(twiss_hr.s, 3 * twiss_hr.envelope_y, -3 * twiss_hr.envelope_y, color="r", alpha=0.25) - axis.fill_between(twiss_hr.s, 3 * twiss_hr.envelope_y, -3 * twiss_hr.envelope_y, color="r", alpha=0.25) - axis.fill_between(machine.s, machine.aper_2, machine.aper_2 * 100, color="k", alpha=0.5) - axis.fill_between(machine.s, -machine.aper_2, -machine.aper_2 * 100, color="k", alpha=0.5) - axis.plot(machine.s, machine.aper_2, "k.-") - axis.plot(machine.s, -machine.aper_2, "k.-") - axis.set_ylabel(r"$Y \ [m]$") - axis.set_xlabel(r"$S \ [m]$") - - axis.set_xlim(xlimits) - axis.set_ylim(ylimits) - axis.set_title(title) - return axis - - -def plot_stay_clear( - madx: Madx, - beam_params: BeamParameters, - xlimits: Tuple[float, float] = None, - title: str = None, - **kwargs, -) -> matplotlib.axes.Axes: - """ - .. versionadded:: 1.0.0 - - Creates a plot representing an estimation of the beam stay-clear through the machine, - One can find an example use of this function in the :ref:`beam enveloppe ` - example gallery. - - Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - beam_params (BeamParameters): a validated `~.models.beam.BeamParameters` object one can - get from `~.optics.beam.compute_beam_parameters`. - xlimits (Tuple[float, float]): will implement xlim (for the ``s`` coordinate) if this is - not ``None``, using the tuple passed. - title (Optional[str]): if provided, is set as title of the plot. Defaults to `None`. - **kwargs: If either `ax` or `axis` is found in the kwargs, the corresponding value is used as the - axis object to plot on. - - Returns: - The `~matplotlib.axes.Axes` on which the stay-clear is drawn. - - Example: .. code-block:: python - >>> title = f"Stay-Clear at {beam_flattop.pc_GeV} GeV/c" >>> fig, ax = plt.subplots(figsize=(10, 9)) - >>> plot_stay_clear(madx, beam_flattop, title=title) + >>> plot_beam_envelope(madx, "lhcb1", "x", nsigma=3, scale=1e3) + >>> plt.setp(ax, xlabel="S [m]", ylabel="X [mm]") + >>> plt.show() """ - _interpolate_madx(madx) - twiss_hr = _get_twiss_hr_from_madx(madx, beam_params) - machine = twiss_hr[twiss_hr.apertype == "ellipse"] + # pylint: disable=too-many-arguments + if plane.lower() not in ("x", "y", "horizontal", "vertical"): + logger.error(f"'plane' argument should be 'x', 'horizontal', 'y' or 'vertical' not '{plane}'") + raise ValueError("Invalid 'plane' argument.") - logger.debug("Plotting the stay-clear envelope") + logger.debug(f"Plotting machine orbit and {nsigma:.2f}sigma beam envelope") axis, kwargs = maybe_get_ax(**kwargs) - axis.plot(machine.s, machine.aper_1 / machine.envelope_x, ".-b", label="Horizontal") - axis.plot(machine.s, machine.aper_2 / machine.envelope_y, ".-r", label="Vertical") - axis.set_xlim(xlimits) - axis.set_ylabel(r"$\mathrm{n1}$") - axis.set_xlabel(r"$S \ [m]$") - axis.legend() - axis.set_title(title) - return axis + + logger.debug("Getting Twiss dframe from MAD-X") + plane_letter = "x" if plane.lower() in ("x", "horizontal") else "y" + twiss_df = madx.twiss(**kwargs).dframe() + if xlimits is not None: + axis.set_xlim(xlimits) + twiss_df = twiss_df[twiss_df.s.between(*xlimits)] + + logger.debug(f"Extracting beam parameters for the '{sequence}' sequence") + geom_emit = madx.sequence[sequence].beam[f"e{plane_letter}"] + sige = madx.sequence[sequence].beam.sige + orbit = twiss_df[plane_letter] * scale # with scaling factor, by default 1 + + logger.debug("Calculating beam enveloppe") + one_sigma = np.sqrt( + geom_emit * twiss_df[f"bet{plane_letter}"] + (sige * twiss_df[f"d{plane_letter}"]) ** 2 + ) + enveloppe = nsigma * one_sigma * scale # with scaling factor, by default 1 + + # Plot a line for the orbit, then fill between orbit + enveloppe and orbit - enveloppe + logger.debug("Plotting orbit and beam enveloppe") + alpha = np.clip(1 - (1 - np.exp(-nsigma / 2.35)), 0.05, 0.8) # lighter shade for higher sigma + plane_color = "b" if plane_letter == "x" else "r" # blue for horizontal, red for vertical + axis.plot(twiss_df.s, twiss_df[plane_letter], color=plane_color) + axis.fill_between( + twiss_df.s, + orbit + enveloppe, + orbit - enveloppe, + alpha=alpha, + color=plane_color, + label=rf"{nsigma}$\sigma$", + ) # ----- Helpers ----- # -def _interpolate_madx(madx: Madx) -> None: +def _interpolate_madx(madx: Madx, /) -> None: """Run interpolation on the provided MAD-X instance with default slice values.""" logger.debug("Running interpolation in MAD-X") madx.command.select(flag="interpolate", class_="drift", slice_=4, range_="#s/#e") @@ -167,16 +128,3 @@ def _interpolate_madx(madx: Madx) -> None: madx.command.select(flag="interpolate", class_="sbend", slice_=10, range_="#s/#e") madx.command.select(flag="interpolate", class_="rbend", slice_=10, range_="#s/#e") madx.command.twiss() - - -def _get_twiss_hr_from_madx(madx: Madx, beam_params: BeamParameters) -> pd.DataFrame: - """Get twiss hr from the provided MAD-X instance.""" - logger.trace("Getting Twiss dframe from MAD-X") - twiss_hr: pd.DataFrame = madx.table.twiss.dframe() - twiss_hr["betatronic_envelope_x"] = np.sqrt(twiss_hr.betx * beam_params.eg_x_m) - twiss_hr["betatronic_envelope_y"] = np.sqrt(twiss_hr.bety * beam_params.eg_y_m) - twiss_hr["dispersive_envelope_x"] = twiss_hr.dx * beam_params.deltap_p - twiss_hr["dispersive_envelope_y"] = twiss_hr.dy * beam_params.deltap_p - twiss_hr["envelope_x"] = np.sqrt(twiss_hr.betatronic_envelope_x ** 2 + (twiss_hr.dx * beam_params.deltap_p) ** 2) - twiss_hr["envelope_y"] = np.sqrt(twiss_hr.betatronic_envelope_y ** 2 + (twiss_hr.dy * beam_params.deltap_p) ** 2) - return twiss_hr diff --git a/pyhdtoolkit/plotting/lattice.py b/pyhdtoolkit/plotting/lattice.py index 73c1b328..eff6afaa 100644 --- a/pyhdtoolkit/plotting/lattice.py +++ b/pyhdtoolkit/plotting/lattice.py @@ -6,17 +6,16 @@ Module with functions to create lattice plots through a `~cpymad.madx.Madx` object. """ -from typing import Optional, Tuple +from typing import Optional, Tuple, Union import matplotlib import matplotlib.axes import matplotlib.pyplot as plt -import pandas as pd from cpymad.madx import Madx from loguru import logger -from pyhdtoolkit.plotting.layout import plot_machine_layout +from pyhdtoolkit.plotting.layout import _ylim_from_input, plot_machine_layout from pyhdtoolkit.plotting.utils import ( _get_twiss_table_with_offsets_and_limits, make_survey_groups, @@ -26,6 +25,7 @@ def plot_latwiss( madx: Madx, + /, title: Optional[str] = None, xoffset: float = 0, xlimits: Tuple[float, float] = None, @@ -33,11 +33,12 @@ def plot_latwiss( plot_dipole_k1: bool = False, plot_quadrupoles: bool = True, plot_bpms: bool = False, - disp_ylim: Tuple[float, float] = None, - beta_ylim: Tuple[float, float] = None, - k0l_lim: Tuple[float, float] = None, - k1l_lim: Tuple[float, float] = None, - k2l_lim: Tuple[float, float] = None, + disp_ylim: Union[Tuple[float, float], float, int] = None, + beta_ylim: Union[Tuple[float, float], float, int] = None, + k0l_lim: Union[Tuple[float, float], float, int] = None, + k1l_lim: Union[Tuple[float, float], float, int] = None, + k2l_lim: Union[Tuple[float, float], float, int] = None, + k3l_lim: Union[Tuple[float, float], float, int] = None, **kwargs, ) -> None: """ @@ -65,7 +66,7 @@ def plot_latwiss( overlap. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. title (Optional[str]): if provided, is set as title of the plot. Defaults to `None`. xoffset (float): An offset applied to the ``S`` coordinate before plotting. This is useful if you want to center a plot around a specific point or element, which would then become located @@ -81,18 +82,33 @@ def plot_latwiss( plot_bpms (bool): if `True`, additional patches will be plotted on the layout subplot to represent Beam Position Monitors. BPMs are plotted in dark grey. disp_ylim (Tuple[float, float]): vertical axis limits for the dispersion values. - Defaults to `None`. + Can be given as a single value (float, int) or a tuple (in which case it should be + symmetric). Defaults to (-10, 125). beta_ylim (Tuple[float, float]): vertical axis limits for the betatron function values. - Defaults to None, to be determined by matplotlib based on the provided beta values. - k0l_lim (Tuple[float, float]): vertical axis limits for the ``k0l`` values used for the - height of dipole patches. Defaults to `None`. - k1l_lim (Tuple[float, float]): vertical axis limits for the ``k1l`` values used for the - height of quadrupole patches. Defaults to `None`. - k2l_lim (Tuple[float, float]): if given, sextupole patches will be plotted on the layout subplot of - the figure, and the provided values act as vertical axis limits for the k2l values used for the - height of sextupole patches. - **kwargs: any keyword argument will be transmitted to `~.plotting.utils.plot_machine_layout`, later on - to `~.plotting.utils._plot_lattice_series`, and then `~matplotlib.patches.Rectangle`, such as ``lw`` etc. + Can be given as a single value (float, int) or a tuple (in which case it should be + symmetric). Defaults to `None`, to be determined by `~matplotlib` based on the + plotted beta values. + k0l_lim (Union[Tuple[float, float], float, int]): vertical axis limits for the ``k0l`` + values used for the height of dipole patches. Can be given as a single value (float, + int) or a tuple (in which case it should be symmetric). If `None` (default) is given, + then the limits will be auto-determined based on the ``k0l`` values of the dipoles in + the plot. + k1l_lim (Union[Tuple[float, float], float, int]): vertical axis limits for the ``k1l`` + values used for the height of quadrupole patches. Can be given as a single value (float, + int) or a tuple (in which case it should be symmetric). If `None` (default) is given, + then the limits will be auto-determined based on the ``k0l`` values of the quadrupoles + in the plot. + k2l_lim (Union[Tuple[float, float], float, int]): if given, sextupole patches will be plotted + on the layout subplot of the figure. If given, acts as vertical axis limits for the k2l + values used for the height of sextupole patches. Can be given as a single value (float, + int) or a tuple (in which case it should be symmetric). + k3l_lim (Union[Tuple[float, float], float, int]): if given, octupole patches will be plotted + on the layout subplot of the figure. If given, acts as vertical axis limits for the k3l + values used for the height of octupole patches. Can be given as a single value (float, + int) or a tuple (in which case it should be symmetric). + **kwargs: any keyword argument will be transmitted to `~.plotting.utils.plot_machine_layout`, + later on to `~.plotting.utils._plot_lattice_series`, and then `~matplotlib.patches.Rectangle`, + such as ``lw`` etc. Example: .. code-block:: python @@ -101,11 +117,26 @@ def plot_latwiss( >>> plt.figure(figsize=(16, 11)) >>> plot_latwiss( ... madx, - title=title, - k0l_lim=(-0.15, 0.15), - k1l_lim=(-0.08, 0.08), - disp_ylim=(-10, 125), - lw=3, + ... title=title, + ... k0l_lim=(-0.15, 0.15), + ... k1l_lim=(-0.08, 0.08), + ... disp_ylim=(-10, 125), + ... lw=3, + ... ) + + One can provide ylimits for the machine layout patches as single values: + + .. code-block:: python + + >>> title = "Machine Layout" + >>> plt.figure(figsize=(16, 11)) + >>> plot_latwiss( + ... madx, + ... title=title, + ... k0l_lim=0.15, # identical to k0l_lim=(-0.15, 0.15) + ... k1l_lim=0.08, # identical to k1l_lim=(-0.08, 0.08) + ... disp_ylim=(-10, 125), + ... lw=3, ... ) """ # pylint: disable=too-many-arguments @@ -130,6 +161,7 @@ def plot_latwiss( k0l_lim=k0l_lim, k1l_lim=k1l_lim, k2l_lim=k2l_lim, + k3l_lim=k3l_lim, **kwargs, ) @@ -153,10 +185,12 @@ def plot_latwiss( if beta_ylim: logger.debug("Setting ylim for betatron functions plot") + beta_ylim = _ylim_from_input(beta_ylim, "beta_ylim") betatron_axis.set_ylim(beta_ylim) if disp_ylim: logger.debug("Setting ylim for dispersion plot") + disp_ylim = _ylim_from_input(disp_ylim, "beta_ylim") dispertion_axis.set_ylim(disp_ylim) if xlimits: @@ -166,6 +200,7 @@ def plot_latwiss( def plot_machine_survey( madx: Madx, + /, title: str = None, show_elements: bool = False, high_orders: bool = False, @@ -180,7 +215,7 @@ def plot_machine_survey( example gallery. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. title (Optional[str]): if provided, is set as title of the plot. Defaults to `None`. show_elements (bool): if `True`, will try to plot by differentiating elements. Defaults to `False`. @@ -219,13 +254,21 @@ def plot_machine_survey( label="Dipoles", **kwargs, ) - plt.scatter(element_dfs["quad_foc"].z, element_dfs["quad_foc"].x, marker="o", color="blue", label="QF") - plt.scatter(element_dfs["quad_defoc"].z, element_dfs["quad_defoc"].x, marker="o", color="red", label="QD") + plt.scatter( + element_dfs["quad_foc"].z, element_dfs["quad_foc"].x, marker="o", color="blue", label="QF" + ) + plt.scatter( + element_dfs["quad_defoc"].z, element_dfs["quad_defoc"].x, marker="o", color="red", label="QD" + ) if high_orders: logger.debug("Plotting high order magnetic elements (up to octupoles)") - plt.scatter(element_dfs["sextupoles"].z, element_dfs["sextupoles"].x, marker=".", color="m", label="MS") - plt.scatter(element_dfs["octupoles"].z, element_dfs["octupoles"].x, marker=".", color="cyan", label="MO") + plt.scatter( + element_dfs["sextupoles"].z, element_dfs["sextupoles"].x, marker=".", color="m", label="MS" + ) + plt.scatter( + element_dfs["octupoles"].z, element_dfs["octupoles"].x, marker=".", color="cyan", label="MO" + ) plt.legend(loc=2) else: diff --git a/pyhdtoolkit/plotting/layout.py b/pyhdtoolkit/plotting/layout.py index 9d195df1..82af24c1 100644 --- a/pyhdtoolkit/plotting/layout.py +++ b/pyhdtoolkit/plotting/layout.py @@ -7,8 +7,7 @@ Module with functions used to represent a machine' elements in an `~matplotlib.axes.Axes` object, mostly used in different `~pyhdtoolkit.plotting` modules. """ -from cmath import log -from typing import Tuple +from typing import Tuple, Union import matplotlib import matplotlib.axes @@ -27,6 +26,7 @@ def plot_machine_layout( madx: Madx, + /, title: str = None, xoffset: float = 0, xlimits: Tuple[float, float] = None, @@ -34,72 +34,98 @@ def plot_machine_layout( plot_dipole_k1: bool = False, plot_quadrupoles: bool = True, plot_bpms: bool = False, - k0l_lim: Tuple[float, float] = None, - k1l_lim: Tuple[float, float] = None, - k2l_lim: Tuple[float, float] = None, + k0l_lim: Union[Tuple[float, float], float, int] = None, + k1l_lim: Union[Tuple[float, float], float, int] = None, + k2l_lim: Union[Tuple[float, float], float, int] = None, + k3l_lim: Union[Tuple[float, float], float, int] = None, **kwargs, ) -> None: """ .. versionadded:: 1.0.0 - Draws patches elements representing the lattice layout on the given *axis*. This is - the function that takes care of the machine layout axis in `~.plotting.lattice.plot_latwiss` - and `~.plotting.aperture.plot_aperture`. Its results can be seen in the - :ref:`machine lattice ` and :ref:`machine aperture ` - example galleries. + Draws patches elements representing the lattice layout on the given + *axis*. This is the function that takes care of the machine layout axis + in `~.plotting.lattice.plot_latwiss` and + `~.plotting.aperture.plot_aperture`. Its results can be seen in the + :ref:`machine lattice ` and + :ref:`machine aperture ` example galleries. .. note:: - This current implementation can plot dipoles, quadrupoles, sextupoles and BPMs. + This current implementation can plot dipoles, quadrupoles, sextupoles, + octupoles and BPMs. .. important:: - If not provided, the limits for the ``k0l_lim``, ``k1l_lim`` will be auto-determined, which might - not be the perfect choice for you plot. When providing these limits (also for ``k2l_lim``), make - sure to provide symmetric values around 0 (so [-x, x]) otherwise the element patches will show up - vertically displaced from the axis' center line. + If not provided, the limits for the ``k0l_lim``, ``k1l_lim`` will be + auto-determined, which might not be the perfect choice for you plot. + When providing these limits (also for ``k2l_lim``), make sure to + provide symmetric values around 0 (so [-x, x]) otherwise the element + patches will show up vertically displaced from the axis' center line. .. warning:: - Currently the function tries to plot legends for the different layout patches. The position of the - different legends has been hardcoded in corners of the `~matplotlib.axes.Axes` and might require users - to tweak the axis limits (through ``k0l_lim``, ``k1l_lim`` and ``k2l_lim``) to ensure legend labels and - plotted elements don't overlap. + Currently the function tries to plot legends for the different layout + patches. The position of the different legends has been hardcoded in + corners of the `~matplotlib.axes.Axes` and might require users to tweak + the axis limits (through ``k0l_lim``, ``k1l_lim`` and ``k2l_lim``) to + ensure legend labels and plotted elements don't overlap. Args: madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. - title (Optional[str]): if provided, is set as title of the plot. Defaults to `None`. - xoffset (float): An offset applied to the ``S`` coordinate before plotting. This is useful if - you want to center a plot around a specific point or element, which would then become located - at :math:`s = 0`. Beware this offset is applied before applying the *xlimits*. Defaults to 0. - xlimits (Tuple[float, float]): will implement xlim (for the ``s`` coordinate) if this is - not ``None``, using the tuple passed. - plot_dipoles (bool): if `True`, dipole patches will be plotted on the layout subplot of - the figure. Defaults to `True`. Dipoles are plotted in blue. - plot_dipole_k1 (bool): if `True`, dipole elements with a quadrupolar gradient will have this - gradient plotted as a quadrupole patch. Defaults to `False`. - plot_quadrupoles (bool): if `True`, quadrupole patches will be plotted on the layout - subplot of the figure. Defaults to `True`. Quadrupoles are plotted in red. - plot_bpms (bool): if `True`, additional patches will be plotted on the layout subplot to - represent Beam Position Monitors. BPMs are plotted in dark grey. - disp_ylim (Tuple[float, float]): vertical axis limits for the dispersion values. - Defaults to (-10, 125). - beta_ylim (Tuple[float, float]): vertical axis limits for the betatron function values. - Defaults to None, to be determined by matplotlib based on the provided beta values. - k0l_lim (Tuple[float, float]): vertical axis limits for the ``k0l`` values used for the - height of dipole patches, should be symmetric. If `None` (default) is provided, then - the limits will be auto-determined based on the ``k0l`` values of the dipoles in the - plot. - k1l_lim (Tuple[float, float]): vertical axis limits for the ``k1l`` values used for the - height of quadrupole patches, should be symmetric. If `None` (default) is provided, - then the limits will be auto-determined based on the ``k0l`` values of the dipoles - in the plot. - k2l_lim (Tuple[float, float]): if given, sextupole patches will be plotted on the layout subplot of - the figure, and the provided values act as vertical axis limits for the k2l values used for the - height of sextupole patches. - **kwargs: any keyword argument will be transmitted to `~.plotting.utils.plot_machine_layout`, later on - to `~.plotting.utils._plot_lattice_series`, and then `~matplotlib.patches.Rectangle`, such as ``lw`` - etc. If either `ax` or `axis` is found in the kwargs, the corresponding value is used as the axis - object to plot on. By definition, the quadrupole elements will be drawn on said axis, and for each - new element type to plot a call to `~matplotlib.axes.Axes.twinx` is made and the new elements will - be drawn on the newly created twin `~matplotlib.axes.Axes`. + Positional only. + title (Optional[str]): if provided, is set as title of the plot. + Defaults to `None`. + xoffset (float): An offset applied to the ``S`` coordinate before + plotting. This is useful if you want to center a plot around a + specific point or element, which would then become located + at :math:`s = 0`. Beware this offset is applied before applying + the *xlimits*. Defaults to 0. + xlimits (Tuple[float, float]): will implement xlim (for the ``s`` + coordinate) if this is not ``None``, using the tuple passed. + plot_dipoles (bool): if `True`, dipole patches will be plotted on + the layout subplot of the figure. Defaults to `True`. Dipoles + are plotted in blue. + plot_dipole_k1 (bool): if `True`, dipole elements with a quadrupolar + gradient will have this gradient plotted as a quadrupole patch. + Defaults to `False`. + plot_quadrupoles (bool): if `True`, quadrupole patches will be plotted + on the layout subplot of the figure. Defaults to `True`. + Quadrupoles are plotted in red. + plot_bpms (bool): if `True`, additional patches will be plotted on the + layout subplot to represent Beam Position Monitors. BPMs are + plotted in dark grey. + k0l_lim (Union[Tuple[float, float], float, int]): vertical axis limits + for the ``k0l`` values used for the height of dipole patches. Can + be given as a single value (float, int) or a tuple (in which case + it should be symmetric). If `None` (default) is given, then the + limits will be auto-determined based on the ``k0l`` values of the + dipoles in the plot. + k1l_lim (Union[Tuple[float, float], float, int]): vertical axis limits + for the ``k1l`` values used for the height of quadrupole patches. + Can be given as a single value (float, int) or a tuple (in which + case it should be symmetric). If `None` (default) is given, then + the limits will be auto-determined based on the ``k0l`` values of + the quadrupoles in the plot. + k2l_lim (Union[Tuple[float, float], float, int]): if given, sextupole + patches will be plotted on the layout subplot of the figure. If + given, acts as vertical axis limits for the k2l values used for + the height of sextupole patches. Can be given as a single value + (float, int) or a tuple (in which case it should be symmetric). + k3l_lim (Union[Tuple[float, float], float, int]): if given, octupole + patches will be plotted on the layout subplot of the figure. If + given, acts as vertical axis limits for the k3l values used for + the height of octupole patches. Can be given as a single value + (float, int) or a tuple (in which case it should be symmetric). + **kwargs: any keyword argument will be transmitted to + `~.plotting.utils.plot_machine_layout`, later on to + `~.plotting.utils._plot_lattice_series`, and then + `~matplotlib.patches.Rectangle`, such as ``lw`` etc. If either + `ax` or `axis` is found in the kwargs, the corresponding value + is used as the axis object to plot on. By definition, the + quadrupole elements will be drawn on said axis, and for each + new element type to plot a call to `~matplotlib.axes.Axes.twinx` + is made and the new elements will be drawn on the newly created + twin `~matplotlib.axes.Axes`. If ``bpms_legend`` is given as + `False` and BPMs are plotted, the BPM legend will not be plotted + on the layout axis. Example: .. code-block:: python @@ -109,6 +135,7 @@ def plot_machine_layout( """ # pylint: disable=too-many-arguments axis, kwargs = maybe_get_ax(**kwargs) + bpms_legend = kwargs.pop("bpms_legend", True) twiss_df = _get_twiss_table_with_offsets_and_limits(madx, xoffset, xlimits) logger.trace("Extracting element-specific dataframes") @@ -116,10 +143,20 @@ def plot_machine_layout( dipoles_df = element_dfs["dipoles"] quadrupoles_df = element_dfs["quadrupoles"] sextupoles_df = element_dfs["sextupoles"] + octupoles_df = element_dfs["octupoles"] bpms_df = element_dfs["bpms"] - k0l_lim = k0l_lim or _determine_default_knl_lim(dipoles_df, col="k0l", coeff=2) - k1l_lim = k1l_lim or _determine_default_knl_lim(quadrupoles_df, col="k1l", coeff=1.3) + logger.trace("Determining the ylimits for k0l and k1l patches") + k0l_lim = ( + _ylim_from_input(k0l_lim, "k0l_lim") + if k0l_lim is not None + else _determine_default_knl_lim(dipoles_df, col="k0l", coeff=2) + ) + k1l_lim = ( + _ylim_from_input(k1l_lim, "k1l_lim") + if k1l_lim is not None + else _determine_default_knl_lim(quadrupoles_df, col="k1l", coeff=1.3) + ) logger.debug("Plotting machine layout") logger.trace(f"Plotting from axis '{axis}'") @@ -164,7 +201,9 @@ def plot_machine_layout( **kwargs, ) plotted_elements += 1 - dipole_patches_axis.legend(loc=1) + logger.debug(f"Plotted {plotted_elements} dipole elements") + if plotted_elements > 0: # If we plotted at least one dipole, we need to plot the legend + dipole_patches_axis.legend(loc=1) if plot_quadrupoles: logger.trace("Plotting quadrupole patches") @@ -181,14 +220,17 @@ def plot_machine_layout( **kwargs, ) plotted_elements += 1 - axis.legend(loc=2) + logger.debug(f"Plotted {plotted_elements} quadrupole elements") + if plotted_elements > 0: # If we plotted at least one quadrupole, we need to plot the legend + axis.legend(loc=2) if k2l_lim: logger.trace("Plotting sextupole patches") sextupoles_patches_axis = axis.twinx() sextupoles_patches_axis.set_ylabel("$K_{2}L$ $[m^{-2}]$", color="darkgoldenrod") sextupoles_patches_axis.tick_params(axis="y", labelcolor="darkgoldenrod") - sextupoles_patches_axis.spines["right"].set_position(("axes", 1.1)) + sextupoles_patches_axis.spines["right"].set_position(("axes", 1.12)) + k2l_lim = _ylim_from_input(k2l_lim, "k2l_lim") sextupoles_patches_axis.set_ylim(k2l_lim) plotted_elements = 0 for sextupole_name, sextupole in sextupoles_df.iterrows(): @@ -203,8 +245,38 @@ def plot_machine_layout( **kwargs, ) plotted_elements += 1 - sextupoles_patches_axis.legend(loc=3) + logger.debug(f"Plotted {plotted_elements} sextupole elements") sextupoles_patches_axis.grid(False) + if plotted_elements > 0: # If we plotted at least one sextupole, we need to plot the legend + sextupoles_patches_axis.legend(loc=3) + + if k3l_lim: + logger.trace("Plotting octupole patches") + octupoles_patches_axis = axis.twinx() + octupoles_patches_axis.set_ylabel("$K_{3}L$ $[m^{-3}]$", color="forestgreen") + octupoles_patches_axis.tick_params(axis="y", labelcolor="forestgreen") + octupoles_patches_axis.yaxis.set_label_position("left") + octupoles_patches_axis.yaxis.tick_left() + octupoles_patches_axis.spines["left"].set_position(("axes", -0.14)) + k3l_lim = _ylim_from_input(k3l_lim, "k3l_lim") + octupoles_patches_axis.set_ylim(k3l_lim) + plotted_elements = 0 + for octupole_name, octupole in octupoles_df.iterrows(): + logger.trace(f"Plotting octupole element '{octupole_name}'") + _plot_lattice_series( + octupoles_patches_axis, + octupole, + height=octupole.k3l, + v_offset=octupole.k3l / 2, + color="forestgreen", + label="MO" if plotted_elements == 0 else None, # avoid duplicating legend labels + **kwargs, + ) + plotted_elements += 1 + logger.debug(f"Plotted {plotted_elements} octupole elements") + octupoles_patches_axis.grid(False) + if plotted_elements > 0: # If we plotted at least one octupole, we need to plot the legend + octupoles_patches_axis.legend(loc=4) if plot_bpms: logger.trace("Plotting BPM patches") @@ -224,7 +296,21 @@ def plot_machine_layout( **kwargs, ) plotted_elements += 1 - bpm_patches_axis.legend(loc=4) + logger.debug(f"Plotted {plotted_elements} BPMs") + logger.trace("Determining BPM legend location") + if bpms_legend is True: + if k2l_lim is not None and k3l_lim is not None: + bpm_legend_loc = 8 # all corners are taken, we go bottom center + elif k2l_lim is not None: + bpm_legend_loc = 4 # sextupoles are here but not octupoles, we go bottom left + elif k3l_lim is not None: + bpm_legend_loc = 3 # octuoles are here but not sextupoles, we go bottom right + else: + bpm_legend_loc = ( + "best" # can't easily determine the best position, go automatic and leave to the user + ) + if plotted_elements > 0: # If we plotted at least one BPM, we need to plot the legend + bpm_patches_axis.legend(loc=bpm_legend_loc) bpm_patches_axis.grid(False) @@ -243,18 +329,21 @@ def _plot_lattice_series( """ .. versionadded:: 1.0.0 - Plots a `~matplotlib.patches.Rectangle` element on the provided `~matplotlib.axes.Axes` to - represent an element of the machine. Original code from :user:`Guido Sterbini `. + Plots a `~matplotlib.patches.Rectangle` element on the provided + `~matplotlib.axes.Axes` to represent an element of the machine. + Original code from :user:`Guido Sterbini `. Args: - ax (matplotlib.axes.Axes): an existing `~matplotlib.axes.Axes` object to draw on. + ax (matplotlib.axes.Axes): an existing `~matplotlib.axes.Axes` + object to draw on. series (pd.DataFrame): a `pandas.DataFrame` with the elements' data. height (float): value to reach for the patch on the y axis. v_offset (float): vertical offset for the patch. color (str): color kwarg to transmit to `~matplotlib.pyplot`. alpha (float): alpha kwarg to transmit to `~matplotlib.pyplot`. - **kwargs: any keyword argument will be transmitted to `~matplotlib.patches.Rectangle`, - for instance ``lw`` for the edge line width. + **kwargs: any keyword argument will be transmitted to + `~matplotlib.patches.Rectangle`, for instance ``lw`` for the + edge line width. """ ax.add_patch( patches.Rectangle( @@ -268,16 +357,53 @@ def _plot_lattice_series( ) +def _ylim_from_input( + ylim: Union[Tuple[float, float], float, int], name_for_error: str = "knl_lim" +) -> Tuple[float, float]: + """ + .. versionadded:: 1.2.0 + + Determines the ylimits for a given axis from the input provided by the + user. This is used in `~.plotting.utils.plot_machine_layout` and handles + different inputs from the user, such as a tuple, a float and an int. + + Args: + ylim (Tuple[float, float], float, int): the input provided by + the user. + name_for_error (str): the name of the variable to use in the + error message. + + Returns: + A tuple for the ylimits from the input. + + Raises: + TypeError: if the input is not a tuple, a float or an int. + """ + if isinstance(ylim, tuple): + return ylim + elif isinstance(ylim, (float, int)): + if ylim >= 0: + return (-ylim, ylim) + else: + return (ylim, -ylim) + else: + raise TypeError( + f"Invalid type for '{name_for_error}': {type(ylim)}. " + "Should be a tuple, a float or an int. Can also be give as None." + ) + + def _determine_default_knl_lim(df: pd.DataFrame, col: str, coeff: float) -> Tuple[float, float]: """ .. versionadded:: 1.0.0 - Determine the default limits for the ``knl`` axis, when plotting machine layout. - This is in case `None` are provided by the user, to make sure the plot still - looks coherent and symmetric in `~.plotting.utils.plot_machine_layout`. + Determine the default limits for the ``knl`` axis, when plotting machine + layout. This is in case `None` are provided by the user, to make sure the + plot still looks coherent and symmetric in + `~.plotting.utils.plot_machine_layout`. - The limits are determined symmetric, using the maximum absolute value of the - knl column in the provided dataframe and a 1.25 scaling factor. + The limits are determined symmetric, using the maximum absolute value of + the knl column in the provided dataframe and a 1.25 scaling factor. Args: df (pd.DataFrame): a `pandas.DataFrame` with the multipoles' data. diff --git a/pyhdtoolkit/plotting/phasespace.py b/pyhdtoolkit/plotting/phasespace.py index b094dd10..6f47634e 100644 --- a/pyhdtoolkit/plotting/phasespace.py +++ b/pyhdtoolkit/plotting/phasespace.py @@ -24,6 +24,7 @@ def plot_courant_snyder_phase_space( madx: Madx, + /, u_coordinates: np.ndarray, pu_coordinates: np.ndarray, plane: str = "Horizontal", @@ -38,7 +39,7 @@ def plot_courant_snyder_phase_space( use of this function in the :ref:`phase space ` example gallery. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. u_coordinates (np.ndarray): `~numpy.ndarray` of particles' coordinates for the given plane. Here ``u_coordinates[0]`` should be the tracked coordinates for the first particle and so on. pu_coordinates (np.ndarray): `~numpy.ndarray` of particles' momentum coordinates for the @@ -90,6 +91,7 @@ def plot_courant_snyder_phase_space( def plot_courant_snyder_phase_space_colored( madx: Madx, + /, u_coordinates: np.ndarray, pu_coordinates: np.ndarray, plane: str = "Horizontal", @@ -106,7 +108,7 @@ def plot_courant_snyder_phase_space_colored( the :ref:`phase space ` example gallery. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. u_coordinates (np.ndarray): `~numpy.ndarray` of particles' coordinates for the given plane. Here ``u_coordinates[0]`` should be the tracked coordinates for the first particle and so on. pu_coordinates (np.ndarray): `~numpy.ndarray` of particles' momentum coordinates for the diff --git a/pyhdtoolkit/plotting/utils.py b/pyhdtoolkit/plotting/utils.py index d028751e..4d194cfd 100644 --- a/pyhdtoolkit/plotting/utils.py +++ b/pyhdtoolkit/plotting/utils.py @@ -6,16 +6,20 @@ Module with functions to used throught the different `~pyhdtoolkit.plotting` modules. """ -from typing import Dict, Tuple +from __future__ import annotations # important for Sphinx to generate short type signatures! import matplotlib import matplotlib.axes import matplotlib.pyplot as plt +import matplotlib.transforms as transforms +import numpy as np import pandas as pd import tfs from cpymad.madx import Madx from loguru import logger +from matplotlib.patches import Ellipse +from numpy.typing import ArrayLike # ------ General Utilities ----- # @@ -50,7 +54,7 @@ def maybe_get_ax(**kwargs): """ logger.debug("Looking for axis object to plot on") if "ax" in kwargs: - logger.debug("Using the provided kwargs 'ax' as the axis to plot one") + logger.debug("Using the provided kwargs 'ax' as the axis to plot on") ax = kwargs.pop("ax") elif "axis" in kwargs: logger.debug("Using the provided kwargs 'axis' as the axis to plot on") @@ -97,7 +101,7 @@ def find_ip_s_from_segment_start(segment_df: tfs.TfsDataFrame, model_df: tfs.Tfs return distance -def get_lhc_ips_positions(dataframe: pd.DataFrame) -> Dict[str, float]: +def get_lhc_ips_positions(dataframe: pd.DataFrame) -> dict[str, float]: """ .. versionadded:: 1.0.0 @@ -135,8 +139,8 @@ def get_lhc_ips_positions(dataframe: pd.DataFrame) -> Dict[str, float]: def make_elements_groups( - madx: Madx, xoffset: float = 0, xlimits: Tuple[float, float] = None -) -> Dict[str, pd.DataFrame]: + madx: Madx, /, xoffset: float = 0, xlimits: tuple[float, float] = None +) -> dict[str, pd.DataFrame]: """ .. versionadded:: 1.0.0 @@ -144,10 +148,10 @@ def make_elements_groups( the twiss table's dataframe for different magnetic elements. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. xoffset (float): An offset applied to the S coordinate before plotting. This is useful is you want to center a plot around a specific point or element, which would then become located at s = 0. - xlimits (Tuple[float, float]): will only consider elements within xlim (for the s coordinate) if this + xlimits (tuple[float, float]): will only consider elements within xlim (for the s coordinate) if this is not None, using the tuple passed. Returns: @@ -175,7 +179,7 @@ def make_elements_groups( } -def make_survey_groups(madx: Madx) -> Dict[str, pd.DataFrame]: +def make_survey_groups(madx: Madx, /) -> dict[str, pd.DataFrame]: """ .. versionadded:: 1.0.0 @@ -183,7 +187,7 @@ def make_survey_groups(madx: Madx) -> Dict[str, pd.DataFrame]: the survey table's dataframe for different magnetic elements. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. Returns: A `dict` containing a `pd.DataFrame` for dipoles, focusing quadrupoles, defocusing @@ -215,7 +219,7 @@ def make_survey_groups(madx: Madx) -> Dict[str, pd.DataFrame]: def draw_ip_locations( - ip_positions: Dict[str, float] = None, + ip_positions: dict[str, float] = None, lines: bool = True, location: str = "outside", **kwargs, @@ -271,10 +275,9 @@ def draw_ip_locations( def set_arrow_label( - axis: matplotlib.axes.Axes, label: str, - arrow_position: Tuple[float, float], - label_position: Tuple[float, float], + arrow_position: tuple[float, float], + label_position: tuple[float, float], color: str = "k", arrow_arc_rad: float = -0.2, fontsize: int = 20, @@ -289,8 +292,8 @@ def set_arrow_label( Args: axis (matplotlib.axes.Axes): a `matplotlib.axes.Axes` to plot on. label (str): label text to print on the axis. - arrow_position (Tuple[float, float]): where on the plot to point the tip of the arrow. - label_position (Tuple[float, float]): where on the plot the text label (and thus start + arrow_position (tuple[float, float]): where on the plot to point the tip of the arrow. + label_position (tuple[float, float]): where on the plot the text label (and thus start of the arrow) is. color (str): color parameter for your arrow and label. Defaults to "k". arrow_arc_rad (float): angle value defining the upwards / downwards shape of and @@ -332,11 +335,76 @@ def set_arrow_label( ) +def draw_confidence_ellipse(x: ArrayLike, y: ArrayLike, n_std: float = 3.0, facecolor="none", **kwargs) -> Ellipse: + """ + .. versionadded:: 1.2.0 + + Plot the covariance confidence ellipse of *x* and *y*. This code is taken from the + `matplotlib gallery `_. + + .. note:: + One might want to provide the `edgecolor` to this function. + + Args: + x (ArrayLike): array-like, should be of shape (n,). + y (ArrayLike): array-like, should be of shape (n,). + n_std (float): The number of standard deviations of the data to + highlight, to determine the ellipse's radiuses. + facecolor (str): The facecolor of the ellipse. + **kwargs: Any keyword argument will be forwarded to `~matplotlib.patches.Ellipse`. + If either `ax` or `axis` is found in the kwargs, the corresponding value is + used as the axis object to plot on. + + Returns: + The corresponding `~matplotlib.patches.Ellipse` object added to the axis. + + Example: + .. code-block:: python + + >>> x = np.random.normal(size=1000) + >>> y = np.random.normal(size=1000) + >>> plt.plot(x, y, ".", markersize=0.8) + >>> draw_confidence_ellipse(x, y, n_std=2.5, edgecolor="red") + """ + axis, kwargs = maybe_get_ax(**kwargs) + x = np.array(x) + y = np.array(y) + + if x.size != y.size: + logger.error(f"x and y must be the same size, but shapes {x.shape} and {y.shape} were given.") + raise ValueError(f"x and y must be the same size, but shapes {x.shape} and {y.shape} were given.") + + logger.debug("Computing covariance matrix and pearson correlation coefficient") + covariance_matrix = np.cov(x, y) + pearson = covariance_matrix[0, 1] / np.sqrt(covariance_matrix[0, 0] * covariance_matrix[1, 1]) + + # Using a special case to obtain the eigenvalues of this two-dimensionl dataset. + logger.debug("Computing radiuses of the ellipse") + radius_x = np.sqrt(1 + pearson) + radius_y = np.sqrt(1 - pearson) + ellipse = Ellipse((0, 0), width=radius_x * 2, height=radius_y * 2, facecolor=facecolor, **kwargs) + + # Calculating the stdev of x from the square root of the variance and multiplying + # with the given number of standard deviations. + scale_x = np.sqrt(covariance_matrix[0, 0]) * n_std + mean_x = np.mean(x) + + # Calculating the stdev of y from the square root of the variance and multiplying + # with the given number of standard deviations. + scale_y = np.sqrt(covariance_matrix[1, 1]) * n_std + mean_y = np.mean(y) + + logger.debug("Preparing and drawing ellipse patch") + transf = transforms.Affine2D().rotate_deg(45).scale(scale_x, scale_y).translate(mean_x, mean_y) + ellipse.set_transform(transf + axis.transData) + return axis.add_patch(ellipse) + + # ----- Private Helpers ----- # def _get_twiss_table_with_offsets_and_limits( - madx: Madx, xoffset: float = 0, xlimits: Tuple[float, float] = None + madx: Madx, /, xoffset: float = 0, xlimits: tuple[float, float] = None, **kwargs ) -> pd.DataFrame: """ .. versionadded:: 1.0.0 @@ -345,17 +413,18 @@ def _get_twiss_table_with_offsets_and_limits( the given `xoffset`. Args: - madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. + madx (cpymad.madx.Madx): an instanciated `~cpymad.madx.Madx` object. Positional only. xoffset (float): An offset applied to the S coordinate in the dataframe. - xlimits (Tuple[float, float]): will only consider elements within xlimits (for the s coordinate) if + xlimits (tuple[float, float]): will only consider elements within xlimits (for the s coordinate) if this is not `None`, using the tuple passed. + **kwargs: any keyword argument will be transmitted to the ``MAD-X`` ``TWISS` command. Returns: The ``TWISS`` dataframe from ``MAD-X``, with the limits and offset applied, if any. """ # Restrict the span of twiss_df to avoid plotting all elements then cropping when xlimits is given logger.trace("Getting TWISS table from MAD-X") - madx.command.twiss() + madx.command.twiss(**kwargs) twiss_df = madx.table.twiss.dframe() twiss_df.s = twiss_df.s - xoffset twiss_df = twiss_df[twiss_df.s.between(*xlimits)] if xlimits else twiss_df diff --git a/pyhdtoolkit/utils/_misc.py b/pyhdtoolkit/utils/_misc.py index 4264c231..1ff5d1dd 100644 --- a/pyhdtoolkit/utils/_misc.py +++ b/pyhdtoolkit/utils/_misc.py @@ -14,19 +14,23 @@ from multiprocessing import cpu_count from pathlib import Path -from typing import Union +from typing import Sequence, Union import cpymad +import numpy as np +import pandas as pd from cpymad.madx import Madx from loguru import logger from pyhdtoolkit import __version__ from pyhdtoolkit.cpymadtools import lhc +from pyhdtoolkit.cpymadtools.constants import LHC_IR_BPM_REGEX # ----- Constants ----- # N_CPUS = cpu_count() +RNG = np.random.default_rng() def log_runtime_versions() -> None: @@ -45,6 +49,125 @@ def log_runtime_versions() -> None: logger.critical(f"Using: pyhdtoolkit {__version__} | cpymad {cpymad.__version__} | {mad.version}") +# ----- DataFrames Utilities ----- # + + +def split_complex_columns(df: pd.DataFrame, drop: bool = False) -> pd.DataFrame: + """ + .. versionadded:: 1.2.0 + + Find complex valued columns in *df* and split them into a column for the real and imaginary parts each. + New columns will be named like the existing ones, with ``_REAL`` or ``_IMAG`` appended. + + Args: + df (tfs.TfsDataFrame): the dataframe to split columns in. + drop (bool): whether to drop the original complex columns or not. Defaults to ``False``. + + Returns: + A new `~pandas.DataFrame` with the complex columns split into real and imaginary parts, and + the original complex columns potentially dropped. + + Exemple: + .. code-block:: python + + >>> df = split_complex_columns(df, drop=True) + """ + res = df.copy() + complex_columns = res.select_dtypes(include="complex").columns + for column in complex_columns: + res[f"{column}_REAL"] = np.real(res[column]) + res[f"{column}_IMAG"] = np.imag(res[column]) + if drop is True: + res = res.drop(columns=complex_columns) + return res + + +def add_noise_to_ir_bpms(df: pd.DataFrame, max_index: int, stdev: float, columns: Sequence[str] = None) -> pd.DataFrame: + """ + .. versionadded:: 1.2.0 + + Selects the appropriate IR BPMs according to the max index provided, and adds gaussian noise + to each relevant column with the provided standard deviation. + + .. important:: + The BPM names should be in the index of the dataframe. Selection is case-insensitive. + + Args: + df (pandas.DataFrame): the dataframe to add noise to. + max_index (int): the maximum index of the IR BPMs to add noise to. This number is + inclusive (i.e. the BPMs with this index will be selected). + stdev (float): the standard deviation of the gaussian noise to add. + columns (Sequence[str]): the columns to add noise to. If not given, all columns will be used. + Defaults to ``None``. + + Returns: + A new `~pandas.DataFrame` with the noise added to the wanted columns. + + Example: + .. code-block:: python + + >>> df = add_noise_to_ir_bpms(df, max_index=5, stdev=1e-6, columns=["DPSI"]) + """ + result = df.copy() + selected_bpms = LHC_IR_BPM_REGEX.format(max_index=max_index) + columns = columns or result.columns + + logger.debug(f"Adding noise to IR BPMs up to index {max_index} (included), with standard deviation {stdev}") + array_length = len(result[result.index.str.match(selected_bpms, case=False)]) + logger.trace(f"Number of affected BPMs: {array_length}") + + for column in columns: + logger.trace(f"Adding noise to column {column}") + result[column][result.index.str.match(selected_bpms, case=False)] += RNG.normal(0, stdev, array_length) + return result + + +def add_noise_to_arc_bpms( + df: pd.DataFrame, min_index: int, stdev: float, columns: Sequence[str] = None +) -> pd.DataFrame: + """ + .. versionadded:: 1.2.0 + + Selects the appropriate non-IR BPMs according to the min index provided, and adds gaussian noise + to each relevant column with the provided standard deviation. + + .. warning:: + This selects BPMs by ommission. It will find all IR BPMs up to *min_index* and will excluse + these from the selection. + + .. important:: + The BPM names should be in the index of the dataframe. Selection is case-insensitive. + + Args: + df (pandas.DataFrame): the dataframe to add noise to. + min_index (int): the minimum index of the BPMs to add noise to. See warning caveat right + above. This number is inclusive (i.e. the BPMs with this index will be selected). + stdev (float): the standard deviation of the gaussian noise to add. + columns (Sequence[str]): the columns to add noise to. If not given, all columns will be used. + Defaults to ``None``. + + Returns: + A new `~pandas.DataFrame` with the noise added to the wanted columns. + + Example: + .. code-block:: python + + >>> df = add_noise_to_arc_bpms(df, min_index=8, stdev=1e-6, columns=["DPSI"]) + """ + result = df.copy() + ir_bpms = LHC_IR_BPM_REGEX.format(max_index=min(1, min_index - 1)) # so that provided min_index is included + columns = columns or result.columns + + logger.debug(f"Adding noise to arc BPMs from index {min_index} (included), with standard deviation {stdev}") + array_length = len(result[~result.index.str.match(ir_bpms, case=False)]) # exclusive selection + logger.trace(f"Number of affected BPMs: {array_length}") + + for column in columns: + logger.trace(f"Adding noise to column {column}") + result[column][~result.index.str.match(ir_bpms, case=False)] += RNG.normal(0, stdev, array_length) + return result + + # ----- MAD-X Setup Utilities ----- # diff --git a/pyhdtoolkit/version.py b/pyhdtoolkit/version.py index 163e8a94..be8ab420 100644 --- a/pyhdtoolkit/version.py +++ b/pyhdtoolkit/version.py @@ -1,4 +1,4 @@ -VERSION = "1.1.1" +VERSION = "1.2.0" def version_info() -> str: diff --git a/pyproject.toml b/pyproject.toml index ef064916..6e6a1838 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ classifiers = [ ] dependencies = [ - "numpy >= 1.21", + "numpy >= 1.21,!=1.24", "pandas >= 1.4", "matplotlib >=3.3", "scipy >= 1.6", @@ -90,6 +90,7 @@ docs = [ "sphinx-panels < 1.0", "sphinx-gallery < 1.0", "sphinx-prompt >= 1.5", + "sphinx_codeautolink>=0.14", ] [project.urls] @@ -148,6 +149,7 @@ exclude = ''' | build | dist | tests/.*/setup.py + | acc-models-lhc )/ ''' diff --git a/tests/conftest.py b/tests/conftest.py index 45e6e8ec..5a63500f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,7 +26,7 @@ def _matched_base_lattice() -> Madx: with Madx(stdout=False) as madx: madx.input(BASE_LATTICE) match_tunes_and_chromaticities( - madx=madx, + madx, sequence="CAS3", q1_target=6.335, q2_target=6.29, @@ -105,7 +105,6 @@ def _cycled_lhc_sequences() -> Madx: lhc.re_cycle_sequence(madx, sequence="lhcb1", start="IP3") lhc.re_cycle_sequence(madx, sequence="lhcb2", start="IP3") lhc.make_lhc_beams(madx, energy=6500) - yield madx diff --git a/tests/inputs/utils/correct_user_tasks.pkl b/tests/inputs/utils/correct_user_tasks.pkl index 3c821e4d..5397667b 100644 Binary files a/tests/inputs/utils/correct_user_tasks.pkl and b/tests/inputs/utils/correct_user_tasks.pkl differ diff --git a/tests/test_cpymadtools/test_lhc.py b/tests/test_cpymadtools/test_lhc.py index f0e70cc7..bb4e7ba0 100644 --- a/tests/test_cpymadtools/test_lhc.py +++ b/tests/test_cpymadtools/test_lhc.py @@ -37,6 +37,7 @@ ) from pyhdtoolkit.cpymadtools.lhc import ( LHCSetup, + _coupling, add_markers_around_lhc_ip, apply_lhc_colinearity_knob, apply_lhc_colinearity_knob_delta, @@ -64,6 +65,8 @@ misalign_lhc_ir_quadrupoles, misalign_lhc_triplets, power_landau_octupoles, + prepare_lhc_run2, + prepare_lhc_run3, query_arc_correctors_powering, query_triplet_correctors_powering, re_cycle_sequence, @@ -183,7 +186,9 @@ def test_misalign_lhc_ir_quadrupoles(_non_matched_lhc_madx, ips, sides, quadrupo def test_misalign_lhc_ir_quadrupoles_specific_value(_non_matched_lhc_madx): madx = _non_matched_lhc_madx - misalign_lhc_ir_quadrupoles(madx, ips=[1, 5], quadrupoles=list(range(1, 11)), beam=1, sides="RL", dy="0.001") + misalign_lhc_ir_quadrupoles( + madx, ips=[1, 5], quadrupoles=list(range(1, 11)), beam=1, sides="RL", dy="0.001" + ) error_table = madx.table["ir_quads_errors"].dframe() assert all(error_table["dy"] == 0.001) @@ -523,7 +528,9 @@ def test_makethin_lhc(_matched_lhc_madx): madx = _matched_lhc_madx make_lhc_thin(madx, sequence="lhcb1", slicefactor=4) - tracks_dict = track_single_particle(madx, initial_coordinates=(1e-4, 0, 1e-4, 0, 0, 0), nturns=10, sequence="lhcb1") + tracks_dict = track_single_particle( + madx, initial_coordinates=(1e-4, 0, 1e-4, 0, 0, 0), nturns=10, sequence="lhcb1" + ) assert isinstance(tracks_dict, dict) tracks = tracks_dict["observation_point_1"] assert len(tracks) == 11 # nturns + 1 because $start coordinates also given by MAD-X @@ -581,7 +588,9 @@ def test_resetting_lhc_bump_flags(_bare_lhc_madx): def test_vary_independent_ir_quads(_non_matched_lhc_madx): # still need to find how to test MAD-X has done this, but don't think we can test just a VARY madx = _non_matched_lhc_madx - vary_independent_ir_quadrupoles(madx, quad_numbers=[4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ip=1, sides=("r", "l")) + vary_independent_ir_quadrupoles( + madx, quad_numbers=[4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ip=1, sides=("r", "l") + ) def test_vary_independent_ir_quads_raises_on_wrong_side(_non_matched_lhc_madx, caplog): @@ -691,7 +700,9 @@ def test_k_modulation(_non_matched_lhc_madx, _reference_kmodulation): assert all(var == 0 for var in results.ERRTUNEY) reference = tfs.read(_reference_kmodulation) - assert_frame_equal(results.convert_dtypes(), reference.convert_dtypes()) # avoid dtype comparison error on 0 cols + assert_frame_equal( + results.convert_dtypes(), reference.convert_dtypes() + ) # avoid dtype comparison error on 0 cols @pytest.mark.parametrize("ir", [1, 2, 5, 8]) @@ -716,11 +727,11 @@ def test_carry_colinearity_knob_over(_non_matched_lhc_madx, ir): @pytest.mark.parametrize("telesqueeze", [True, False]) -def test_correct_lhc_global_coupling(_non_matched_lhc_madx, telesqueeze): +def test_correct_lhc_global_coupling_routine(_non_matched_lhc_madx, telesqueeze): madx = _non_matched_lhc_madx madx.globals["CMRS.b1"] = 0.001 madx.globals["CMIS.b1"] = 0.001 - madx.command.twiss(chrom=True) + madx.command.twiss() assert madx.table.summ.dqmin[0] > 0 correct_lhc_global_coupling(madx, telescopic_squeeze=telesqueeze) @@ -728,6 +739,19 @@ def test_correct_lhc_global_coupling(_non_matched_lhc_madx, telesqueeze): assert math.isclose(madx.table.summ.dqmin[0], 0, abs_tol=1e-7) +@pytest.mark.parametrize("telesqueeze", [True, False]) +def test_correct_lhc_global_coupling_from_coupling_module(_non_matched_lhc_madx, telesqueeze): + madx = _non_matched_lhc_madx + madx.globals["CMRS.b1"] = 0.001 + madx.globals["CMIS.b1"] = 0.001 + madx.command.twiss() + assert madx.table.summ.dqmin[0] > 0 + + _coupling.correct_lhc_global_coupling(madx, telescopic_squeeze=telesqueeze) + assert madx.table.summ.dqmin[0] >= 0 + assert math.isclose(madx.table.summ.dqmin[0], 0, abs_tol=1e-7) + + @pytest.mark.parametrize("ip", [1, 5]) def test_get_ip_beam_sizes(_non_matched_lhc_madx, ip): madx = _non_matched_lhc_madx @@ -765,21 +789,64 @@ def test_get_irs_twiss(ir, _matched_lhc_madx): # Only runs if the acc-models-lhc is accessible at root level @pytest.mark.skipif(not (TESTS_DIR.parent / "acc-models-lhc").is_dir(), reason="acc-models-lhc not found") -def test_lhc_run3_setup(): - with LHCSetup(run=3, opticsfile="R2022a_A30cmC30cmA10mL200cm.madx") as madx: +@pytest.mark.parametrize("slicefactor", [None, 4]) +def test_lhc_run3_setup_context_manager(slicefactor): + with LHCSetup(run=3, opticsfile="R2022a_A30cmC30cmA10mL200cm.madx", slicefactor=slicefactor) as madx: + for ip in [1, 2, 5, 8]: + for beam in [1, 2]: + for plane in ["x", "y"]: + assert madx.globals[f"tar_on_{plane}ip{ip:d}b{beam:d}"] is not None + + +@pytest.mark.skipif(not (TESTS_DIR.parent / "acc-models-lhc").is_dir(), reason="acc-models-lhc not found") +def test_lhc_run3_setup_context_manager_fullpath_to_opticsfile(): + with LHCSetup(opticsfile="acc-models-lhc/operation/optics/R2022a_A30cmC30cmA10mL200cm.madx") as madx: for ip in [1, 2, 5, 8]: for beam in [1, 2]: for plane in ["x", "y"]: assert madx.globals[f"tar_on_{plane}ip{ip:d}b{beam:d}"] is not None -def test_lhc_run2_setup(_proton_opticsfile): +@pytest.mark.skipif(not (TESTS_DIR.parent / "acc-models-lhc").is_dir(), reason="acc-models-lhc not found") +def test_lhc_run3_setup_context_manager_raises_on_wrong_b4_conditions(): + with pytest.raises(ValueError): # using b4 with beam1 setup crashes + with LHCSetup(opticsfile="R2022a_A30cmC30cmA10mL200cm.madx", beam=1, use_b4=True) as madx: + pass + + +@pytest.mark.skipif(not (TESTS_DIR.parent / "acc-models-lhc").is_dir(), reason="acc-models-lhc not found") +def test_lhc_run3_setup_context_manager_raises_on_wrong_run_value(): + with pytest.raises(NotImplementedError): # using b4 with beam1 setup crashes + with LHCSetup(run=1, opticsfile="R2022a_A30cmC30cmA10mL200cm.madx") as madx: + pass + + +@pytest.mark.skipif(not (TESTS_DIR.parent / "acc-models-lhc").is_dir(), reason="acc-models-lhc not found") +def test_lhc_run3_setup_raises_on_wrong_b4_conditions(_proton_opticsfile): + with pytest.raises(ValueError): # using b4 with beam1 setup crashes + madx = prepare_lhc_run3(opticsfile="R2022a_A30cmC30cmA10mL200cm.madx", beam=1, use_b4=True) + + +# ------------------- Run2 Setup Tests ------------------- # + +@pytest.mark.parametrize("slicefactor", [None, 4]) +def test_lhc_run2_setup_context_manager(_proton_opticsfile, slicefactor): # If works properly we have beams with lhc seq and old 30cm optics # And there the optics are matched for specific tune values - with LHCSetup(run=2, opticsfile=_proton_opticsfile) as madx: + with LHCSetup(run=2, opticsfile=_proton_opticsfile, slicefactor=slicefactor) as madx: madx.command.twiss() - assert math.isclose(madx.table.summ.q1[0], 62.31, abs_tol=1e-6) - assert math.isclose(madx.table.summ.q2[0], 60.32, abs_tol=1e-6) + assert math.isclose(madx.table.summ.q1[0], 62.31, abs_tol=1e-4, rel_tol=1e-2) + assert math.isclose(madx.table.summ.q2[0], 60.32, abs_tol=1e-4, rel_tol=1e-2) + + +def test_lhc_run2_setup_raises_on_wrong_b4_conditions(_proton_opticsfile): + with pytest.raises(ValueError): # using b4 with beam1 setup crashes + madx = prepare_lhc_run2(opticsfile=_proton_opticsfile, beam=1, use_b4=True) + + +def test_lhc_run2_setup_raises_on_absent_sequence_file(): + with pytest.raises(ValueError): # will not find the sequence file from this opticsfile value + madx = prepare_lhc_run2(opticsfile="some/place/here.madx") # ---------------------- Private Utilities ---------------------- # diff --git a/tests/test_cpymadtools/test_matching.py b/tests/test_cpymadtools/test_matching.py index 141cfcee..daa0ba6f 100644 --- a/tests/test_cpymadtools/test_matching.py +++ b/tests/test_cpymadtools/test_matching.py @@ -26,7 +26,7 @@ def test_tune_and_chroma_matching(q1_target, q2_target, dq1_target, dq2_target, assert madx.table.summ.dq2[0] != dq2_target match_tunes_and_chromaticities( - madx=madx, + madx, sequence="CAS3", q1_target=q1_target, q2_target=q2_target, @@ -64,7 +64,7 @@ def test_tune_only_matching(q1_target, q2_target, telescopic_squeeze): assert madx.table.summ.q2[0] != q2_target match_tunes_and_chromaticities( - madx=madx, + madx, sequence="CAS3", q1_target=q1_target, q2_target=q2_target, @@ -86,7 +86,7 @@ def test_tune_matching_wrapper(q1_target, q2_target, telescopic_squeeze): assert madx.table.summ.q2[0] != q2_target match_tunes( - madx=madx, + madx, sequence="CAS3", q1_target=q1_target, q2_target=q2_target, @@ -108,7 +108,7 @@ def test_chroma_only_matching(dq1_target, dq2_target, telescopic_squeeze): assert madx.table.summ.dq2[0] != dq2_target match_tunes_and_chromaticities( - madx=madx, + madx, sequence="CAS3", dq1_target=dq1_target, dq2_target=dq2_target, @@ -130,7 +130,7 @@ def test_chroma_matching_wrapper(dq1_target, dq2_target, telescopic_squeeze): assert madx.table.summ.dq2[0] != dq2_target match_chromaticities( - madx=madx, + madx, sequence="CAS3", dq1_target=dq1_target, dq2_target=dq2_target, diff --git a/tests/test_plotting/baseline/test_confidence_ellipse_several_stds.png b/tests/test_plotting/baseline/test_confidence_ellipse_several_stds.png new file mode 100644 index 00000000..9e4d8d0e Binary files /dev/null and b/tests/test_plotting/baseline/test_confidence_ellipse_several_stds.png differ diff --git a/tests/test_plotting/baseline/test_confidence_ellipse_subplots.png b/tests/test_plotting/baseline/test_confidence_ellipse_subplots.png new file mode 100644 index 00000000..5db2c644 Binary files /dev/null and b/tests/test_plotting/baseline/test_confidence_ellipse_subplots.png differ diff --git a/tests/test_plotting/baseline/test_ip_locations.png b/tests/test_plotting/baseline/test_ip_locations.png index 628e4aa5..e037023e 100644 Binary files a/tests/test_plotting/baseline/test_ip_locations.png and b/tests/test_plotting/baseline/test_ip_locations.png differ diff --git a/tests/test_plotting/baseline/test_ip_locations_with_xlimits.png b/tests/test_plotting/baseline/test_ip_locations_with_xlimits.png index 2502b437..bbb56b9d 100644 Binary files a/tests/test_plotting/baseline/test_ip_locations_with_xlimits.png and b/tests/test_plotting/baseline/test_ip_locations_with_xlimits.png differ diff --git a/tests/test_plotting/baseline/test_plot_aperture_cell_injection.png b/tests/test_plotting/baseline/test_plot_aperture_cell_injection.png index 8bae8530..1ab28811 100644 Binary files a/tests/test_plotting/baseline/test_plot_aperture_cell_injection.png and b/tests/test_plotting/baseline/test_plot_aperture_cell_injection.png differ diff --git a/tests/test_plotting/baseline/test_plot_aperture_ir5_collision.png b/tests/test_plotting/baseline/test_plot_aperture_ir5_collision.png index caace3dd..6d912e21 100644 Binary files a/tests/test_plotting/baseline/test_plot_aperture_ir5_collision.png and b/tests/test_plotting/baseline/test_plot_aperture_ir5_collision.png differ diff --git a/tests/test_plotting/baseline/test_plot_crossing_schemes_ip15.png b/tests/test_plotting/baseline/test_plot_crossing_schemes_ip15.png index 520f06f0..70eef3c5 100644 Binary files a/tests/test_plotting/baseline/test_plot_crossing_schemes_ip15.png and b/tests/test_plotting/baseline/test_plot_crossing_schemes_ip15.png differ diff --git a/tests/test_plotting/baseline/test_plot_crossing_schemes_ip28_no_highlight.png b/tests/test_plotting/baseline/test_plot_crossing_schemes_ip28_no_highlight.png index 31bddf0f..55f70e44 100644 Binary files a/tests/test_plotting/baseline/test_plot_crossing_schemes_ip28_no_highlight.png and b/tests/test_plotting/baseline/test_plot_crossing_schemes_ip28_no_highlight.png differ diff --git a/tests/test_plotting/baseline/test_plot_envelope_combined.png b/tests/test_plotting/baseline/test_plot_envelope_combined.png index 31735b3b..4e8fb0f2 100644 Binary files a/tests/test_plotting/baseline/test_plot_envelope_combined.png and b/tests/test_plotting/baseline/test_plot_envelope_combined.png differ diff --git a/tests/test_plotting/baseline/test_plot_envelope_horizontal.png b/tests/test_plotting/baseline/test_plot_envelope_horizontal.png index 9b259e74..5d0270e7 100644 Binary files a/tests/test_plotting/baseline/test_plot_envelope_horizontal.png and b/tests/test_plotting/baseline/test_plot_envelope_horizontal.png differ diff --git a/tests/test_plotting/baseline/test_plot_envelope_vertical.png b/tests/test_plotting/baseline/test_plot_envelope_vertical.png index 94a46297..ab095096 100644 Binary files a/tests/test_plotting/baseline/test_plot_envelope_vertical.png and b/tests/test_plotting/baseline/test_plot_envelope_vertical.png differ diff --git a/tests/test_plotting/baseline/test_plot_envelope_with_xlimits.png b/tests/test_plotting/baseline/test_plot_envelope_with_xlimits.png new file mode 100644 index 00000000..3bfd8c2c Binary files /dev/null and b/tests/test_plotting/baseline/test_plot_envelope_with_xlimits.png differ diff --git a/tests/test_plotting/baseline/test_plot_latwiss_single_value_ylimts_inputs.png b/tests/test_plotting/baseline/test_plot_latwiss_single_value_ylimts_inputs.png new file mode 100644 index 00000000..5a6f6642 Binary files /dev/null and b/tests/test_plotting/baseline/test_plot_latwiss_single_value_ylimts_inputs.png differ diff --git a/tests/test_plotting/baseline/test_plot_latwiss_with_octupoles.png b/tests/test_plotting/baseline/test_plot_latwiss_with_octupoles.png new file mode 100644 index 00000000..83eceb84 Binary files /dev/null and b/tests/test_plotting/baseline/test_plot_latwiss_with_octupoles.png differ diff --git a/tests/test_plotting/baseline/test_plot_physical_apertures_ir5_collision_hozirontal.png b/tests/test_plotting/baseline/test_plot_physical_apertures_ir5_collision_hozirontal.png new file mode 100644 index 00000000..aa1a2e85 Binary files /dev/null and b/tests/test_plotting/baseline/test_plot_physical_apertures_ir5_collision_hozirontal.png differ diff --git a/tests/test_plotting/baseline/test_plot_physical_apertures_ir5_collision_vertical.png b/tests/test_plotting/baseline/test_plot_physical_apertures_ir5_collision_vertical.png new file mode 100644 index 00000000..ca2407bf Binary files /dev/null and b/tests/test_plotting/baseline/test_plot_physical_apertures_ir5_collision_vertical.png differ diff --git a/tests/test_plotting/baseline/test_plot_stay_clear.png b/tests/test_plotting/baseline/test_plot_stay_clear.png deleted file mode 100644 index 4694c602..00000000 Binary files a/tests/test_plotting/baseline/test_plot_stay_clear.png and /dev/null differ diff --git a/tests/test_plotting/test_aperture.py b/tests/test_plotting/test_aperture.py index e47fdb0d..cd1f3026 100644 --- a/tests/test_plotting/test_aperture.py +++ b/tests/test_plotting/test_aperture.py @@ -2,7 +2,9 @@ import matplotlib.pyplot as plt import pytest -from pyhdtoolkit.plotting.aperture import plot_aperture +from cpymad.madx import Madx + +from pyhdtoolkit.plotting.aperture import plot_aperture, plot_physical_apertures # Forcing non-interactive Agg backend so rendering is done similarly across platforms during tests matplotlib.use("Agg") @@ -48,3 +50,46 @@ def test_plot_aperture_ir5_collision(_collision_aperture_tolerances_lhc_madx): color="brown", ) return figure + + +@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +def test_plot_physical_apertures_ir5_collision_hozirontal(_collision_aperture_tolerances_lhc_madx): + + madx = _collision_aperture_tolerances_lhc_madx + madx.command.twiss() + twiss_df = madx.table.twiss.dframe() + ip5s = twiss_df.s[twiss_df.name.str.contains("ip5")].to_numpy()[0] + + figure = plt.figure(figsize=(18, 11)) + limits = (ip5s - 350, ip5s + 350) + plot_physical_apertures(madx, plane="x", xlimits=limits, scale=1e2) + + plt.ylim(-5, 5) + plt.ylabel("X [cm]") + plt.xlabel("S [m]") + return figure + + +@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +def test_plot_physical_apertures_ir5_collision_vertical(_collision_aperture_tolerances_lhc_madx): + + madx = _collision_aperture_tolerances_lhc_madx + madx.command.twiss() + twiss_df = madx.table.twiss.dframe() + ip5s = twiss_df.s[twiss_df.name.str.contains("ip5")].to_numpy()[0] + + figure = plt.figure(figsize=(18, 11)) + limits = (ip5s - 350, ip5s + 350) + plot_physical_apertures(madx, plane="y", xlimits=limits, scale=1e3) + + plt.ylim(-50, 50) + plt.ylabel("Y [mm]") + plt.xlabel("S [m]") + return figure + + +def test_plot_physical_apertures_raises_on_wrong_plane(): + madx = Madx(stdout=False) + + with pytest.raises(ValueError): + plot_physical_apertures(madx, plane="invalid") diff --git a/tests/test_plotting/test_crossing.py b/tests/test_plotting/test_crossing.py index afde810b..d37d6eb4 100644 --- a/tests/test_plotting/test_crossing.py +++ b/tests/test_plotting/test_crossing.py @@ -8,7 +8,7 @@ matplotlib.use("Agg") -@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +@pytest.mark.mpl_image_compare(tolerance=35, style="default", savefig_kwargs={"dpi": 200}) def test_plot_crossing_schemes_ip15(_cycled_lhc_sequences): madx = _cycled_lhc_sequences @@ -17,7 +17,7 @@ def test_plot_crossing_schemes_ip15(_cycled_lhc_sequences): return figure -@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +@pytest.mark.mpl_image_compare(tolerance=35, style="default", savefig_kwargs={"dpi": 200}) def test_plot_crossing_schemes_ip28_no_highlight(_cycled_lhc_sequences): madx = _cycled_lhc_sequences diff --git a/tests/test_plotting/test_envelope.py b/tests/test_plotting/test_envelope.py index 10b03ba5..d128a4e0 100644 --- a/tests/test_plotting/test_envelope.py +++ b/tests/test_plotting/test_envelope.py @@ -6,8 +6,7 @@ from cpymad.madx import Madx -from pyhdtoolkit.optics.beam import compute_beam_parameters -from pyhdtoolkit.plotting.envelope import plot_envelope, plot_stay_clear +from pyhdtoolkit.plotting.envelope import _interpolate_madx, plot_beam_envelope # Forcing non-interactive Agg backend so rendering is done similarly across platforms during tests matplotlib.use("Agg") @@ -18,78 +17,70 @@ def test_plot_enveloppe_raises_on_wrong_plane(): - beam_injection = compute_beam_parameters(1.9, en_x_m=5e-6, en_y_m=5e-6, deltap_p=2e-3) madx = Madx(stdout=False) with pytest.raises(ValueError): - plot_envelope(madx, beam_injection, plane="invalid") - + plot_beam_envelope(madx, "lhcb1", plane="invalid") @pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) -def test_plot_envelope_horizontal(): - beam_injection = compute_beam_parameters(1.9, en_x_m=5e-6, en_y_m=5e-6, deltap_p=2e-3) - title = f"Horizontal Aperture at {beam_injection.pc_GeV} GeV/c" - - madx = Madx(stdout=False) - madx.call(str(GUIDO_LATTICE)) +def test_plot_envelope_with_xlimits(): + with Madx(stdout=False) as madx: + madx.call(str(GUIDO_LATTICE)) + _interpolate_madx(madx) # let's interpolate for a smoother plot - figure = plt.figure(figsize=(16, 9)) - plot_envelope(madx, beam_injection, ylimits=(-0.12, 0.12), title=title) - return figure + figure, ax = plt.subplots(figsize=(12, 7)) + # Let's plot 1 sigma and 2.5 sigma enveloppes + plot_beam_envelope(madx, "cas19", "x", nsigma=1, xlimits=(200, 300), centre=True) + plot_beam_envelope(madx, "cas19", "horizontal", nsigma=2.5, xlimits=(200, 300), centre=True) + plt.setp(ax, xlabel="S [m]", ylabel="X [m]") + plt.legend() + return figure @pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) -def test_plot_envelope_vertical(): - beam_injection = compute_beam_parameters(1.9, en_x_m=5e-6, en_y_m=5e-6, deltap_p=2e-3) - title = f"Vertical Aperture at {beam_injection.pc_GeV} GeV/c" - - madx = Madx(stdout=False) - madx.call(str(GUIDO_LATTICE)) +def test_plot_envelope_horizontal(): + with Madx(stdout=False) as madx: + madx.call(str(GUIDO_LATTICE)) - figure = plt.figure(figsize=(16, 9)) - plot_envelope(madx, beam_injection, plane="vertical", ylimits=(-0.12, 0.12), title=title) - return figure + figure, ax = plt.subplots(figsize=(12, 7)) + # Let's plot 1 sigma and 2.5 sigma enveloppes + plot_beam_envelope(madx, "cas19", "x", nsigma=1) + plot_beam_envelope(madx, "cas19", "horizontal", nsigma=2.5) + plt.setp(ax, xlabel="S [m]", ylabel="X [m]") + plt.legend() + return figure @pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) -def test_plot_stay_clear(): - beam_injection = compute_beam_parameters(1.9, en_x_m=5e-6, en_y_m=5e-6, deltap_p=2e-3) - title = f"Stay-Clear at {beam_injection.pc_GeV} GeV/c" - l_cell = 20 # only plot first cell for this one, tests xlimits param - - madx = Madx(stdout=False) - madx.call(str(GUIDO_LATTICE)) +def test_plot_envelope_vertical(): + with Madx(stdout=False) as madx: + madx.call(str(GUIDO_LATTICE)) - figure = plt.figure(figsize=(16, 9)) - plot_stay_clear( - madx, - beam_injection, - xlimits=(0, l_cell), - title=title, - ) - return figure + figure, ax = plt.subplots(figsize=(12, 7)) + # Let's plot 1 sigma and 2.5 sigma enveloppes + plot_beam_envelope(madx, "cas19", "y", nsigma=1, scale=1e3) + plot_beam_envelope(madx, "cas19", "vertical", nsigma=2.5, scale=1e3) + plt.setp(ax, xlabel="S [m]", ylabel="Y [mm]") + plt.legend() + return figure @pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) def test_plot_envelope_combined(): - beam_injection = compute_beam_parameters(1.9, en_x_m=5e-6, en_y_m=5e-6, deltap_p=2e-3) - madx = Madx(stdout=False) - madx.call(str(GUIDO_LATTICE)) - figure, axes = plt.subplots(3, 1, figsize=(18, 20)) - plot_envelope( - madx, - beam_injection, - ylimits=(-0.12, 0.12), - title=f"Horizontal aperture at {beam_injection.pc_GeV} GeV/c", - axis=axes[0], - ) - plot_envelope( - madx, - beam_injection, - ylimits=(-0.12, 0.12), - plane="vertical", - title=f"Vertical aperture at {beam_injection.pc_GeV} GeV/c", - axis=axes[1], - ) - plot_stay_clear(madx, beam_injection, title=f"Stay-Clear at {beam_injection.pc_GeV} GeV/c", axis=axes[2]) - return figure + with Madx(stdout=False) as madx: + madx.call(str(GUIDO_LATTICE)) + figure, axes = plt.subplots(2, 1, sharex=True, figsize=(12, 10)) + + # First let's plot 1 sigma and 2.5 sigma horizontal enveloppes + plot_beam_envelope(madx, "cas19", "x", nsigma=1, scale=1e3, ax=axes[0]) + plot_beam_envelope(madx, "cas19", "horizontal", nsigma=2.5, scale=1e3, ax=axes[0]) + plt.setp(axes[0], ylabel="X [mm]") + axes[0].legend() + + # Then plot 1 sigma and 2.5 sigma vertical enveloppes + plot_beam_envelope(madx, "cas19", "y", nsigma=1, scale=1e3, ax=axes[1]) + plot_beam_envelope(madx, "cas19", "vertical", nsigma=2.5, scale=1e3, ax=axes[1]) + plt.setp(axes[1], xlabel="S [m]", ylabel="Y [mm]") + axes[1].legend() + + return figure diff --git a/tests/test_plotting/test_lattice.py b/tests/test_plotting/test_lattice.py index 13c330ff..99d5cb65 100644 --- a/tests/test_plotting/test_lattice.py +++ b/tests/test_plotting/test_lattice.py @@ -16,6 +16,7 @@ CURRENT_DIR = pathlib.Path(__file__).parent INPUTS_DIR = CURRENT_DIR.parent / "inputs" BASE_LATTICE = LatticeGenerator.generate_base_cas_lattice() +OCT_LATTICE = LatticeGenerator.generate_oneoct_cas_lattice() ELETTRA_LATTICE = INPUTS_DIR / "madx" / "elettra2_v15_VADER_2.3T.madx" ELETTRA_OPTICS = INPUTS_DIR / "madx" / "optics_elettra2_v15_VADER_2.3T.madx" @@ -31,7 +32,7 @@ def test_plot_latwiss(): figure = plt.figure(figsize=(18, 11)) plot_latwiss( - madx=madx, + madx, title="Project 3 Base Lattice", xlimits=(-50, 1_050), beta_ylim=(5, 75), @@ -41,6 +42,64 @@ def test_plot_latwiss(): return figure +@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +def test_plot_latwiss_single_value_ylimts_inputs(): + """Using my CAS 19 project's base lattice.""" + with Madx(stdout=False) as madx: + madx.input(BASE_LATTICE) + match_tunes_and_chromaticities( + madx, None, "CAS3", 6.335, 6.29, 100, 100, varied_knobs=["kqf", "kqd", "ksf", "ksd"] + ) + + figure = plt.figure(figsize=(18, 11)) + plot_latwiss( + madx, + title="Project 3 Base Lattice", + xlimits=(-50, 1_050), + beta_ylim=(5, 75), + k1l_lim=8e-2, + k2l_lim=0.35, + plot_bpms=True, + ) + return figure + + +@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +def test_plot_latwiss_with_octupoles(): + """Using my CAS 19 project's base lattice.""" + with Madx(stdout=False) as madx: + madx.input(OCT_LATTICE) + madx.input("koct = -15;") + match_tunes_and_chromaticities( + madx, None, "CAS3", 6.335, 6.29, 100, 100, varied_knobs=["kqf", "kqd", "ksf", "ksd"] + ) + + figure = plt.figure(figsize=(18, 11)) + plot_latwiss( + madx, + title="Project 3 Octupole Lattice", + xlimits=(-50, 1_050), + beta_ylim=(5, 75), + k1l_lim=-1e-1, # tests that negative values are handled correctly too + k2l_lim=0.6, + k3l_lim=25, + lw=2, + plot_bpms=True, + ) + plt.tight_layout() + return figure + + +def test_plot_layout_raises_on_wrong_limits_type(): + """Using my CAS 19 project's base lattice.""" + with Madx(stdout=False) as madx: + madx.input(BASE_LATTICE) + plt.figure(figsize=(18, 11)) + + with pytest.raises(TypeError): + plot_latwiss(madx, k1l_lim=[8e-2]) + + @pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) def test_plot_latwiss_with_dipole_k1(): """Using ELETTRA2.0 lattice provided by Axel.""" @@ -60,7 +119,7 @@ def test_plot_latwiss_with_dipole_k1(): figure = plt.figure(figsize=(18, 11)) plot_latwiss( - madx=madx, + madx, title="Elettra Cell", xlimits=(x0, x1), k0l_lim=(-7e-2, 7e-2), @@ -79,7 +138,7 @@ def test_plot_machine_survey_with_elements(): madx.input(BASE_LATTICE) figure = plt.figure(figsize=(16, 11)) - plot_machine_survey(madx=madx, show_elements=True, high_orders=True) + plot_machine_survey(madx, show_elements=True, high_orders=True) return figure @@ -89,5 +148,5 @@ def test_plot_machine_survey_without_elements(): with Madx(stdout=False) as madx: madx.input(BASE_LATTICE) figure = plt.figure(figsize=(16, 11)) - plot_machine_survey(madx=madx, show_elements=False, high_orders=True) + plot_machine_survey(madx, show_elements=False, high_orders=True) return figure diff --git a/tests/test_plotting/test_plotting_utils.py b/tests/test_plotting/test_plotting_utils.py index 120f8997..85d3e3bb 100644 --- a/tests/test_plotting/test_plotting_utils.py +++ b/tests/test_plotting/test_plotting_utils.py @@ -2,12 +2,14 @@ import pathlib import matplotlib.pyplot as plt +import numpy as np import pytest import tfs from pyhdtoolkit.plotting.utils import ( _determine_default_sbs_coupling_ylabel, _determine_default_sbs_phase_ylabel, + draw_confidence_ellipse, draw_ip_locations, find_ip_s_from_segment_start, get_lhc_ips_positions, @@ -98,6 +100,67 @@ def test_ip_locations_with_xlimits(_non_matched_lhc_madx): return figure +@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +def test_confidence_ellipse_subplots(): + """Confidence ellipse on three correlated datasets in subplots.""" + np.random.seed(0) + PARAMETERS = { + "Positive correlation": [[0.85, 0.35], [0.15, -0.65]], + "Negative correlation": [[0.9, -0.4], [0.1, -0.6]], + "Weak correlation": [[1, 0], [0, 1]], + } + mu = 2, 4 + scale = 3, 5 + + figure, axs = plt.subplots(1, 3, figsize=(9, 3)) + for ax, (title, dependency) in zip(axs, PARAMETERS.items()): + x, y = get_correlated_dataset(800, dependency, mu, scale) + ax.scatter(x, y, s=0.5) + ax.axvline(c="grey", lw=1) + ax.axhline(c="grey", lw=1) + draw_confidence_ellipse(x, y, ax=ax, n_std=2.5, edgecolor="red") + ax.scatter(mu[0], mu[1], c="red", s=3) + ax.set_title(title) + ax.set_xlim(-8, 12) + ax.set_ylim(-15, 20) + + return figure + + +@pytest.mark.mpl_image_compare(tolerance=20, style="default", savefig_kwargs={"dpi": 200}) +def test_confidence_ellipse_several_stds(): + np.random.seed(0) + figure, ax_nstd = plt.subplots(figsize=(6, 6)) + dependency_nstd = [[0.8, 0.75], [-0.2, 0.35]] + mu = 0, 0 + scale = 8, 5 + + ax_nstd.axvline(c="grey", lw=1) + ax_nstd.axhline(c="grey", lw=1) + x, y = get_correlated_dataset(500, dependency_nstd, mu, scale) + ax_nstd.scatter(x, y, s=0.5) + draw_confidence_ellipse(x, y, ax=ax_nstd, n_std=1, label=r"$1\sigma$", edgecolor="firebrick") + draw_confidence_ellipse(x, y, ax=ax_nstd, n_std=2, label=r"$2\sigma$", edgecolor="fuchsia", linestyle="--") + draw_confidence_ellipse(x, y, ax=ax_nstd, n_std=3, label=r"$3\sigma$", edgecolor="blue", linestyle=":") + ax_nstd.scatter(mu[0], mu[1], c="red", s=3) + ax_nstd.set_title("Different standard deviations") + ax_nstd.legend() + return figure + + +# ----- Helpers ----- # + + +def get_correlated_dataset(n, dependency, mu, scale): + """This one is from the matplotlib doc.""" + latent = np.random.randn(n, 2) + dependent = latent.dot(dependency) + scaled = dependent * scale + scaled_with_offset = scaled + mu + # return x and y of the new, correlated dataset + return scaled_with_offset[:, 0], scaled_with_offset[:, 1] + + # ----- Fixtures ----- # diff --git a/tests/test_utils.py b/tests/test_utils.py index 510025c7..48e509b4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -11,9 +11,13 @@ import matplotlib import matplotlib.pyplot as plt +import numpy as np +import pandas as pd import pytest +import tfs from loguru import logger +from numpy.testing import assert_array_equal from rich.table import Table from pyhdtoolkit.utils import _misc, deprecated, logging @@ -662,6 +666,33 @@ def test_colin_balance_applies(self, _non_matched_lhc_madx): assert madx.globals[f"kqsx3.{side}{ip}"] != 0 assert "ir_quads_errors" in madx.table.keys() + @pytest.mark.parametrize("drop", [True, False]) + def test_complex_columns_split(self, _complex_columns_df, drop): + original = _complex_columns_df + split = _misc.split_complex_columns(original, drop) + + for col in original.columns: + assert f"{col}_REAL" in split.columns + assert f"{col}_IMAG" in split.columns + + assert_array_equal(np.real(original[col]), split[f"{col}_REAL"].to_numpy()) + assert_array_equal(np.imag(original[col]), split[f"{col}_IMAG"].to_numpy()) + + if drop is True: + assert col not in split.columns + + def test_bpm_noise_addition(self, _rdts_df): + original = tfs.read(_rdts_df) + original = original.loc[original.index.str.contains("bpm", case=False)] + + ir_noisy = _misc.add_noise_to_ir_bpms(original, max_index=5, stdev=1e-2) + with pytest.raises(AssertionError): + pd.testing.assert_frame_equal(original, ir_noisy) + + arc_noisy = _misc.add_noise_to_arc_bpms(original, min_index=5, stdev=1e-2) + with pytest.raises(AssertionError): + pd.testing.assert_frame_equal(original, arc_noisy) + # ----- Fixtures ----- # @@ -707,3 +738,14 @@ def _correct_cluster_summary() -> ClusterSummary: pickle_file_path = INPUTS_DIR / "utils" / "correct_cluster_summary.pkl" with pickle_file_path.open("rb") as file: return pickle.load(file) + + +@pytest.fixture() +def _complex_columns_df() -> pd.DataFrame: + array = np.random.rand(50, 5) + 1j * np.random.rand(50, 5) + return pd.DataFrame(data=array, columns=["A", "B", "C", "D", "E"]) + + +@pytest.fixture() +def _rdts_df() -> pathlib.Path: + return INPUTS_DIR / "cpymadtools" / "lhc_coupling_bump.tfs"