001: // HEPsimulator
002: // High Energy Particle Physics Simulator
003: //
004: // Written by Mark Hale (mark.hale@physics.org)
005:
006: import java.awt.*;
007: import java.awt.event.*;
008: import JSci.physics.relativity.*;
009: import JSci.physics.quantum.*;
010: import JSci.physics.particles.*;
011:
012: /**
013: * HEPsimulator.
014: * This is the main application class.
015: * @version 1.3
016: * @author Mark Hale (mark.hale@physics.org)
017: */
018: public final class HEPsimulator extends Frame implements Runnable {
019: private final Runnable thr = this ;
020: private MenuBar mBar = new MenuBar();
021: private Menu mExp = new Menu("Experiment");
022: private Menu mRes = new Menu("Results");
023: private MenuItem mDev = new MenuItem("Device");
024: private MenuItem mSet = new MenuItem("Setup");
025: private MenuItem mCalc = new MenuItem("Calculate");
026: private DeviceDialog dlgDevice;
027: private SetupDialog dlgSetup;
028: /**
029: * The Particle Database.
030: */
031: private QuantumParticle particles[] = new QuantumParticle[38];
032:
033: private QuantumParticle ingoing[] = new QuantumParticle[2];
034: private QuantumParticle outgoing[] = new QuantumParticle[2];
035: private CMframe uni;
036:
037: /**
038: * Constructor.
039: */
040: public HEPsimulator() {
041: super ("HEP simulator");
042: addWindowListener(new WindowAdapter() {
043: public void windowClosing(WindowEvent evt) {
044: dlgDevice.dispose();
045: dlgSetup.dispose();
046: dispose();
047: System.exit(0);
048: }
049: });
050: mDev.addActionListener(new ActionListener() {
051: public void actionPerformed(ActionEvent evt) {
052: dlgDevice.show();
053: }
054: });
055: mSet.addActionListener(new ActionListener() {
056: public void actionPerformed(ActionEvent evt) {
057: dlgSetup.show();
058: }
059: });
060: mCalc.addActionListener(new ActionListener() {
061: public void actionPerformed(ActionEvent evt) {
062: new Thread(thr).start();
063: }
064: });
065: setSize(400, 300);
066: mBar.add(mExp);
067: mExp.add(mDev);
068: mExp.add(mSet);
069: mBar.add(mRes);
070: mRes.add(mCalc);
071: setMenuBar(mBar);
072: loadDatabase();
073: dlgDevice = new DeviceDialog(this , dlgSetup = new SetupDialog(
074: this , particles));
075: }
076:
077: public static void main(String args[]) {
078: HEPsimulator sim = new HEPsimulator();
079: sim.start();
080: }
081:
082: public void start() {
083: show();
084: }
085:
086: /**
087: * Performs the experiment.
088: */
089: public void run() {
090: uni = new CMframe();
091: ingoing[0] = dlgSetup.getParticleA();
092: ingoing[1] = dlgSetup.getParticleB();
093: uni.setRelCMvel(ingoing[0], ingoing[1]);
094: uni.addParticle(ingoing[0]);
095: if (ingoing[1] != null)
096: uni.addParticle(ingoing[1]);
097:
098: try {
099: for (int i = 0; i < particles.length; i++) {
100: outgoing[0] = (QuantumParticle) particles[i].getClass()
101: .newInstance();
102: for (int j = i; j < particles.length; j++) {
103: outgoing[1] = (QuantumParticle) particles[j]
104: .getClass().newInstance();
105: if (uni.conserve(outgoing[0], outgoing[1]))
106: new ResultsDialog(this , ingoing, outgoing, uni
107: .interact()).show();
108: }
109: }
110: } catch (Exception e) {
111: }
112: }
113:
114: /**
115: * Loads up the particle database.
116: */
117: private void loadDatabase() {
118: // LEPTIONS (12)
119: particles[0] = new Electron();
120: particles[1] = new Positron();
121: particles[2] = new ElectronNeutrino();
122: particles[3] = new AntiElectronNeutrino();
123: particles[4] = new Muon();
124: particles[5] = new AntiMuon();
125: particles[6] = new MuonNeutrino();
126: particles[7] = new AntiMuonNeutrino();
127: particles[8] = new Tau();
128: particles[9] = new AntiTau();
129: particles[10] = new TauNeutrino();
130: particles[11] = new AntiTauNeutrino();
131: // MESONS (8)
132: particles[12] = new PiZero();
133: particles[13] = new PiPlus();
134: particles[14] = new PiMinus();
135: particles[15] = new KPlus();
136: particles[16] = new KMinus();
137: particles[17] = new KZero();
138: particles[18] = new AntiKZero();
139: particles[19] = new Eta();
140: // BARYONS (18)
141: particles[20] = new Proton();
142: particles[21] = new AntiProton();
143: particles[22] = new Neutron();
144: particles[23] = new AntiNeutron();
145: particles[24] = new Lambda();
146: particles[25] = new AntiLambda();
147: particles[26] = new SigmaPlus();
148: particles[27] = new AntiSigmaPlus();
149: particles[28] = new SigmaZero();
150: particles[29] = new AntiSigmaZero();
151: particles[30] = new SigmaMinus();
152: particles[31] = new AntiSigmaMinus();
153: particles[32] = new XiZero();
154: particles[33] = new AntiXiZero();
155: particles[34] = new XiMinus();
156: particles[35] = new XiPlus();
157: particles[36] = new OmegaMinus();
158: particles[37] = new AntiOmegaMinus();
159: }
160: }
161:
162: /**
163: * DeviceDialog.
164: * Sets the experimental apparatus.
165: * @version 1.3
166: * @author Mark Hale
167: */
168: class DeviceDialog extends Dialog {
169: private Button ok = new Button("OK");
170: private SetupDialog dlgRel;
171: private CheckboxGroup cbg = new CheckboxGroup();
172: private Checkbox cbChamber = new Checkbox("Bubble chamber", cbg,
173: true);
174: private Checkbox cbAccelerator = new Checkbox(
175: "Particle accelerator", cbg, false);
176:
177: public DeviceDialog(Frame f, SetupDialog sd) {
178: super (f, "Device Selection", true);
179: addWindowListener(new WindowAdapter() {
180: public void windowClosing(WindowEvent evt) {
181: setVisible(false);
182: }
183: });
184: cbChamber.addItemListener(new ItemListener() {
185: public void itemStateChanged(ItemEvent evt) {
186: dlgRel.particleB.setEnabled(false);
187: dlgRel.energyB.setEnabled(false);
188: }
189: });
190: cbAccelerator.addItemListener(new ItemListener() {
191: public void itemStateChanged(ItemEvent evt) {
192: dlgRel.particleB.setEnabled(true);
193: dlgRel.energyB.setEnabled(true);
194: }
195: });
196: ok.addActionListener(new ActionListener() {
197: public void actionPerformed(ActionEvent evt) {
198: dispose();
199: }
200: });
201: setSize(200, 120);
202: dlgRel = sd;
203: setLayout(new GridLayout(3, 1));
204: add(cbChamber);
205: add(cbAccelerator);
206: add(ok);
207: dlgRel.particleB.setEnabled(false);
208: dlgRel.energyB.setEnabled(false);
209: }
210: }
211:
212: /**
213: * SetupDialog.
214: * Sets the experimental parameters.
215: * @version 1.3
216: * @author Mark Hale
217: */
218: class SetupDialog extends Dialog {
219: private Button ok = new Button("OK");
220: public Choice particleA = new Choice();
221: public Choice particleB = new Choice();
222: public TextField energyA, energyB;
223: private QuantumParticle array[];
224:
225: public SetupDialog(Frame f, QuantumParticle qpArray[]) {
226: super (f, "Device Settings", true);
227: array = qpArray;
228: energyA = new TextField(Double.toString(array[0].restMass()));
229: energyB = new TextField(Double.toString(array[0].restMass()));
230: addWindowListener(new WindowAdapter() {
231: public void windowClosing(WindowEvent evt) {
232: setVisible(false);
233: }
234: });
235: particleA.addItemListener(new ItemListener() {
236: public void itemStateChanged(ItemEvent evt) {
237: energyA.setText(Double.toString(array[particleA
238: .getSelectedIndex()].restMass()));
239: }
240: });
241: particleB.addItemListener(new ItemListener() {
242: public void itemStateChanged(ItemEvent evt) {
243: energyB.setText(Double.toString(array[particleB
244: .getSelectedIndex()].restMass()));
245: }
246: });
247: energyA.addActionListener(new ActionListener() {
248: public void actionPerformed(ActionEvent evt) {
249: if (Double.valueOf(energyA.getText()).doubleValue() < array[particleA
250: .getSelectedIndex()].restMass())
251: energyA.setText(Double.toString(array[particleA
252: .getSelectedIndex()].restMass()));
253: }
254: });
255: energyB.addActionListener(new ActionListener() {
256: public void actionPerformed(ActionEvent evt) {
257: if (Double.valueOf(energyB.getText()).doubleValue() < array[particleB
258: .getSelectedIndex()].restMass())
259: energyB.setText(Double.toString(array[particleB
260: .getSelectedIndex()].restMass()));
261: }
262: });
263: ok.addActionListener(new ActionListener() {
264: public void actionPerformed(ActionEvent evt) {
265: dispose();
266: }
267: });
268: setSize(350, 130);
269: GridBagLayout gb = new GridBagLayout();
270: GridBagConstraints c = new GridBagConstraints();
271: for (int n = 0; n < array.length; n++) {
272: particleA.addItem(array[n].toString());
273: particleB.addItem(array[n].toString());
274: }
275: setLayout(gb);
276: c.anchor = GridBagConstraints.CENTER;
277: c.gridwidth = GridBagConstraints.RELATIVE;
278: gb.setConstraints(particleA, c);
279: add(particleA);
280: c.gridwidth = GridBagConstraints.REMAINDER;
281: gb.setConstraints(particleB, c);
282: add(particleB);
283: c.gridwidth = GridBagConstraints.RELATIVE;
284: gb.setConstraints(energyA, c);
285: add(energyA);
286: c.gridwidth = GridBagConstraints.REMAINDER;
287: gb.setConstraints(energyB, c);
288: add(energyB);
289: c.gridwidth = GridBagConstraints.REMAINDER;
290: gb.setConstraints(ok, c);
291: add(ok);
292: }
293:
294: public QuantumParticle getParticleA() {
295: QuantumParticle temp;
296: try {
297: temp = (QuantumParticle) array[particleA.getSelectedIndex()]
298: .getClass().newInstance();
299: } catch (Exception e) {
300: temp = null;
301: }
302: double energy = Double.valueOf(energyA.getText()).doubleValue();
303: double mass = temp.restMass();
304: temp.momentum = new Rank1Tensor(energy, Math.sqrt(energy
305: * energy - mass * mass), 0.0, 0.0);
306: return temp;
307: }
308:
309: public QuantumParticle getParticleB() {
310: QuantumParticle temp;
311: if (particleB.isEnabled()) {
312: try {
313: temp = (QuantumParticle) array[particleB
314: .getSelectedIndex()].getClass().newInstance();
315: } catch (Exception e) {
316: temp = null;
317: }
318: double energy = Double.valueOf(energyB.getText())
319: .doubleValue();
320: double mass = temp.restMass();
321: temp.momentum = new Rank1Tensor(energy, -Math.sqrt(energy
322: * energy - mass * mass), 0.0, 0.0);
323: } else
324: temp = null;
325: return temp;
326: }
327: }
328:
329: /**
330: * ResultsDialog.
331: * Displays the results of an experiment.
332: * @version 1.3
333: * @author Mark Hale
334: */
335: class ResultsDialog extends Dialog {
336: private Button ok = new Button("OK");
337:
338: public ResultsDialog(Frame f, QuantumParticle in[],
339: QuantumParticle out[], String interact) {
340: super (f, "Results", false);
341: addWindowListener(new WindowAdapter() {
342: public void windowClosing(WindowEvent evt) {
343: dispose();
344: }
345: });
346: ok.addActionListener(new ActionListener() {
347: public void actionPerformed(ActionEvent evt) {
348: dispose();
349: }
350: });
351: setSize(300, 300);
352: setLayout(new GridLayout(9, 1));
353: if (in[1] == null)
354: add(new Label(in[0].toString() + "------>"));
355: else
356: add(new Label(in[0].toString() + " ---> <--- "
357: + in[1].toString()));
358: add(new Label(out[0].toString()));
359: add(new Label("Energy = "
360: + Double.toString(out[0].momentum.getComponent(0))));
361: add(new Label("Momentum = "
362: + Double.toString(out[0].momentum.getComponent(1))));
363: add(new Label(out[1].toString()));
364: add(new Label("Energy = "
365: + Double.toString(out[1].momentum.getComponent(0))));
366: add(new Label("Momentum = "
367: + Double.toString(out[1].momentum.getComponent(1))));
368: add(new Label("Interaction was the " + interact));
369: add(ok);
370: }
371: }
372:
373: /**
374: * CMframe.
375: * Defines a class representing the Centre of Mass frame.
376: * @version 1.3
377: * @author Mark Hale
378: */
379: final class CMframe {
380: /**
381: * Total energy.
382: */
383: private double energy;
384: /**
385: * Quantum number totals.
386: */
387: private int B, I, Iz, Le, Lm, Lt, Q, S;
388: /**
389: * The relative velocity between the Centre
390: * of Mass frame and the lab frame.
391: */
392: private double relCMvel;
393: /**
394: * Interaction.
395: */
396: private int interaction;
397: /**
398: * Interaction codes.
399: */
400: private final static int STRONG = 1, WEAK = 2, EM = 3;
401:
402: /**
403: * Constructor.
404: */
405: public CMframe() {
406: energy = B = I = Iz = Le = Lm = Lt = Q = S = 0;
407: }
408:
409: /**
410: * Checks whether Particles a and b obey the
411: * conservation laws.
412: */
413: public boolean conserve(QuantumParticle a, QuantumParticle b) {
414: double Ei, Ej, pi, pj;
415: // work out interaction type (also check conservation of quantum numbers)
416: if (a.charge() + b.charge() == Q
417: && a.eLeptonQN() + b.eLeptonQN() == Le
418: && a.muLeptonQN() + b.muLeptonQN() == Lm
419: && a.tauLeptonQN() + b.tauLeptonQN() == Lt
420: && a.baryonQN() + b.baryonQN() == B
421: && Math.abs(S - a.strangeQN() - b.strangeQN()) <= 1) {
422: if (a.strangeQN() + b.strangeQN() == S
423: && a.isospinZ() + b.isospinZ() == Iz) {
424: if (a.isospin() == 0 && b.isospin() == 0) {
425: // EM Interaction
426: interaction = EM;
427: } else {
428: // Strong Interaction
429: interaction = STRONG;
430: }
431: } else {
432: // Weak Interaction
433: interaction = WEAK;
434: }
435: } else {
436: interaction = 0;
437: }
438: // check quantum numbers are conserved
439: if (interaction > 0) {
440: // Convert to keV for better accuracy
441: double massA = 1000.0 * a.restMass();
442: double massB = 1000.0 * b.restMass();
443: // work out Ei,Ej,pi,pj
444: Ei = (energy * energy - massB * massB + massA * massA)
445: / (2.0 * energy);
446: Ej = (energy * energy - massA * massA + massB * massB)
447: / (2.0 * energy);
448: pi = (energy * energy * energy * energy + massA * massA
449: * massA * massA + massB * massB * massB * massB
450: - 2.0 * massA * massA * energy * energy - 2.0
451: * massB * massB * energy * energy - 2.0 * massA
452: * massA * massB * massB);
453: // check consistency
454: if (pi >= 0.0 && Ei >= 0.0 && Ej >= 0.0) {
455: pi = Math.sqrt(pi) / (2.0 * energy);
456: // Convert back to MeV and update E & p values
457: pi /= 1000.0;
458: a.momentum = new Rank1Tensor(Ei / 1000.0, pi, 0.0, 0.0);
459: b.momentum = new Rank1Tensor(Ej / 1000.0, -pi, 0.0, 0.0);
460: // Perform Lorentz Transform
461: if (relCMvel != 0.0) {
462: LorentzBoost lb = new LorentzBoost(-relCMvel, 0.0,
463: 0.0);
464: a.momentum = lb.multiply(a.momentum);
465: b.momentum = lb.multiply(b.momentum);
466: }
467: return true;
468: }
469: }
470: return false;
471: }
472:
473: public void addParticle(QuantumParticle qp) {
474: B += qp.baryonQN();
475: I += qp.isospin();
476: Iz += qp.isospinZ();
477: Le += qp.eLeptonQN();
478: Lm += qp.muLeptonQN();
479: Lt += qp.tauLeptonQN();
480: Q += qp.charge();
481: S += qp.strangeQN();
482:
483: LorentzBoost lb = new LorentzBoost(relCMvel, 0.0, 0.0);
484: qp.momentum = lb.multiply(qp.momentum);
485:
486: energy += qp.momentum.getComponent(0) * 1000.0; // store as keV for better accuracy
487: }
488:
489: /**
490: * Set the relative centre of mass velocity.
491: */
492: public void setRelCMvel(QuantumParticle a, QuantumParticle b) {
493: if (b == null)
494: relCMvel = a.momentum.getComponent(1)
495: / a.momentum.getComponent(0);
496: else
497: relCMvel = (a.momentum.getComponent(1) + b.momentum
498: .getComponent(1))
499: / (a.momentum.getComponent(0) + b.momentum
500: .getComponent(0));
501: }
502:
503: /**
504: * Returns the type of interaction that took place.
505: */
506: public String interact() {
507: switch (interaction) {
508: case STRONG:
509: return new String("Strong Force");
510: case WEAK:
511: return new String("Weak Force");
512: case EM:
513: return new String("Electromagnetic Force");
514: default:
515: return new String("Unknown - error");
516: }
517: }
518: }
|