// FILE: SpectrometerAnimation.java // PURPOSE: animation of AMS detector // MODS: McKeeman@zko.dec.com -- 97.11.24 -- original // who@where -- when -- what // METHOD: The AMS detector is a combination of components that interact // with charged high energy particles. This applet visually displays // simulated interactions. The kind of particle and its velocity are // parameters to the simulation. // The controls are in the applet itself and in a separate schematic showing // the track of a particle through the detector. // The particle direction and point of impact are set by manipulating the // simulated track in the AMS schematic window. // The particle velocity, mass and charge are set in the applet. // The main applet class launches a second window for the AMS schematic. // The schematic appears in two views: section and top. Each of the two // views is represented by a separate class. Each view has the means to // display itself by displaying its components. // The sectional view shows the several active components of the detector. // Each component is also represented by a class. Each component has means // to display itself. Each component class carries the characteristic // information about the componet, including its dimensions, as global // fields. // Everything scales to the window size. All dimensions are carried in // type double until they must be converted to int for the graphics display // primitives. The units are mm, taken from the AMS web pages. // TABLE OF CLASSES // SpectrometerAnimation // Physics // Element // XY // Box // Quad // Circle // Art // Titles // Animate // Popup // SideView // TopView // Shield // Magnet // AntiCoincidence // Tracker // TimeOfFlight // Cerenkov // Meter // Particle import java.awt.*; import java.applet.*; public class SpectrometerAnimation extends Applet { // main class public static boolean trace = false; // trace on/off Label title = new Label("Change particle properties here."); public static int massParticle = 1; Label massTitle = new Label("mass"); Button massMore = new Button("+"); TextField mass = new TextField(" 1"); Button massLess = new Button("-"); Panel massData = new Panel(); public static int chargeParticle = 1; Label chargeTitle = new Label("charge"); Button chargeMore = new Button("+"); TextField charge = new TextField(" 1"); Button chargeLess = new Button("-"); Panel chargeData = new Panel(); public static double speedParticle = .8; Label speedTitle = new Label("v/c :"); TextField speed = new TextField("0.x0000000000000000"); Panel speedControl = new Panel(); public static double energyParticle; Label energyTitle = new Label("GeV:"); TextField energy = new TextField("0.00000000000000000"); Panel energyControl = new Panel(); public static double radiusParticle; Label radiusData = new Label("radius 0.00000000000000000000 cm"); Panel radiusControl = new Panel(); Label symbolParticle = new Label("Symbolic particle name: nnnXX"); Panel symbolControl = new Panel(); Label nameTitle = new Label("Atomic name: "); String name; TextField nameParticle = new TextField("xxxxxxxxxx"); Button nameAnti = new Button(" "); Panel nameControl = new Panel(); Animate pic = new Animate(this); // AMS diagram frame public void init() { // lay out applet setBackground(Color.white); setLayout(new GridLayout(8,1)); add(title); massData.add(massTitle); massData.add(massLess); massData.add(mass); massData.add(massMore); add(massData); chargeData.add(chargeTitle); chargeData.add(chargeLess); chargeData.add(charge); chargeData.add(chargeMore); add(chargeData); speedControl.add(speedTitle); speedControl.add(speed); add(speedControl); energyControl.add(energyTitle); energyControl.add(energy); add(energyControl); radiusControl.add(radiusData); add(radiusControl); symbolControl.add(symbolParticle); add(symbolControl); nameControl.add(nameTitle); nameControl.add(nameAnti); nameControl.add(nameParticle); add(nameControl); repaint(); // fix GUI field sizes } public void start() { computeEnergy(); computeRadius(); setLabels(); repaint(); pic.repaint(); } private void computeEnergy() { String ans; energyParticle = Physics.kineticEnergy(massParticle, speedParticle); double gev = Physics.erg2gev(energyParticle); if (Double.isNaN(gev)) { ans = "NaN"; } else if (Double.isInfinite(gev)) { ans = "Infinity"; } else { ans = ""+gev; } if (SpectrometerAnimation.trace) { // trace System.out.println("erg="+energyParticle+" GeV="+gev); } energy.setText(""+ans); // GeV } private void setEnergy() { energy.setText(""+Physics.erg2gev(energyParticle)); // GeV } private void setLabels() { setSpeed(); setMass(); setCharge(); setParticle(); setRadius(); } private void computeSpeed() { speedParticle = Physics.velocity(massParticle, energyParticle); if (SpectrometerAnimation.trace) { // trace System.out.println("v/c="+speedParticle); } setSpeed(); } private void setSpeed() { speed.setText(""+speedParticle); } private void setMass() { mass.setText(""+massParticle); } private void setCharge() { charge.setText(""+chargeParticle); } public void computeRadius() { radiusParticle = Physics.radiusOfCurvature( massParticle, chargeParticle, speedParticle, Math.sin(Animate.theta) ); if (SpectrometerAnimation.trace) { // trace System.out.println("roc="+radiusParticle); } setRadius(); } public void setRadius() { double r = Math.abs(radiusParticle); radiusData.setText("radius = "+r+" cm"); } private void setParticle() { int an = Math.abs(chargeParticle); String name = Element.data[an].symbol; name = massParticle + name; if (chargeParticle < 0) name = "-" + name; name = "Symbolic particle name: " + name; symbolParticle.setText(name); if (chargeParticle < 0) name = "anti"; else name = " "; nameAnti.setLabel(name); name = Element.data[an].name; nameParticle.setText(name); } public boolean handleEvent(Event e) { if (e.target == massMore && e.id == Event.ACTION_EVENT) { massParticle++; computeEnergy(); computeRadius(); setMass(); // make consistent setParticle(); // make consistent pic.repaint(); } else if (e.target == massLess && e.id == Event.ACTION_EVENT) { if (massParticle > Math.abs(chargeParticle)) { massParticle--; computeEnergy(); computeRadius(); setMass(); // make consistent setParticle(); // make consistent pic.repaint(); } } else if (e.target == mass && // user entry (e.id == Event.ACTION_EVENT || e.id == Event.LOST_FOCUS) ) { String m = mass.getText(); int tm = massParticle; try { tm = Integer.valueOf(m).intValue(); // new value } catch (NumberFormatException badInput) { tm = massParticle; // restore value } massParticle = tm; if (massParticle < Math.abs(chargeParticle)) { // constraint massParticle = Math.abs(chargeParticle); } if (massParticle > 300) { // arbitrary constraint massParticle = 300; } computeEnergy(); computeRadius(); setMass(); // make consistent setParticle(); // make consistent pic.repaint(); } else if (e.target == chargeMore && e.id == Event.ACTION_EVENT) { if (chargeParticle < Element.data.length - 1 && chargeParticle < massParticle) { chargeParticle++; if (chargeParticle == 0) chargeParticle++; computeRadius(); setCharge(); // make consistent setParticle(); pic.repaint(); } } else if (e.target == chargeLess && e.id == Event.ACTION_EVENT) { if (-chargeParticle < Element.data.length - 1 && -chargeParticle < massParticle) { chargeParticle--; if (chargeParticle == 0) chargeParticle--; computeRadius(); setCharge(); // make consistent setParticle(); pic.repaint(); } } else if (e.target == charge && // user entry (e.id == Event.ACTION_EVENT || e.id == Event.LOST_FOCUS) ) { String c = charge.getText(); int tc; try { tc = Integer.valueOf(c).intValue(); // new value } catch (NumberFormatException badInput) { tc = chargeParticle; // restore value } if (tc > massParticle) { // constraints tc = massParticle; } else if (-tc > massParticle) { tc = -massParticle; } if (tc >= Element.data.length) { tc = Element.data.length-1; } else if (-tc >= Element.data.length) { tc = -Element.data.length+1; } int an = Math.abs(tc); chargeParticle = tc; computeRadius(); setMass(); // make consistent setCharge(); // make consistent setParticle(); // make consistent pic.repaint(); } else if (e.target == speed && (e.id == Event.ACTION_EVENT // user is looking || e.id == Event.LOST_FOCUS) // elsewhere ) { String s = speed.getText(); double ts = speedParticle; try { ts = Double.valueOf(s).doubleValue(); } catch (NumberFormatException badInput) { ts = speedParticle; } speedParticle = ts; if (speedParticle <= 0) speedParticle = 0; // no negative if (speedParticle > 1.0) { // Einstein says speedParticle = 1.0; // lightspeed } computeEnergy(); // compute energy computeRadius(); // compute roc setSpeed(); // notify user pic.repaint(); } else if (e.target == energy && (e.id == Event.ACTION_EVENT // user is looking || e.id == Event.LOST_FOCUS) // elsewhere ) { String gev = energy.getText(); double tg; try { tg = Double.valueOf(gev).doubleValue(); } catch (NumberFormatException badInput) { tg = Physics.erg2gev(energyParticle); } if (tg < 0) tg = 0; // non negative energyParticle = Physics.gev2erg(tg); computeSpeed(); computeRadius(); setEnergy(); // make consistent pic.repaint(); } else if (e.target == nameAnti && e.id == Event.ACTION_EVENT) { String a = nameAnti.getLabel(); if (a.equals("anti")) { // user cleared chargeParticle = Math.abs(chargeParticle); } else { // user set chargeParticle = -Math.abs(chargeParticle); } computeRadius(); setParticle(); setCharge(); // make consistent pic.repaint(); } else if (e.target == nameParticle && (e.id == Event.ACTION_EVENT // user is looking || e.id == Event.LOST_FOCUS) // elsewhere ) { String n = nameParticle.getText().trim().toLowerCase(); int ti = 0; for (int i=1; i SideView.refPoint.x+Shield.radius) {// hit right double right = SideView.refPoint.x+Shield.radius; double dy = (right-origin.x)*(topMagnet-origin.y)/sx; hit = new XY(right, origin.y+dy); } Art.drawTrack(g, origin, hit); // draw to shield Art.addArrow(g, origin, hit); Art.drawAtom(g, origin, charge, mass); // atom picture return; // particle stops here } XY out = Art.drawTrack(g, start, end); // into magnetic field Art.addArrow(g, start, end); Art.drawAtom(g, start, charge, mass); // side view -- entering particle start = origin; end = new XY(impact.x, topMagnet); out = Art.drawTrack(g, start, end); // track above magnet Art.addArrow(g, start, end); Art.drawAtom(g, start, charge, mass); // atom picture // both views, helical track XY strt = Animate.impact; Quad t; t = Art.drawTrack(g,strt,radius,theta,target,topMagnet,bottomMagnet); // top view -- exiting particle if (t.z >= bottomMagnet) { start = t.pt1; double a = t.phi; // exit angle double escape = Magnet.outer; // arbitrary double dx = escape*Math.cos(a); double dy = escape*Math.sin(a); if (theta < 0) { end = new XY(start.x+dx, start.y-dy); } else { end = new XY(start.x-dx, start.y+dy); } out = Art.drawTrack(g, start, end); Art.addArrow(g, start, end); } // side view -- exiting particle if (t.z >= bottomMagnet) { start.x = t.pt2.x; start.y = t.z; double a = t.phi; double dy = Magnet.outer; // arbitrary double dx = dy*Math.tan(theta)*Math.cos(a); end = new XY(start.x-dx, start.y+dy); out = Art.drawTrack(g, start, end); Art.addArrow(g, start, end); } } } // end class Particle