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 testDuplicate(self): v1 = StokesVector([1, 2, 3, 4]) v2 = v1.duplicate() self.assertTrue(v1 == v2) v1.setFromValues(0, 2, 3, 4) self.assertFalse(v1 == v2)
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 test_operator_not_equal(self): stokes_vector1 = self.stokes_vector stokes_vector2 = StokesVector([ 0.7817796945787793, 0.22595711869558588, 0.2879756775648755, 0.585518610609899 ]) # without final zeros stokes_vector3 = StokesVector([ round(0.78177969457877930, 6), round(0.22595711869558588, 6), round(0.28797567756487550, 6), round(0.58551861060989900, 6) ]) # rounded float self.assertFalse(stokes_vector1 != stokes_vector1) self.assertFalse(stokes_vector1 != stokes_vector2) self.assertTrue(stokes_vector1 != stokes_vector3)
def test_create_matrix1(self): # phase plate 1 element_list = [1, 1, 0, 0] incoming_stokes_vector = StokesVector(element_list) intensity_sigma = 0.5366453389170862 phase_sigma = 1.9718541179322164 # radians intensity_pi = 0.9685465109320982 phase_pi = -2.8903921744707217 # radians inclination_angle = 45.0 * np.pi / 180 # radians 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) phase_plate_matrix = phase_plate.matrix self.assertIsInstance(phase_plate_matrix, np.ndarray) candidate = np.array( [[0.7525959249245922, 0.0, -0.215950586007506, 0.0], [-0.215950586007506, 0.0, 0.7525959249245922, 0.0], [0.0, -0.10763540116609252, 0.0, -0.7128678636549213], [0.0, -0.7128678636549213, 0.0, 0.10763540116609252]]) # default precision: 6 decimal places. np.testing.assert_array_almost_equal(candidate, phase_plate_matrix)
def test_calculate_stokes_vector(self): # phase plate 1 # outgoing_stokes_vector = self.phase_plate1.calculate_stokes_vector() element_list = [1, 1, 0, 0] incoming_stokes_vector = StokesVector(element_list) intensity_sigma = 0.5366453389170862 phase_sigma = 1.9718541179322164 # radians intensity_pi = 0.9685465109320982 phase_pi = -2.8903921744707217 # radians inclination_angle = 45.0 * np.pi / 180 # radians 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) outgoing_stokes_vector = phase_plate.calculate_stokes_vector( incoming_stokes_vector) self.assertIsInstance(outgoing_stokes_vector, StokesVector) candidate = np.array([ 0.7525959249245922, -0.215950586007506, -0.10763540116609252, -0.7128678636549213 ]) # default precision: 6 decimal places. np.testing.assert_array_almost_equal( candidate, outgoing_stokes_vector.components())
def _use_default(self): """ Use the default diffraction parameters. """ # diffraction part self.geometry_type = GeometryType.BraggDiffraction() self.crystal_name = "Si" self.thickness = 0.01 # centimetres self.miller_h = 1 # int self.miller_k = 1 # int self.miller_l = 1 # int self.asymmetry_angle = 0.0 # degrees self.azimuthal_angle = 90.0 # degrees self.energy_min = 8.0 # keV self.energy_max = 8.0 # keV self.energy_points = 1 # int self.angle_deviation_min = -100 # micro radians self.angle_deviation_max = 100 # micro radians self.angle_deviation_points = 200 # int self.deg = True # phase results in degrees by default. self.inclination_angle = 45.0 # degrees self.phase_inf_limit = -180 # degrees self.phase_sup_limit = 180 # degrees # polarization part self.incoming_stokes_vector = StokesVector([1, 1, 0, 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 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 calculate_stokes_vector(self, incoming_stokes_vector): """ Takes an incoming Stokes vector, multiplies it by a Mueller matrix and gives an outgoing Stokes vector as a result. :return: StokesVector object. """ # incoming_stokes_vector = self.incoming_stokes_vector.get_array() # Stokes vector. element_list = self.matrix_by_vector(incoming_stokes_vector.getList()) outgoing_stokes_vector = StokesVector(element_list) return outgoing_stokes_vector
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 calculate_standard_interface(): # Create a diffraction setup. print("\nCreating a diffraction setup...") diffraction_setup = DiffractionSetupSweeps( 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 energy_min=8000.0, # eV energy_max=8000.0, # eV energy_points=1, # int angle_deviation_min=-100e-6, # radians angle_deviation_max=100e-6, # radians angle_deviation_points=500) # int # Create a Diffraction object. diffraction = Diffraction() # Create a DiffractionResult object holding the results of the diffraction calculations. print("\nCalculating the diffraction results...") diffraction_result = diffraction.calculateDiffraction(diffraction_setup) # # Now the Mueller/Stokes calculation from the diffraction results # mueller_diffraction = MuellerDiffraction( diffraction_result, StokesVector([1, 0, 1, 0]), inclination_angle=0.0) #np.pi*45/180) # Create a MullerResult object. print("\nCalculating the Stokes vector...") mueller_result = mueller_diffraction.calculate_stokes() return mueller_result
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)
def testEnergy(self): photon = PolarizedPhoton(4000, Vector(0, 0, 1), StokesVector([1.0, 0.0, 1.0, 0.0])) self.assertEqual(photon.energy(), 4000)
def _use_hirano(self): """ Use the settings for the figures in the Hirano et al. article. """ print( "\n----------\nUse the settings specified in the figures from\n" "K.Hirano et al., 'Perfect crystal X-ray phase retarders'\n----------\n" ) figure_number = int( input( "Which figure do you want to reproduce? [e.g. for 'Fig.4' write '4'] " )) # I call the files where I store the parameters "Fig_$_Hirano.dat", where $ = figure_number. file_name = "Fig_{}_Hirano.dat".format(figure_number) # Open file. file = open(file_name, "r") # Read file. file_data = list() for line in file: file_data.append(line.partition(" #")[0]) self.geometry_type = str(file_data[0]) if self.geometry_type == "BraggDiffraction": self.geometry_type = GeometryType.BraggDiffraction() elif self.geometry_type == "BraggTransmission": self.geometry_type = GeometryType.BraggTransmission() elif self.geometry_type == "LaueDiffraction": self.geometry_type = GeometryType.LaueDiffraction() elif self.geometry_type == "LaueTransmission": self.geometry_type = GeometryType.LaueTransmission() else: raise Exception("The file content couldn't be read.") try: self.crystal_name = str(file_data[1]) self.thickness = float(file_data[2]) self.miller_h = int(file_data[3]) self.miller_k = int(file_data[4]) self.miller_l = int(file_data[5]) self.asymmetry_angle = float(file_data[6]) self.energy_min = float(file_data[7]) self.energy_max = float(file_data[8]) self.energy_points = int(file_data[9]) self.angle_deviation_min = float(file_data[10]) self.angle_deviation_max = float(file_data[11]) self.angle_deviation_points = int(file_data[12]) self.inclination_angle = float(file_data[13]) self.deg = bool(file_data[14]) self._stokes_parameters = list() for i in range(4): new_element = float(file_data[15 + i]) self._stokes_parameters.append(new_element) self.incoming_stokes_vector = StokesVector(self._stokes_parameters) # read the phase limitations. self.phase_inf_limit = float(file_data[19]) self.phase_sup_limit = float(file_data[20]) # read the intervals to set to zero. self.intervals_number = int(file_data[21]) # number of intervals. self.intervals = list() # if there are no intervals to set to zero close the file and return. if self.intervals_number == 0: file.close() return # else start reading the intervals. else: for i in range(self.intervals_number): interval = Interval(float(file_data[22 + i * 2]), float(file_data[23 + i * 2])) self.intervals.append(interval) except ValueError: raise Exception("The file content couldn't be read.") finally: file.close()
def _set_values(self): """ Set values for the diffraction parameters. """ while True: self.geometry_type = int( input( "\nBragg diffraction [0]\nBragg transmission [1]\n" "Laue diffraction [2]\nLaue transmission [3]\n\nChoose one type: " )) if self.geometry_type == 0: self.geometry_type = GeometryType.BraggDiffraction() break elif self.geometry_type == 1: self.geometry_type = GeometryType.BraggTransmission() break elif self.geometry_type == 2: self.geometry_type = GeometryType.LaueDiffraction() break elif self.geometry_type == 3: self.geometry_type = GeometryType.LaueTransmission() break else: print("The input could not be interpreted. Try again.\n") while True: try: self.crystal_name = str(input("\nCrystal name: ")) self.thickness = float(input("\nCrystal thickness [cm]: ")) self.miller_h = int(input("\nMiller H: ")) self.miller_k = int(input("\nMiller K: ")) self.miller_l = int(input("\nMiller L: ")) self.asymmetry_angle = float( input("\nAsymmetry angle [degrees]: ")) self.azimuthal_angle = float( input("\nAzimuthal angle [degrees]: ")) self.energy_min = float(input("\nMinimum energy [keV]: ")) self.energy_max = float(input("\nMaximum energy [keV]: ")) self.energy_points = int(input("\nNumber of energy points: ")) self.angle_deviation_min = float( input( "\nMinimum deviation from Bragg angle [micro radians]: " )) self.angle_deviation_max = float( input( "\nMaximum deviation from Bragg angle [micro radians]: " )) self.angle_deviation_points = int( input("\nNumber of deviation points: ")) self.inclination_angle = float( input("\nInclination angle [degrees]: ")) self.deg = bool( int( input( "\nShould the phase be represented in:\nradians[0]?\ndegrees[1]?" ))) self._stokes_parameters = list() self.phase_inf_limit = -180 # degrees self.phase_sup_limit = 180 # degrees for i in range(4): new_element = float( input("\nStokes parameter S{}: ".format(i))) self._stokes_parameters.append(new_element) self.incoming_stokes_vector = StokesVector( self._stokes_parameters) break except ValueError: print("\nAn error occurred. Try again.")
def calculate_external_CrystalCalculator(GEOMETRY_TYPE, CRYSTAL_NAME, THICKNESS, MILLER_H, MILLER_K, MILLER_L, ASYMMETRY_ANGLE, AZIMUTHAL_ANGLE, ENERGY_POINTS, ENERGY_MIN, ENERGY_MAX, ANGLE_DEVIATION_POINTS, ANGLE_DEVIATION_MIN, ANGLE_DEVIATION_MAX, STOKES_S0, STOKES_S1, STOKES_S2, STOKES_S3, INCLINATION_ANGLE, DUMP_TO_FILE, FILE_NAME="tmp.dat"): # Create a GeometryType object: # Bragg diffraction = 0 # Bragg transmission = 1 # Laue diffraction = 2 # Laue transmission = 3 if GEOMETRY_TYPE == 0: GEOMETRY_TYPE_OBJECT = BraggDiffraction() elif GEOMETRY_TYPE == 1: GEOMETRY_TYPE_OBJECT = BraggTransmission() elif GEOMETRY_TYPE == 2: GEOMETRY_TYPE_OBJECT = LaueDiffraction() elif GEOMETRY_TYPE == 3: GEOMETRY_TYPE_OBJECT = LaueTransmission() else: raise Exception( "CrystalCalculator: The geometry type could not be interpreted!" ) # Create a diffraction setup. # At this stage I translate angles in radians, energy in eV and all other values in SI units. print("CrystalCalculator: Creating a diffraction setup...\n") if ENERGY_POINTS == 1: if ENERGY_MIN != ENERGY_MAX: raise Exception( "CrystalCalculator: Finite energy range with only one sampled value!" ) diffraction_setup = DiffractionSetupSweeps( geometry_type=GEOMETRY_TYPE_OBJECT, # GeometryType object crystal_name=str(CRYSTAL_NAME), # string thickness=float(THICKNESS) * 1e-2, # meters miller_h=int(MILLER_H), # int miller_k=int(MILLER_K), # int miller_l=int(MILLER_L), # int asymmetry_angle=float(ASYMMETRY_ANGLE) / 180 * np.pi, # radians azimuthal_angle=float(AZIMUTHAL_ANGLE) / 180 * np.pi, # radians energy_min=float(ENERGY_MIN), # eV energy_max=float(ENERGY_MAX), # eV energy_points=int(ENERGY_POINTS), # int angle_deviation_min=float(ANGLE_DEVIATION_MIN) * 1e-6, # radians angle_deviation_max=float(ANGLE_DEVIATION_MAX) * 1e-6, # radians angle_deviation_points=int(ANGLE_DEVIATION_POINTS)) # int # Create a Diffraction object. diffraction = Diffraction() # Create a DiffractionResult object holding the results of the diffraction calculations. print("CrystalCalculator: Calculating the diffraction results...\n") diffraction_result = diffraction.calculateDiffraction( diffraction_setup) # Create a StokesVector object. incoming_stokes_vector = StokesVector( [STOKES_S0, STOKES_S1, STOKES_S2, STOKES_S3]) # Create a MuellerDiffraction object. mueller_diffraction = MuellerDiffraction( diffraction_result, incoming_stokes_vector, float(INCLINATION_ANGLE) * np.pi / 180) # Create a MullerResult object. print("CrystalCalculator: Calculating the Stokes vector...\n") mueller_result = mueller_diffraction.calculate_stokes() # Create the data to output. output_data = MailingBox(diffraction_result, mueller_result) # Dump data to file if requested. if DUMP_TO_FILE == 1: print("CrystalCalculator: Writing data in {file}...\n".format( file=FILE_NAME)) with open(FILE_NAME, "w") as file: try: file.write( "VALUES:\n\n" "geometry type: {geometry_type}\n" "crystal name: {crystal_name}\n" "thickness: {thickness}\n" "miller H: {miller_h}\n" "miller K: {miller_k}\n" "miller L: {miller_l}\n" "asymmetry angle: {asymmetry_angle}\n" "azimuthal angle: {azimuthal_angle}\n" "energy points: {energy_points}\n" "energy minimum: {energy_min}\n" "energy maximum: {energy_max}\n" "deviation angle points: {angle_deviation_points}\n" "deviation angle minimum: {angle_deviation_min}\n" "deviation angle maximum: {angle_deviation_max}\n" "inclination angle: {inclination_angle}\n" "incoming Stokes vector: {incoming_stokes_vector}\n\n" "RESULTS:\n\n" "S-Polarization:\n" "Intensity: {s_intensity}\n" "Phase: {s_phase}\n\n" "P-Polarization:\n" "Intensity: {p_intensity}\n" "Phase: {p_phase}\n\n" "SP-Difference:\n" "Intensity: {sp_intensity}\n" "Phase: {sp_phase}\n\n" "Stokes vector:\n" "S0: {s0}\n" "S1: {s1}\n" "S2: {s2}\n" "S3: {s3}\n\n" "Degree of circular polarization: {pol_degree}".format( geometry_type=GEOMETRY_TYPE_OBJECT.description(), crystal_name=CRYSTAL_NAME, thickness=THICKNESS, miller_h=MILLER_H, miller_k=MILLER_K, miller_l=MILLER_L, asymmetry_angle=ASYMMETRY_ANGLE, azimuthal_angle=AZIMUTHAL_ANGLE, energy_points=ENERGY_POINTS, energy_min=ENERGY_MIN, energy_max=ENERGY_MAX, angle_deviation_points=ANGLE_DEVIATION_POINTS, angle_deviation_min=ANGLE_DEVIATION_MIN, angle_deviation_max=ANGLE_DEVIATION_MAX, inclination_angle=INCLINATION_ANGLE, incoming_stokes_vector=incoming_stokes_vector. components(), s_intensity=diffraction_result.sIntensityByEnergy( ENERGY_MIN), s_phase=diffraction_result.sPhaseByEnergy( ENERGY_MIN, deg=True), p_intensity=diffraction_result.pIntensityByEnergy( ENERGY_MIN), p_phase=diffraction_result.pPhaseByEnergy( ENERGY_MIN, deg=True), sp_intensity=diffraction_result. differenceIntensityByEnergy(ENERGY_MIN), sp_phase=diffraction_result. differencePhaseByEnergy(ENERGY_MIN, deg=True), s0=mueller_result.s0_by_energy(ENERGY_MIN), s1=mueller_result.s1_by_energy(ENERGY_MIN), s2=mueller_result.s2_by_energy(ENERGY_MIN), s3=mueller_result.s3_by_energy(ENERGY_MIN), pol_degree=mueller_result. polarization_degree_by_energy(ENERGY_MIN))) file.close() print("File written to disk: %s" % FILE_NAME) except: raise Exception( "CrystalCalculator: The data could not be dumped onto the specified file!" ) return output_data
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 setUp(self): self.element_list = [ 0.78177969457877930, 0.22595711869558588, 0.28797567756487550, 0.58551861060989900 ] self.stokes_vector = StokesVector(self.element_list)
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")
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()
class StokesVectorTest(unittest.TestCase): def setUp(self): self.element_list = [ 0.78177969457877930, 0.22595711869558588, 0.28797567756487550, 0.58551861060989900 ] self.stokes_vector = StokesVector(self.element_list) def testConstructor(self): self.assertIsInstance(self.stokes_vector, StokesVector) self.assertEqual(self.stokes_vector.s0, 0.78177969457877930) self.assertEqual(self.stokes_vector.s1, 0.22595711869558588) self.assertEqual(self.stokes_vector.s2, 0.28797567756487550) self.assertEqual(self.stokes_vector.s3, 0.58551861060989900) def testGetArray(self): array1 = self.stokes_vector.components() array2 = self.stokes_vector.getList() self.assertEqual(type(array1), np.ndarray) self.assertEqual(type(array2), list) np.testing.assert_array_equal(array1, np.asarray(self.element_list)) self.assertListEqual(array2, self.element_list) def testPolarizationDegree(self): pol_deg = self.stokes_vector.circularPolarizationDegree() self.assertEqual(type(pol_deg), float) self.assertAlmostEqual(pol_deg, 0.7489560226111716) def test_operator_equal(self): stokes_vector1 = self.stokes_vector stokes_vector2 = StokesVector([ 0.7817796945787793, 0.22595711869558588, 0.2879756775648755, 0.585518610609899 ]) # without final zeros stokes_vector3 = StokesVector([ round(0.78177969457877930, 6), round(0.22595711869558588, 6), round(0.28797567756487550, 6), round(0.58551861060989900, 6) ]) # rounded float self.assertTrue(stokes_vector1 == stokes_vector1) # identity self.assertTrue(stokes_vector1 == stokes_vector2) self.assertFalse(stokes_vector1 == stokes_vector3) def test_operator_not_equal(self): stokes_vector1 = self.stokes_vector stokes_vector2 = StokesVector([ 0.7817796945787793, 0.22595711869558588, 0.2879756775648755, 0.585518610609899 ]) # without final zeros stokes_vector3 = StokesVector([ round(0.78177969457877930, 6), round(0.22595711869558588, 6), round(0.28797567756487550, 6), round(0.58551861060989900, 6) ]) # rounded float self.assertFalse(stokes_vector1 != stokes_vector1) self.assertFalse(stokes_vector1 != stokes_vector2) self.assertTrue(stokes_vector1 != stokes_vector3) def testDuplicate(self): v1 = StokesVector([1, 2, 3, 4]) v2 = v1.duplicate() self.assertTrue(v1 == v2) v1.setFromValues(0, 2, 3, 4) self.assertFalse(v1 == v2)