Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Beam docstrings and type hints #284

Merged
merged 7 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This is a major release with significant upgrades under the hood of Cheetah. Des

### 🚨 Breaking Changes

- Cheetah is now vectorised. This means that you can run multiple simulations in parallel by passing a batch of beams and settings, resulting a number of interfaces being changed. For Cheetah developers this means that you now have to account for an arbitrary-dimensional tensor of most of the properties of you element, rather than a single value, vector or whatever else a property was before. (see #116, #157, #170, #172, #173, #198, #208, #213, #215, #218, #229, #233, #258, #265) (@jank324, @cr-xu, @hespe, @roussel-ryan)
- The fifth particle coordinate `s` is renamed to `tau`. Now Cheetah uses the canonical variables in phase space $(x,px=\frac{P_x}{p_0},y,py, \tau=c\Delta t, \delta=\Delta E/{p_0 c})$. In addition, the trailing "s" was removed from some beam property names (e.g. `beam.xs` becomes `beam.x`). (see #163) (@cr-xu)
- Cheetah is now vectorised. This means that you can run multiple simulations in parallel by passing a batch of beams and settings, resulting a number of interfaces being changed. For Cheetah developers this means that you now have to account for an arbitrary-dimensional tensor of most of the properties of you element, rather than a single value, vector or whatever else a property was before. (see #116, #157, #170, #172, #173, #198, #208, #213, #215, #218, #229, #233, #258, #265, #284) (@jank324, @cr-xu, @hespe, @roussel-ryan)
- The fifth particle coordinate `s` is renamed to `tau`. Now Cheetah uses the canonical variables in phase space $(x,px=\frac{P_x}{p_0},y,py, \tau=c\Delta t, \delta=\Delta E/{p_0 c})$. In addition, the trailing "s" was removed from some beam property names (e.g. `beam.xs` becomes `beam.x`). (see #163, #284) (@cr-xu, @hespe)
- `Screen` no longer blocks the beam (by default). To return to old behaviour, set `Screen.is_blocking = True`. (see #208) (@jank324, @roussel-ryan)

### 🚀 Features
Expand Down Expand Up @@ -51,6 +51,7 @@ This is a major release with significant upgrades under the hood of Cheetah. Des
- Add CI runs for macOS (arm64) and Windows (see #226) (@cr-xu, @jank324, @hespe)
- Clean up CI pipelines (see #243, #244) (@jank324)
- Fix logo display in README (see #252) (@jank324)
- Made `Beam` an abstract class (see #284) (@hespe)

## [v0.6.3](https://github.com/desy-ml/cheetah/releases/tag/v0.6.3) (2024-03-28)

Expand Down
50 changes: 42 additions & 8 deletions cheetah/particles/beam.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from abc import ABC, abstractmethod
from typing import Optional

import torch
Expand All @@ -7,7 +8,7 @@
electron_mass_eV = physical_constants["electron mass energy equivalent in MeV"][0] * 1e6


class Beam(nn.Module):
class Beam(ABC, nn.Module):
r"""
Parent class to represent a beam of particles. You should not instantiate this
class directly, but use one of the subclasses.
Expand Down Expand Up @@ -35,6 +36,7 @@ class directly, but use one of the subclasses.
empty = "I'm an empty beam!"

@classmethod
@abstractmethod
def from_parameters(
cls,
mu_x: Optional[torch.Tensor] = None,
Expand All @@ -52,6 +54,8 @@ def from_parameters(
cor_tau: Optional[torch.Tensor] = None,
energy: Optional[torch.Tensor] = None,
total_charge: Optional[torch.Tensor] = None,
device=None,
dtype=torch.float32,
) -> "Beam":
"""
Create beam that with given beam parameters.
Expand All @@ -75,10 +79,14 @@ def from_parameters(
:param cor_tau: Correlation between tau and p.
:param energy: Reference energy of the beam in eV.
:param total_charge: Total charge of the beam in C.
:param device: Device to create the beam on. If set to `"auto"` a CUDA GPU is
selected if available. The CPU is used otherwise.
:param dtype: Data type of the beam.
"""
raise NotImplementedError

@classmethod
@abstractmethod
def from_twiss(
cls,
beta_x: Optional[torch.Tensor] = None,
Expand All @@ -87,9 +95,9 @@ def from_twiss(
beta_y: Optional[torch.Tensor] = None,
alpha_y: Optional[torch.Tensor] = None,
emittance_y: Optional[torch.Tensor] = None,
sigma_s: Optional[torch.Tensor] = None,
sigma_tau: Optional[torch.Tensor] = None,
sigma_p: Optional[torch.Tensor] = None,
cor_s: Optional[torch.Tensor] = None,
cor_tau: Optional[torch.Tensor] = None,
energy: Optional[torch.Tensor] = None,
total_charge: Optional[torch.Tensor] = None,
device=None,
Expand All @@ -104,24 +112,29 @@ def from_twiss(
:param beta_y: Beta function in y direction in meters.
:param alpha_y: Alpha function in y direction in rad.
:param emittance_y: Emittance in y direction in m*rad.
:param sigma_s: Sigma of the particle distribution in s direction in meters.
:param sigma_p: Sigma of the particle distribution in p direction in meters.
:param cor_s: Correlation of the particle distribution in s direction.
:param sigma_tau: Sigma of the particle distribution in longitudinal direction,
in meters.
:param sigma_p: Sigma of the particle distribution in p direction,
dimensionless.
:param cor_tau: Correlation between tau and p.
:param energy: Energy of the beam in eV.
:param total_charge: Total charge of the beam in C.
:param device: Device to create the beam on.
:param device: Device to create the beam on. If set to `"auto"` a CUDA GPU is
selected if available. The CPU is used otherwise.
:param dtype: Data type of the beam.
"""
raise NotImplementedError

@classmethod
@abstractmethod
def from_ocelot(cls, parray) -> "Beam":
"""
Convert an Ocelot ParticleArray `parray` to a Cheetah Beam.
"""
raise NotImplementedError

@classmethod
@abstractmethod
def from_astra(cls, path: str, **kwargs) -> "Beam":
"""Load an Astra particle distribution as a Cheetah Beam."""
raise NotImplementedError
Expand All @@ -140,6 +153,8 @@ def transformed_to(
sigma_p: Optional[torch.Tensor] = None,
energy: Optional[torch.Tensor] = None,
total_charge: Optional[torch.Tensor] = None,
device=None,
dtype=torch.float32,
) -> "Beam":
"""
Create version of this beam that is transformed to new beam parameters.
Expand All @@ -160,6 +175,9 @@ def transformed_to(
dimensionless.
:param energy: Reference energy of the beam in eV.
:param total_charge: Total charge of the beam in C.
:param device: Device to create the transformed beam on. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the transformed beam.
"""
# Figure out vector dimensions of the original beam and check that passed
# arguments have the same vector dimensions.
Expand Down Expand Up @@ -213,6 +231,8 @@ def transformed_to(
sigma_p=sigma_p,
energy=energy,
total_charge=total_charge,
device=device,
dtype=dtype,
)

@property
Expand All @@ -232,50 +252,62 @@ def parameters(self) -> dict:
}

@property
@abstractmethod
def mu_x(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def sigma_x(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def mu_px(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def sigma_px(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def mu_y(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def sigma_y(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def mu_py(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def sigma_py(self) -> torch.Tensor:
raise NotImplementedError

@property
def mu_s(self) -> torch.Tensor:
@abstractmethod
def mu_tau(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def sigma_tau(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def mu_p(self) -> torch.Tensor:
raise NotImplementedError

@property
@abstractmethod
def sigma_p(self) -> torch.Tensor:
raise NotImplementedError

Expand All @@ -299,11 +331,13 @@ def p0c(self) -> torch.Tensor:
return self.relativistic_beta * self.relativistic_gamma * electron_mass_eV

@property
@abstractmethod
def sigma_xpx(self) -> torch.Tensor:
# The covariance of (x,px) ~ $\sigma_{xpx}$
raise NotImplementedError

@property
@abstractmethod
def sigma_ypy(self) -> torch.Tensor:
raise NotImplementedError

Expand Down
3 changes: 3 additions & 0 deletions cheetah/particles/parameter_beam.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ def transformed_to(
:param sigma_p: Sigma of the particle distribution in p, dimensionless.
:param energy: Reference energy of the beam in eV.
:param total_charge: Total charge of the beam in C.
:param device: Device to create the transformed beam on. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the transformed beam.
"""
device = device if device is not None else self.mu_x.device
dtype = dtype if dtype is not None else self.mu_x.dtype
Expand Down
26 changes: 10 additions & 16 deletions cheetah/particles/particle_beam.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ParticleBeam(Beam):
:param total_charge: Total charge of the beam in C.
:param device: Device to move the beam's particle array to. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the generated particles.
"""

def __init__(
Expand Down Expand Up @@ -55,7 +56,7 @@ def __init__(
@classmethod
def from_parameters(
cls,
num_particles: Optional[torch.Tensor] = None,
num_particles: int = 100_000,
mu_x: Optional[torch.Tensor] = None,
mu_y: Optional[torch.Tensor] = None,
mu_px: Optional[torch.Tensor] = None,
Expand Down Expand Up @@ -99,12 +100,10 @@ def from_parameters(
:total_charge: Total charge of the beam in C.
:param device: Device to move the beam's particle array to. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the generated particles.
"""

# Set default values without function call in function signature
num_particles = (
num_particles if num_particles is not None else torch.tensor(100_000)
)
mu_x = mu_x if mu_x is not None else torch.tensor(0.0)
mu_px = mu_px if mu_px is not None else torch.tensor(0.0)
mu_y = mu_y if mu_y is not None else torch.tensor(0.0)
Expand Down Expand Up @@ -188,7 +187,7 @@ def from_parameters(
@classmethod
def from_twiss(
cls,
num_particles: Optional[torch.Tensor] = None,
num_particles: int = 1_000_000,
beta_x: Optional[torch.Tensor] = None,
alpha_x: Optional[torch.Tensor] = None,
emittance_x: Optional[torch.Tensor] = None,
Expand Down Expand Up @@ -228,9 +227,6 @@ def from_twiss(
), "Arguments must have the same shape."

# Set default values without function call in function signature
num_particles = (
num_particles if num_particles is not None else torch.tensor(1_000_000)
)
beta_x = beta_x if beta_x is not None else torch.full(shape, 0.0)
alpha_x = alpha_x if alpha_x is not None else torch.full(shape, 0.0)
emittance_x = emittance_x if emittance_x is not None else torch.full(shape, 0.0)
Expand Down Expand Up @@ -276,7 +272,7 @@ def from_twiss(
@classmethod
def uniform_3d_ellipsoid(
cls,
num_particles: Optional[torch.Tensor] = None,
num_particles: int = 1_000_000,
radius_x: Optional[torch.Tensor] = None,
radius_y: Optional[torch.Tensor] = None,
radius_tau: Optional[torch.Tensor] = None,
Expand Down Expand Up @@ -310,7 +306,8 @@ def uniform_3d_ellipsoid(
:param sigma_p: Sigma of the particle distribution in p, dimensionless.
:param energy: Reference energy of the beam in eV.
:param total_charge: Total charge of the beam in C.
:param device: Device to move the beam's particle array to.
:param device: Device to move the beam's particle array to. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the generated particles.

:return: ParticleBeam with uniformly distributed particles inside an ellipsoid.
Expand Down Expand Up @@ -343,9 +340,6 @@ def uniform_3d_ellipsoid(
# Set default values without function call in function signature
# NOTE that this does not need to be done for values that are passed to the
# Gaussian beam generation.
num_particles = (
num_particles if num_particles is not None else torch.tensor(1_000_000)
)
radius_x = (
radius_x.expand(vector_shape)
if radius_x is not None
Expand Down Expand Up @@ -414,7 +408,7 @@ def uniform_3d_ellipsoid(
@classmethod
def make_linspaced(
cls,
num_particles: Optional[torch.Tensor] = None,
num_particles: int = 10,
mu_x: Optional[torch.Tensor] = None,
mu_y: Optional[torch.Tensor] = None,
mu_px: Optional[torch.Tensor] = None,
Expand Down Expand Up @@ -450,10 +444,10 @@ def make_linspaced(
:param energy: Energy of the beam in eV.
:param device: Device to move the beam's particle array to. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the generated particles.
"""

# Set default values without function call in function signature
num_particles = num_particles if num_particles is not None else torch.tensor(10)
mu_x = mu_x if mu_x is not None else torch.tensor(0.0)
mu_px = mu_px if mu_px is not None else torch.tensor(0.0)
mu_y = mu_y if mu_y is not None else torch.tensor(0.0)
Expand Down Expand Up @@ -564,7 +558,6 @@ def transformed_to(
"""
Create version of this beam that is transformed to new beam parameters.

:param n: Number of particles to generate.
:param mu_x: Center of the particle distribution on x in meters.
:param mu_y: Center of the particle distribution on y in meters.
:param mu_px: Center of the particle distribution on px, dimensionless.
Expand All @@ -582,6 +575,7 @@ def transformed_to(
:param total_charge: Total charge of the beam in C.
:param device: Device to move the beam's particle array to. If set to `"auto"` a
CUDA GPU is selected if available. The CPU is used otherwise.
:param dtype: Data type of the transformed particles.
"""
device = device if device is not None else self.mu_x.device
dtype = dtype if dtype is not None else self.mu_x.dtype
Expand Down