public SingleMaterialMonteCarlo() {
    double energyEV = 1000000;

    System.out.println("energy " + energyEV / 1000000 + " MeV");

    double photo =
        material.getAttenuation(energyEV / 1000, AttenuationType.PHOTOELECTRIC_ABSORPTION);
    double compton =
        material.getAttenuation(energyEV / 1000, AttenuationType.INCOHERENT_ATTENUATION);
    System.out.println("cross section photo: " + photo + "cm" + " compton " + compton + "cm");
    System.out.println("mean free path compton " + 1 / compton + "cm");
    System.out.println("mean free path photo " + 1 / photo + "cm");

    double totalCrossSection =
        material.getAttenuation(
            energyEV / 1000, AttenuationType.TOTAL_WITHOUT_COHERENT_ATTENUATION);
    System.out.println("total attenuation " + 1 / totalCrossSection + "cm");
    System.out.println("total attenuation sum " + 1 / (photo + compton) + "cm");

    raytrace(energyEV, numRays);

    double mean = XRayTracer.computeMean(pathlengths);
    double stddev = XRayTracer.computeStddev(pathlengths, mean);
    System.out.println("pathlength = " + mean + " +- " + stddev);

    mean = XRayTracer.computeMean(xs);
    stddev = XRayTracer.computeStddev(xs, mean);
    System.out.println("x = " + mean + " +- " + stddev);

    mean = XRayTracer.computeMean(ys);
    stddev = XRayTracer.computeStddev(ys, mean);
    System.out.println("y = " + mean + " +- " + stddev);

    mean = XRayTracer.computeMean(zs);
    stddev = XRayTracer.computeStddev(zs, mean);
    System.out.println("z = " + mean + " +- " + stddev);

    // rayTrace(800000, numRays);

    // visualize the klein-nishina angle distribution and the rejection
    // sampling for different energies
    //		 visualizeKleinNishina(10000000*eV);
    visualizeKleinNishina(140000 * eV);

    // visualizeKleinNishina(2.75*eV);
    //
    // visualizeKleinNishina(55000*eV);

  }
  private void followRay(
      SimpleVector pos,
      SimpleVector dir,
      double energyEV,
      FloatProcessor imp,
      int scatterCount,
      double totalDistance) {
    if (energyEV <= 1 || scatterCount > 20000) {
      System.out.println("energy low, times scattered: " + scatterCount);
      return;
    }
    // follow ray until next interaction point
    SimpleVector oldPos = pos.clone();
    double dist = sampler.getDistanceUntilNextInteractionCm(material, energyEV);
    pos.add(dir.multipliedBy(dist));
    pathlengths.add(dist);
    // draw the entire path
    // imp.drawLine((int)(scale*oldPos.getElement(0)), (int)(scale*oldPos.getElement(1)),
    // (int)(scale*pos.getElement(0)), (int)(scale*pos.getElement(1)));
    // draw interaction points only
    imp.drawDot((int) (scale * pos.getElement(0)), (int) (scale * pos.getElement(1)));

    // choose compton or photoelectric effect
    double photo =
        material.getAttenuation(energyEV / 1000, AttenuationType.PHOTOELECTRIC_ABSORPTION);
    double compton =
        material.getAttenuation(energyEV / 1000, AttenuationType.INCOHERENT_ATTENUATION);

    if (sampler.random() * (photo + compton) <= photo) {
      // photoelectric absorption
      energyEV = 0;
      // System.out.println("absorbed after " + scatterCount + " collisions");
      xs.add(pos.getElement(0));
      ys.add(pos.getElement(1));
      zs.add(pos.getElement(2));

      return;
    } else {
      // compton scattering

      energyEV = sampler.sampleComptonScattering(energyEV, dir);

      // send new ray
      followRay(pos, dir, energyEV, imp, scatterCount + 1, totalDistance + dist);
    }
  }
 public static synchronized void updateAttenuationCoefficientsMap(
     HashMap<Material, double[]> attenuationCoefficientsMap,
     double[] energies,
     Material mat,
     AttenuationType att) {
   double[] attenuation = new double[energies.length];
   try {
     for (int j = 0; j < attenuation.length; j++) {
       attenuation[j] = mat.getAttenuation(energies[j], att);
     }
     attenuationCoefficientsMap.put(mat, attenuation);
   } catch (NoSuchElementException e) {
     System.out.println(
         "Skipping " + mat + " as attenuation data is incomplete for the configured energies.");
   } catch (NullPointerException e2) {
     System.out.println("Skipping " + mat + " as attenuation data is incomplete.");
   }
 }