-
Notifications
You must be signed in to change notification settings - Fork 31
MPL: KnownElementsList.plot_survey()
#1040
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| """ | ||
| This file is part of ImpactX | ||
|
|
||
| Copyright 2025 ImpactX contributors | ||
| Authors: Axel Huebl | ||
| License: BSD-3-Clause-LBNL | ||
| """ | ||
|
|
||
|
|
||
| def register_KnownElementsList_extension(kel): | ||
| """KnownElementsList helper methods""" | ||
| from ..madx_to_impactx import read_lattice | ||
| from ..plot.Survey import plot_survey | ||
|
|
||
| # register member functions for KnownElementsList | ||
| kel.load_file = lambda self, madx_file, nslice=1: self.extend( | ||
| read_lattice(madx_file, nslice) | ||
| ) | ||
| kel.plot_survey = plot_survey |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| """ | ||
| This file is part of ImpactX | ||
| Copyright 2025 ImpactX contributors | ||
| Authors: Axel Huebl | ||
| License: BSD-3-Clause-LBNL | ||
| """ | ||
|
|
||
|
|
||
| def get_element_color_palette(palette="cern-lhc", plot_library="mpl"): | ||
| """Return a dictionary with colors for all elements. | ||
| The key is a regex that can be matched against the element type string. TODO TODO | ||
| """ | ||
| color_palette = { | ||
| "cern-lhc": { | ||
| "Quad": "tab:blue", | ||
| "Multipole": "tab:orange", | ||
| "Sbend": "tab:green", | ||
| "CFbend": "tab:olive", # TODO: improve and plot as two on top of each other | ||
| "ConstF": "tab:red", | ||
| "ChrPlasmaLens": "tab:red", | ||
| "SoftSolenoid": "tab:red", | ||
| "TaperedPL": "tab:red", | ||
| "RFCavity": "tab:brown", | ||
| "ShortRF": "tab:brown", | ||
| "Buncher": "tab:purple", | ||
| "Aperture": "black", | ||
| "Kicker": "tab:pink", | ||
| # 'tab:cyan' | ||
| "other": "tab:gray", | ||
|
Comment on lines
+17
to
+31
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To double check, I asked again about the colors according to ChatGPT:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
(visual appearance in the LCLS-II tunnel / gallery)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will add such palettes in a follow-up PR where we can do a careful review and detailed docs.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did some checks on the SPS/LHC complex and there are def some hallucinations in the above responses :) |
||
| } | ||
| } | ||
|
|
||
| colors = color_palette[palette] | ||
|
|
||
| if plot_library != "mpl": | ||
| # remove "tab:" prefix | ||
| for k, v in colors.items(): | ||
| colors[k] = v[4:] | ||
|
|
||
| return colors | ||
|
|
||
|
|
||
| def get_element_color(element_kind: str, palette="cern-lhc", plot_library="mpl"): | ||
| """Get the color for a given element type string.""" | ||
| color_palette = get_element_color_palette(palette, plot_library) | ||
|
|
||
| # sub-string matching of keys | ||
| found_keys = [key for key in color_palette.keys() if key in element_kind] | ||
|
|
||
| if found_keys: | ||
| first_found = found_keys[0] | ||
| return color_palette[first_found] | ||
| else: | ||
| return color_palette["other"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| """ | ||
| This file is part of ImpactX | ||
| Copyright 2025 ImpactX contributors | ||
| Authors: Axel Huebl | ||
| License: BSD-3-Clause-LBNL | ||
| """ | ||
|
|
||
|
|
||
| def plot_survey( | ||
| self, ref=None, ax=None, legend=True, legend_ncols=5, palette="cern-lhc" | ||
| ): | ||
| """Plot over s of all elements in the KnownElementsList. | ||
| The signs of element strengths are determined by the sign of the charge of the reference particle. | ||
ax3l marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| The projection of all element strengths is s-x ("vertical"). | ||
ax3l marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Parameters | ||
| ---------- | ||
| self : ImpactXParticleContainer_* | ||
| The KnownElementsList class in ImpactX | ||
| ref : RefPart | ||
| A reference particle, checked for the charge sign to plot focusing/defocusing strength directions properly. | ||
| ax : matplotlib axes | ||
| A plotting area in matplotlib (called axes there). | ||
| legend: bool | ||
| Plot a legend if true. | ||
| legend_ncols: int | ||
| Number of columns for lattice element types in the legend. | ||
| palette: string | ||
| Color palette. | ||
| Returns | ||
| ------- | ||
| Either populates the matplotlib axes in ax or creates a new axes containing the plot. | ||
| """ | ||
| from math import copysign | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
| from matplotlib.patches import Rectangle | ||
|
|
||
| from .ElementColors import get_element_color | ||
|
|
||
| charge_qe = 1.0 if ref is None else ref.charge_qe | ||
|
|
||
| ax = ax or plt.subplot(111) | ||
|
|
||
| element_lengths = [element.ds for element in self] | ||
|
|
||
| # NumPy 2.1+ (i.e. Python 3.10+): | ||
| # element_s = np.cumulative_sum(element_lengths, include_initial=True) | ||
| # backport: | ||
| element_s = np.insert(np.cumsum(element_lengths), 0, 0) | ||
|
|
||
| ax.hlines(0, 0, element_s[-1], color="black", linestyle="--") | ||
|
|
||
| # plot config | ||
| skip_names = [ | ||
| "Drift", | ||
| "ChrDrift", | ||
| "ExactDrift", | ||
| "Empty", | ||
| "Marker", | ||
| "Source", | ||
| ] | ||
|
|
||
| handles = {} | ||
|
|
||
| for i, element in enumerate(self): | ||
| el_dict = element.to_dict() | ||
| el_type = el_dict["type"] | ||
| if el_type in skip_names: | ||
| continue | ||
|
|
||
| color = get_element_color(el_type, palette=palette) | ||
|
|
||
| y0 = 0 # default start in y for unspecified elements | ||
| height = 0.5 # default height for unspecified elements | ||
|
|
||
| # note the sub-string matching for el_type | ||
| if el_type == "BeamMonitor": | ||
| y0 = -0.5 | ||
| height = 1.0 | ||
| if "Quad" in el_type: | ||
| height = copysign(0.8, el_dict["k"] * charge_qe) | ||
| if "Sbend" in el_type: | ||
| if ref is None: | ||
| height = copysign(0.8, element.rc(ref)) | ||
| else: # guess | ||
| height = copysign(0.8, el_dict["phi"]) | ||
| # TODO: sign dependent, read m_p_scale | ||
| # if el_type == "Kicker": | ||
| # height = copysign(0.8, el_dict["xkick"]) | ||
|
Comment on lines
+92
to
+93
Check noticeCode scanning / CodeQL Commented-out code Note
This comment appears to contain commented-out code.
|
||
|
|
||
| # plot thin elements on top of thick elements | ||
| zorder = 2 | ||
| if element.ds == 0: | ||
| zorder = 3 | ||
|
|
||
| patch = Rectangle( | ||
| (element_s[i], y0), | ||
| element_lengths[i], | ||
| height, | ||
| color=color, | ||
| alpha=0.8, | ||
| zorder=zorder, | ||
| ) | ||
| ax.add_patch(patch) | ||
|
|
||
| handles[el_type] = patch | ||
|
|
||
| if legend: | ||
| labels = list(handles.keys()) | ||
| values = list(handles.values()) | ||
| ax.legend( | ||
| handles=values, | ||
| labels=labels, | ||
| bbox_to_anchor=(0.0, 1.02, 1.0, 0.102), | ||
| loc="lower left", | ||
| ncols=legend_ncols, | ||
| mode="expand", | ||
| borderaxespad=0.0, | ||
| ) | ||
|
|
||
| ax.set_xlabel(r"$s$ [m]") | ||
|
|
||
| ax.set_ylim(-1, 1) | ||
| ax.set_yticks([]) | ||
|
|
||
| ax.set_aspect(1 / 1.618) # golden ratio | ||
|
|
||
| return ax | ||
Uh oh!
There was an error while loading. Please reload this page.