def testDuplicate(self): ph1 = PolarizedPhoton(8000.0, Vector(2, 4, 5), StokesVector([1, 2, 3, 4])) ph2 = ph1.duplicate() assert_array_almost_equal(ph1.stokesVector().components(), ph2.stokesVector().components()) assert_array_almost_equal(ph1.unitDirectionVector().components(), ph2.unitDirectionVector().components())
def testConstructor(self): photon = PolarizedPhoton(energy_in_ev=8000, direction_vector=Vector(0.0, 1.0, 0.0), stokes_vector=StokesVector( [1.0, 0.0, 1.0, 0.0])) self.assertIsInstance(photon, PolarizedPhoton) self.assertTrue(photon.unitDirectionVector() == Vector(0.0, 1.0, 0.0)) self.assertTrue( photon.stokesVector() == StokesVector([1.0, 0.0, 1.0, 0.0]))
def testInheritatedMethods(self): ph1 = PolarizedPhoton(8000.0, Vector(2, 4, 5), StokesVector([1, 2, 3, 4])) ph1.setUnitDirectionVector(Vector(1, 0, 0)) ph1.setEnergy(9000) self.assertTrue(ph1.unitDirectionVector().components()[0] == 1) self.assertTrue(ph1.unitDirectionVector().components()[1] == 0) self.assertTrue(ph1.unitDirectionVector().components()[2] == 0) self.assertTrue(ph1.energy() == 9000.0)
def from_shadow_beam_to_photon_bunch(self): vx = self.incoming_shadow_beam._beam.getshcol(4, nolost=1) vy = self.incoming_shadow_beam._beam.getshcol(5, nolost=1) vz = self.incoming_shadow_beam._beam.getshcol(6, nolost=1) s0 = self.incoming_shadow_beam._beam.getshcol(30, nolost=1) s1 = self.incoming_shadow_beam._beam.getshcol(31, nolost=1) s2 = self.incoming_shadow_beam._beam.getshcol(32, nolost=1) s3 = self.incoming_shadow_beam._beam.getshcol(33, nolost=1) energies = self.incoming_shadow_beam._beam.getshcol(11, nolost=1) photon_bunch = PolarizedPhotonBunch([]) photons_list = list() for i, energy in enumerate(energies): photon = PolarizedPhoton( energy_in_ev=energy, direction_vector=Vector(vx[i], vy[i], vz[i]), stokes_vector=StokesVector([s0[i], s1[i], s2[i], s3[i]])) #photon_bunch.add(photon) # print("<><> appending photon",i) photons_list.append(photon) photon_bunch.addPhotonsFromList(photons_list) return photon_bunch
def testChangePhotonValue(self): nphotons = 10 from crystalpy.util.Vector import Vector from crystalpy.util.StokesVector import StokesVector bunch = PolarizedPhotonBunch([]) for i in range(nphotons): polarized_photon = PolarizedPhoton( energy_in_ev=1000.0 + i, direction_vector=Vector(0, 1.0, 0), stokes_vector=StokesVector([1.0, 0, 1.0, 0])) bunch.addPhoton(polarized_photon) # photon5_stokes = bunch.get_photon_index(5).stokesVector().get_array(numpy=True) # print("photon 5 stokes ",photon5_stokes) photon5 = bunch.getPhotonIndex(5) photon5.setStokesVector(StokesVector([1, 0, 0, 0])) photon5_stokes_new = bunch.getPhotonIndex( 5).stokesVector().components() # print("photon 5 stokes new ",photon5_stokes_new) assert_almost_equal(photon5_stokes_new, numpy.array([1.0, 0, 0, 0]))
def calculateDiffractedPolarizedPhoton(self, diffraction_setup, incoming_polarized_photon, inclination_angle): """ Calculates the diffraction/transmission given by the setup. :param diffraction_setup: The diffraction setup. :return: PhotonBunch object made up of diffracted/transmitted photons. """ # Retrieve the incoming Stokes vector. incoming_stokes_vector = incoming_polarized_photon.stokesVector() # Get PerfectCrystal instance for the current photon. perfect_crystal = self._perfectCrystalForPhoton( diffraction_setup, incoming_polarized_photon) # Calculate diffraction for current incoming photon. complex_amplitudes = perfect_crystal.calculateDiffraction( incoming_polarized_photon) # Calculate outgoing Photon. outgoing_photon = perfect_crystal._calculatePhotonOut( incoming_polarized_photon) # Calculate intensities and phases of the crystal reflectivities or transmitivities intensity_pi = complex_amplitudes["P"].intensity() intensity_sigma = complex_amplitudes["S"].intensity() phase_pi = complex_amplitudes["P"].phase() phase_sigma = complex_amplitudes["S"].phase() # Get a CrystalPhasePlate instance which contains the Mueller matrix phase_plate = CrystalPhasePlate( #incoming_stokes_vector=incoming_stokes_vector, intensity_sigma=intensity_sigma, phase_sigma=phase_sigma, intensity_pi=intensity_pi, phase_pi=phase_pi, inclination_angle=inclination_angle) # Use intensities and phases to calculate the Stokes vector for the outgoing photon. outgoing_stokes_vector = phase_plate.calculate_stokes_vector( incoming_stokes_vector) # Piece together the PolarizedPhoton object. outgoing_polarized_photon = PolarizedPhoton( energy_in_ev=outgoing_photon.energy(), direction_vector=outgoing_photon.unitDirectionVector(), stokes_vector=outgoing_stokes_vector) return outgoing_polarized_photon
def testFromArray(self): npoint = 1000 vx = numpy.zeros(npoint) + 0.0 vy = numpy.zeros(npoint) + 1.0 vz = numpy.zeros(npoint) + 0.0 s0 = numpy.zeros(npoint) + 1 s1 = numpy.zeros(npoint) + 0 s2 = numpy.zeros(npoint) + 1 s3 = numpy.zeros(npoint) + 0 energy = numpy.zeros(npoint) + 3000.0 photon_bunch = PolarizedPhotonBunch([]) photons_list = list() for i in range(npoint): photon = PolarizedPhoton( energy_in_ev=energy[i], direction_vector=Vector(vx[i], vy[i], vz[i]), stokes_vector=StokesVector([s0[i], s1[i], s2[i], s3[i]])) photons_list.append(photon) photon_bunch.addPhotonsFromList(photons_list) # print("<><><>",photon_bunch.toString()) # print("<><><>",photon_bunch.toDictionary()) self.assertTrue(1.0 == any(photon_bunch.getArrayByKey("s0"))) self.assertTrue(0.0 == any(photon_bunch.getArrayByKey("s1"))) self.assertTrue(1.0 == any(photon_bunch.getArrayByKey("s2"))) self.assertTrue(0.0 == any(photon_bunch.getArrayByKey("s3"))) energies = photon_bunch.getArrayByKey("energies") for energy in energies: self.assertAlmostEqual(energy, 3000.0)
if __name__ == '__main__': from PyQt5.QtWidgets import QApplication from crystalpy.util.Vector import Vector from crystalpy.util.StokesVector import StokesVector from crystalpy.util.PolarizedPhoton import PolarizedPhoton from crystalpy.util.PolarizedPhotonBunch import PolarizedPhotonBunch app = QApplication([]) ow = OWPhotonViewer() nphotons = 10 from crystalpy.util.Vector import Vector from crystalpy.util.StokesVector import StokesVector bunch = PolarizedPhotonBunch([]) for i in range(nphotons): polarized_photon = PolarizedPhoton(energy_in_ev=1000.0 + i, direction_vector=Vector(0, 1.0, 0), stokes_vector=StokesVector( [1.0, 0, 1.0, 0])) bunch.addPhoton(polarized_photon) ow._set_input(bunch) ow.do_plot() ow.show() app.exec_() ow.saveSettings()
def stokes_calculator(energy, deviations, e_gev=6.04, i_a=0.2, hdiv_mrad=1.0, ec_ev=19551.88): """ Calculates the Stokes vectors for each PolarizedPhoton according to the theory of the BM emission. It does this by use of a modified version of the sync_ang function that allows the computation of the power densities for the RCP and LCP components on top of the linear components. The dW_+1 and dW_-1 values are obtained by setting respectively: l2 = l3 = 1/sqrt(2) for RCP and l2 = -l3 = 1/sqrt(2) for LCP. The computation of dW_-1 (LCP), dW_1 (RCP), dW_2 (sigma) and dW_3 (pi) makes it possible to use the formula 3.23 from Sokolov & Ternov for the phase difference delta = phi_3 - phi_2. This in turn lead to the Stokes parameters as defined in Jackson, 7.27. The default values for the parameters are the typical ESRF values. :param energy: desired photon energy in eV. :type energy: float :param deviations: array of angle deviations in milliradians. :type deviations: numpy.ndarray :param e_gev: energy of the electrons in GeV. :type e_gev: float :param i_a: beam current in A. :type i_a: float :param hdiv_mrad: horizontal divergence of the beam in milliradians. :type hdiv_mrad; float :param ec_ev: critical energy as in Green, pag.3. :type ec_ev: float :return: list of StokesVector objects. :rtype: PolarizedPhotonBunch """ # # Calculate some parameters needed by sync_f (copied from sync_ang by Manuel Sanchez del Rio). # a8 = 1.3264d13 # deviations = np.array(deviations) a8 = codata_ec / np.power(codata_mee, 2) / codata_h * (9e-2 / 2 / np.pi) energy = float(energy) eene = energy / ec_ev gamma = e_gev * 1e3 / codata_mee # # Calculate the power densities for the 4 cases: # # -1 --> left circularly polarized l2 = -l3 = 1/sqrt(2) # 1 --> right circularly polarized l2 = l3 = 1/sqrt(2) # 2 --> linear sigma component l2 = 1 & l3 = 0 # 3 --> linear pi component l3 = 1 & l2 = 0 # left_circular = modified_sync_f(deviations * gamma / 1e3, eene, polarization=-1) * \ np.power(eene, 2) * \ a8 * i_a * hdiv_mrad * np.power(e_gev, 2) # -1 right_circular = modified_sync_f(deviations * gamma / 1e3, eene, polarization=1) * \ np.power(eene, 2) * \ a8 * i_a * hdiv_mrad * np.power(e_gev, 2) # 1 sigma_linear = modified_sync_f(deviations * gamma / 1e3, eene, polarization=2) * \ np.power(eene, 2) * \ a8 * i_a * hdiv_mrad * np.power(e_gev, 2) # 2 pi_linear = modified_sync_f(deviations * gamma / 1e3, eene, polarization=3) * \ np.power(eene, 2) * \ a8 * i_a * hdiv_mrad * np.power(e_gev, 2) # 3 # # Calculate the phase difference delta according to Sokolov, formula 3.23. # delta = (left_circular - right_circular) / \ (2 * np.sqrt(sigma_linear * pi_linear)) delta = np.arcsin(delta) # # Calculate the Stokes parameters. # s_0 = sigma_linear + pi_linear s_1 = sigma_linear - pi_linear s_2 = np.sqrt(sigma_linear) * np.sqrt(pi_linear) * np.cos(delta) s_3 = np.sqrt(sigma_linear) * np.sqrt(pi_linear) * np.sin(delta) s_2 *= 2 # TODO: try to understand why if I multiply by 2 in the line above I get a warning. s_3 *= 2 # # Normalize the Stokes parameters. # # modulus = np.sqrt(s_0 ** 2 + s_1 ** 2 + s_2 ** 2 + s_3 ** 2) # # if np.any(modulus != 0): # s_0 /= modulus # s_1 /= modulus # s_2 /= modulus # s_3 /= modulus # # else: # raise Exception("BendingMagnet.stokes_calculator: Stokes vector is null vector!\n") maximum = np.max(s_0) s_0 /= maximum s_1 /= maximum s_2 /= maximum s_3 /= maximum # Following XOP conventions, the photon bunch travels along the y axis in the lab frame of reference. base_direction = Vector(0, 1, 0) rotation_axis = Vector(1, 0, 0) # # Create the PolarizedPhotonBunch. # # To match the deviation sign conventions with those adopted in the crystal diffraction part, # I have to define a positive deviation as a clockwise rotation from the y axis, # and consequently a negative deviation as a counterclockwise rotation from the y axis. # polarized_photons = list() deviations = np.multiply(deviations, 1e-3) # mrad --> rad for i in range(len(deviations)): stokes_vector = StokesVector([s_0[i], s_1[i], s_2[i], s_3[i]]) direction = base_direction.rotateAroundAxis(rotation_axis, deviations[i]) incoming_photon = PolarizedPhoton(energy, direction, stokes_vector) polarized_photons.append(incoming_photon) photon_bunch = PolarizedPhotonBunch(polarized_photons) return photon_bunch
def calculate_with_polarized_photon(method=0): # Create a diffraction setup. print("\nCreating a diffraction setup...") diffraction_setup = DiffractionSetup( geometry_type=BraggDiffraction(), # GeometryType object crystal_name="Si", # string thickness=1e-2, # meters miller_h=1, # int miller_k=1, # int miller_l=1, # int asymmetry_angle= 0, #10.0*numpy.pi/180., # radians azimuthal_angle=0.0) # radians # int energy = 8000.0 # eV angle_deviation_min = -100e-6 # radians angle_deviation_max = 100e-6 # radians angle_deviation_points = 500 angle_step = (angle_deviation_max - angle_deviation_min) / angle_deviation_points bunch_in = PolarizedPhotonBunch() bragg_angle = diffraction_setup.angleBragg(energy) print("Bragg angle for E=%f eV is %f deg" % (energy, bragg_angle * 180.0 / numpy.pi)) # Create a Diffraction object. diffraction = Diffraction() # # get wavevector with incident direction matching Bragg angle # K0 = diffraction_setup.getK0(energy) K0unitary = K0.getNormalizedVector() print("K0", K0.components()) # method = 0 # diffraction for individual photons # method = 1 # diffraction for bunch ZZ = numpy.zeros(angle_deviation_points) if method == 0: bunch_out = PolarizedPhotonBunch() for ia in range(angle_deviation_points): deviation = angle_deviation_min + ia * angle_step # angle = deviation + bragg_angle # yy = numpy.cos(angle) # zz = - numpy.abs(numpy.sin(angle)) # photon = PolarizedPhoton(energy_in_ev=energy,direction_vector=Vector(0.0,yy,zz), # stokes_vector=StokesVector([1,0,1,0])) # minus sign in angle is to perform cw rotation when deviation increses Vin = K0unitary.rotateAroundAxis(Vector(1, 0, 0), -deviation) photon = PolarizedPhoton(energy_in_ev=energy, direction_vector=Vin, stokes_vector=StokesVector([1, 0, 1, 0])) photon_out = diffraction.calculateDiffractedPolarizedPhoton( diffraction_setup, incoming_polarized_photon=photon, inclination_angle=0.0) bunch_out.addPhoton(photon_out) ZZ[ia] = angle_deviation_min + ia * angle_step elif method == 1: # diffraction for bunch for ia in range(angle_deviation_points): deviation = angle_deviation_min + ia * angle_step # angle = deviation + bragg_angle # yy = numpy.cos(angle) # zz = - numpy.abs(numpy.sin(angle)) # photon = PolarizedPhoton(energy_in_ev=energy,direction_vector=Vector(0.0,yy,zz), # stokes_vector=StokesVector([1,0,1,0])) # minus sign in angle is to perform cw rotation when deviation increses Vin = K0unitary.rotateAroundAxis(Vector(1, 0, 0), -deviation) photon = PolarizedPhoton(energy_in_ev=energy, direction_vector=Vin, stokes_vector=StokesVector([1, 0, 1, 0])) bunch_in.addPhoton(photon) ZZ[ia] = angle_deviation_min + ia * angle_step bunch_out = diffraction.calculateDiffractedPolarizedPhotonBunch( diffraction_setup, bunch_in, 0.0) bunch_out_dict = bunch_out.toDictionary() plot(1e6 * ZZ, bunch_out_dict["s0"], 1e6 * ZZ, bunch_out_dict["s1"], legend=["S0", "S1"], xtitle="theta - thetaB [urad]", title="Polarized reflectivity calculation using method %d" % method)
def generate(self): if self.ENERGY_POINTS == 1: # monochromatic bunch. self.ENERGY_MIN = self.ENERGY_MAX = self.ENERGY energies = np.linspace(self.ENERGY_MIN, self.ENERGY_MAX, self.ENERGY_POINTS) if self.ANGLE_DEVIATION_POINTS == 1: # unidirectional bunch. ANGLE_DEVIATION_MIN = ANGLE_DEVIATION_MAX = self.ANGLE_DEVIATION * 1e-6 # urad else: ANGLE_DEVIATION_MIN = self.ANGLE_DEVIATION_MIN * 1e-6 # urad ANGLE_DEVIATION_MAX = self.ANGLE_DEVIATION_MAX * 1e-6 # urad deviations = np.linspace(ANGLE_DEVIATION_MIN, ANGLE_DEVIATION_MAX, self.ANGLE_DEVIATION_POINTS) stokes_vector = StokesVector( [self.STOKES_S0, self.STOKES_S1, self.STOKES_S2, self.STOKES_S3]) # Following XOP conventions, the photon bunch travels along the y axis in the lab frame of reference. base_direction = Vector(0, 1, 0) # TODO: check this sign and possibly change it. # Setting (-1,0,0) means that negative deviation correspond to positive vz and after rotation # to match the crystal correspond to theta_bragg - delta, so deviation has the same sign of delta rotation_axis = Vector(-1, 0, 0) # To match the deviation sign conventions with those adopted in the crystal diffraction part, # I have to define a positive deviation as a clockwise rotation from the y axis, # and consequently a negative deviation as a counterclockwise rotation from the y axis. polarized_photons = list() for energy in energies: for deviation in deviations: direction = base_direction.rotateAroundAxis( rotation_axis, deviation) incoming_photon = PolarizedPhoton(energy, direction, stokes_vector) polarized_photons.append(incoming_photon) photon_bunch = PolarizedPhotonBunch(polarized_photons) # Dump data to file if requested. if self.DUMP_TO_FILE: print("PhotonSource: Writing data in {file}...\n".format( file=self.FILE_NAME)) with open(self.FILE_NAME, "w") as file: try: file.write( "#S 1 photon bunch\n" "#N 9\n" "#L Energy [eV] Vx Vy Vz S0 S1 S2 S3 CircularPolarizationDregree\n" ) file.write(photon_bunch.toString()) file.close() print("File written to disk: %s \n" % self.FILE_NAME) except: raise Exception( "PhotonSource: The data could not be dumped onto the specified file!\n" ) self.send("photon bunch", photon_bunch) print("PhotonSource: Photon bunch generated.\n")
def testEnergy(self): photon = PolarizedPhoton(4000, Vector(0, 0, 1), StokesVector([1.0, 0.0, 1.0, 0.0])) self.assertEqual(photon.energy(), 4000)