diff --git a/pyproject.toml b/pyproject.toml index a78776df..c3fb8391 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "xcoll" -version = "0.5.10" +version = "0.5.11" description = "Xsuite collimation package" homepage = "https://github.com/xsuite/xcoll" repository = "https://github.com/xsuite/xcoll" @@ -26,16 +26,16 @@ ruamel-yaml = { version = "^0.17.31", optional = true } numpy = ">=1.0" pandas = ">=1.4" xobjects = ">=0.4.5" -xdeps = ">=0.7.4" -xpart = ">=0.19.1" -xtrack = ">=0.69.7" +xdeps = ">=0.8.1" +xpart = ">=0.19.3" +xtrack = ">=0.70.3" [tool.poetry.dev-dependencies] pytest = ">=7.3" xaux = ">=0.2.1" [tool.poetry.extras] -tests = ["pytest", "ruamel-yaml"] +tests = ["pytest", "ruamel-yaml", "pytest-html", "pytest-xdist"] [build-system] # Needed for pip install -e (BTW: need pip version 22) diff --git a/tests/data/assert_listing.py b/tests/data/assert_listing.py index 47ad0010..bd4ba76e 100755 --- a/tests/data/assert_listing.py +++ b/tests/data/assert_listing.py @@ -6,6 +6,7 @@ import sys from pathlib import Path +from xobjects.context import get_test_contexts path = Path(__file__).parent @@ -25,9 +26,14 @@ def pytest_collection_modifyitems(self, session, config, items): with contextlib.redirect_stdout(open(os.devnull, 'w')): pytest.main(["--collect-only"], plugins=[TestCollector()]) + # do not keep GPU contexts as tests in the listing, as the names can change + collected_tests = [tst for tst in collected_tests + if 'Context' not in tst or 'ContextCpu' in tst] + return set(collected_tests) current_tests = _collect_tests() +# available_contexts = [ctx.__class__.__name__ for ctx in get_test_contexts()] with open(path / 'all_tests.list', 'r') as fid: expected_tests = set([line.replace('\n', '') for line in fid.readlines()]) only_current = current_tests.difference(expected_tests) diff --git a/tests/data/colldb_lhc_run3_ir7.yaml b/tests/data/colldb_lhc_run3_ir7.yaml index 3b494056..5f1703b5 100644 --- a/tests/data/colldb_lhc_run3_ir7.yaml +++ b/tests/data/colldb_lhc_run3_ir7.yaml @@ -15,7 +15,7 @@ emittance: collimators: b1: tcp.d6l7.b1: { <<: *TCP7, angle: 90, material: MoGR, length: 1.565 } - tcp.c6l7.b1: { <<: *TCP7, angle: 0, material: MoGR, tilt: [-250e-6, 250e-6] } + tcp.c6l7.b1: { <<: *TCP7, angle: 0, material: MoGR, tilt: [2.5e-6, -2.5e-6] } tcp.b6l7.b1: { <<: *TCP7, angle: 127.5 } tcsg.a6l7.b1: { <<: *TCSG7, angle: 141.1 } tcpcv.a6l7.b1: { <<: *CRY7, angle: 90, bending_radius: 85.10, width: 5.0e-3, height: 30.0e-3 } @@ -29,7 +29,7 @@ collimators: b2: tcp.d6r7.b2: { <<: *TCP7, angle: 90, material: MoGR, length: 1.565 } - tcp.c6r7.b2: { <<: *TCP7, angle: 0, material: MoGR, tilt: [-250e-6, 250e-6] } + tcp.c6r7.b2: { <<: *TCP7, angle: 0, material: MoGR, tilt: [2.5e-6, -2.5e-6] } tcp.b6r7.b2: { <<: *TCP7, angle: 127.5 } tcsg.a6r7.b2: { <<: *TCSG7, angle: 141.1 } tcpcv.a6r7.b2: { <<: *CRY7, angle: 90, bending_radius: 74.88, width: 5.0e-3, height: 30.0e-3 } diff --git a/tests/data/store_all_tests.py b/tests/data/store_all_tests.py index 1239f17b..f8bcbfce 100755 --- a/tests/data/store_all_tests.py +++ b/tests/data/store_all_tests.py @@ -25,6 +25,10 @@ def pytest_collection_modifyitems(self, session, config, items): with contextlib.redirect_stdout(open(os.devnull, 'w')): pytest.main(["--collect-only"], plugins=[TestCollector()]) +# do not keep GPU contexts as tests in the listing, as the names can change +collected_tests = [tst for tst in collected_tests + if 'Context' not in tst or 'ContextCpu' in tst] + with open(path / 'all_tests.list', 'w') as fid: for line in collected_tests: fid.write(f"{line}\n") diff --git a/tests/run_all.sh b/tests/run_all.sh index fbade8e0..38ca8b84 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -13,7 +13,7 @@ then xsuite-prebuild r # "xsuite-prebuild c" to remove kernels echo "done." - pytest -n 10 -vv --html="pytest_results.html" --self-contained-html + pytest -n 13 -vv --html="pytest_results.html" --self-contained-html else echo "Not all tests are verified. Aborting." exit 1 diff --git a/tests/test_elements.py b/tests/test_elements.py index 17110aa8..4925c27d 100644 --- a/tests/test_elements.py +++ b/tests/test_elements.py @@ -64,7 +64,7 @@ {'field': 'record_exits', 'val': True, 'expected': {'record_impacts': True, 'record_exits': True, 'record_scatterings': True, '_record_interactions': 7}} ] base_user_fields = {} -base_user_fields_read_only = [] +base_user_fields_read_only = ['gemitt_x', 'gemitt_y'] # BaseCollimator base_coll_fields = {**base_fields, diff --git a/tests/test_lossmap.py b/tests/test_lossmap.py index 559486c2..c335b88c 100644 --- a/tests/test_lossmap.py +++ b/tests/test_lossmap.py @@ -24,7 +24,7 @@ [2, 'V', 500, 0.3, True], [1, 'V', 3500, 0.1, False], [2, 'H', 30000, 0.15, False] - ], ids=["B1", "B2V", "B1V_crystals", "B2H_crystals"]) + ], ids=["B1H", "B2V", "B1V_crystals", "B2H_crystals"]) def test_run_lossmap(beam, plane, npart, interpolation, ignore_crystals, test_context): line = xt.Line.from_json(path / f'sequence_lhc_run3_b{beam}.json') @@ -45,12 +45,15 @@ def test_run_lossmap(beam, plane, npart, interpolation, ignore_crystals, test_co line.scattering.enable() line.track(part, num_turns=2) line.scattering.disable() + assert_lossmap(beam, npart, line, part, tcp, interpolation, ignore_crystals, 'EverestCollimator', 'EverestCrystal') - line_is_reversed = True if beam == 2 else False - with flaky_assertions(): +def assert_lossmap(beam, npart, line, part, tcp, interpolation, ignore_crystals, coll_cls, cry_cls): + with flaky_assertions(): + line_is_reversed = True if beam == 2 else False ThisLM = xc.LossMap(line, line_is_reversed=line_is_reversed, part=part, interpolation=interpolation) + print(ThisLM.summary) ThisLM.to_json("lossmap.json") assert Path("lossmap.json").exists() @@ -65,9 +68,10 @@ def test_run_lossmap(beam, plane, npart, interpolation, ignore_crystals, test_co # TODO: check the lossmap quantitaively: rough amount of losses at given positions summ = ThisLM.summary assert list(summ.columns) == ['collname', 'nabs', 'length', 's', 'type'] - assert len(summ[summ.type=='EverestCollimator']) == 10 + assert len(summ[summ.type==coll_cls]) == 10 if not ignore_crystals: - assert len(summ[summ.type=='EverestCrystal']) == 2 + assert len(summ[summ.type==cry_cls]) == 2 + # We want at least 5% absorption on the primary assert summ.loc[summ.collname==tcp,'nabs'].values[0] > 0.05*npart @@ -94,5 +98,3 @@ def test_run_lossmap(beam, plane, npart, interpolation, ignore_crystals, test_co assert np.all([s < lm['machine_length'] for s in lm['aperture']['s']]) assert lm['interpolation'] == interpolation assert lm['reversed'] == line_is_reversed - - diff --git a/tests/test_version.py b/tests/test_version.py index 5bf4e1aa..b59f0fc0 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -6,4 +6,4 @@ from xcoll import __version__ def test_version(): - assert __version__ == '0.5.10' + assert __version__ == '0.5.11' diff --git a/xcoll/beam_elements/base.py b/xcoll/beam_elements/base.py index 634ad3b0..78a8b457 100644 --- a/xcoll/beam_elements/base.py +++ b/xcoll/beam_elements/base.py @@ -12,8 +12,8 @@ from ..general import _pkg_root -OPEN_JAW = 3. -OPEN_GAP = 999. +OPEN_JAW = 3 +OPEN_GAP = 999 class InvalidXcoll(xt.BeamElement): @@ -659,6 +659,11 @@ def nemitt_x(self, val): self._nemitt_x = val self._apply_optics() + @property + def gemitt_x(self): + if self.nemitt_x is not None and self.optics_ready(): + return self.nemitt_x / self.optics['beta_gamma_rel'] + @property def nemitt_y(self): if self._nemitt_y == 0: @@ -674,6 +679,11 @@ def nemitt_y(self, val): self._nemitt_y = val self._apply_optics() + @property + def gemitt_y(self): + if self.nemitt_y is not None and self.optics_ready(): + return self.nemitt_y / self.optics['beta_gamma_rel'] + @property def emittance(self): if self.nemitt_x is not None and self.nemitt_y is not None: @@ -734,8 +744,8 @@ def divergence(self): alfy = self.optics[self.align]['alfy'][0] betx = self.optics[self.align]['betx'][0] bety = self.optics[self.align]['bety'][0] - divx = -np.sqrt(self.nemitt_x/self.optics['beta_gamma_rel']/betx)*alfx - divy = -np.sqrt(self.nemitt_y/self.optics['beta_gamma_rel']/bety)*alfy + divx = -np.sqrt(self.gemitt_x/betx)*alfx + divy = -np.sqrt(self.gemitt_y/bety)*alfy if hasattr(self, '_cos_zL'): if self.side != 'right': return divx if abs(self.angle_L) < 1e-6 else divy @@ -1256,6 +1266,10 @@ def nemitt_x(self): def nemitt_x(self, val): BaseCollimator.nemitt_x.fset(self, val) + @property + def gemitt_x(self): + return BaseCollimator.gemitt_x.fget(self) + @property def nemitt_y(self): return BaseCollimator.nemitt_y.fget(self) @@ -1264,6 +1278,10 @@ def nemitt_y(self): def nemitt_y(self, val): BaseCollimator.nemitt_y.fset(self, val) + @property + def gemitt_y(self): + return BaseCollimator.gemitt_y.fget(self) + @property def emittance(self): return BaseCollimator.emittance.fget(self) diff --git a/xcoll/general.py b/xcoll/general.py index 2e9458ee..d1250bdb 100644 --- a/xcoll/general.py +++ b/xcoll/general.py @@ -12,5 +12,5 @@ # ====================== # Do not change # ====================== -__version__ = '0.5.10' +__version__ = '0.5.11' # ====================== diff --git a/xcoll/initial_distribution.py b/xcoll/initial_distribution.py index 7c1e8ada..f3d55c76 100644 --- a/xcoll/initial_distribution.py +++ b/xcoll/initial_distribution.py @@ -57,8 +57,14 @@ def generate_pencil_on_collimator(line, name, num_particles, *, side='+-', penci if twiss is None: twiss = line.twiss() - # Is it converging or diverging? # TODO: This might change with a tilt!!!!!! - is_converging = twiss[f'alf{plane}', name] > 0 + # Is it converging or diverging? + # TODO: dispersion might change this + # TODO: this should be checked jaw by jaw (we are currently checking the left jaw - watch out for sign of tilt of right jaw) + # TODO: skew collimators + tilt = coll.tilt[0] if isinstance(coll.tilt, list) else coll.tilt + betatron_angle = coll.gap * coll.divergence + tolerance_tilt = 1e-12 # 0.1 urad tolerance on jaw tilt => we prioritise converging + is_converging = tilt + tolerance_tilt >= betatron_angle print(f"Collimator {name} is {'con' if is_converging else 'di'}verging.") beam_sizes = twiss.get_beam_covariance(nemitt_x=coll.nemitt_x, nemitt_y=coll.nemitt_y) diff --git a/xcoll/line_tools.py b/xcoll/line_tools.py index d7721146..ad7c35ea 100644 --- a/xcoll/line_tools.py +++ b/xcoll/line_tools.py @@ -33,7 +33,7 @@ def enable(self): if nemitt_y is None: nemitt_y = el.nemitt_y if not np.isclose(el.nemitt_x, nemitt_x) \ - or not np.isclose(el.nemitt_x, nemitt_x): + or not np.isclose(el.nemitt_y, nemitt_y): raise ValueError("Not all collimators have the same " + "emittance. This is not supported.") if hasattr(el, 'enable_scattering'): @@ -243,7 +243,7 @@ def assign_optics(self, *, nemitt_x=None, nemitt_y=None, twiss=None, tw=None): tw_upstream, tw_downstream = self.get_optics_at(names, twiss=twiss) beta_gamma_rel = self.line.particle_ref._xobject.gamma0[0]*self.line.particle_ref._xobject.beta0[0] for coll in names: - self.line[coll].assign_optics(name=coll, nemitt_x=nemitt_x, nemitt_y=nemitt_x, twiss_upstream=tw_upstream, + self.line[coll].assign_optics(name=coll, nemitt_x=nemitt_x, nemitt_y=nemitt_y, twiss_upstream=tw_upstream, twiss_downstream=tw_downstream, beta_gamma_rel=beta_gamma_rel) def open(self, names=None): @@ -282,22 +282,22 @@ def _get_s_start(self, name, *, length, table=None): def assign_optics_to_collimators(line, nemitt_x=None, nemitt_y=None, twiss=None): warn("The function xcoll.assign_optics_to_collimators() is deprecated and will be " - + "removed in the future. Please use line.scattering.assign_optics() instead.", FutureWarning) + + "removed in the future. Please use line.collimators.assign_optics() instead.", FutureWarning) line.collimators.assign_optics(nemitt_x=nemitt_x, nemitt_y=nemitt_y, twiss=twiss) def get_optics_at(names, *, twiss=None, line=None): warn("The function xcoll.get_optics_at() is deprecated and will be " - + "removed in the future. Please use line.scattering.get_optics_at() instead.", FutureWarning) + + "removed in the future. Please use line.collimators.get_optics_at() instead.", FutureWarning) return line.collimators.get_optics_at(names=names, twiss=twiss) def open_collimators(line, names=None): warn("The function xcoll.open_collimators() is deprecated and will be " - + "removed in the future. Please use line.scattering.open_collimators() instead.", FutureWarning) + + "removed in the future. Please use line.collimators.open_collimators() instead.", FutureWarning) line.collimators.open(names=names) def send_to_parking(line, names=None): warn("The function xcoll.send_to_parking() is deprecated and will be " - + "removed in the future. Please use line.scattering.send_to_parking() instead.", FutureWarning) + + "removed in the future. Please use line.collimators.send_to_parking() instead.", FutureWarning) line.collimators.to_parking(names=names) def enable_scattering(line):