# This file is part of MAUS: http://micewww.pp.rl.ac.uk/projects/maus # # MAUS is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # MAUS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with MAUS. If not, see . """ Handler for making plots of beam envelopes based on a list of ellipses and a list of Hits (representing the reference trajectory). """ import math import copy import numpy from xboa.bunch import Bunch import xboa.common as common from plot_setup import PlotSetup # pylint: disable=W0403, F0401 class Plotter: """ Plotter makes a histogram and a list of graphs and Draws them on the specified canvas. Superimpose apertures over the plot - for envelope and mean "x" and "y" the physical apertures are superimposed. For everything else, apertures are pushed into the top of the canvas (making a reference z trajectory). """ def __init__(self, plot_options, canvas, #pylint: disable=R0913 ref_list, ellipse_list, magnets): """ Plot a graph, specified by plot_options, on canvas. Plot data is taken from ref_list (for the reference trajectory), ellipse_list (for the beam envelope) and magnets (for apertures) - plot_options: map specifying variable type (should be a class) - canvas: ROOT canvas where plots will be made - ref_list: list of reference hits makes the reference trajectory - ellipse_list: list of beam ellipses defines the envelope - magnets: list of magnets for plotting apertures """ self.ref_list = ref_list self.ellipse_list = ellipse_list self.var_type = PlotSetup.get_variable_type(plot_options) self.first_var = PlotSetup.get_first_var(plot_options) self.x_var = [] self.y_var = [] self.magnets = magnets try: if self.var_type != "mean" and len(self.ellipse_list) <= 1: print "Failed to propagate ellipses" elif self.var_type == "mean" and len(self.ref_list) <= 1: print "Failed to propagate reference trajectory" else: self.set_variables_function()() except KeyError: print "Did not recognise plot option "+str(self.var_type) if plot_options[0]["plot_apertures"]: self.get_apertures() plot_name = self.var_type+":"+self.first_var canvas.cd() hist, graph_list = common.make_root_multigraph(plot_name, self.x_var, "z [mm]", self.y_var, plot_name) hist.Draw() for graph in graph_list: graph.SetLineColor(1) graph.Draw('l') canvas.Update() def get_apertures(self): """ Convert from magnet to a set of apertures and add them to the list of graphs """ if len(self.magnets) == 0: return axis = self._axis() magnet_x, magnet_y = [], [] for a_magnet in self.magnets: x_list = [ a_magnet["aperture"][axis], a_magnet["aperture"][axis], a_magnet["outer"][axis], a_magnet["outer"][axis], a_magnet["aperture"][axis], ] if self.position_is_start_dict[a_magnet["field_type"]]: z_list = [ 0., +a_magnet["aperture"]["z"], +a_magnet["outer"]["z"], 0., 0., ] else: z_list = [ -a_magnet["aperture"]["z"]/2., +a_magnet["aperture"]["z"]/2., +a_magnet["outer"]["z"]/2., -a_magnet["outer"]["z"]/2., -a_magnet["aperture"]["z"]/2., ] if self._show_physical_apertures(): # as in physical scalings z_pair, x_pair = self._transform(z_list, x_list, a_magnet) magnet_x += z_pair magnet_y += x_pair else: z_list = [z_pos+a_magnet["position"]["z"] for z_pos in z_list] magnet_x.append(z_list) magnet_y.append(x_list) if not self._show_physical_apertures(): # non-physical scalings self._get_scaled_apertures(magnet_y, axis) self.x_var += magnet_x self.y_var += magnet_y def _axis(self): """ Return the axis to be used for plotting magnet apertures """ if self.first_var in ["x", "y"]: return self.first_var else: return "x" def _transform(self, points_x, points_y, a_magnet): """ Rotate magnet aperture boxes For plotting rotated physical apertures """ axis = self._axis() phi = a_magnet["rotation"][{"x":"y", "y":"x"}[axis]] translation = [a_magnet["position"]["z"], a_magnet["position"][axis]] list_points_x = [points_x, copy.deepcopy(points_x)] list_points_y = [points_y, copy.deepcopy(points_y)] list_points_y[1] = [-y for y in list_points_y[1]] for i in range(2): for j in range(len(points_x)): x_temp = list_points_x[i][j]*math.cos(phi)+\ list_points_y[i][j]*math.sin(phi) y_temp = list_points_y[i][j]*math.cos(phi)-\ list_points_x[i][j]*math.sin(phi) list_points_x[i][j] = x_temp+translation[0] list_points_y[i][j] = y_temp+translation[1] return list_points_x, list_points_y def _curtail(self): """ Require ref_list and ellipse_list have same length by cutting hits or ellipses off the end (depending on which is longer) """ if len(self.ellipse_list) == len(self.ref_list): return if len(self.ellipse_list) > len(self.ref_list): self.ellipse_list = self.ellipse_list[0:len(self.ref_list)] else: self.ref_list = self.ref_list[0:len(self.ellipse_list)] def get_means(self): """Fill data for mean from ref_list and ellipse_list""" self.y_var = [[reference[self.first_var] \ for reference in self.ref_list]] self.x_var = [[reference['z'] for reference in self.ref_list]] def get_rms(self): """Fill data for RMS from ref_list and ellipse_list""" self._curtail() my_var = self.ellipse_var.index(self.first_var)+1 self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[ell.get_element(my_var, my_var)**0.5 \ for ell in self.ellipse_list]] def get_envelope(self): """ Fill data for envelope from ref_list and ellipse_list Envelope has a positive element and a negative element """ self.get_rms() self.x_var *= 2 rms_list = self.y_var[0] self.y_var = [range(len(rms_list)), range(len(rms_list))] for i, rms in enumerate(rms_list): if i < len(self.ref_list): ref_var = self.ref_list[i][self.first_var] self.y_var[0][i] = ref_var-rms self.y_var[1][i] = ref_var+rms def get_beta(self): """Fill data for optical beta from ref_list and ellipse_list""" self._curtail() self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[]] for i, ell in enumerate(self.ellipse_list): ref = self.ref_list[i] beta = 0 axes = self.axis_dict[self.first_var] axes = [self.ellipse_var.index(var)+1 for var in axes] for axis in axes: beta += ell.get_element(axis, axis)/float(len(axes)) beta /= ref['mass']*self._get_emittance(ref, ell)/ref['p'] self.y_var[0].append(beta) def get_alpha(self): """Fill data for alpha from ref_list and ellipse_list""" self._curtail() self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[]] for i, ell in enumerate(self.ellipse_list): ref = self.ref_list[i] alpha = 0 axes = self.axis_dict[self.first_var] axes = [self.ellipse_var.index(var)+1 for var in axes] for axis in axes: alpha -= ell.get_element(axis+1, axis)/float(len(axes)) alpha /= ref['mass']*self._get_emittance(ref, ell) self.y_var[0].append(alpha) def get_gamma(self): """Fill data for optical gamma from ref_list and ellipse_list""" self._curtail() self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[]] for i, ell in enumerate(self.ellipse_list): ref = self.ref_list[i] gamma = 0 axes = self.axis_dict[self.first_var] axes = [self.ellipse_var.index(var)+1 for var in axes] for axis in axes: gamma -= ell.get_element(axis+1, axis+1)/float(len(axes)) gamma /= ref['mass']*self._get_emittance(ref, ell)*ref['p'] self.y_var[0].append(gamma) def get_emittance(self): """Fill data for emittance from ref_list and ellipse_list""" self._curtail() self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[]] for i, ell in enumerate(self.ellipse_list): self.y_var[0].append(self._get_emittance(self.ref_list[i], ell)) def get_dispersion(self): """Fill data for D from ref_list and ellipse_list""" self._curtail() self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[]] for i, ell in enumerate(self.ellipse_list): energy = self.ref_list[i]['energy'] axis_str = self.axis_dict[self.first_var][0] axis_int = self.ellipse_var.index(axis_str)+1 disp = ell.get_element(2, axis_int)*energy/ell.get_element(2, 2) self.y_var[0].append(disp) def get_dispersion_prime(self): """Fill data for D' from ref_list and ellipse_list""" self._curtail() self.x_var = [[ref['z'] for ref in self.ref_list]] self.y_var = [[]] for i, ell in enumerate(self.ellipse_list): energy = self.ref_list[i]['energy'] axis_str = self.axis_dict[self.first_var][0] axis_int = self.ellipse_var.index(axis_str)+2 disp_p = ell.get_element(2, axis_int)*energy/ell.get_element(2, 2) self.y_var[0].append(disp_p) def _get_emittance(self, ref, ellipse): """Get the beam emittance for given reference hit and ellipse""" axis_list = self.axis_dict[self.first_var] el_list = self.el_dict[self.first_var] bunch = Bunch.new_from_hits([ref]) cov_matrix = [None]*len(el_list) for cov_i, ell_i in enumerate(el_list): cov_matrix[cov_i] = [None]*len(el_list) for cov_j, ell_j in enumerate(el_list): cov_matrix[cov_i][cov_j] = ellipse.get_element(ell_i, ell_j) cov_matrix = numpy.array(cov_matrix) emittance = bunch.get_emittance(axis_list, cov_matrix) return emittance def _show_physical_apertures(self): """ Return true if we should show physical apertures; return false if we should show scaled apertures """ return self.var_type in ["envelope", "mean", "