xcoll 0.3.6__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xcoll might be problematic. Click here for more details.
- xcoll/__init__.py +13 -4
- xcoll/beam_elements/__init__.py +14 -6
- xcoll/beam_elements/absorber.py +41 -7
- xcoll/beam_elements/base.py +1202 -247
- xcoll/beam_elements/blowup.py +198 -0
- xcoll/beam_elements/elements_src/black_absorber.h +136 -0
- xcoll/beam_elements/elements_src/black_crystal.h +129 -0
- xcoll/beam_elements/elements_src/blowup.h +42 -0
- xcoll/beam_elements/elements_src/emittance_monitor.h +109 -0
- xcoll/beam_elements/{collimators_src → elements_src}/everest_block.h +59 -30
- xcoll/beam_elements/elements_src/everest_collimator.h +237 -0
- xcoll/beam_elements/elements_src/everest_crystal.h +280 -0
- xcoll/beam_elements/everest.py +65 -119
- xcoll/beam_elements/monitor.py +428 -0
- xcoll/colldb.py +276 -747
- xcoll/general.py +5 -5
- xcoll/headers/checks.h +1 -1
- xcoll/headers/particle_states.h +2 -2
- xcoll/initial_distribution.py +207 -0
- xcoll/install.py +179 -0
- xcoll/interaction_record/__init__.py +1 -0
- xcoll/interaction_record/interaction_record.py +298 -0
- xcoll/interaction_record/interaction_record_src/interaction_record.h +98 -0
- xcoll/{impacts → interaction_record}/interaction_types.py +11 -4
- xcoll/line_tools.py +82 -0
- xcoll/lossmap.py +219 -0
- xcoll/manager.py +2 -937
- xcoll/rf_sweep.py +1 -1
- xcoll/scattering_routines/everest/amorphous.h +232 -0
- xcoll/scattering_routines/everest/channeling.h +240 -0
- xcoll/scattering_routines/everest/crystal_parameters.h +137 -0
- xcoll/scattering_routines/everest/everest.h +11 -30
- xcoll/scattering_routines/everest/everest.py +13 -10
- xcoll/scattering_routines/everest/jaw.h +28 -197
- xcoll/scattering_routines/everest/materials.py +37 -15
- xcoll/scattering_routines/everest/multiple_coulomb_scattering.h +31 -10
- xcoll/scattering_routines/everest/nuclear_interaction.h +86 -0
- xcoll/scattering_routines/everest/properties.h +6 -1
- xcoll/scattering_routines/fluka/flukaio/lib/libFlukaIO64.a +0 -0
- xcoll/scattering_routines/geant4/collimasim/.git +1 -0
- xcoll/scattering_routines/geant4/collimasim/.gitignore +12 -0
- xcoll/scattering_routines/geant4/collimasim/.gitmodules +3 -0
- xcoll/scattering_routines/geant4/collimasim/CMakeLists.txt +26 -0
- xcoll/scattering_routines/geant4/collimasim/README.md +21 -0
- xcoll/scattering_routines/geant4/collimasim/docs/Makefile +20 -0
- xcoll/scattering_routines/geant4/collimasim/docs/make.bat +35 -0
- xcoll/scattering_routines/geant4/collimasim/docs/source/collimasim.rst +10 -0
- xcoll/scattering_routines/geant4/collimasim/docs/source/conf.py +59 -0
- xcoll/scattering_routines/geant4/collimasim/docs/source/index.rst +26 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.appveyor.yml +37 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.clang-format +19 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.clang-tidy +65 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.cmake-format.yaml +73 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.git +1 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/CODEOWNERS +9 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/CONTRIBUTING.md +386 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml +45 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/ISSUE_TEMPLATE/config.yml +8 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/dependabot.yml +16 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/labeler.yml +8 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/labeler_merged.yml +3 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/pull_request_template.md +19 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/workflows/ci.yml +969 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/workflows/configure.yml +84 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/workflows/format.yml +48 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/workflows/labeler.yml +16 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.github/workflows/pip.yml +103 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.gitignore +45 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.pre-commit-config.yaml +151 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/.readthedocs.yml +3 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/CMakeLists.txt +297 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/LICENSE +29 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/MANIFEST.in +6 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/README.rst +180 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/Doxyfile +23 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/Makefile +192 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/_static/theme_overrides.css +11 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/chrono.rst +81 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/custom.rst +93 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/eigen.rst +310 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/functional.rst +109 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/index.rst +43 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/overview.rst +171 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/stl.rst +251 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/cast/strings.rst +305 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/classes.rst +1297 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/embedding.rst +262 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/exceptions.rst +396 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/functions.rst +568 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/misc.rst +337 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/pycpp/index.rst +13 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/pycpp/numpy.rst +463 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/pycpp/object.rst +286 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/pycpp/utilities.rst +155 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/advanced/smart_ptrs.rst +174 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/basics.rst +308 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/benchmark.py +91 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/benchmark.rst +95 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/changelog.rst +2050 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/classes.rst +542 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/cmake/index.rst +8 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/compiling.rst +648 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/conf.py +381 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/faq.rst +343 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/index.rst +48 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/installing.rst +105 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/limitations.rst +72 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/pybind11-logo.png +0 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/pybind11_vs_boost_python1.png +0 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/pybind11_vs_boost_python1.svg +427 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/pybind11_vs_boost_python2.png +0 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/pybind11_vs_boost_python2.svg +427 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/reference.rst +130 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/release.rst +96 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/requirements.txt +8 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/docs/upgrade.rst +548 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/attr.h +605 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/buffer_info.h +144 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/cast.h +1432 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/chrono.h +213 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/common.h +2 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/complex.h +65 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/class.h +709 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/common.h +1021 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/descr.h +104 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/init.h +346 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/internals.h +467 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/type_caster_base.h +978 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/detail/typeid.h +55 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/eigen.h +606 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/embed.h +284 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/eval.h +163 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/functional.h +121 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/gil.h +193 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/iostream.h +275 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/numpy.h +1741 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/operators.h +163 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/options.h +65 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/pybind11.h +2497 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/pytypes.h +1879 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/stl/filesystem.h +103 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/stl.h +375 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/include/pybind11/stl_bind.h +747 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/noxfile.py +88 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/__init__.py +11 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/__main__.py +52 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/_version.py +12 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/_version.pyi +6 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/commands.py +21 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/py.typed +0 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/setup_helpers.py +482 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pybind11/setup_helpers.pyi +63 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/pyproject.toml +41 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/setup.cfg +56 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/setup.py +155 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/CMakeLists.txt +503 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/conftest.py +208 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/constructor_stats.h +275 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/cross_module_gil_utils.cpp +73 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/env.py +33 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/extra_python_package/pytest.ini +0 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/extra_python_package/test_files.py +279 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/extra_setuptools/pytest.ini +0 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/extra_setuptools/test_setuphelper.py +143 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/local_bindings.h +85 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/object.h +179 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/pybind11_cross_module_tests.cpp +151 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/pybind11_tests.cpp +91 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/pybind11_tests.h +85 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/pytest.ini +19 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/requirements.txt +12 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_async.cpp +26 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_async.py +25 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_buffers.cpp +216 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_buffers.py +163 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_builtin_casters.cpp +286 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_builtin_casters.py +536 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_call_policies.cpp +107 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_call_policies.py +248 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_callbacks.cpp +227 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_callbacks.py +202 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_chrono.cpp +84 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_chrono.py +210 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_class.cpp +550 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_class.py +473 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/CMakeLists.txt +84 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/embed.cpp +21 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/installed_embed/CMakeLists.txt +28 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/installed_function/CMakeLists.txt +39 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/installed_target/CMakeLists.txt +46 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/main.cpp +6 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt +41 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +35 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +41 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_cmake_build/test.py +10 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_constants_and_functions.cpp +165 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_constants_and_functions.py +53 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_copy_move.cpp +238 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_copy_move.py +126 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_custom_type_casters.cpp +141 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_custom_type_casters.py +117 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_custom_type_setup.cpp +41 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_custom_type_setup.py +50 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_docstring_options.cpp +69 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_docstring_options.py +42 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_eigen.cpp +348 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_eigen.py +771 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_embed/CMakeLists.txt +47 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_embed/catch.cpp +22 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_embed/external_module.cpp +23 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_embed/test_interpreter.cpp +326 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_embed/test_interpreter.py +15 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_enum.cpp +148 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_enum.py +272 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_eval.cpp +119 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_eval.py +51 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_eval_call.py +5 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_exceptions.cpp +285 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_exceptions.h +12 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_exceptions.py +265 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_factory_constructors.cpp +397 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_factory_constructors.py +520 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_gil_scoped.cpp +49 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_gil_scoped.py +94 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_iostream.cpp +125 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_iostream.py +331 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_kwargs_and_defaults.cpp +153 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_kwargs_and_defaults.py +284 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_local_bindings.cpp +107 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_local_bindings.py +257 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_methods_and_attributes.cpp +412 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_methods_and_attributes.py +517 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_modules.cpp +102 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_modules.py +92 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_multiple_inheritance.cpp +233 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_multiple_inheritance.py +360 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_numpy_array.cpp +472 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_numpy_array.py +593 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_numpy_dtypes.cpp +524 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_numpy_dtypes.py +441 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_numpy_vectorize.cpp +103 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_numpy_vectorize.py +267 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_opaque_types.cpp +73 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_opaque_types.py +59 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_operator_overloading.cpp +235 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_operator_overloading.py +146 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_pickling.cpp +189 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_pickling.py +82 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_pytypes.cpp +560 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_pytypes.py +651 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_sequences_and_iterators.cpp +500 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_sequences_and_iterators.py +253 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_smart_ptr.cpp +452 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_smart_ptr.py +318 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_stl.cpp +342 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_stl.py +291 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_stl_binders.cpp +131 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_stl_binders.py +318 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_tagbased_polymorphic.cpp +144 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_tagbased_polymorphic.py +29 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_thread.cpp +66 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_thread.py +44 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_union.cpp +22 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_union.py +9 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_virtual_functions.cpp +510 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/test_virtual_functions.py +408 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/valgrind-numpy-scipy.supp +140 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tests/valgrind-python.supp +117 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/FindCatch.cmake +70 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/FindEigen3.cmake +86 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/FindPythonLibsNew.cmake +257 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/check-style.sh +44 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/cmake_uninstall.cmake.in +23 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/libsize.py +39 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/make_changelog.py +64 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/pybind11Common.cmake +402 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/pybind11Config.cmake.in +233 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/pybind11NewTools.cmake +276 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/pybind11Tools.cmake +214 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/pyproject.toml +3 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/setup_global.py.in +65 -0
- xcoll/scattering_routines/geant4/collimasim/lib/pybind11/tools/setup_main.py.in +41 -0
- xcoll/scattering_routines/geant4/collimasim/pyproject.toml +8 -0
- xcoll/scattering_routines/geant4/collimasim/setup.py +144 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/BDSPyATInterface.cpp +403 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/BDSPyATInterface.hh +100 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/BDSXtrackInterface.cpp +763 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/BDSXtrackInterface.hh +118 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/__init__.py +8 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/bindings.cpp +63 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/pyCollimatorPass.py +142 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim/xtrack_collimator.py +556 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim.egg-info/PKG-INFO +6 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim.egg-info/SOURCES.txt +24 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim.egg-info/dependency_links.txt +1 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim.egg-info/not-zip-safe +1 -0
- xcoll/scattering_routines/geant4/collimasim/src/collimasim.egg-info/top_level.txt +1 -0
- xcoll/scattering_routines/geant4/collimasim/tests/README.md +25 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/CollDB_forions.dat +25 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/CollDB_new_example.dat +18 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/CollDB_old_example.dat +68 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/CollDB_testing.dat +15 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/CollDB_yaml_example.yaml +110 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/collgaps.dat +7 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/collgaps_pyat_test.dat +3 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/collonly_twiss_file_example.tfs +54 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/settings.gmad +3 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/settings_black_absorber.gmad +3 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/settings_ions.gmad +5 -0
- xcoll/scattering_routines/geant4/collimasim/tests/resources/twiss_file_testing.tfs +51 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_pyat.py +65 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_pyat_passmethod.py +59 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_pyat_tracking.py +102 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack.py +75 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_angle.py +74 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_colldb_load.py +84 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_interaction.py +159 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_interaction_ion.py +99 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_ions.py +78 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_lost_energy.py +88 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_tilt.py +80 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_tracking.py +97 -0
- xcoll/scattering_routines/geant4/collimasim/tests/test_xtrack_tracking_ions.py +96 -0
- xcoll/scattering_routines/geometry/__init__.py +6 -0
- xcoll/scattering_routines/geometry/collimator_geometry.h +218 -0
- xcoll/scattering_routines/geometry/crystal_geometry.h +153 -0
- xcoll/scattering_routines/geometry/geometry.py +26 -0
- xcoll/scattering_routines/geometry/get_s.h +92 -0
- xcoll/scattering_routines/geometry/methods.h +111 -0
- xcoll/scattering_routines/geometry/objects.h +154 -0
- xcoll/scattering_routines/geometry/rotation.h +23 -0
- xcoll/scattering_routines/geometry/segments.h +226 -0
- xcoll/scattering_routines/geometry/sort.h +184 -0
- {xcoll-0.3.6.dist-info → xcoll-0.5.0.dist-info}/METADATA +1 -1
- xcoll-0.5.0.dist-info/RECORD +413 -0
- xcoll/beam_elements/collimators_src/absorber.h +0 -141
- xcoll/beam_elements/collimators_src/everest_collimator.h +0 -142
- xcoll/beam_elements/collimators_src/everest_crystal.h +0 -115
- xcoll/collimator_settings.py +0 -457
- xcoll/impacts/__init__.py +0 -1
- xcoll/impacts/impacts.py +0 -102
- xcoll/impacts/impacts_src/impacts.h +0 -99
- xcoll/scattering_routines/everest/crystal.h +0 -1302
- xcoll/scattering_routines/everest/scatter.h +0 -169
- xcoll/scattering_routines/everest/scatter_crystal.h +0 -260
- xcoll/scattering_routines/fluka/build_fluka_input.py +0 -58
- xcoll-0.3.6.dist-info/RECORD +0 -111
- {xcoll-0.3.6.dist-info → xcoll-0.5.0.dist-info}/LICENSE +0 -0
- {xcoll-0.3.6.dist-info → xcoll-0.5.0.dist-info}/NOTICE +0 -0
- {xcoll-0.3.6.dist-info → xcoll-0.5.0.dist-info}/WHEEL +0 -0
xcoll/beam_elements/base.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# copyright ############################### #
|
|
2
2
|
# This file is part of the Xcoll Package. #
|
|
3
|
-
# Copyright (c) CERN,
|
|
3
|
+
# Copyright (c) CERN, 2024. #
|
|
4
4
|
# ######################################### #
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
@@ -8,11 +8,14 @@ import numpy as np
|
|
|
8
8
|
import xobjects as xo
|
|
9
9
|
import xtrack as xt
|
|
10
10
|
|
|
11
|
-
from ..
|
|
12
|
-
from ..impacts import CollimatorImpacts
|
|
11
|
+
from ..interaction_record import InteractionRecord
|
|
13
12
|
from ..general import _pkg_root
|
|
14
13
|
|
|
15
14
|
|
|
15
|
+
OPEN_JAW = 3.
|
|
16
|
+
OPEN_GAP = 999.
|
|
17
|
+
|
|
18
|
+
|
|
16
19
|
class InvalidXcoll(xt.BeamElement):
|
|
17
20
|
_xofields = {
|
|
18
21
|
'length': xo.Float64
|
|
@@ -22,7 +25,7 @@ class InvalidXcoll(xt.BeamElement):
|
|
|
22
25
|
behaves_like_drift = True
|
|
23
26
|
allow_track = False
|
|
24
27
|
skip_in_loss_location_refinement = True
|
|
25
|
-
|
|
28
|
+
allow_loss_refinement = True
|
|
26
29
|
|
|
27
30
|
# InvalidXcoll catches unallowed cases, like backtracking through a collimator
|
|
28
31
|
_extra_c_sources = [
|
|
@@ -42,7 +45,9 @@ class InvalidXcoll(xt.BeamElement):
|
|
|
42
45
|
|
|
43
46
|
class BaseBlock(xt.BeamElement):
|
|
44
47
|
_xofields = {
|
|
45
|
-
'length':
|
|
48
|
+
'length': xo.Float64,
|
|
49
|
+
'active': xo.Int8,
|
|
50
|
+
'_record_interactions': xo.Int8
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
isthick = True
|
|
@@ -50,8 +55,12 @@ class BaseBlock(xt.BeamElement):
|
|
|
50
55
|
behaves_like_drift = True
|
|
51
56
|
skip_in_loss_location_refinement = True
|
|
52
57
|
|
|
58
|
+
_skip_in_to_dict = ['_record_interactions']
|
|
59
|
+
_store_in_to_dict = ['record_impacts', 'record_exits', 'record_scatterings']
|
|
60
|
+
|
|
53
61
|
_depends_on = [InvalidXcoll]
|
|
54
|
-
|
|
62
|
+
|
|
63
|
+
_internal_record_class = InteractionRecord
|
|
55
64
|
|
|
56
65
|
# This is an abstract class and cannot be instantiated
|
|
57
66
|
def __new__(cls, *args, **kwargs):
|
|
@@ -60,55 +69,111 @@ class BaseBlock(xt.BeamElement):
|
|
|
60
69
|
instance = super().__new__(cls)
|
|
61
70
|
return instance
|
|
62
71
|
|
|
72
|
+
def __init__(self, **kwargs):
|
|
73
|
+
if '_xobject' not in kwargs:
|
|
74
|
+
kwargs.setdefault('active', True)
|
|
75
|
+
super().__init__(**kwargs)
|
|
76
|
+
|
|
63
77
|
def enable_scattering(self):
|
|
64
78
|
if hasattr(self, '_tracking'):
|
|
79
|
+
if hasattr(self, 'optics') and self.optics is None and \
|
|
80
|
+
(hasattr(self, '_gap_L_set_manually') and self._gap_L_set_manually() \
|
|
81
|
+
or hasattr(self, '_gap_R_set_manually') and self._gap_R_set_manually()):
|
|
82
|
+
raise ValueError("Gap set but optics not yet assigned! "
|
|
83
|
+
+ "Cannot enable scattering.")
|
|
65
84
|
self._tracking = True
|
|
66
85
|
|
|
67
86
|
def disable_scattering(self):
|
|
68
87
|
if hasattr(self, '_tracking'):
|
|
69
88
|
self._tracking = False
|
|
70
89
|
|
|
90
|
+
@property
|
|
91
|
+
def record_impacts(self):
|
|
92
|
+
return bool(self._record_interactions % 2)
|
|
93
|
+
|
|
94
|
+
@record_impacts.setter
|
|
95
|
+
def record_impacts(self, val):
|
|
96
|
+
if val and not self.record_impacts:
|
|
97
|
+
self._record_interactions += 1
|
|
98
|
+
elif not val and self.record_impacts:
|
|
99
|
+
self._record_interactions -= 1
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def record_exits(self):
|
|
103
|
+
return bool((self._record_interactions >> 1) % 2)
|
|
104
|
+
|
|
105
|
+
@record_exits.setter
|
|
106
|
+
def record_exits(self, val):
|
|
107
|
+
if val and not self.record_exits:
|
|
108
|
+
self._record_interactions += 2
|
|
109
|
+
elif not val and self.record_exits:
|
|
110
|
+
self._record_interactions -= 2
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def record_scatterings(self):
|
|
114
|
+
return bool((self._record_interactions >> 2) % 2)
|
|
115
|
+
|
|
116
|
+
@record_scatterings.setter
|
|
117
|
+
def record_scatterings(self, val):
|
|
118
|
+
if val and not self.record_scatterings:
|
|
119
|
+
self._record_interactions += 4
|
|
120
|
+
elif not val and self.record_scatterings:
|
|
121
|
+
self._record_interactions -= 4
|
|
122
|
+
|
|
123
|
+
def _verify_consistency(self):
|
|
124
|
+
assert isinstance(self.active, bool) or self.active in [0, 1]
|
|
125
|
+
assert self._record_interactions in list(range(8))
|
|
126
|
+
|
|
71
127
|
def get_backtrack_element(self, _context=None, _buffer=None, _offset=None):
|
|
72
128
|
return InvalidXcoll(length=-self.length,
|
|
73
129
|
_context=_context, _buffer=_buffer, _offset=_offset)
|
|
74
130
|
|
|
75
131
|
|
|
76
|
-
class BaseCollimator(
|
|
77
|
-
_xofields = {
|
|
78
|
-
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
'
|
|
90
|
-
'
|
|
91
|
-
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
132
|
+
class BaseCollimator(BaseBlock):
|
|
133
|
+
_xofields = {**BaseBlock._xofields,
|
|
134
|
+
# Collimator angle
|
|
135
|
+
'_sin_zL': xo.Float64,
|
|
136
|
+
'_cos_zL': xo.Float64,
|
|
137
|
+
'_sin_zR': xo.Float64,
|
|
138
|
+
'_cos_zR': xo.Float64,
|
|
139
|
+
'_sin_zDiff': xo.Float64, # Angle of right jaw: difference with respect to angle of left jaw
|
|
140
|
+
'_cos_zDiff': xo.Float64,
|
|
141
|
+
'_jaws_parallel': xo.Int8,
|
|
142
|
+
# Jaw corners (this is the x-coordinate in the rotated frame)
|
|
143
|
+
'_jaw_LU': xo.Float64, # left upstream
|
|
144
|
+
'_jaw_RU': xo.Float64, # right upstream
|
|
145
|
+
'_jaw_LD': xo.Float64, # left downstream
|
|
146
|
+
'_jaw_RD': xo.Float64, # right downstream
|
|
147
|
+
# Tilts (superfluous but added to speed up calculations)
|
|
148
|
+
'_sin_yL': xo.Float64,
|
|
149
|
+
'_cos_yL': xo.Float64,
|
|
150
|
+
'_tan_yL': xo.Float64,
|
|
151
|
+
'_sin_yR': xo.Float64,
|
|
152
|
+
'_cos_yR': xo.Float64,
|
|
153
|
+
'_tan_yR': xo.Float64,
|
|
154
|
+
# Other
|
|
155
|
+
'_side': xo.Int8,
|
|
156
|
+
# These are not used in C, but need to be an xofield to get them in the to_dict:
|
|
157
|
+
'_align': xo.Int8,
|
|
158
|
+
'_gap_L': xo.Float64,
|
|
159
|
+
'_gap_R': xo.Float64,
|
|
160
|
+
'_nemitt_x': xo.Float64,
|
|
161
|
+
'_nemitt_y': xo.Float64
|
|
97
162
|
}
|
|
98
163
|
|
|
99
|
-
isthick =
|
|
100
|
-
allow_track =
|
|
101
|
-
behaves_like_drift =
|
|
102
|
-
skip_in_loss_location_refinement =
|
|
164
|
+
isthick = BaseBlock.isthick
|
|
165
|
+
allow_track = BaseBlock.allow_track
|
|
166
|
+
behaves_like_drift = BaseBlock.behaves_like_drift
|
|
167
|
+
skip_in_loss_location_refinement = BaseBlock.skip_in_loss_location_refinement
|
|
168
|
+
allow_double_sided = True
|
|
103
169
|
|
|
104
|
-
_skip_in_to_dict = [
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
170
|
+
_skip_in_to_dict = [*BaseBlock._skip_in_to_dict, *[f for f in _xofields if f.startswith('_')]]
|
|
171
|
+
_store_in_to_dict = [*BaseBlock._store_in_to_dict, 'angle', 'jaw', 'tilt', 'gap', 'side', 'align', 'emittance']
|
|
172
|
+
|
|
173
|
+
_depends_on = [BaseBlock]
|
|
174
|
+
|
|
175
|
+
_internal_record_class = BaseBlock._internal_record_class
|
|
109
176
|
|
|
110
|
-
_depends_on = [InvalidXcoll, xt.Drift, xt.XYShift, xt.SRotation, xt.YRotation]
|
|
111
|
-
_internal_record_class = CollimatorImpacts
|
|
112
177
|
|
|
113
178
|
# This is an abstract class and cannot be instantiated
|
|
114
179
|
def __new__(cls, *args, **kwargs):
|
|
@@ -118,200 +183,700 @@ class BaseCollimator(xt.BeamElement):
|
|
|
118
183
|
return instance
|
|
119
184
|
|
|
120
185
|
def __init__(self, **kwargs):
|
|
186
|
+
to_assign = {}
|
|
121
187
|
if '_xobject' not in kwargs:
|
|
122
|
-
# Set jaw
|
|
123
|
-
if 'jaw' in kwargs:
|
|
124
|
-
if 'jaw_L' in kwargs or 'jaw_R' in kwargs:
|
|
125
|
-
raise ValuError("Cannot use both 'jaw' and 'jaw_L/R'!")
|
|
126
|
-
_set_LR(kwargs, 'jaw', kwargs.pop('jaw'), neg=True)
|
|
127
|
-
else:
|
|
128
|
-
kwargs.setdefault('jaw_L', 1)
|
|
129
|
-
kwargs.setdefault('jaw_R', -1)
|
|
130
|
-
|
|
131
|
-
# Set reference_center
|
|
132
|
-
if 'reference_center' in kwargs:
|
|
133
|
-
if 'ref_x' in kwargs or 'ref_y' in kwargs:
|
|
134
|
-
raise ValuError("Cannot use both 'reference_center' and 'ref_x/y'!")
|
|
135
|
-
_set_LR(kwargs, 'ref', kwargs.pop('reference_center'), name_L='_x', name_R='_y')
|
|
136
|
-
else:
|
|
137
|
-
kwargs.setdefault('ref_x', 0)
|
|
138
|
-
kwargs.setdefault('ref_y', 0)
|
|
139
|
-
|
|
140
188
|
# Set side
|
|
141
|
-
|
|
189
|
+
to_assign['side'] = kwargs.pop('side', 'both')
|
|
142
190
|
|
|
143
191
|
# Set angle
|
|
144
192
|
if 'angle' in kwargs:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
193
|
+
for key in ['angle_L', 'angle_R']:
|
|
194
|
+
if key in kwargs:
|
|
195
|
+
raise ValueError(f"Cannot use both `angle` and `{key}`!")
|
|
196
|
+
to_assign['angle'] = kwargs.pop('angle')
|
|
149
197
|
else:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
to_assign['angle_L'] = kwargs.pop('angle_L', 0)
|
|
199
|
+
to_assign['angle_R'] = kwargs.pop('angle_R', 0)
|
|
200
|
+
|
|
201
|
+
# Set jaw
|
|
202
|
+
if 'jaw' in kwargs:
|
|
203
|
+
for key in ['jaw_L', 'jaw_R', 'jaw_LU', 'jaw_LD', 'jaw_RU', 'jaw_RD', 'gap', 'gap_L', 'gap_R']:
|
|
204
|
+
if key in kwargs:
|
|
205
|
+
raise ValueError(f"Cannot use both `jaw` and `{key}`!")
|
|
206
|
+
to_assign['jaw'] = kwargs.pop('jaw')
|
|
207
|
+
elif 'jaw_L' in kwargs or 'jaw_R' in kwargs:
|
|
208
|
+
for key in ['jaw_LU', 'jaw_LD', 'jaw_RU', 'jaw_RD', 'gap', 'gap_L', 'gap_R']:
|
|
209
|
+
if key in kwargs:
|
|
210
|
+
raise ValueError(f"Cannot use `jaw_L` or `jaw_R` together with `{key}`!")
|
|
211
|
+
to_assign['jaw_L'] = kwargs.pop('jaw_L', None)
|
|
212
|
+
to_assign['jaw_R'] = kwargs.pop('jaw_R', None)
|
|
213
|
+
elif 'jaw_LU' in kwargs or 'jaw_LD' in kwargs or 'jaw_RU' in kwargs or 'jaw_RD' in kwargs:
|
|
214
|
+
for key in ['tilt', 'tilt_L', 'tilt_R', 'gap', 'gap_L', 'gap_R']:
|
|
215
|
+
if key in kwargs:
|
|
216
|
+
raise ValueError(f"Cannot use both `jaw_LU` etc with `{key}`!")
|
|
217
|
+
to_assign['jaw_LU'] = kwargs.pop('jaw_LU', None)
|
|
218
|
+
to_assign['jaw_RU'] = kwargs.pop('jaw_RU', None)
|
|
219
|
+
to_assign['jaw_LD'] = kwargs.pop('jaw_LD', None)
|
|
220
|
+
to_assign['jaw_RD'] = kwargs.pop('jaw_RD', None)
|
|
221
|
+
kwargs.setdefault('_jaw_LU', OPEN_JAW) # Important that these are initialised, in
|
|
222
|
+
kwargs.setdefault('_jaw_RU', -OPEN_JAW) # order to keep a tilt (given together with
|
|
223
|
+
kwargs.setdefault('_jaw_LD', OPEN_JAW) # a gap) when optics are not known yet.
|
|
224
|
+
kwargs.setdefault('_jaw_RD', -OPEN_JAW)
|
|
156
225
|
|
|
157
226
|
# Set tilt
|
|
158
227
|
if 'tilt' in kwargs:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
= _angle_setter(kwargs.pop('tilt', 0), rad=True)
|
|
228
|
+
for key in ['tilt_L', 'tilt_R']:
|
|
229
|
+
if key in kwargs:
|
|
230
|
+
raise ValueError(f"Cannot use both `tilt` and `{key}`!")
|
|
231
|
+
to_assign['tilt'] = kwargs.pop('tilt')
|
|
164
232
|
else:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
kwargs['inactive_front'] = kwargs.pop('length') - kwargs['inactive_back'] - kwargs['active_length']
|
|
187
|
-
else:
|
|
188
|
-
kwargs['inactive_front'] = (kwargs['length']- kwargs['active_length'])/2
|
|
189
|
-
kwargs['inactive_back'] = (kwargs.pop('length') - kwargs['active_length'])/2
|
|
190
|
-
elif 'inactive_front' in kwargs.keys():
|
|
191
|
-
if 'inactive_back' in kwargs.keys():
|
|
192
|
-
kwargs['active_length'] = kwargs.pop('length') - kwargs['inactive_back'] - kwargs['inactive_front']
|
|
193
|
-
else:
|
|
194
|
-
kwargs['active_length'] = kwargs.pop('length') - kwargs['inactive_front']
|
|
195
|
-
elif 'inactive_back' in kwargs.keys():
|
|
196
|
-
kwargs['active_length'] = kwargs.pop('length') - kwargs['inactive_back']
|
|
197
|
-
else:
|
|
198
|
-
kwargs['active_length'] = kwargs.pop('length')
|
|
199
|
-
kwargs.setdefault('inactive_front', 0)
|
|
200
|
-
kwargs.setdefault('inactive_back', 0)
|
|
201
|
-
kwargs.setdefault('active_length', 0)
|
|
202
|
-
kwargs.setdefault('active', True)
|
|
233
|
+
to_assign['tilt_L'] = kwargs.pop('tilt_L', 0)
|
|
234
|
+
to_assign['tilt_R'] = kwargs.pop('tilt_R', 0)
|
|
235
|
+
|
|
236
|
+
# Set gap
|
|
237
|
+
if 'gap' in kwargs:
|
|
238
|
+
for key in ['jaw', 'jaw_L', 'jaw_R', 'jaw_LU', 'jaw_LD', 'jaw_RU', 'jaw_RD', 'gap_L', 'gap_R']:
|
|
239
|
+
if key in kwargs:
|
|
240
|
+
raise ValueError(f"Cannot use both `gap` and `{key}`!")
|
|
241
|
+
to_assign['gap'] = kwargs.pop('gap')
|
|
242
|
+
elif 'gap_L' in kwargs or 'gap_R' in kwargs:
|
|
243
|
+
for key in ['jaw', 'jaw_L', 'jaw_R', 'jaw_LU', 'jaw_LD', 'jaw_RU', 'jaw_RD', 'gap']:
|
|
244
|
+
if key in kwargs:
|
|
245
|
+
raise ValueError(f"Cannot use both `gap` and `{key}`!")
|
|
246
|
+
to_assign['gap_L'] = kwargs.pop('gap_L', None)
|
|
247
|
+
to_assign['gap_R'] = kwargs.pop('gap_R', None)
|
|
248
|
+
kwargs.setdefault('_gap_L', OPEN_GAP)
|
|
249
|
+
kwargs.setdefault('_gap_R', -OPEN_GAP)
|
|
250
|
+
|
|
251
|
+
# Set others
|
|
252
|
+
to_assign['align'] = kwargs.pop('align', 'upstream')
|
|
253
|
+
to_assign['emittance'] = kwargs.pop('emittance', None)
|
|
203
254
|
|
|
204
255
|
super().__init__(**kwargs)
|
|
256
|
+
# Careful: non-xofields are not passed correctly between copy's / to_dict. This messes with flags etc..
|
|
257
|
+
# We also have to manually initialise them for xobject generation
|
|
258
|
+
if not hasattr(self, '_optics'):
|
|
259
|
+
self._optics = None
|
|
260
|
+
for key, val in to_assign.items():
|
|
261
|
+
setattr(self, key, val)
|
|
262
|
+
self._verify_consistency()
|
|
205
263
|
|
|
206
264
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
self._tracking = True
|
|
265
|
+
# Main collimator angle
|
|
266
|
+
# =====================
|
|
210
267
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
268
|
+
@property
|
|
269
|
+
def angle(self):
|
|
270
|
+
return self.angle_L if self.angle_L==self.angle_R else [self.angle_L, self.angle_R]
|
|
271
|
+
|
|
272
|
+
@angle.setter
|
|
273
|
+
def angle(self, val):
|
|
274
|
+
if not hasattr(val, '__iter__'):
|
|
275
|
+
self.angle_L = val
|
|
276
|
+
self.angle_R = val
|
|
277
|
+
elif len(val) == 1:
|
|
278
|
+
self.angle_L = val[0]
|
|
279
|
+
self.angle_R = val[0]
|
|
280
|
+
elif len(val) == 2:
|
|
281
|
+
self.angle_L = val[0]
|
|
282
|
+
self.angle_R = val[1]
|
|
283
|
+
else:
|
|
284
|
+
raise ValueError(f"The attribute `angle` should be of the form LR or [L, R] "
|
|
285
|
+
+ f"but got {val}.")
|
|
214
286
|
|
|
215
287
|
@property
|
|
216
|
-
def
|
|
217
|
-
return
|
|
288
|
+
def angle_L(self):
|
|
289
|
+
return round(np.rad2deg(np.arctan2(self._sin_zL, self._cos_zL)), 10)
|
|
218
290
|
|
|
219
|
-
@
|
|
291
|
+
@angle_L.setter
|
|
292
|
+
def angle_L(self, angle_L):
|
|
293
|
+
self._sin_zL = np.sin(np.deg2rad(angle_L))
|
|
294
|
+
self._cos_zL = np.cos(np.deg2rad(angle_L))
|
|
295
|
+
if np.isclose(self.angle_R, angle_L):
|
|
296
|
+
self._jaws_parallel = True
|
|
297
|
+
self._sin_zDiff = 0.
|
|
298
|
+
self._cos_zDiff = 1.
|
|
299
|
+
else:
|
|
300
|
+
self._jaws_parallel = False
|
|
301
|
+
self._sin_zDiff = np.sin(np.deg2rad(self.angle_R - angle_L))
|
|
302
|
+
self._cos_zDiff = np.cos(np.deg2rad(self.angle_R - angle_L))
|
|
303
|
+
self._apply_optics()
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def angle_R(self):
|
|
307
|
+
return round(np.rad2deg(np.arctan2(self._sin_zR, self._cos_zR)), 10)
|
|
308
|
+
|
|
309
|
+
@angle_R.setter
|
|
310
|
+
def angle_R(self, angle_R):
|
|
311
|
+
self._sin_zR = np.sin(np.deg2rad(angle_R))
|
|
312
|
+
self._cos_zR = np.cos(np.deg2rad(angle_R))
|
|
313
|
+
if np.isclose(self.angle_L, angle_R):
|
|
314
|
+
self._jaws_parallel = True
|
|
315
|
+
self._sin_zDiff = 0.
|
|
316
|
+
self._cos_zDiff = 1.
|
|
317
|
+
else:
|
|
318
|
+
self._jaws_parallel = False
|
|
319
|
+
self._sin_zDiff = np.sin(np.deg2rad(angle_R - self.angle_L))
|
|
320
|
+
self._cos_zDiff = np.cos(np.deg2rad(angle_R - self.angle_L))
|
|
321
|
+
self._apply_optics()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# Jaw attributes
|
|
325
|
+
# ==============
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def jaw(self):
|
|
329
|
+
if self.jaw_L is None and self.jaw_R is None:
|
|
330
|
+
return None
|
|
331
|
+
elif (self.side == 'left' and self.tilt_L == 0) \
|
|
332
|
+
or (self.side == 'right' and self.tilt_R == 0) \
|
|
333
|
+
or (self.tilt_L == 0 and self.tilt_R == 0):
|
|
334
|
+
return [self.jaw_L, self.jaw_R]
|
|
335
|
+
else:
|
|
336
|
+
return [[self.jaw_LU, self.jaw_RU], [self.jaw_LD, self.jaw_RD]]
|
|
337
|
+
|
|
338
|
+
@jaw.setter # Keeps the tilts unless all 4 corners are specified
|
|
220
339
|
def jaw(self, val):
|
|
221
|
-
|
|
340
|
+
if not hasattr(val, '__iter__') or len(val) == 1:
|
|
341
|
+
val = val[0] if hasattr(val, '__iter__') else val
|
|
342
|
+
if self.side == 'left':
|
|
343
|
+
self.jaw_L = val
|
|
344
|
+
elif self.side == 'right':
|
|
345
|
+
# self.jaw_R = -val if val is not None else None
|
|
346
|
+
self.jaw_R = val
|
|
347
|
+
else:
|
|
348
|
+
self.jaw_L = val
|
|
349
|
+
self.jaw_R = -val if val is not None else None
|
|
350
|
+
return
|
|
351
|
+
elif len(val) == 2:
|
|
352
|
+
if hasattr(val[0], '__iter__'):
|
|
353
|
+
if hasattr(val[1], '__iter__') and len(val[0]) == 2 and len(val[1]) == 2:
|
|
354
|
+
self.jaw_LU = val[0][0]
|
|
355
|
+
self.jaw_RU = val[0][1]
|
|
356
|
+
self.jaw_LD = val[1][0]
|
|
357
|
+
self.jaw_RD = val[1][1]
|
|
358
|
+
return
|
|
359
|
+
else:
|
|
360
|
+
self.jaw_L = val[0]
|
|
361
|
+
self.jaw_R = val[1]
|
|
362
|
+
return
|
|
363
|
+
# If we got here, val is incompatible
|
|
364
|
+
raise ValueError(f"The attribute `jaw` should be of the form [L, R] or "
|
|
365
|
+
+ f"[[LU, RU], [LD, RD], but got {val}.")
|
|
222
366
|
|
|
223
367
|
@property
|
|
224
|
-
def
|
|
225
|
-
|
|
368
|
+
def jaw_L(self):
|
|
369
|
+
jaw_L = (self._jaw_LU + self._jaw_LD) / 2
|
|
370
|
+
if not np.isclose(jaw_L, OPEN_JAW, atol=1.e-10): # open position
|
|
371
|
+
return jaw_L
|
|
372
|
+
|
|
373
|
+
@jaw_L.setter # This moves both jaw_LU and jaw_LD in parallel
|
|
374
|
+
def jaw_L(self, val):
|
|
375
|
+
if self.side == 'right' and val is not None:
|
|
376
|
+
val = None
|
|
377
|
+
print("Warning: Ignored value for jaw_L (right-sided collimator).")
|
|
378
|
+
if val is None:
|
|
379
|
+
val = OPEN_JAW
|
|
380
|
+
self._gap_L = OPEN_GAP
|
|
381
|
+
diff = val - (self._jaw_LU + self._jaw_LD) / 2
|
|
382
|
+
self._jaw_LU += diff
|
|
383
|
+
self._jaw_LD += diff
|
|
384
|
+
self._update_gaps(only_L=True)
|
|
226
385
|
|
|
227
|
-
@
|
|
228
|
-
def
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
386
|
+
@property
|
|
387
|
+
def jaw_R(self):
|
|
388
|
+
jaw_R = (self._jaw_RU + self._jaw_RD) / 2
|
|
389
|
+
if not np.isclose(jaw_R, -OPEN_JAW, atol=1.e-10): # open position
|
|
390
|
+
return jaw_R
|
|
391
|
+
|
|
392
|
+
@jaw_R.setter # This moves both jaw_RU and jaw_RD in parallel
|
|
393
|
+
def jaw_R(self, val):
|
|
394
|
+
if self.side == 'left' and val is not None:
|
|
395
|
+
val = None
|
|
396
|
+
print("Warning: Ignored value for jaw_R (left-sided collimator).")
|
|
397
|
+
if val is None:
|
|
398
|
+
val = -OPEN_JAW
|
|
399
|
+
self._gap_R = -OPEN_GAP
|
|
400
|
+
diff = val - (self._jaw_RU + self._jaw_RD) / 2
|
|
401
|
+
self._jaw_RU += diff
|
|
402
|
+
self._jaw_RD += diff
|
|
403
|
+
self._update_gaps(only_R=True)
|
|
234
404
|
|
|
235
405
|
@property
|
|
236
|
-
def
|
|
237
|
-
|
|
406
|
+
def jaw_LU(self):
|
|
407
|
+
if not self.jaw_L is None:
|
|
408
|
+
return self._jaw_LU
|
|
409
|
+
|
|
410
|
+
@jaw_LU.setter # This assumes jaw_LD remains fixed, hence both jaw_L and the tilt change
|
|
411
|
+
def jaw_LU(self, val):
|
|
412
|
+
if self.side == 'right':
|
|
413
|
+
if val is not None:
|
|
414
|
+
print("Warning: Ignored value for jaw_LU (right-sided collimator).")
|
|
415
|
+
return
|
|
416
|
+
if val is None:
|
|
417
|
+
raise ValueError("Cannot set corner to None! Use open_jaws() or set jaw_L to None.")
|
|
418
|
+
self._jaw_LU = val
|
|
419
|
+
self._update_tilts() # Extra, to update tilts which are also in C for efficiency
|
|
420
|
+
self._update_gaps(only_L=True)
|
|
238
421
|
|
|
239
|
-
@
|
|
240
|
-
def jaw_LD(self
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
422
|
+
@property
|
|
423
|
+
def jaw_LD(self):
|
|
424
|
+
if not self.jaw_L is None:
|
|
425
|
+
return self._jaw_LD
|
|
426
|
+
|
|
427
|
+
@jaw_LD.setter # This assumes jaw_LU remains fixed, hence both jaw_L and the tilt change
|
|
428
|
+
def jaw_LD(self, val):
|
|
429
|
+
if self.side == 'right':
|
|
430
|
+
if val is not None:
|
|
431
|
+
print("Warning: Ignored value for jaw_LD (right-sided collimator).")
|
|
432
|
+
return
|
|
433
|
+
if val is None:
|
|
434
|
+
raise ValueError("Cannot set corner to None! Use open_jaws() or set jaw_L to None.")
|
|
435
|
+
self._jaw_LD = val
|
|
436
|
+
self._update_tilts() # Extra, to update tilts which are also in C for efficiency
|
|
437
|
+
self._update_gaps(only_L=True)
|
|
246
438
|
|
|
247
439
|
@property
|
|
248
440
|
def jaw_RU(self):
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
self.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
441
|
+
if not self.jaw_R is None:
|
|
442
|
+
return self._jaw_RU
|
|
443
|
+
|
|
444
|
+
@jaw_RU.setter # This assumes jaw_RD remains fixed, hence both jaw_R and the tilt change
|
|
445
|
+
def jaw_RU(self, val):
|
|
446
|
+
if self.side == 'left':
|
|
447
|
+
if val is not None:
|
|
448
|
+
print("Warning: Ignored value for jaw_RU (left-sided collimator).")
|
|
449
|
+
return
|
|
450
|
+
if val is None:
|
|
451
|
+
raise ValueError("Cannot set corner to None! Use open_jaws() or set jaw_R to None.")
|
|
452
|
+
self._jaw_RU = val
|
|
453
|
+
self._update_tilts() # Extra, to update tilts which are also in C for efficiency
|
|
454
|
+
self._update_gaps(only_R=True)
|
|
258
455
|
|
|
259
456
|
@property
|
|
260
457
|
def jaw_RD(self):
|
|
261
|
-
|
|
458
|
+
if not self.jaw_R is None:
|
|
459
|
+
return self._jaw_RD
|
|
460
|
+
|
|
461
|
+
@jaw_RD.setter # This assumes jaw_RU remains fixed, hence both jaw_R and the tilt change
|
|
462
|
+
def jaw_RD(self, val):
|
|
463
|
+
if self.side == 'left':
|
|
464
|
+
if val is not None:
|
|
465
|
+
print("Warning: Ignored value for jaw_RD (left-sided collimator).")
|
|
466
|
+
return
|
|
467
|
+
if val is None:
|
|
468
|
+
raise ValueError("Cannot set corner to None! Use open_jaws() or set jaw_R to None.")
|
|
469
|
+
self._jaw_RD = val
|
|
470
|
+
self._update_tilts() # Extra, to update tilts which are also in C for efficiency
|
|
471
|
+
self._update_gaps(only_R=True)
|
|
262
472
|
|
|
263
|
-
@
|
|
264
|
-
def
|
|
265
|
-
|
|
266
|
-
self.jaw_R = (jaw_RU+jaw_RD)/2
|
|
267
|
-
self.sin_yR = (jaw_RD-jaw_RU)/self.active_length
|
|
268
|
-
self.cos_yR = np.sqrt(1-self.sin_yR**2)
|
|
269
|
-
self.tan_yR = self.sin_yR / self.cos_yR
|
|
473
|
+
@property
|
|
474
|
+
def jaw_s_LU(self):
|
|
475
|
+
return self.length/2 * (1 - self._cos_yL)
|
|
270
476
|
|
|
271
477
|
@property
|
|
272
|
-
def
|
|
273
|
-
return
|
|
478
|
+
def jaw_s_LD(self):
|
|
479
|
+
return self.length/2 * (1 + self._cos_yL)
|
|
274
480
|
|
|
275
|
-
@
|
|
276
|
-
def
|
|
277
|
-
|
|
278
|
-
self.sin_zL = np.sin(anglerad_L)
|
|
279
|
-
self.cos_zL = np.cos(anglerad_L)
|
|
481
|
+
@property
|
|
482
|
+
def jaw_s_RU(self):
|
|
483
|
+
return self.length/2 * (1 - self._cos_yR)
|
|
280
484
|
|
|
281
485
|
@property
|
|
282
|
-
def
|
|
283
|
-
return
|
|
486
|
+
def jaw_s_RD(self):
|
|
487
|
+
return self.length/2 * (1 + self._cos_yR)
|
|
488
|
+
|
|
489
|
+
def open_jaws(self, keep_tilts=False):
|
|
490
|
+
self.jaw_L = None
|
|
491
|
+
self.jaw_R = None
|
|
492
|
+
if not keep_tilts:
|
|
493
|
+
self.tilt = 0
|
|
494
|
+
|
|
495
|
+
def _update_tilts(self):
|
|
496
|
+
if self.side != 'right':
|
|
497
|
+
self._sin_yL = (self.jaw_LD - self.jaw_LU) / self.length
|
|
498
|
+
self._cos_yL = np.sqrt(1 - self._sin_yL**2)
|
|
499
|
+
self._tan_yL = self._sin_yL / self._cos_yL
|
|
500
|
+
if self.side != 'left':
|
|
501
|
+
self._sin_yR = (self.jaw_RD - self.jaw_RU) / self.length
|
|
502
|
+
self._cos_yR = np.sqrt(1 - self._sin_yR**2)
|
|
503
|
+
self._tan_yR = self._sin_yR / self._cos_yR
|
|
504
|
+
|
|
505
|
+
def _update_gaps(self, only_L=False, only_R=False):
|
|
506
|
+
# If we had set a value for the gap manually, this needs to be updated
|
|
507
|
+
# as well after setting the jaw
|
|
508
|
+
if self._gap_L_set_manually() and not only_R:
|
|
509
|
+
self._gap_L = self.gap_L
|
|
510
|
+
if self._gap_R_set_manually() and not only_L:
|
|
511
|
+
self._gap_R = self.gap_R
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
# Tilt attributes
|
|
515
|
+
# ===============
|
|
284
516
|
|
|
285
|
-
|
|
286
|
-
def angle_R(self, angle_R):
|
|
287
|
-
anglerad_R = angle_R / 180. * np.pi
|
|
288
|
-
self.sin_zR = np.sin(anglerad_R)
|
|
289
|
-
self.cos_zR = np.cos(anglerad_R)
|
|
517
|
+
# TODO: tilts are in rad! Do we want that? It's a bit inconsistent with angle which is in deg...
|
|
290
518
|
|
|
291
519
|
@property
|
|
292
|
-
def
|
|
293
|
-
|
|
520
|
+
def tilt(self):
|
|
521
|
+
if self.side == 'left':
|
|
522
|
+
return self.tilt_L
|
|
523
|
+
elif self.side == 'right':
|
|
524
|
+
return self.tilt_R
|
|
525
|
+
else:
|
|
526
|
+
return self.tilt_L if self.tilt_L==self.tilt_R else [self.tilt_L, self.tilt_R]
|
|
294
527
|
|
|
295
|
-
@
|
|
296
|
-
def
|
|
297
|
-
|
|
528
|
+
@tilt.setter
|
|
529
|
+
def tilt(self, val):
|
|
530
|
+
if not hasattr(val, '__iter__') or len(val) == 1:
|
|
531
|
+
val = val[0] if hasattr(val, '__iter__') else val
|
|
532
|
+
if self.side == 'left':
|
|
533
|
+
self.tilt_L = val
|
|
534
|
+
elif self.side == 'right':
|
|
535
|
+
self.tilt_R = val
|
|
536
|
+
else:
|
|
537
|
+
self.tilt_L = val
|
|
538
|
+
self.tilt_R = val
|
|
539
|
+
elif len(val) == 2:
|
|
540
|
+
self.tilt_L = val[0]
|
|
541
|
+
self.tilt_R = val[1]
|
|
542
|
+
else:
|
|
543
|
+
raise ValueError
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def tilt_L(self):
|
|
547
|
+
if self.side != 'right':
|
|
548
|
+
return round(np.arctan2(self._sin_yL, self._cos_yL), 10)
|
|
549
|
+
|
|
550
|
+
@tilt_L.setter # This assumes jaw_L remains fixed (hence jaw_LU and jaw_LD change)
|
|
551
|
+
def tilt_L(self, val):
|
|
552
|
+
if self.side == 'right' and val != 0:
|
|
553
|
+
val = 0
|
|
554
|
+
print("Warning: Ignored value for tilt_L (right-sided collimator).")
|
|
555
|
+
if val != 0:
|
|
556
|
+
print("Warning: Setting a tilt does not preserve the hierarchy, as there "
|
|
557
|
+
+ "will always be one corner that tightens (the tilt is applied at "
|
|
558
|
+
+ "the centre of the jaw).")
|
|
559
|
+
self._sin_yL = np.sin(val)
|
|
560
|
+
self._cos_yL = np.cos(val)
|
|
561
|
+
self._tan_yL = np.tan(val)
|
|
562
|
+
jaw_L = (self._jaw_LU + self._jaw_LD) / 2
|
|
563
|
+
self._jaw_LD = jaw_L + self._sin_yL * self.length / 2.
|
|
564
|
+
self._jaw_LU = jaw_L - self._sin_yL * self.length / 2.
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def tilt_R(self):
|
|
568
|
+
if self.side != 'left':
|
|
569
|
+
return round(np.arctan2(self._sin_yR, self._cos_yR), 10)
|
|
570
|
+
|
|
571
|
+
@tilt_R.setter # This assumes jaw_R remains fixed (hence jaw_RU and jaw_RD change)
|
|
572
|
+
def tilt_R(self, val):
|
|
573
|
+
if self.side == 'left' and val != 0:
|
|
574
|
+
val = 0
|
|
575
|
+
print("Warning: Ignored value for tilt_R (left-sided collimator).")
|
|
576
|
+
if val != 0:
|
|
577
|
+
print("Warning: Setting a tilt does not preserve the hierarchy, as there "
|
|
578
|
+
+ "will always be one corner that tightens (the tilt is applied at "
|
|
579
|
+
+ "the centre of the jaw).")
|
|
580
|
+
self._sin_yR = np.sin(val)
|
|
581
|
+
self._cos_yR = np.cos(val)
|
|
582
|
+
self._tan_yR = np.tan(val)
|
|
583
|
+
jaw_R = (self._jaw_RU + self._jaw_RD) / 2
|
|
584
|
+
self._jaw_RD = jaw_R + self._sin_yR * self.length / 2.
|
|
585
|
+
self._jaw_RU = jaw_R - self._sin_yR * self.length / 2.
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
# Optics
|
|
589
|
+
# ======
|
|
590
|
+
|
|
591
|
+
@property
|
|
592
|
+
def optics(self):
|
|
593
|
+
return self._optics
|
|
594
|
+
|
|
595
|
+
def optics_ready(self):
|
|
596
|
+
return self.emittance is not None and self.optics is not None
|
|
597
|
+
|
|
598
|
+
def assign_optics(self, *, nemitt_x=None, nemitt_y=None, beta_gamma_rel=None, name=None, twiss=None,
|
|
599
|
+
twiss_upstream=None, twiss_downstream=None):
|
|
600
|
+
from xcoll.beam_elements import _all_collimator_classes
|
|
601
|
+
if not isinstance(self, _all_collimator_classes):
|
|
602
|
+
raise ValueError("Please install collimator before assigning optics.")
|
|
603
|
+
if nemitt_x is None:
|
|
604
|
+
if self.nemitt_x is None:
|
|
605
|
+
raise ValueError("Need to provide `nemitt_x`.")
|
|
606
|
+
else:
|
|
607
|
+
self.nemitt_x = nemitt_x
|
|
608
|
+
if nemitt_y is None:
|
|
609
|
+
if self.nemitt_y is None:
|
|
610
|
+
raise ValueError("Need to provide `nemitt_y`.")
|
|
611
|
+
else:
|
|
612
|
+
self.nemitt_y = nemitt_y
|
|
613
|
+
if beta_gamma_rel is None:
|
|
614
|
+
raise ValueError("Need to provide `beta_gamma_rel`.")
|
|
615
|
+
if twiss is None:
|
|
616
|
+
if twiss_upstream is None or twiss_downstream is None:
|
|
617
|
+
raise ValueError("Use either `twiss` or `twiss_upstream` and `twiss_downstream`.")
|
|
618
|
+
if name is None:
|
|
619
|
+
if len(twiss_upstream.name) > 1 or len(twiss_downstream.name) > 1:
|
|
620
|
+
raise ValueError("Need to provide `name` or twisses that are a single row each.")
|
|
621
|
+
tw_up = twiss_upstream
|
|
622
|
+
tw_down = twiss_downstream
|
|
623
|
+
else:
|
|
624
|
+
tw_up = twiss_upstream.rows[name]
|
|
625
|
+
tw_down = twiss_downstream.rows[name]
|
|
626
|
+
elif twiss_downstream is not None or twiss_downstream is not None:
|
|
627
|
+
raise ValueError("Use either `twiss` or `twiss_upstream` and `twiss_downstream`.")
|
|
628
|
+
elif name is None:
|
|
629
|
+
raise ValueError("When using `twiss`, need to provide the name as well.")
|
|
630
|
+
else:
|
|
631
|
+
tw_up = twiss.rows[name]
|
|
632
|
+
tw_down = twiss.rows[twiss.mask[[name]]+1]
|
|
633
|
+
if not np.isclose(tw_up.s[0] + self.length, tw_down.s[0]):
|
|
634
|
+
raise ValueError(f"Downstream twiss not compatible with length {self.length}m.")
|
|
635
|
+
self._optics = {
|
|
636
|
+
'upstream': tw_up,
|
|
637
|
+
'downstream': tw_down,
|
|
638
|
+
'beta_gamma_rel': beta_gamma_rel
|
|
639
|
+
}
|
|
640
|
+
self._apply_optics()
|
|
641
|
+
|
|
642
|
+
@property
|
|
643
|
+
def nemitt_x(self):
|
|
644
|
+
if self._nemitt_x == 0:
|
|
645
|
+
return None
|
|
646
|
+
return self._nemitt_x
|
|
647
|
+
|
|
648
|
+
@nemitt_x.setter
|
|
649
|
+
def nemitt_x(self, val):
|
|
650
|
+
if val is None:
|
|
651
|
+
val = 0
|
|
652
|
+
elif val <= 0:
|
|
653
|
+
raise ValueError(f"The field `nemitt_x` should be positive, but got {val}.")
|
|
654
|
+
self._nemitt_x = val
|
|
655
|
+
self._apply_optics()
|
|
656
|
+
|
|
657
|
+
@property
|
|
658
|
+
def nemitt_y(self):
|
|
659
|
+
if self._nemitt_y == 0:
|
|
660
|
+
return None
|
|
661
|
+
return self._nemitt_y
|
|
662
|
+
|
|
663
|
+
@nemitt_y.setter
|
|
664
|
+
def nemitt_y(self, val):
|
|
665
|
+
if val is None:
|
|
666
|
+
val = 0
|
|
667
|
+
elif val <= 0:
|
|
668
|
+
raise ValueError(f"The field `nemitt_y` should be positive, but got {val}.")
|
|
669
|
+
self._nemitt_y = val
|
|
670
|
+
self._apply_optics()
|
|
671
|
+
|
|
672
|
+
@property
|
|
673
|
+
def emittance(self):
|
|
674
|
+
if self.nemitt_x is not None and self.nemitt_y is not None:
|
|
675
|
+
if np.isclose(self.nemitt_x, self.nemitt_y):
|
|
676
|
+
return self.nemitt_x
|
|
677
|
+
else:
|
|
678
|
+
return [self.nemitt_x, self.nemitt_y]
|
|
679
|
+
|
|
680
|
+
@emittance.setter
|
|
681
|
+
def emittance(self, val):
|
|
682
|
+
if val is None:
|
|
683
|
+
self._nemitt_x = 0
|
|
684
|
+
self._nemitt_y = 0
|
|
685
|
+
else:
|
|
686
|
+
if not hasattr(val, '__iter__'):
|
|
687
|
+
val = [val]
|
|
688
|
+
if len(val) == 1:
|
|
689
|
+
val = [val[0], val[0]]
|
|
690
|
+
assert len(val) == 2
|
|
691
|
+
if val[0] <= 0 or val[1] <= 0:
|
|
692
|
+
raise ValueError(f"The field `emittance` should be positive, but got {val}.")
|
|
693
|
+
self._nemitt_x = val[0]
|
|
694
|
+
self._nemitt_y = val[1]
|
|
695
|
+
self._apply_optics()
|
|
696
|
+
|
|
697
|
+
@property
|
|
698
|
+
def sigma(self):
|
|
699
|
+
if self.optics_ready():
|
|
700
|
+
betx = self.optics[self.align]['betx'][0]
|
|
701
|
+
bety = self.optics[self.align]['bety'][0]
|
|
702
|
+
sigma_x = np.sqrt(betx*self.nemitt_x/self.optics['beta_gamma_rel'])
|
|
703
|
+
sigma_y = np.sqrt(bety*self.nemitt_y/self.optics['beta_gamma_rel'])
|
|
704
|
+
if hasattr(self, '_cos_zL'):
|
|
705
|
+
sigma_L = np.sqrt((sigma_x*self._cos_zL)**2 + (sigma_y*self._sin_zL)**2)
|
|
706
|
+
sigma_R = np.sqrt((sigma_x*self._cos_zR)**2 + (sigma_y*self._sin_zR)**2)
|
|
707
|
+
return [sigma_L, sigma_R], [sigma_x, sigma_y]
|
|
708
|
+
else: # crystal
|
|
709
|
+
sigma = np.sqrt((sigma_x*self._cos_z)**2 + (sigma_y*self._sin_z)**2)
|
|
710
|
+
return sigma, [sigma_x, sigma_y]
|
|
711
|
+
|
|
712
|
+
@property
|
|
713
|
+
def co(self):
|
|
714
|
+
if self.optics_ready():
|
|
715
|
+
x = self.optics[self.align]['x'][0]
|
|
716
|
+
y = self.optics[self.align]['y'][0]
|
|
717
|
+
if hasattr(self, '_cos_zL'):
|
|
718
|
+
co_L = x*self._cos_zL + y*self._sin_zL
|
|
719
|
+
co_R = x*self._cos_zR + y*self._sin_zR
|
|
720
|
+
return [co_L, co_R], [x, y]
|
|
721
|
+
else: # crystal
|
|
722
|
+
co = x*self._cos_z + y*self._sin_z
|
|
723
|
+
return co, [x, y]
|
|
724
|
+
|
|
725
|
+
@property
|
|
726
|
+
def divergence(self):
|
|
727
|
+
if self.optics_ready():
|
|
728
|
+
alfx = self.optics[self.align]['alfx'][0]
|
|
729
|
+
alfy = self.optics[self.align]['alfy'][0]
|
|
730
|
+
betx = self.optics[self.align]['betx'][0]
|
|
731
|
+
bety = self.optics[self.align]['bety'][0]
|
|
732
|
+
divx = -np.sqrt(self.nemitt_x/self.optics['beta_gamma_rel']/betx)*alfx
|
|
733
|
+
divy = -np.sqrt(self.nemitt_y/self.optics['beta_gamma_rel']/bety)*alfy
|
|
734
|
+
if hasattr(self, '_cos_zL'):
|
|
735
|
+
if self.side != 'right':
|
|
736
|
+
return divx if abs(self.angle_L) < 1e-6 else divy
|
|
737
|
+
else:
|
|
738
|
+
return divx if abs(self.angle_R) < 1e-6 else divy
|
|
739
|
+
else:
|
|
740
|
+
return divx if abs(self.angle) < 1e-6 else divy
|
|
741
|
+
|
|
742
|
+
@property
|
|
743
|
+
def align(self):
|
|
744
|
+
if self._align == 0:
|
|
745
|
+
return 'upstream'
|
|
746
|
+
elif self._align == 1:
|
|
747
|
+
return 'downstream'
|
|
748
|
+
else:
|
|
749
|
+
raise ValueError(f"The attribute `align` can only be 'upstream' or "
|
|
750
|
+
+f"'downstream', but stored as {self._align}.")
|
|
751
|
+
|
|
752
|
+
@align.setter
|
|
753
|
+
def align(self, val):
|
|
754
|
+
if val == 'upstream':
|
|
755
|
+
self._align = 0
|
|
756
|
+
elif val == 'downstream':
|
|
757
|
+
self._align = 1
|
|
758
|
+
else:
|
|
759
|
+
raise ValueError(f"The attribute `align` can only be 'upstream' or "
|
|
760
|
+
+f"'downstream', but got {val}.")
|
|
761
|
+
self._apply_optics()
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
# Gap attributes
|
|
765
|
+
# ==============
|
|
766
|
+
|
|
767
|
+
@property
|
|
768
|
+
def gap(self):
|
|
769
|
+
if self.gap_L is None and self.gap_R is None:
|
|
770
|
+
return None
|
|
771
|
+
elif self.gap_R is not None and self.gap_L == -self.gap_R:
|
|
772
|
+
return self.gap_L
|
|
773
|
+
else:
|
|
774
|
+
return [self.gap_L, self.gap_R]
|
|
775
|
+
|
|
776
|
+
@gap.setter
|
|
777
|
+
def gap(self, val):
|
|
778
|
+
if not hasattr(val, '__iter__') or len(val) == 1:
|
|
779
|
+
val = val[0] if hasattr(val, '__iter__') else val
|
|
780
|
+
if self.side == 'left':
|
|
781
|
+
self.gap_L = val
|
|
782
|
+
elif self.side == 'right':
|
|
783
|
+
# self.gap_R = -val if val is not None else None
|
|
784
|
+
self.gap_R = val
|
|
785
|
+
else:
|
|
786
|
+
self.gap_L = val
|
|
787
|
+
self.gap_R = -val if val is not None else None
|
|
788
|
+
return
|
|
789
|
+
elif len(val) == 2:
|
|
790
|
+
if not hasattr(val[0], '__iter__') \
|
|
791
|
+
and not hasattr(val[1], '__iter__'):
|
|
792
|
+
if val[0] is not None and val[1] is not None:
|
|
793
|
+
if val[0] <= val[1]:
|
|
794
|
+
raise ValueError(f"The attribute `gap_L` should be larger "
|
|
795
|
+
+ f"than `gap_R` but got {val}.")
|
|
796
|
+
self.gap_L = val[0]
|
|
797
|
+
self.gap_R = val[1]
|
|
798
|
+
return
|
|
799
|
+
# If we got here, val is incompatible
|
|
800
|
+
raise ValueError(f"The attribute `gap` should be of the form `gap` or "
|
|
801
|
+
+ f"`[gap_L, gap_R]`, but got {val}.")
|
|
802
|
+
|
|
803
|
+
@property
|
|
804
|
+
def gap_L(self):
|
|
805
|
+
if self.side != 'right':
|
|
806
|
+
if self.optics_ready() and self.jaw_L is not None:
|
|
807
|
+
return round((self.jaw_L - self.co[0][0])/self.sigma[0][0], 6)
|
|
808
|
+
elif not self._gap_L_set_manually():
|
|
809
|
+
return None
|
|
810
|
+
else:
|
|
811
|
+
return self._gap_L
|
|
812
|
+
|
|
813
|
+
@gap_L.setter
|
|
814
|
+
def gap_L(self, val):
|
|
815
|
+
if val is None:
|
|
816
|
+
val = OPEN_GAP
|
|
817
|
+
self.jaw_L = None
|
|
818
|
+
elif val <= 0:
|
|
819
|
+
raise ValueError(f"The field `gap_L` should be positive, but got {val}.")
|
|
820
|
+
self._gap_L = val
|
|
821
|
+
self._apply_optics(only_L=True)
|
|
298
822
|
|
|
299
823
|
@property
|
|
300
|
-
def
|
|
301
|
-
|
|
824
|
+
def gap_R(self):
|
|
825
|
+
if self.side != 'left':
|
|
826
|
+
if self.optics_ready() and self.jaw_R is not None:
|
|
827
|
+
return round((self.jaw_R - self.co[0][1])/self.sigma[0][1], 6)
|
|
828
|
+
elif not self._gap_R_set_manually():
|
|
829
|
+
return None
|
|
830
|
+
else:
|
|
831
|
+
return self._gap_R
|
|
832
|
+
|
|
833
|
+
@gap_R.setter
|
|
834
|
+
def gap_R(self, val):
|
|
835
|
+
if val is None:
|
|
836
|
+
val = -OPEN_GAP
|
|
837
|
+
self.jaw_R = None
|
|
838
|
+
elif val >= 0:
|
|
839
|
+
raise ValueError(f"The field `gap_R` should be negative, but got {val}.")
|
|
840
|
+
self._gap_R = val
|
|
841
|
+
self._apply_optics(only_R=True)
|
|
842
|
+
|
|
843
|
+
@property
|
|
844
|
+
def gap_LU(self):
|
|
845
|
+
if self.gap_L is not None and self.optics_ready():
|
|
846
|
+
return round(self._gap_L - self._sin_yL * self.length / 2. / self.sigma[0][0], 6)
|
|
302
847
|
|
|
303
|
-
@
|
|
304
|
-
def
|
|
305
|
-
|
|
848
|
+
@property
|
|
849
|
+
def gap_LD(self):
|
|
850
|
+
if self.gap_L is not None and self.optics_ready():
|
|
851
|
+
return round(self._gap_L + self._sin_yL * self.length / 2. / self.sigma[0][0], 6)
|
|
306
852
|
|
|
307
853
|
@property
|
|
308
|
-
def
|
|
309
|
-
|
|
854
|
+
def gap_RU(self):
|
|
855
|
+
if self.gap_R is not None and self.optics_ready():
|
|
856
|
+
return round(self._gap_R - self._sin_yR * self.length / 2. / self.sigma[0][1], 6)
|
|
857
|
+
|
|
858
|
+
@property
|
|
859
|
+
def gap_RD(self):
|
|
860
|
+
if self.gap_R is not None and self.optics_ready():
|
|
861
|
+
return round(self._gap_R + self._sin_yR * self.length / 2. / self.sigma[0][1], 6)
|
|
862
|
+
|
|
863
|
+
def _gap_L_set_manually(self):
|
|
864
|
+
return not np.isclose(self._gap_L, OPEN_GAP)
|
|
310
865
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
866
|
+
def _gap_R_set_manually(self):
|
|
867
|
+
return not np.isclose(self._gap_R, -OPEN_GAP)
|
|
868
|
+
|
|
869
|
+
def _apply_optics(self, only_L=False, only_R=False):
|
|
870
|
+
if self.optics_ready():
|
|
871
|
+
# Only if we have set a value for the gap manually, this needs to be updated
|
|
872
|
+
if self._gap_L_set_manually() and not only_R:
|
|
873
|
+
self.jaw_L = self._gap_L * self.sigma[0][0] + self.co[0][0]
|
|
874
|
+
if self._gap_R_set_manually() and not only_L:
|
|
875
|
+
self.jaw_R = self._gap_R * self.sigma[0][1] + self.co[0][1]
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
# Other attributes
|
|
879
|
+
# ================
|
|
315
880
|
|
|
316
881
|
@property
|
|
317
882
|
def side(self):
|
|
@@ -319,55 +884,94 @@ class BaseCollimator(xt.BeamElement):
|
|
|
319
884
|
return 'both'
|
|
320
885
|
elif self._side == 1:
|
|
321
886
|
return 'left'
|
|
322
|
-
elif self._side ==
|
|
887
|
+
elif self._side == -1:
|
|
323
888
|
return 'right'
|
|
324
889
|
|
|
325
890
|
@side.setter
|
|
326
|
-
def side(self,
|
|
327
|
-
|
|
891
|
+
def side(self, val):
|
|
892
|
+
if isinstance(val, str):
|
|
893
|
+
if val.lower() == 'both' or val == '+-' or val == '-+':
|
|
894
|
+
self._side = 0
|
|
895
|
+
return
|
|
896
|
+
elif val.lower() == 'left' or val.lower() == 'l' or val == '+':
|
|
897
|
+
self._side = 1
|
|
898
|
+
self.gap_R = None
|
|
899
|
+
return
|
|
900
|
+
elif val.lower() == 'right' or val.lower() == 'r' or val == '-':
|
|
901
|
+
self._side = -1
|
|
902
|
+
self.gap_L = None
|
|
903
|
+
return
|
|
904
|
+
raise ValueError(f"Unkown setting {val} for 'side'! Choose from "
|
|
905
|
+
+ f"('left', 'L', '+'), ('right', 'R', '-'), or ('both', '+-').")
|
|
328
906
|
|
|
329
|
-
# TODO: tilts are in rad! Do we want that? It's a bit inconsistent with angle which is in deg...
|
|
330
|
-
# ==============================================================================================
|
|
331
907
|
@property
|
|
332
|
-
def
|
|
333
|
-
|
|
334
|
-
return round(np.arctan2(self.sin_yL, self.cos_yL), 10)
|
|
335
|
-
|
|
336
|
-
@tilt_L.setter
|
|
337
|
-
def tilt_L(self, tilt_L):
|
|
338
|
-
# anglerad_L = tilt_L / 180. * np.pi
|
|
339
|
-
anglerad_L = tilt_L
|
|
340
|
-
self.sin_yL = np.sin(anglerad_L)
|
|
341
|
-
self.cos_yL = np.cos(anglerad_L)
|
|
342
|
-
self.tan_yL = np.tan(anglerad_L)
|
|
908
|
+
def active_length(self):
|
|
909
|
+
raise ValueError("`active_length`is deprecated. Please use `length`.")
|
|
343
910
|
|
|
344
911
|
@property
|
|
345
|
-
def
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
@tilt_R.setter
|
|
350
|
-
def tilt_R(self, tilt_R):
|
|
351
|
-
# anglerad_R = tilt_R / 180. * np.pi
|
|
352
|
-
anglerad_R = tilt_R
|
|
353
|
-
self.sin_yR = np.sin(anglerad_R)
|
|
354
|
-
self.cos_yR = np.cos(anglerad_R)
|
|
355
|
-
self.tan_yR = np.tan(anglerad_R)
|
|
912
|
+
def inactive_front(self):
|
|
913
|
+
raise ValueError("`inactive_front`is deprecated. Collimators now only "
|
|
914
|
+
+ "contain their active length (implemented as `length`).")
|
|
356
915
|
|
|
357
916
|
@property
|
|
358
|
-
def
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
917
|
+
def inactive_back(self):
|
|
918
|
+
raise ValueError("`inactive_back`is deprecated. Collimators now only "
|
|
919
|
+
+ "contain their active length (implemented as `length`).")
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
# Methods
|
|
923
|
+
# =======
|
|
924
|
+
|
|
925
|
+
def _verify_consistency(self):
|
|
926
|
+
BaseBlock._verify_consistency(self)
|
|
927
|
+
# Verify angles
|
|
928
|
+
if abs(self.angle_L - self.angle_R) >= 90.:
|
|
929
|
+
raise ValueError("Angles of both jaws differ more than 90 degrees!")
|
|
930
|
+
ang = abs(np.arccos(self._cos_zL))
|
|
931
|
+
ang = np.pi - ang if ang > np.pi/2 else ang
|
|
932
|
+
assert np.isclose(ang, abs(np.arcsin(self._sin_zL)))
|
|
933
|
+
ang = abs(np.arccos(self._cos_zR))
|
|
934
|
+
ang = np.pi - ang if ang > np.pi/2 else ang
|
|
935
|
+
assert np.isclose(ang, abs(np.arcsin(self._sin_zR)))
|
|
936
|
+
if np.isclose(self.angle_L, self.angle_R):
|
|
937
|
+
assert self._jaws_parallel == True
|
|
938
|
+
assert np.isclose(self._sin_zL, self._sin_zR)
|
|
939
|
+
assert np.isclose(self._cos_zL, self._cos_zR)
|
|
940
|
+
assert np.isclose(self._sin_zDiff, 0.)
|
|
941
|
+
assert np.isclose(self._cos_zDiff, 1.)
|
|
942
|
+
else:
|
|
943
|
+
assert self._jaws_parallel == False
|
|
944
|
+
assert np.isclose(self._sin_zDiff, self._cos_zL*self._sin_zR - self._sin_zL*self._cos_zR)
|
|
945
|
+
assert np.isclose(self._cos_zDiff, self._cos_zL*self._cos_zR + self._sin_zL*self._sin_zR)
|
|
946
|
+
if self.side == 'both' and abs(self.tilt_L - self.tilt_R) >= 90.:
|
|
947
|
+
raise ValueError("Tilts of both jaws differ more than 90 degrees!")
|
|
948
|
+
if self.side != 'right':
|
|
949
|
+
ang = abs(np.arccos(self._cos_yL))
|
|
950
|
+
ang = np.pi - ang if ang > np.pi/2 else ang
|
|
951
|
+
assert np.isclose(ang, abs(np.arcsin(self._sin_yL)))
|
|
952
|
+
assert np.isclose(self._sin_yL/self._cos_yL, self._tan_yL)
|
|
953
|
+
if self.side != 'left':
|
|
954
|
+
ang = abs(np.arccos(self._cos_yR))
|
|
955
|
+
ang = np.pi - ang if ang > np.pi/2 else ang
|
|
956
|
+
assert np.isclose(ang, abs(np.arcsin(self._sin_yR)))
|
|
957
|
+
assert np.isclose(self._sin_yR/self._cos_yR, self._tan_yR)
|
|
958
|
+
|
|
959
|
+
# Verify bools
|
|
960
|
+
assert self._side in [-1, 1, 0]
|
|
961
|
+
assert isinstance(self._jaws_parallel, bool) or self._jaws_parallel in [0, 1]
|
|
364
962
|
|
|
365
963
|
def jaw_func(self, pos):
|
|
366
964
|
positions = ['LU', 'RU', 'LD', 'RD']
|
|
965
|
+
if pos[0] == 'L':
|
|
966
|
+
other_pos = 'R'
|
|
967
|
+
else:
|
|
968
|
+
other_pos = 'L'
|
|
969
|
+
point_x = ((getattr(self, 'jaw_' + pos[0]) * getattr(self, 'cos_z' + pos[0])
|
|
970
|
+
+ getattr(self, 'jaw_' + other_pos) * getattr(self, 'cos_z' + other_pos))/2)
|
|
971
|
+
point_y = ((getattr(self, 'jaw_' + pos[0]) * getattr(self, 'sin_z' + pos[0])
|
|
972
|
+
+ getattr(self, 'jaw_' + other_pos) * getattr(self, 'sin_z' + other_pos))/2)
|
|
367
973
|
if not pos in positions:
|
|
368
974
|
raise ValueError(f"Parameter {pos} needs to be one of {positions}!")
|
|
369
|
-
point_x = getattr(self, 'ref_x')
|
|
370
|
-
point_y = getattr(self, 'ref_y')
|
|
371
975
|
sinz = getattr(self, 'sin_z' + pos[0])
|
|
372
976
|
cosz = getattr(self, 'cos_z' + pos[0])
|
|
373
977
|
# Shift to the jaw, whose location is given as the shortest distance:
|
|
@@ -376,32 +980,383 @@ class BaseCollimator(xt.BeamElement):
|
|
|
376
980
|
return lambda t: (point_x - t*sinz, point_y + t*cosz)
|
|
377
981
|
|
|
378
982
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
983
|
+
class BaseCrystal(BaseBlock):
|
|
984
|
+
_xofields = {**BaseBlock._xofields,
|
|
985
|
+
# Collimator angle
|
|
986
|
+
'_sin_z': xo.Float64,
|
|
987
|
+
'_cos_z': xo.Float64,
|
|
988
|
+
# Jaw corners (this is the x-coordinate in the rotated frame)
|
|
989
|
+
'_jaw_U': xo.Float64,
|
|
990
|
+
# Tilts (not superfluous)
|
|
991
|
+
'_sin_y': xo.Float64,
|
|
992
|
+
'_cos_y': xo.Float64,
|
|
993
|
+
'_tan_y': xo.Float64,
|
|
994
|
+
# Other
|
|
995
|
+
'_side': xo.Int8,
|
|
996
|
+
# These are not used in C, but need to be an xofield to get them in the to_dict:
|
|
997
|
+
'_align': xo.Int8,
|
|
998
|
+
'_gap': xo.Float64,
|
|
999
|
+
'_nemitt_x': xo.Float64,
|
|
1000
|
+
'_nemitt_y': xo.Float64,
|
|
1001
|
+
# Crystal specific
|
|
1002
|
+
'_bending_radius': xo.Float64,
|
|
1003
|
+
'_bending_angle': xo.Float64,
|
|
1004
|
+
'width': xo.Float64,
|
|
1005
|
+
'height': xo.Float64
|
|
1006
|
+
# 'thick': xo.Float64
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
isthick = BaseBlock.isthick
|
|
1010
|
+
allow_track = BaseBlock.allow_track
|
|
1011
|
+
behaves_like_drift = BaseBlock.behaves_like_drift
|
|
1012
|
+
skip_in_loss_location_refinement = BaseBlock.skip_in_loss_location_refinement
|
|
1013
|
+
allow_double_sided = False
|
|
1014
|
+
|
|
1015
|
+
_skip_in_to_dict = [*BaseBlock._skip_in_to_dict, *[f for f in _xofields if f.startswith('_')]]
|
|
1016
|
+
_store_in_to_dict = [*BaseBlock._store_in_to_dict, 'angle', 'jaw', 'tilt', 'gap', 'side', 'align', 'emittance',
|
|
1017
|
+
'bending_radius', 'bending_angle']
|
|
1018
|
+
|
|
1019
|
+
_depends_on = [BaseCollimator]
|
|
1020
|
+
|
|
1021
|
+
_internal_record_class = BaseBlock._internal_record_class
|
|
1022
|
+
|
|
1023
|
+
# This is an abstract class and cannot be instantiated
|
|
1024
|
+
def __new__(cls, *args, **kwargs):
|
|
1025
|
+
if cls == BaseCrystal:
|
|
1026
|
+
raise Exception("Abstract class `BaseCrystal` cannot be instantiated!")
|
|
1027
|
+
instance = super().__new__(cls)
|
|
1028
|
+
return instance
|
|
1029
|
+
|
|
1030
|
+
def __init__(self, **kwargs):
|
|
1031
|
+
to_assign = {}
|
|
1032
|
+
if '_xobject' not in kwargs:
|
|
1033
|
+
# Set side
|
|
1034
|
+
to_assign['side'] = kwargs.pop('side', 'left')
|
|
1035
|
+
|
|
1036
|
+
# Set angle
|
|
1037
|
+
to_assign['angle'] = kwargs.pop('angle', 0)
|
|
1038
|
+
|
|
1039
|
+
# Set jaw
|
|
1040
|
+
if 'jaw' in kwargs:
|
|
1041
|
+
for key in ['jaw_U', 'jaw_D', 'gap']:
|
|
1042
|
+
if key in kwargs:
|
|
1043
|
+
raise ValueError(f"Cannot use both `jaw` and `{key}`!")
|
|
1044
|
+
to_assign['jaw'] = kwargs.pop('jaw')
|
|
1045
|
+
elif 'jaw_D' in kwargs:
|
|
1046
|
+
for key in ['tilt', 'gap']:
|
|
1047
|
+
if key in kwargs:
|
|
1048
|
+
raise ValueError(f"Cannot use both `jaw_D` with `{key}`!")
|
|
1049
|
+
if not 'jaw_U' in kwargs:
|
|
1050
|
+
raise ValueError("Need to provide `jaw_U` when setting `jaw_D`!")
|
|
1051
|
+
to_assign['jaw_U'] = kwargs.pop('jaw_U')
|
|
1052
|
+
to_assign['jaw_D'] = kwargs.pop('jaw_D')
|
|
1053
|
+
elif 'jaw_U' in kwargs:
|
|
1054
|
+
if 'gap' in kwargs:
|
|
1055
|
+
raise ValueError(f"Cannot use both `jaw_U` and `gap`!")
|
|
1056
|
+
to_assign['jaw_U'] = kwargs.pop('jaw_U')
|
|
1057
|
+
# TODO: correct sign if right-sided
|
|
1058
|
+
kwargs.setdefault('_jaw_U', OPEN_JAW)
|
|
1059
|
+
|
|
1060
|
+
# Set gap
|
|
1061
|
+
if 'gap' in kwargs:
|
|
1062
|
+
for key in ['jaw', 'jaw_U', 'jaw_D']:
|
|
1063
|
+
if key in kwargs:
|
|
1064
|
+
raise ValueError(f"Cannot use both `gap` and `{key}`!")
|
|
1065
|
+
to_assign['gap'] = kwargs.pop('gap')
|
|
1066
|
+
# TODO: correct sign if right-sided
|
|
1067
|
+
kwargs.setdefault('_gap', OPEN_GAP)
|
|
1068
|
+
|
|
1069
|
+
# Set tilt
|
|
1070
|
+
if 'jaw_D' not in kwargs:
|
|
1071
|
+
to_assign['tilt'] = kwargs.pop('tilt', 0)
|
|
1072
|
+
|
|
1073
|
+
# Set others
|
|
1074
|
+
to_assign['align'] = kwargs.pop('align', 'upstream')
|
|
1075
|
+
to_assign['emittance'] = kwargs.pop('emittance', None)
|
|
1076
|
+
kwargs.setdefault('active', True)
|
|
1077
|
+
|
|
1078
|
+
# Set crystal specific
|
|
1079
|
+
if 'bending_angle' in kwargs:
|
|
1080
|
+
if 'bending_radius' in kwargs:
|
|
1081
|
+
raise ValueError("Need to choose between 'bending_radius' and 'bending_angle'!")
|
|
1082
|
+
to_assign['bending_angle'] = kwargs.pop('bending_angle')
|
|
1083
|
+
else:
|
|
1084
|
+
to_assign['bending_radius'] = kwargs.pop('bending_radius', 1)
|
|
1085
|
+
kwargs.setdefault('width', 0)
|
|
1086
|
+
kwargs.setdefault('height', 0)
|
|
1087
|
+
|
|
1088
|
+
xt.BeamElement.__init__(self, **kwargs)
|
|
1089
|
+
# Careful: non-xofields are not passed correctly between copy's / to_dict. This messes with flags etc..
|
|
1090
|
+
# We also have to manually initialise them for xobject generation
|
|
1091
|
+
if not hasattr(self, '_optics'):
|
|
1092
|
+
self._optics = None
|
|
1093
|
+
for key, val in to_assign.items():
|
|
1094
|
+
setattr(self, key, val)
|
|
1095
|
+
if self.side == 'right':
|
|
1096
|
+
if np.isclose(self._jaw_U, OPEN_JAW):
|
|
1097
|
+
self._jaw_U *= -1
|
|
1098
|
+
if np.isclose(self._gap, OPEN_GAP):
|
|
1099
|
+
self._gap *= -1
|
|
1100
|
+
self._verify_consistency()
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
# Main crystal angle
|
|
1104
|
+
# ==================
|
|
1105
|
+
|
|
1106
|
+
@property
|
|
1107
|
+
def angle(self):
|
|
1108
|
+
return round(np.rad2deg(np.arctan2(self._sin_z, self._cos_z)), 10)
|
|
1109
|
+
|
|
1110
|
+
@angle.setter
|
|
1111
|
+
def angle(self, val):
|
|
1112
|
+
self._sin_z = np.sin(np.deg2rad(val))
|
|
1113
|
+
self._cos_z = np.cos(np.deg2rad(val))
|
|
1114
|
+
self._apply_optics()
|
|
1115
|
+
|
|
406
1116
|
|
|
1117
|
+
# Jaw attributes
|
|
1118
|
+
# ==============
|
|
1119
|
+
|
|
1120
|
+
@property
|
|
1121
|
+
def jaw(self):
|
|
1122
|
+
return self.jaw_U
|
|
1123
|
+
|
|
1124
|
+
@jaw.setter
|
|
1125
|
+
def jaw(self, val):
|
|
1126
|
+
if val is None:
|
|
1127
|
+
val = self._side*OPEN_JAW
|
|
1128
|
+
self.jaw_U = val
|
|
1129
|
+
|
|
1130
|
+
@property
|
|
1131
|
+
def jaw_U(self):
|
|
1132
|
+
if not np.isclose(self._jaw_U, self._side*OPEN_JAW, atol=1.e-10): # open position
|
|
1133
|
+
return self._jaw_U
|
|
1134
|
+
|
|
1135
|
+
@jaw_U.setter # This moves both jaw_LU and jaw_LD in parallel
|
|
1136
|
+
def jaw_U(self, val):
|
|
1137
|
+
if val is None:
|
|
1138
|
+
raise ValueError("Cannot set corner to None! Use open_jaws() or set jaw to None.")
|
|
1139
|
+
self._jaw_U = val
|
|
1140
|
+
self._update_gaps()
|
|
1141
|
+
|
|
1142
|
+
@property
|
|
1143
|
+
def jaw_D(self):
|
|
1144
|
+
if not np.isclose(self._jaw_U, self._side*OPEN_JAW, atol=1.e-10): # open position
|
|
1145
|
+
length = self.length
|
|
1146
|
+
if self._side*self.bending_radius < 0:
|
|
1147
|
+
# Correction for inner corner point
|
|
1148
|
+
length -= self.width*np.sin(abs(self._bending_angle))
|
|
1149
|
+
shift = np.tan(self._bending_angle/2)*self._cos_y + self._sin_y
|
|
1150
|
+
return self._jaw_U + length*shift
|
|
1151
|
+
|
|
1152
|
+
@jaw_D.setter # This moves both jaw_LU and jaw_LD in parallel
|
|
1153
|
+
def jaw_D(self, val):
|
|
1154
|
+
if val is None:
|
|
1155
|
+
self.tilt = 0
|
|
1156
|
+
else:
|
|
1157
|
+
shift = (val - self._jaw_U )/self.length * np.cos(self._bending_angle/2)
|
|
1158
|
+
self._sin_y = shift*np.cos(self._bending_angle/2)
|
|
1159
|
+
self._sin_y -= np.sin(self._bending_angle/2)*np.sqrt(1 - shift**2)
|
|
1160
|
+
self._cos_y = np.sqrt(1 - self._sin_y**2)
|
|
1161
|
+
self._tan_y = self._sin_y / self._cos_y
|
|
1162
|
+
self._update_gaps()
|
|
1163
|
+
|
|
1164
|
+
def open_jaws(self, keep_tilts=False):
|
|
1165
|
+
self.jaw = None
|
|
1166
|
+
if not keep_tilts:
|
|
1167
|
+
self.tilt = 0
|
|
1168
|
+
|
|
1169
|
+
def _update_gaps(self):
|
|
1170
|
+
# If we had set a value for the gap manually, this needs to be updated
|
|
1171
|
+
# as well after setting the jaw
|
|
1172
|
+
if self._gap_set_manually():
|
|
1173
|
+
self._gap = self.gap
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
# Tilt attributes
|
|
1177
|
+
# ===============
|
|
1178
|
+
|
|
1179
|
+
# TODO: tilts are in rad! Do we want that? It's a bit inconsistent with angle which is in deg...
|
|
1180
|
+
|
|
1181
|
+
@property
|
|
1182
|
+
def tilt(self):
|
|
1183
|
+
return round(np.arctan2(self._sin_y, self._cos_y), 10)
|
|
1184
|
+
|
|
1185
|
+
@tilt.setter # This assumes jaw_U remains fixed (hence jaw_D changes)
|
|
1186
|
+
def tilt(self, val):
|
|
1187
|
+
if self.side == 'left':
|
|
1188
|
+
if val < min(0, self.bending_angle/2):
|
|
1189
|
+
print("Warning: Setting a negative tilt does not preserve the hierarchy, as the "
|
|
1190
|
+
+ "crystal tightens towards the beam.")
|
|
1191
|
+
elif self.side == 'right':
|
|
1192
|
+
if val > min(0, -self.bending_angle/2):
|
|
1193
|
+
print("Warning: Setting a positive tilt does not preserve the hierarchy, as the "
|
|
1194
|
+
+ "crystal tightens towards the beam.")
|
|
1195
|
+
self._sin_y = np.sin(val)
|
|
1196
|
+
self._cos_y = np.cos(val)
|
|
1197
|
+
self._tan_y = np.tan(val)
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
# Optics
|
|
1201
|
+
# ======
|
|
1202
|
+
|
|
1203
|
+
@property
|
|
1204
|
+
def optics(self):
|
|
1205
|
+
return self._optics
|
|
1206
|
+
|
|
1207
|
+
def optics_ready(self):
|
|
1208
|
+
return BaseCollimator.optics_ready(self)
|
|
1209
|
+
|
|
1210
|
+
def assign_optics(self, *, nemitt_x=None, nemitt_y=None, beta_gamma_rel=None, name=None, twiss=None,
|
|
1211
|
+
twiss_upstream=None, twiss_downstream=None):
|
|
1212
|
+
return BaseCollimator.assign_optics(self, nemitt_x=nemitt_x, nemitt_y=nemitt_y,
|
|
1213
|
+
beta_gamma_rel=beta_gamma_rel, name=name, twiss=twiss,
|
|
1214
|
+
twiss_upstream=twiss_upstream, twiss_downstream=twiss_downstream)
|
|
1215
|
+
|
|
1216
|
+
@property
|
|
1217
|
+
def nemitt_x(self):
|
|
1218
|
+
return BaseCollimator.nemitt_x.fget(self)
|
|
1219
|
+
|
|
1220
|
+
@nemitt_x.setter
|
|
1221
|
+
def nemitt_x(self, val):
|
|
1222
|
+
BaseCollimator.nemitt_x.fset(self, val)
|
|
1223
|
+
|
|
1224
|
+
@property
|
|
1225
|
+
def nemitt_y(self):
|
|
1226
|
+
return BaseCollimator.nemitt_y.fget(self)
|
|
1227
|
+
|
|
1228
|
+
@nemitt_y.setter
|
|
1229
|
+
def nemitt_y(self, val):
|
|
1230
|
+
BaseCollimator.nemitt_y.fset(self, val)
|
|
1231
|
+
|
|
1232
|
+
@property
|
|
1233
|
+
def emittance(self):
|
|
1234
|
+
return BaseCollimator.emittance.fget(self)
|
|
1235
|
+
|
|
1236
|
+
@emittance.setter
|
|
1237
|
+
def emittance(self, val):
|
|
1238
|
+
BaseCollimator.emittance.fset(self, val)
|
|
1239
|
+
|
|
1240
|
+
@property
|
|
1241
|
+
def sigma(self):
|
|
1242
|
+
return BaseCollimator.sigma.fget(self)
|
|
1243
|
+
|
|
1244
|
+
@property
|
|
1245
|
+
def co(self):
|
|
1246
|
+
return BaseCollimator.co.fget(self)
|
|
1247
|
+
|
|
1248
|
+
@property
|
|
1249
|
+
def divergence(self):
|
|
1250
|
+
return BaseCollimator.divergence.fget(self)
|
|
1251
|
+
|
|
1252
|
+
@property
|
|
1253
|
+
def align(self):
|
|
1254
|
+
return BaseCollimator.align.fget(self)
|
|
1255
|
+
|
|
1256
|
+
@align.setter
|
|
1257
|
+
def align(self, val):
|
|
1258
|
+
if val != 'upstream':
|
|
1259
|
+
raise NotImplementedError("Crystals cannot be aligned to the downstream optics!")
|
|
1260
|
+
BaseCollimator.align.fset(self, val)
|
|
1261
|
+
|
|
1262
|
+
def align_to_beam_divergence(self):
|
|
1263
|
+
if not self.optics_ready():
|
|
1264
|
+
raise ValueError("Optics not assigned! Cannot align to beam divergence.")
|
|
1265
|
+
if self.gap is None:
|
|
1266
|
+
raise ValueError("Need to set `gap` to align to beam divergence.")
|
|
1267
|
+
self.tilt = self.divergence * self.gap
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
# Gap attributes
|
|
1271
|
+
# ==============
|
|
1272
|
+
|
|
1273
|
+
@property
|
|
1274
|
+
def gap(self):
|
|
1275
|
+
if self.optics_ready() and self.jaw_U is not None:
|
|
1276
|
+
return round((self.jaw_U - self.co[0])/self.sigma[0], 6)
|
|
1277
|
+
elif not self._gap_set_manually():
|
|
1278
|
+
return None
|
|
1279
|
+
else:
|
|
1280
|
+
return self._gap
|
|
1281
|
+
|
|
1282
|
+
# Gap is always positive, irrespective of the side
|
|
1283
|
+
@gap.setter
|
|
1284
|
+
def gap(self, val):
|
|
1285
|
+
if val is None:
|
|
1286
|
+
val = OPEN_GAP
|
|
1287
|
+
self.jaw = None
|
|
1288
|
+
if hasattr(val, '__iter__'):
|
|
1289
|
+
raise ValueError("The attribute `gap` should be a single value, not a list.")
|
|
1290
|
+
if val <= 0:
|
|
1291
|
+
raise ValueError(f"The field `gap` should be positive, but got {val}.")
|
|
1292
|
+
self._gap = val
|
|
1293
|
+
self._apply_optics()
|
|
1294
|
+
|
|
1295
|
+
def _gap_set_manually(self):
|
|
1296
|
+
return not np.isclose(self._gap, OPEN_GAP)
|
|
1297
|
+
|
|
1298
|
+
def _apply_optics(self):
|
|
1299
|
+
if self.optics_ready():
|
|
1300
|
+
# Only if we have set a value for the gap manually, this needs to be updated
|
|
1301
|
+
if self._gap_set_manually():
|
|
1302
|
+
self.jaw_U = self._gap * self.sigma[0] + self.co[0]
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
# Other attributes
|
|
1306
|
+
# ================
|
|
1307
|
+
|
|
1308
|
+
@property
|
|
1309
|
+
def bending_radius(self):
|
|
1310
|
+
return self._bending_radius
|
|
1311
|
+
|
|
1312
|
+
@bending_radius.setter
|
|
1313
|
+
def bending_radius(self, bending_radius):
|
|
1314
|
+
bending_angle = np.arcsin(self.length/bending_radius)
|
|
1315
|
+
if abs(bending_angle) > np.pi/2:
|
|
1316
|
+
raise ValueError("Bending angle cannot be larger than 90 degrees!")
|
|
1317
|
+
self._bending_radius = bending_radius
|
|
1318
|
+
self._bending_angle = bending_angle
|
|
1319
|
+
|
|
1320
|
+
@property
|
|
1321
|
+
def bending_angle(self):
|
|
1322
|
+
return self._bending_angle
|
|
1323
|
+
|
|
1324
|
+
@bending_angle.setter
|
|
1325
|
+
def bending_angle(self, bending_angle):
|
|
1326
|
+
if abs(bending_angle) > np.pi/2:
|
|
1327
|
+
raise ValueError("Bending angle cannot be larger than 90 degrees!")
|
|
1328
|
+
self._bending_angle = bending_angle
|
|
1329
|
+
self._bending_radius = self.length / np.sin(bending_angle)
|
|
1330
|
+
|
|
1331
|
+
@property
|
|
1332
|
+
def side(self):
|
|
1333
|
+
return BaseCollimator.side.fget(self)
|
|
1334
|
+
|
|
1335
|
+
@side.setter
|
|
1336
|
+
def side(self, val):
|
|
1337
|
+
temp = self._side
|
|
1338
|
+
BaseCollimator.side.fset(self, val)
|
|
1339
|
+
if self._side == 0:
|
|
1340
|
+
self._side = temp
|
|
1341
|
+
raise ValueError("Crystal cannot be two-sided! Please set `side` "
|
|
1342
|
+
+ "to 'left' or 'right'.")
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
# Methods
|
|
1346
|
+
# =======
|
|
1347
|
+
|
|
1348
|
+
def _verify_consistency(self):
|
|
1349
|
+
BaseBlock._verify_consistency(self)
|
|
1350
|
+
# Verify angles
|
|
1351
|
+
ang = abs(np.arccos(self._cos_z))
|
|
1352
|
+
ang = np.pi - ang if ang > np.pi/2 else ang
|
|
1353
|
+
assert np.isclose(ang, abs(np.arcsin(self._sin_z)))
|
|
1354
|
+
ang = abs(np.arccos(self._cos_y))
|
|
1355
|
+
ang = np.pi - ang if ang > np.pi/2 else ang
|
|
1356
|
+
assert np.isclose(ang, abs(np.arcsin(self._sin_y)))
|
|
1357
|
+
assert np.isclose(self._sin_y/self._cos_y, self._tan_y)
|
|
1358
|
+
# Verify bools
|
|
1359
|
+
assert self._side in [-1, 1]
|
|
1360
|
+
# Crystal specific
|
|
1361
|
+
assert np.isclose(self._bending_angle, np.arcsin(self.length/self._bending_radius))
|
|
407
1362
|
|