kklmn / xrt

Package xrt (XRayTracer) is a python software library for ray tracing and wave propagation in x-ray regime. It is primarily meant for modeling synchrotron sources, beamlines and beamline elements.
MIT License
83 stars 30 forks source link

Es and Ep calculation for the undulator #91

Open yangfg-bsrf opened 2 years ago

yangfg-bsrf commented 2 years ago

Hi, I am now trying to use the XRT to generate the Es、Ep for further calculation. here, I compared the three method, (1) Es, Ep = und.multi_electron_stack(energy, theta, psi) (2) screen for the observation in beamline

beamLine = raycing.BeamLine()
beamLine.source = rs.Undulator(beamLine,nrays = nrays,  **kwargs_SR)
beamLine.fsm1 = rsc.Screen(beamLine, 'FSM1', (0, p, 0))

limitx, limitz = 0.5, 0.5
bins= 256
ppb=1
edges = np.linspace(-limitx, limitx, bins+1)
beamLine.fsmExpX = (edges[:-1] + edges[1:]) * 0.5
edges = np.linspace(-limitz, limitz, bins+1)
beamLine.fsmExpZ = (edges[:-1] + edges[1:]) * 0.5

wave1 = beamLine.fsm1.prepare_wave(beamLine.source, beamLine.fsmExpX, beamLine.fsmExpZ)
beamSource = beamLine.source.shine(fixedEnergy=E1, wave= wave1)
outDict = {'beamSource': beamSource, 'beamFSM1': wave1}
Es1 = np.reshape(wave1.Es,[bins,bins])
phase1 = np.angle(Es1)

(3) bulild a beamline in a standard way following the examples. the results are shown below. My problem is that the phase of the wavefront are different, I have checked the code raying and found they use the build_I_map. Thanks! image

kklmn commented 2 years ago

I guess you assume something about all these objects. And what you assume I certainly don't know. Please explain what you want to achieve by each of these methods.

yangfg-bsrf commented 2 years ago

This is the field for single electron case. I want to confirm the fields are same for the coherence analysis as suggested The input for the analysis functions is a 3D stack of field images. It can be obtained directly from the undulator class, or from a plot object after several repeats of wave propagation of a filament beam through a beamline. Examples can be found in ...\tests\raycing\test_coherent_fraction_stack.py and in SoftiMAX at MAX IV. But the method followingtest_coherent_fraction_stack.py gives result that I can not explain.

yangfg-bsrf commented 2 years ago

I guess you assume something about all these objects. And what you assume I certainly don't know. Please explain what you want to achieve by each of these methods. I just followed the example test_coherent_fraction_stack.py

kklmn commented 2 years ago

still don't know what you want. Let's go over the three methods.

multi_electron_stack()

It gives a stack of single electron field maps, each one with its own shift in angle, position and e-beam energy. What is "phase" in this case that you plot?

yangfg-bsrf commented 2 years ago

still don't know what you want. Let's go over the three methods.

multi_electron_stack()

It gives a stick of single electron field maps, each one with its own shift in angle, position and e-beam energy. What is "phase" in this case that you plot?

Yes, I select one electron. it is calculated by Es, Ep = und.multi_electron_stack(energy, theta, psi) Es0 = Es[0,:,:] phase = np.angle(Es0)

kklmn commented 2 years ago

Yes, one slice from the stack gives a one-electron image, and then phase makes sense.

We go to the 2nd example. shine() gives samples of the radiation field. Unless you specify filamentBeam=True in the kwargs of Undulator(), these samples collectively belong to different electrons, which is ok for ray-tracing but not ok for wave propagation. The wave object in the shine() method, if it is given, specifies coordinates where you want to calculate these samples. The presence of wave does not tell that you calculate a one-electron field. What was your intent here and what is in kwargs_SR?

yangfg-bsrf commented 2 years ago

yes, the previous calculation filamentBeam=False, for the True case, the result is shown as: image the phase for the one-electron field is more reasonable for the 2nd method. but their phase are still different, but the intensity distribution show no difference. The intent of this work is to perform the coherence analysis on the Es / Ep using the multi_electron_stack, and I want to check Es,Ep by a different way.

I post the code below.

import os, sys; sys.path.append(os.path.join('..', '..'))  # analysis:ignore
import numpy as np
import time, cmath
import matplotlib.pyplot as plt
# import matplotlib as mpl
# mpl.use('Agg')
import xrt.backends.raycing.sources as rs
import xrt.backends.raycing.coherence as rco
import xrt.backends.raycing as raycing
import xrt.backends.raycing.screens as rsc
import xrt.backends.raycing.run as rr
import xrt.plotter as xrtp
import xrt.runner as xrtr
from xrt.backends.raycing.physconsts import E0, C, M0, EV2ERG, K2B, SIE0, EMC,\
    SIM0, FINE_STR, PI, PI2, SQ3, E2W, E2WC, CHeVcm

"""系统参数"""
accept_ang_x, accept_ang_z = 20e-6, 20e-6
binsx, binsz = 256, 256
repeats = 1  # "macro-electrons"
E0, n0 = 10000, 1  # 插入间工作能量点,及谐波级次
nrays= 2e5
p = 25000
n1 = 1 # 待分析谐波的能量点
# what = 'rays'
what = 'hybrid'

""" 光源 """
if True:
    E1 = E0/n0*n1  # 待分析的能量点
    if what == 'rays':
        uniformRayDensity = False
        filamentBeam = False
        isMono = False
        prefix = 'rays-'
    else:
        uniformRayDensity = True
        filamentBeam = True
        isMono = True
        prefix = 'hybr-'

dE = E1*0.001/2
eMin0, eMax0 = E1-dE, E1+dE
kwargs_SR = dict(
    center = [0, 0, 0],
    eE = 6.0, eI = 0.2, eEspread = 0.00111*0,
    eEpsilonX = 0.02728*0, eEpsilonZ = 0.002728*0,
    # betaX = 10.12, betaZ = 9.64,
    betaX = 2.83871, betaZ = 1.91667, 
    xPrimeMax = accept_ang_x, zPrimeMax = accept_ang_z,
    xPrimeMaxAutoReduce = False, zPrimeMaxAutoReduce = False,
    targetE = [E0, n0], 
    eMin = eMin0, eMax = eMax0,
    uniformRayDensity = uniformRayDensity,
    filamentBeam = filamentBeam,
    period=16.8,n=176,
    # R0=p,
    # period=35,n=142,
    K = r"auto")

und = rs.Undulator(**kwargs_SR)
thetaMax, psiMax = accept_ang_x, accept_ang_z  # rad
theta = np.linspace(-thetaMax, thetaMax, binsx)
psi = np.linspace(-psiMax, psiMax, binsz)

energy = np.ones(repeats) * E1
Es, Ep = und.multi_electron_stack(energy, theta, psi)

harmonic = n1
if und.eEspread > 1e-12:
    ses = '{0:.1e}'.format(und.eEspread)
    ses = ses[:-2] + ses[-1]  # removes "0" from power
else:
    ses = '0'
st = 'st' if harmonic == 1 else 'nd' if harmonic == 2 else 'rd'\
            if harmonic == 3 else 'th'
txt = '{0}{1} harmonic ({2:.0f} eV){5}{3} energy spread{5}' +\
    '$\\epsilon_x$ = {4:.0f} pmrad + $\\epsilon_z$ = {6:.0f} pmrad'
cap = txt.format(harmonic, st, E1, ses, und.eEpsilonX*1e9, ', ', und.eEpsilonZ*1e9)
baseName = 'h{0:02d}-esp{1}-emx{2:04.0f}--emz{3:04.0f}'.format(
    harmonic, und.eEspread, und.eEpsilonX*1e9, und.eEpsilonZ*1e9)

Es, Ep = und.multi_electron_stack(energy, theta, psi)
print("Es.shape", Es.shape)

Es0 = Es[0,:,:]
phase = np.angle(Es0)
plt.figure(8)
plt.subplot(1,2,1)
plt.imshow(phase)
plt.title('phase of the wavefront by the multi_electron_stack')
plt.subplot(1,2,2)
plt.title('Intensity by the multi_electron_stack')
plt.imshow(np.abs(Es0)**2)
plt.show()

#********The field generated by the shine method************
#***************************************
nrays = 5e5
beamLine = raycing.BeamLine()
beamLine.source = rs.Undulator(beamLine,nrays = nrays,  **kwargs_SR)
beamLine.fsm1 = rsc.Screen(beamLine, 'FSM1', (0, p, 0))

limitx, limitz = 0.5, 0.5
bins0= 500
# ppb=1
edges = np.linspace(-limitx, limitx, bins0+1)
beamLine.fsmExpX = (edges[:-1] + edges[1:]) * 0.5
edges = np.linspace(-limitz, limitz, bins0+1)
beamLine.fsmExpZ = (edges[:-1] + edges[1:]) * 0.5

wave1 = beamLine.fsm1.prepare_wave(beamLine.source, beamLine.fsmExpX, beamLine.fsmExpZ)
beamSource = beamLine.source.shine(fixedEnergy=E1, wave= wave1)
outDict = {'beamSource': beamSource, 'beamFSM1': wave1}
Es1 = np.reshape(wave1.Es,[bins0,bins0])
phase1 = np.angle(Es1)
plt.figure(10)
plt.subplot(1,2,1)
plt.imshow(phase1)
plt.title('phase of the wavefront by the shine function')
plt.subplot(1,2,2)
plt.title('Intensity by the shine function')
plt.imshow(np.abs(Es1)**2)
plt.show()
kklmn commented 2 years ago

The method multi_electron_stack() and the underlying method build_I_map() calculate the field in energy and two angles. They do not have any notion of distance to a screen or an optical element. If you want a field distribution on a particular surface then by specifying that surface the propagation path r becomes known (per wave sample) and we should add the propagation phase exp(ikr). We do this in the shine() method when wave is specified.

If you don't want the propagation phase then plot not the wave object but rather the beam object returned by shine(), its origin is at the source plane, and it doesn't have the propagation phase there.

Otherwise, add the propagation phase to multi_electron_stack() as you know the propagation path. You can search the source code at the end of shine() for how the phase can be added.

Note that if you calculate in near field (uncomment the kwarg R0=p) then the distance is known and the propagation phase is a part of the field inside multi_electron_stack() (and build_I_map()).

yangfg-bsrf commented 2 years ago

Yea, I have tried it as you suggested. They became same in the new codes. Thanks!