forked from ochubar/SRW
-
Notifications
You must be signed in to change notification settings - Fork 1
/
srwlib.py
4928 lines (4374 loc) · 257 KB
/
srwlib.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#############################################################################
# SRWLib for Python v 0.13
#############################################################################
from __future__ import print_function #Python 2.7 compatibility
import srwlpy as srwl
from array import *
from math import *
from copy import *
import datetime
import json
import random
import sys
import os
import traceback
import uti_math
import errno
import tempfile
import shutil
from srwl_uti_cryst import *
#try:
# from uti_plot import * #universal simple plotting module distributed together with SRWLib
#except:
# #excInf = sys.exc_info()
# #print(excInf[1]) #printing exception value
# traceback.print_exc()
# print('Plotting utilities module was not loaded.')
# print('1D and 2D plotting (generation of graphs, image plots, etc.) will not be possible.')
#****************************************************************************
#****************************************************************************
# Global Constants
#****************************************************************************
#****************************************************************************
_Pi = 3.14159265358979
_ElCh = 1.60217646263E-19 #1.602189246E-19 #Electron Charge [Q]
_ElMass_kg = 9.1093818872E-31 #9.10953447E-31 #Electron Mass in [kg]
_ElMass_MeV = 0.51099890221 #Electron Mass in [MeV]
_LightSp = 2.9979245812E+08 #Speed of Light [m/c]
_Light_eV_mu = 1.23984186 #Wavelength <-> Photon Energy conversion constant ([um] <-> [eV])
_PlanckConst_eVs = 4.13566766225E-15 #Planck constant in [eV*s]
#****************************************************************************
#****************************************************************************
# SRWLib Python Classes
#****************************************************************************
#****************************************************************************
class SRWLParticle(object):
"""Charged Particle"""
def __init__(self, _x=0, _y=0, _z=0, _xp=0, _yp=0, _gamma=1, _relE0=1, _nq=-1):
"""
:param _x: instant coordinates [m]
:param _y: instant coordinates [m]
:param _z: instant coordinates [m]
:param _xp: instant transverse velocity component btx = vx/c (angles for relativistic particle)
:param _yp: instant transverse velocity component bty = vy/c (angles for relativistic particle)
:param _gamma: relative energy
:param _relE0: rest mass (energy) in units of electron rest mass, e.g. 1 for electron, 1836.1526988 (=938.272013/0.510998902) for proton
:param _nq: charge of the particle related to absolute value of electron charge, -1 for electron, 1 for positron and for proton
"""
self.x = _x
self.y = _y
self.z = _z
self.xp = _xp
self.yp = _yp
self.gamma = _gamma
self.relE0 = _relE0
self.nq = _nq
def drift(self, _dist):
"""Propagates particle beam statistical moments over a distance in free space
:param _dist: distance the beam has to be propagated over [m]
"""
self.z += _dist
self.x += self.xp*_dist
self.y += self.yp*_dist
def get_E(self, _unit='GeV'):
en = self.gamma*self.relE0*_ElMass_MeV #[MeV]
if _unit == 'TeV': en *= 1e-06
elif _unit == 'GeV': en *= 1e-03
elif _unit == 'keV': en *= 1e+03
elif _unit == 'eV': en *= 1e+06
elif _unit == 'meV': en *= 1e+09
return en
#****************************************************************************
class SRWLPartBeam(object):
"""Particle Beam"""
def __init__(self, _Iavg=0, _nPart=0, _partStatMom1=None, _arStatMom2=None):
"""
:param _Iavg: average current [A]
:param _nPart: number of electrons (in a bunch)
:param _partStatMom1: particle type and 1st order statistical moments
:param _arStatMom2: 2nd order statistical moments
[0]: <(x-x0)^2>
[1]: <(x-x0)*(xp-xp0)>
[2]: <(xp-xp0)^2>
[3]: <(y-y0)^2>
[4]: <(y-y0)*(yp-yp0)>
[5]: <(yp-yp0)^2>
[6]: <(x-x0)*(y-y0)>
[7]: <(xp-xp0)*(y-y0)>
[8]: <(x-x0)*(yp-yp0)>
[9]: <(xp-xp0)*(yp-yp0)>
[10]: <(E-E0)^2>/E0^2
[11]: <(s-s0)^2>
[12]: <(s-s0)*(E-E0)>/E0
[13]: <(x-x0)*(E-E0)>/E0
[14]: <(xp-xp0)*(E-E0)>/E0
[15]: <(y-y0)*(E-E0)>/E0
[16]: <(yp-yp0)*(E-E0)>/E0
[17]: <(x-x0)*(s-s0)>
[18]: <(xp-xp0)*(s-s0)>
[19]: <(y-y0)*(s-s0)>
[20]: <(yp-yp0)*(s-s0)>
"""
self.Iavg = _Iavg
self.nPart = _nPart
self.partStatMom1 = SRWLParticle() if _partStatMom1 is None else _partStatMom1
self.arStatMom2 = array('d', [0] * 21) if _arStatMom2 is None else _arStatMom2
def from_Twiss(self, _Iavg=0, _e=0, _sig_e=0, _emit_x=0, _beta_x=0, _alpha_x=0, _eta_x=0, _eta_x_pr=0, _emit_y=0, _beta_y=0, _alpha_y=0, _eta_y=0, _eta_y_pr=0):
"""Sets up particle (electron) beam internal data from Twiss parameters
:param _Iavg: average current [A]
:param _e: energy [GeV]
:param _sig_e: RMS energy spread
:param _emit_x: horizontal emittance [m]
:param _beta_x: horizontal beta-function [m]
:param _alpha_x: horizontal alpha-function [rad]
:param _eta_x: horizontal dispersion function [m]
:param _eta_x_pr: horizontal dispersion function derivative [rad]
:param _emit_y: vertical emittance [m]
:param _beta_y: vertical beta-function [m]
:param _alpha_y: vertical alpha-function [rad]
:param _eta_y: vertical dispersion function [m]
:param _eta_y_pr: vertical dispersion function derivative [rad]
"""
self.Iavg = _Iavg
self.partStatMom1.gamma = _e/0.51099890221e-03 #assuming electrons
sigeE2 = _sig_e*_sig_e
self.arStatMom2[0] = _emit_x*_beta_x + sigeE2*_eta_x*_eta_x #<(x-<x>)^2>
self.arStatMom2[1] = -_emit_x*_alpha_x + sigeE2*_eta_x*_eta_x_pr #<(x-<x>)(x'-<x'>)>
self.arStatMom2[2] = _emit_x*(1 + _alpha_x*_alpha_x)/_beta_x + sigeE2*_eta_x_pr*_eta_x_pr #<(x'-<x'>)^2>
self.arStatMom2[3] = _emit_y*_beta_y + sigeE2*_eta_y*_eta_y #<(y-<y>)^2>
self.arStatMom2[4] = -_emit_y*_alpha_y + sigeE2*_eta_y*_eta_y_pr #<(y-<y>)(y'-<y'>)>
self.arStatMom2[5] = _emit_y*(1 + _alpha_y*_alpha_y)/_beta_y + sigeE2*_eta_y_pr*_eta_y_pr #<(y'-<y'>)^2>
self.arStatMom2[10] = sigeE2
def from_RMS(self, _Iavg=0, _e=0, _sig_e=0, _sig_x=0, _sig_x_pr=0, _m_xx_pr=0, _sig_y=0, _sig_y_pr=0, _m_yy_pr=0):
"""Sets up particle (electron) beam internal data from Twiss parameters
:param _Iavg: average current [A]
:param _e: energy [GeV]
:param _sig_e: RMS energy spread
:param _sig_x: horizontal RMS size [m]
:param _sig_x_pr: horizontal RMS divergence [rad]
:param _m_xx_pr: <(x-<x>)(x'-<x'>)> [m]
:param _sig_y: vertical RMS size [m]
:param _sig_y_pr: vertical RMS divergence [rad]
:param _m_yy_pr: <(y-<y>)(y'-<y'>)> [m]
"""
self.Iavg = _Iavg
self.partStatMom1.gamma = _e/0.51099890221e-03 #assuming electrons
sigeE2 = _sig_e*_sig_e
self.arStatMom2[0] = _sig_x*_sig_x #<(x-<x>)^2>
self.arStatMom2[1] = _m_xx_pr #<(x-<x>)(x'-<x'>)>
self.arStatMom2[2] = _sig_x_pr*_sig_x_pr #<(x'-<x'>)^2>
self.arStatMom2[3] = _sig_y*_sig_y #<(y-<y>)^2>
self.arStatMom2[4] = _m_yy_pr #<(y-<y>)(y'-<y'>)>
self.arStatMom2[5] = _sig_y_pr*_sig_y_pr #<(y'-<y'>)^2>
self.arStatMom2[10] = sigeE2
def drift(self, _dist):
"""Propagates particle beam statistical moments over a distance in free space
:param _dist: distance the beam has to be propagated over [m]
"""
self.partStatMom1.drift(_dist)
self.arStatMom2[0] += self.arStatMom2[1]*_dist*2 + self.arStatMom2[2]*_dist*_dist
self.arStatMom2[1] += self.arStatMom2[2]*_dist
self.arStatMom2[3] += self.arStatMom2[4]*_dist*2 + self.arStatMom2[5]*_dist*_dist
self.arStatMom2[4] += self.arStatMom2[5]*_dist
#to be checked and extended for other stat. moments
#****************************************************************************
class SRWLMagFld(object):
"""Magnetic Field (base class)"""
class SRWLMagFld3D(SRWLMagFld):
"""Magnetic Field: Arbitrary 3D"""
def __init__(self, _arBx=None, _arBy=None, _arBz=None, _nx=0, _ny=0, _nz=0, _rx=0, _ry=0, _rz=0, _nRep=1, _interp=1, _arX=None, _arY=None, _arZ=None):
"""
:param _arBx: horizontal magnetic field component array [T]
:param _arBy: vertical magnetic field component array [T]
:param _arBz: longitudinal magnetic field component array [T]
:param _nx: number of magnetic field data points in the horizontal direction
:param _ny: number of magnetic field data points in the vertical direction
:param _nz: number of magnetic field data points in the longitudinal direction
:param _rx: range of horizontal coordinate for which the field is defined [m]
:param _ry: range of vertical coordinate for which the field is defined [m]
:param _rz: range of longitudinal coordinate for which the field is defined [m]
:param _nRep: "number of periods", i.e. number of times the field is "repeated" in the longitudinal direction
:param _interp: interpolation method to use (e.g. for trajectory calculation), 1- bi-linear (3D), 2- (bi-)quadratic (3D), 3- (bi-)cubic (3D)
:param _arX: optional array of horizontal transverse coordinate of an irregular 3D mesh (if this array is defined, rx will be ignored)
:param _arY: optional array of vertical transverse coordinate of an irregular 3D mesh (if this array is defined, ry will be ignored)
:param _arZ: optional array of longitudinal coordinate of an irregular 3D mesh (if this array is defined, rz will be ignored)
"""
self.arBx = array('d') if _arBx is None else _arBx
self.arBy = array('d') if _arBy is None else _arBy
self.arBz = array('d') if _arBz is None else _arBz
self.nx = _nx
self.ny = _ny
self.nz = _nz
self.rx = _rx
self.ry = _ry
self.rz = _rz
self.arX = array('d') if _arX is None else _arX
self.arY = array('d') if _arY is None else _arY
self.arZ = array('d') if _arZ is None else _arZ
self.nRep = _nRep
self.interp = _interp
def add_const(self, _bx=0, _by=0, _bz=0):
"""Adds constant magnetic field to the entire tabulated field (to simulate e.g. background magnetic field effects)
:param _bx: horizontal magnetic field component to add [T]
:param _by: vertical magnetic field component to add [T]
:param _bz: longitudinal magnetic field component to add [T]
"""
nTot = self.nx*self.ny*self.nz
for i in range(nTot):
self.arBx[i] += _bx
self.arBy[i] += _by
self.arBz[i] += _bz
def save_ascii(self, _file_path, _xc=0, _yc=0, _zc=0):
"""Auxiliary function to write tabulated Arbitrary 3D Magnetic Field data to ASCII file"""
sHead = '#Bx [T], By [T], Bz [T] on 3D mesh: inmost loop vs X (horizontal transverse position), outmost loop vs Z (longitudinal position)\n'
sHead += '#' + repr(-0.5*self.rx + _xc) + ' #initial X position [m]\n'
sHead += '#' + repr(0. if(self.nx <= 1) else self.rx/(self.nx - 1)) + ' #step of X [m]\n'
sHead += '#' + repr(self.nx) + ' #number of points vs X\n'
sHead += '#' + repr(-0.5*self.ry + _yc) + ' #initial Y position [m]\n'
sHead += '#' + repr(0. if(self.ny <= 1) else self.ry/(self.ny - 1)) + ' #step of Y [m]\n'
sHead += '#' + repr(self.ny) + ' #number of points vs Y\n'
sHead += '#' + repr(-0.5*self.rz + _zc) + ' #initial Z position [m]\n'
sHead += '#' + repr(0. if(self.nz <= 1) else self.rz/(self.nz - 1)) + ' #step of Z [m]\n'
sHead += '#' + repr(self.nz) + ' #number of points vs Z\n'
arColsWr = [self.arBx, self.arBy, self.arBz]
srwl_uti_write_data_cols(_file_path, arColsWr, '\t', sHead)
class SRWLMagFldM(SRWLMagFld):
"""Magnetic Field: Multipole Magnet"""
#def __init__(self, _G=0, _m=2, _n_or_s='n', _Leff=0, _Ledge=0):
def __init__(self, _G=0, _m=2, _n_or_s='n', _Leff=0, _Ledge=0, _R=0):
"""
:param _G: field parameter [T] for dipole, [T/m] for quadrupole (negative means defocusing for x), [T/m^2] for sextupole, [T/m^3] for octupole
:param _m: multipole order 1 for dipole, 2 for quadrupoole, 3 for sextupole, 4 for octupole
:param _n_or_s: normal ('n') or skew ('s')
:param _Leff: effective length [m]
:param _Ledge: "soft" edge length for field variation from 10% to 90% [m]; G/(1 + ((z-zc)/d)^2)^2 fringe field dependence is assumed
:param _R: radius of curvature of central trajectory [m] (for simulating e.g. quadrupole component integrated to a bending magnet; effective if > 0)
"""
self.G = _G
self.m = _m
self.n_or_s = _n_or_s
self.Leff = _Leff
self.Ledge = _Ledge
self.R = _R
class SRWLMagFldS(SRWLMagFld):
"""Magnetic Field: Solenoid"""
def __init__(self, _B=0, _Leff=0):
"""
:param _B: magnetic field [T]
:param _Leff: effective length [m]
"""
self.B = _B
self.Leff = _Leff
class SRWLMagFldH(SRWLMagFld):
"""Magnetic Field: Undulator Harmonic"""
def __init__(self, _n=1, _h_or_v='v', _B=0, _ph=0, _s=1, _a=1):
"""
:param _n: harmonic number
:param _h_or_v: magnetic field plane horzontal ('h') or vertical ('v')
:param _B: magnetic field amplitude [T]
:param _ph: initial phase [rad]
:param _s: symmetry vs longitudinal position 1 - symmetric (B ~ cos(2*Pi*n*z/per + ph)) , -1 - anti-symmetric (B ~ sin(2*Pi*n*z/per + ph))
:param _a: coefficient for transverse depenednce B*cosh(2*Pi*n*a*y/per)*cos(2*Pi*n*z/per + ph)
"""
self.n = _n
self.h_or_v = _h_or_v
self.B = _B
self.ph = _ph
self.s = _s
self.a = _a
class SRWLMagFldU(SRWLMagFld):
"""Magnetic Field: Undulator"""
def __init__(self, _arHarm=None, _per=0, _nPer=0):
"""
:param _arHarm: array of field harmonics
:param _per: period length [m]
:param _nPer: number of periods (will be rounded to integer)
"""
self.arHarm = [] if _arHarm is None else _arHarm
self.per = _per
self.nPer = _nPer
def allocate(self, _nHarm):
#self.arHarm = [SRWLMagFldH()]*_nHarm
arHarmLoc = []
for i in range(_nHarm): arHarm.append(SRWLMagFldH())
def set_sin(self, _per=0.02, _len=1, _bx=0, _by=0, _phx=0, _phy=0, _sx=1, _sy=1):
"""Setup basic undulator with sinusoidal magnetic field
:param _per: period length [m]
:param _len: undulator length [m]
:param _bx: horizontal magnetic field amplitude [m]
:param _by: vertical magnetic field amplitude [m]
:param _phx: initial phase of the horizontal magnetic field [rad]
:param _phx: initial phase of the vertical magnetic field [rad]
:param _sx: symmetry of the horizontal magnetic field vs longitudinal position 1 - symmetric (B ~ cos(2*Pi*n*z/per + ph)) , -1 - anti-symmetric (B ~ sin(2*Pi*n*z/per + ph))
:param _sy: symmetry of the vertical magnetic field vs longitudinal position
"""
nPerAvg = int(round(_len/_per))
self.nPer = nPerAvg
self.per = _per
if(len(self.arHarm) > 0):
del self.arHarm; self.arHarm = []
if(_bx != 0): self.arHarm.append(SRWLMagFldH(_h_or_v='h', _B=_bx, _ph=_phx, _s=_sx))
if(_by != 0): self.arHarm.append(SRWLMagFldH(_h_or_v='v', _B=_by, _ph=_phy, _s=_sy))
def get_K(self):
"""Estimate K (deflection parameter) value"""
mult = _ElCh/(2.*_Pi*_ElMass_kg*_LightSp)
nHarm = len(self.arHarm)
sumBdNe2 = 0
for i in range(nHarm):
curHarm = self.arHarm[i]
curBdN = curHarm.B/curHarm.n
sumBdNe2 += curBdN*curBdN
return mult*self.per*sqrt(sumBdNe2)
def K_2_B(self, K): #MR31072016 (added)
"""Convert K (deflection parameter) to B (magnetic field amplitude)"""
mult = _ElCh/(2.*_Pi*_ElMass_kg*_LightSp)
B = K / (mult * self.per)
return B
def get_E1(self, _en_elec=3., _unit='eV'):
"""Estimate fundamental photon energy
:param _en_elec: electron energy [GeV]
:return: fundamental photon energy [eV]
"""
K = self.get_K()
gamma = 1000.*_en_elec/_ElMass_MeV
lamda_m = self.per*(1. + 0.5*K*K)/(2.*gamma*gamma)
return srwl_uti_ph_en_conv(lamda_m, _in_u='m', _out_u=_unit)
def E1_2_K(self, _e1, _en_elec=3.):
"""Estimate deflection parameter from
:param _e1: fundamental photon energy [eV]
:param _en_elec: electron energy [GeV]
:return: deflection parameter
"""
buf = 9.4963421866853*_en_elec*_en_elec/self.per/_e1
if(buf < 1): return 0
else: return sqrt((buf - 1)*2)
def E1_2_B(self, _e1, _en_elec=3.):
"""Estimate deflection parameter from
:param _e1: fundamental photon energy [eV]
:param _en_elec: electron energy [GeV]
:return: magnetic field amplitude [T]
"""
K = self.E1_2_K(_e1, _en_elec)
return 2*_Pi*_ElMass_kg*_LightSp*K/(_ElCh*self.per)
class SRWLMagFldC(SRWLMagFld):
"""Magnetic Field: Container"""
def __init__(self, _arMagFld=None, _arXc=None, _arYc=None, _arZc=None, _arVx=None, _arVy=None, _arVz=None, _arAng=None):
#def __init__(self, _arMagFld=None, _arXc=None, _arYc=None, _arZc=None):
"""
:param _arMagFld: magnetic field structures array
:param _arXc: horizontal center positions of magnetic field elements in arMagFld array [m]
:param _arYc: vertical center positions of magnetic field elements in arMagFld array [m]
:param _arZc: longitudinal center positions of magnetic field elements in arMagFld array [m]
:param _arVx: horizontal components of axis vectors of magnetic field elements in arMagFld array [rad]
:param _arVy: vertical components of axis vectors of magnetic field elements in arMagFld array [rad]
:param _arVz: longitudinal components of axis vectors of magnetic field elements in arMagFld array [rad]
:param _arAng: rotation angles of magnetic field elements about their axes [rad]
"""
#self.arMagFld = [] if _arMagFld is None else _arMagFld
if(_arMagFld == None):
self.arMagFld = []
self.arXc = array('d') if _arXc is None else _arXc
self.arYc = array('d') if _arYc is None else _arYc
self.arZc = array('d') if _arZc is None else _arZc
#The following arrays are optional
#self.arVx = array('d') if _arVx is None else _arVx
#self.arVy = array('d') if _arVy is None else _arVy
#self.arVz = array('d') if _arVz is None else _arVz
#self.arAng = array('d') if _arAng is None else _arAng
else:
if(not(isinstance(_arMagFld, list) or isinstance(_arMagFld, array) or isinstance(_arMagFld, tuple))):
self.arMagFld = [_arMagFld] #to allow for simple initialization by one element
nElem = 1
else:
self.arMagFld = _arMagFld
nElem = len(_arMagFld)
if(_arXc == None):
self.arXc = array('d', [0]*nElem)
elif(isinstance(_arXc, array)):
self.arXc = _arXc
elif(isinstance(_arXc, list)): #or isinstance(_arXc, tuple)):
self.arXc = array('d', _arXc)
elif(nElem == 1):
self.arXc = array('d', [0])
self.arXc[0] = _arXc
if(_arYc == None):
self.arYc = array('d', [0]*nElem)
#elif(isinstance(_arYc, list) or isinstance(_arYc, array) or isinstance(_arYc, tuple)):
# self.arYc = _arYc
elif(isinstance(_arYc, array)):
self.arYc = _arYc
elif(isinstance(_arYc, list)): #or isinstance(_arYc, tuple)):
self.arYc = array('d', _arYc)
elif(nElem == 1):
self.arYc = array('d', [0])
self.arYc[0] = _arYc
if(_arZc == None):
self.arZc = array('d', [0]*nElem)
#elif(isinstance(_arZc, list) or isinstance(_arZc, array) or isinstance(_arZc, tuple)):
# self.arZc = _arZc
elif(isinstance(_arZc, array)):
self.arZc = _arZc
elif(isinstance(_arZc, list)): #or isinstance(_arZc, tuple)):
self.arZc = array('d', _arZc)
elif(nElem == 1):
self.arZc = array('d', [0])
self.arZc[0] = _arZc
arVxWasSubm = False
if(_arVx == None):
self.arVx = array('d', [0]*nElem)
elif(isinstance(_arVx, array)):
self.arVx = _arVx
arVxWasSubm = True
elif(isinstance(_arVx, list)):
self.arVx = array('d', _arVx)
arVxWasSubm = True
elif(nElem == 1):
self.arVx = array('d', [0])
self.arVx[0] = _arVx
arVyWasSubm = False
if(_arVy == None):
self.arVy = array('d', [0]*nElem)
elif(isinstance(_arVy, array)):
self.arVy = _arVy
arVyWasSubm = True
elif(isinstance(_arVy, list)):
self.arVy = array('d', _arVy)
arVyWasSubm = True
elif(nElem == 1):
self.arVy = array('d', [0])
self.arVy[0] = _arVy
if(_arVz == None):
self.arVz = array('d', [1]*nElem)
if(arVxWasSubm and arVyWasSubm):
lenArVx = len(_arVx)
lenArVy = len(_arVy)
if(lenArVx == lenArVy):
for i in range(lenArVx):
self.arVz[i] = sqrt(1. - _arVx[i]*_arVx[i] - _arVy[i]*_arVy[i])
elif(isinstance(_arVz, array)):
self.arVz = _arVz
elif(isinstance(_arVz, list)):
self.arVz = array('d', _arVz)
elif(nElem == 1):
self.arVz = array('d', [1])
self.arVz[0] = _arVz
if(_arAng == None):
self.arAng = array('d', [0]*nElem)
elif(isinstance(_arAng, array)):
self.arAng = _arAng
elif(isinstance(_arAng, list)):
self.arAng = array('d', _arAng)
elif(nElem == 1):
self.arAng = array('d', [0])
self.arAng[0] = _arAng
def allocate(self, _nElem):
self.arMagFld = [SRWLMagFld()]*_nElem
self.arXc = array('d', [0]*_nElem)
self.arYc = array('d', [0]*_nElem)
self.arZc = array('d', [0]*_nElem)
self.arVx = array('d', [0]*_nElem)
self.arVy = array('d', [0]*_nElem)
self.arVz = array('d', [1]*_nElem)
self.arAng = array('d', [0]*_nElem)
def add(self, _mag, _xc=None, _yc=None, _zc=None, _vx=None, _vy=None, _vz=None, _ang=None):
"""Adds magnetic element to container
:param _mag: magnetic element (or array of elements) to be added
:param _xc: horizontal center position (or array of center positions) of magnetic field element to be added [m]
:param _yc: vertical center positions (or array of center positions) of magnetic field element to be added [m]
:param _zc: longitudinal center positions (or array of center positions) of magnetic field element to be added [m]
:param _vx: horizontal component of axis vectors of magnetic field element to be added [rad]
:param _vy: vertical component of axis vectors of magnetic field element to be added [rad]
:param _vz: longitudinal components of axis vector of magnetic field element to be added [rad]
:param _ang: rotation angle about axis [rad]
"""
if(_mag == None):
raise Exception("No magnetic field elements were supplied for adding to container")
if(isinstance(_mag, list) or isinstance(_mag, array)):
lenMag = len(_mag)
if((_xc == None) and (_yc == None) and (_zc == None) and
(_vx == None) and (_vy == None) and (_vz == None) and (_ang == None)):
for i in range(lenMag): self.add(_mag[i])
elif((isinstance(_xc, list) or isinstance(_xc, array)) and
(isinstance(_yc, list) or isinstance(_yc, array)) and
(isinstance(_zc, list) or isinstance(_zc, array)) and
(isinstance(_vx, list) or isinstance(_vx, array)) and
(isinstance(_vy, list) or isinstance(_vy, array)) and
(isinstance(_vz, list) or isinstance(_vz, array)) and
(isinstance(_ang, list) or isinstance(_ang, array))):
lenXc = len(_xc)
lenYc = len(_yc)
lenZc = len(_zc)
lenVx = len(_vx)
lenVy = len(_vy)
lenVz = len(_vz)
lenAng = len(_ang)
if((lenXc == lenMag) and (lenYc == lenMag) and (lenZc == lenMag) and
(lenVx == lenMag) and (lenVy == lenMag) and (lenVz == lenMag) and (lenAng == lenMag)):
for i in range(lenMag): self.add(_mag[i], _xc[i], _yc[i], _zc[i], _vx[i], _vy[i], _vz[i])
else: raise Exception("Inconsistent magnetic element positions data")
else:
self.arMagFld.append(_mag)
if(_xc == None): _xc = 0
if(_yc == None): _yc = 0
if(_zc == None): _zc = 0
if(_vx == None): _vx = 0
if(_vy == None): _vy = 0
if(_vz == None):
_vz = 1.
if((_vx != None) and (_vy != None)):
_vz = sqrt(1. - _vx*_vx - _vy*_vy)
if(_ang == None): _ang = 0
self.arXc.append(_xc)
self.arYc.append(_yc)
self.arZc.append(_zc)
self.arVx.append(_vx)
self.arVy.append(_vy)
self.arVz.append(_vz)
self.arAng.append(_ang)
#****************************************************************************
class SRWLPrtTrj(object):
"""Charged Particle Trajectory"""
def __init__(self, _arX=None, _arXp=None, _arY=None, _arYp=None, _arZ=None, _arZp=None, _arBx=None, _arBy=None, _arBz=None, _np=0, _ctStart=0, _ctEnd=0, _partInitCond=None):
"""
:param _arX: array of horizontal position [m]
:param _arXp: array of horizontal relative velocity (trajectory angle) [rad]
:param _arY: array of vertical position [m]
:param _arYp: array of vertical relative velocity (trajectory angle) [rad]
:param _arZ: array of longitudinal positions [m]
:param _arZp: array of longitudinal relative velocity [rad]
:param _arBx: array of horizontal magnetic field component "seen" by particle [T]
:param _arBy: array of vertical magnetic field component "seen" by particle [T]
:param _arBz: array of longitudinal magnetic field component "seen" by particle [T]
:param _np: number of trajectory points
:param _ctStart: start value of independent variable (c*t) for which the trajectory should be (/is) calculated (is constant step enough?)
:param _ctEnd: end value of independent variable (c*t) for which the trajectory should be (/is) calculated (is constant step enough?)
:param _partInitCond: particle type and initial conditions for which the trajectory should be (/is) calculated
"""
if(_np > 0):
self.arX = array('d', [0]*_np) if _arX is None else _arX
self.arY = array('d', [0]*_np) if _arY is None else _arY
self.arZ = array('d', [0]*_np) if _arZ is None else _arZ
self.arXp = array('d', [0]*_np) if _arXp is None else _arXp
self.arYp = array('d', [0]*_np) if _arYp is None else _arYp
self.arZp = array('d', [0]*_np) if _arZp is None else _arZp
else:
self.arX = array('d') if _arX is None else _arX
self.arY = array('d') if _arY is None else _arY
self.arZ = array('d') if _arZ is None else _arZ
self.arXp = array('d') if _arXp is None else _arXp
self.arYp = array('d') if _arYp is None else _arYp
self.arZp = array('d') if _arZp is None else _arZp
if _arBx != None: self.arBx = _arBx #by default, arBx, _arBy, arBz are not created
if _arBy != None: self.arBy = _arBy
if _arBz != None: self.arBz = _arBz
self.np = _np
self.ctStart = _ctStart
self.ctEnd = _ctEnd
self.partInitCond = SRWLParticle() if _partInitCond is None else _partInitCond
def allocate(self, _np, _allB=False):
_np = int(_np)
self.arX = array('d', [0]*_np)
self.arXp = array('d', [0]*_np)
self.arY = array('d', [0]*_np)
self.arYp = array('d', [0]*_np)
self.arZ = array('d', [0]*_np)
self.arZp = array('d', [0]*_np)
self.np = _np
if _allB == True:
self.arBx = array('d', [0]*_np)
self.arBy = array('d', [0]*_np)
self.arBz = array('d', [0]*_np)
def save_ascii(self, _file_path):
"""Auxiliary function to write tabulated Trajectory data to ASCII file"""
f = open(_file_path, 'w')
resStr = '#ct [m], X [m], BetaX [rad], Y [m], BetaY [rad], Z [m], BetaZ [rad]'
if(hasattr(self, 'arBx')):
resStr += ', Bx [T]'
if(hasattr(self, 'arBy')):
resStr += ', By [T]'
if(hasattr(self, 'arBz')):
resStr += ', Bz [T]'
f.write(resStr + '\n')
ctStep = 0
if self.np > 0:
ctStep = (self.ctEnd - self.ctStart)/(self.np - 1)
ct = self.ctStart
for i in range(self.np):
resStr = str(ct) + '\t' + repr(self.arX[i]) + '\t' + repr(self.arXp[i]) + '\t' + repr(self.arY[i]) + '\t' + repr(self.arYp[i]) + '\t' + repr(self.arZ[i]) + '\t' + repr(self.arZp[i])
if(hasattr(self, 'arBx')):
resStr += '\t' + repr(self.arBx[i])
if(hasattr(self, 'arBy')):
resStr += '\t' + repr(self.arBy[i])
if(hasattr(self, 'arBz')):
resStr += '\t' + repr(self.arBz[i])
f.write(resStr + '\n')
ct += ctStep
f.close()
#****************************************************************************
class SRWLKickM(object):
"""Kick Matrix (for fast trajectory calculation)"""
def __init__(self, _arKickMx=None, _arKickMy=None, _order=2, _nx=0, _ny=0, _nz=0, _rx=0, _ry=0, _rz=0, _x=0, _y=0, _z=0):
"""
:param _arKickMx: horizontal kick-matrix (tabulated on the same transverse grid vs x and y as vertical kick-matrix)
:param _arKickMy: vertical kick-matrix (tabulated on the same transverse grid vs x and y as horizontal kick-matrix)
:param _order: kick order: 1- first order (in this case kick matrix data is assumed to be in [T*m]), 2- second order (kick matrix data is assumed to be in [T^2*m^2])
:param _nx: numbers of points in kick matrices in horizontal direction
:param _ny: numbers of points in kick matrices in vertical direction
:param _nz: number of steps in longitudinal direction
:param _rx: range covered by kick matrices in horizontal direction [m]
:param _ry: range covered by kick matrices in vertical direction [m]
:param _rz: extension in longitudinal direction [m]
:param _x: horizontal coordinate of center point [m]
:param _y: vertical coordinate of center point [m]
:param _z: longitudinal coordinate of center point [m]
"""
self.arKickMx = array('d') if _arKickMx is None else _arKickMx
self.arKickMy = array('d') if _arKickMy is None else _arKickMy
self.order = _order
self.nx = _nx
self.ny = _ny
self.nz = _nz
self.rx = _rx
self.ry = _ry
self.rz = _rz
self.x = _x
self.y = _y
self.z = _z
#****************************************************************************
class SRWLGsnBm(object):
"""Gaussian Beam"""
def __init__(self, _x=0, _y=0, _z=0, _xp=0, _yp=0, _avgPhotEn=1, _pulseEn=1, _repRate=1, _polar=1, _sigX=10e-06,
_sigY=10e-06, _sigT=1e-15, _mx=0, _my=0):
"""
:param _x: average horizontal coordinates of waist [m]
:param _y: average vertical coordinates of waist [m]
:param _z: average longitudinal coordinate of waist [m]
:param _xp: average horizontal angle at waist [rad]
:param _yp: average verical angle at waist [rad]
:param _avgPhotEn: average photon energy [eV]
:param _pulseEn: energy per pulse [J]
:param _repRate: rep. rate [Hz]
:param _polar: polarization 1- lin. hor., 2- lin. vert., 3- lin. 45 deg., 4- lin.135 deg., 5- circ. right, 6- circ. left
:param _sigX: rms beam size vs horizontal position [m] at waist (for intensity)
:param _sigY: rms beam size vs vertical position [m] at waist (for intensity)
:param _sigT: rms pulse duration [s] (for intensity)
:param _mx: transverse Gauss-Hermite mode order in horizontal direction
:param _my: transverse Gauss-Hermite mode order in vertical direction
"""
self.x = _x
self.y = _y
self.z = _z
self.xp = _xp
self.yp = _yp
self.avgPhotEn = _avgPhotEn
self.pulseEn = _pulseEn
self.repRate = _repRate
self.polar = _polar
self.sigX = _sigX
self.sigY = _sigY
self.sigT = _sigT
self.mx = _mx
self.my = _my
#****************************************************************************
class SRWLRadMesh(object):
"""Radiation Mesh (Sampling)"""
def __init__(self, _eStart=0, _eFin=0, _ne=1, _xStart=0, _xFin=0, _nx=1, _yStart=0, _yFin=0, _ny=1, _zStart=0, _nvx=0, _nvy=0, _nvz=1, _hvx=1, _hvy=0, _hvz=0, _arSurf=None):
"""
:param _eStart: initial value of photon energy (/time)
:param _eFin: final value of photon energy (/time)
:param _ne: number of points vs photon energy (/time)
:param _xStart: initial value of horizontal position (/angle)
:param _xFin: final value of horizontal position (/angle)
:param _nx: number of points vs horizontal position (/angle)
:param _yStart: initial value of vertical position (/angle)
:param _yFin: final value of vertical position (/angle)
:param _ny: number of points vs vertical position (/angle)
:param _zStart: longitudinal position
:param _nvx: horizontal lab-frame coordinate of inner normal to observation plane (/ surface in its center)
:param _nvy: vertical lab-frame coordinate of inner normal to observation plane (/ surface in its center)
:param _nvz: longitudinal lab-frame coordinate of inner normal to observation plane (/ surface in its center)
:param _hvx: horizontal lab-frame coordinate of the horizontal base vector of the observation plane (/ surface in its center)
:param _hvy: vertical lab-frame coordinate of the horizontal base vector of the observation plane (/ surface in its center)
:param _hvz: longitudinal lab-frame coordinate of the horizontal base vector of the observation plane (/ surface in its center)
:param _arSurf: array defining the observation surface (as function of 2 variables - x & y - on the mesh given by _xStart, _xFin, _nx, _yStart, _yFin, _ny; to be used in case this surface differs from plane)
"""
self.eStart = _eStart
self.eFin = _eFin
self.ne = _ne
self.xStart = _xStart
self.xFin = _xFin
self.nx = _nx
self.yStart = _yStart
self.yFin = _yFin
self.ny = _ny
self.zStart = _zStart
self.nvx = _nvx
self.nvy = _nvy
self.nvz = _nvz
self.hvx = _hvx
self.hvy = _hvy
self.hvz = _hvz
self.arSurf = _arSurf
def set_from_other(self, _mesh):
self.eStart = _mesh.eStart; self.eFin = _mesh.eFin; self.ne = _mesh.ne;
self.xStart = _mesh.xStart; self.xFin = _mesh.xFin; self.nx = _mesh.nx;
self.yStart = _mesh.yStart; self.yFin = _mesh.yFin; self.ny = _mesh.ny;
self.zStart = _mesh.zStart
self.nvx = _mesh.nvx; self.nvy = _mesh.nvy; self.nvz = _mesh.nvz
self.hvx = _mesh.hvx; self.hvy = _mesh.hvy; self.hvz = _mesh.hvz
del self.arSurf; self.arSurf = None
if(_mesh.arSurf != None):
try:
lenArSurf = len(_mesh.arSurf)
if(lenArSurf > 0):
self.arSurf = array('d', [0]*lenArSurf)
for i in range(lenArSurf):
self.arSurf[i] = _mesh.arSurf[i]
except:
pass
#****************************************************************************
class SRWLStokes(object):
"""Radiation Stokes Parameters"""
#def __init__(self, _arS0=None, _arS1=None, _arS2=None, _arS3=None, _typeStokes='f', _eStart=0, _eFin=0, _ne=0, _xStart=0, _xFin=0, _nx=0, _yStart=0, _yFin=0, _ny=0):
def __init__(self, _arS=None, _typeStokes='f', _eStart=0, _eFin=0, _ne=0, _xStart=0, _xFin=0, _nx=0, _yStart=0, _yFin=0, _ny=0, _mutual=0):
"""
:param _arS: flat C-aligned array of all Stokes components (outmost loop over Stokes parameter number); NOTE: only 'f' (float) is supported for the moment (Jan. 2012)
:param _typeStokes: numerical type: 'f' (float) or 'd' (double, not supported yet)
:param _eStart: initial value of photon energy (/time)
:param _eFin: final value of photon energy (/time)
:param _ne: numbers of points vs photon energy
:param _xStart: initial value of horizontal position
:param _xFin: final value of photon horizontal position
:param _nx: numbers of points vs horizontal position
:param _yStart: initial value of vertical position
:param _yFin: final value of vertical position
:param _ny: numbers of points vs vertical position
:param _mutual: mutual Stokes components (4*(_ne*_nx*_ny_)^2 values)
"""
self.arS = _arS #flat C-aligned array of all Stokes components (outmost loop over Stokes parameter number); NOTE: only 'f' (float) is supported for the moment (Jan. 2012)
self.numTypeStokes = _typeStokes #electric field numerical type: 'f' (float) or 'd' (double)
self.mesh = SRWLRadMesh(_eStart, _eFin, _ne, _xStart, _xFin, _nx, _yStart, _yFin, _ny) #to make mesh an instance variable
self.avgPhotEn = 0 #average photon energy for time-domain simulations
self.presCA = 0 #presentation/domain: 0- coordinates, 1- angles
self.presFT = 0 #presentation/domain: 0- frequency (photon energy), 1- time
self.unitStokes = 1 #Stokes units: 0- arbitrary, 1- Phot/s/0.1%bw/mm^2 ?
self.mutual = _mutual #indicator of Mutual Stokes components
nProd = _ne*_nx*_ny #array length to store one component of complex electric field
if((_arS == 1) and (nProd > 0)):
self.allocate(_ne, _nx, _ny, _typeStokes, _mutual)
#s0needed = 0
#s1needed = 0
#s2needed = 0
#s3needed = 0
#if((_arS0 == 1) and (nProd > 0)):
# s0needed = 1
#if((_arS1 == 1) and (nProd > 0)):
# s1needed = 1
#if((_arS2 == 1) and (nProd > 0)):
# s2needed = 1
#if((_arS3 == 1) and (nProd > 0)):
# s3needed = 1
#if((s0needed > 0) or (s1needed > 0) or (s2needed > 0) or (s3needed > 0)):
# self.allocate(_ne, _nx, _ny, s0needed, s1needed, s2needed, s3needed)
#def allocate(self, _ne, _nx, _ny, s0needed=1, s1needed=1, s2needed=1, s3needed=1, _typeStokes='f'):
def allocate(self, _ne, _nx, _ny, _typeStokes='f', _mutual=0):
#print('') #debugging
#print(' (re-)allocating: old point numbers: ne=',self.mesh.ne,' nx=',self.mesh.nx,' ny=',self.mesh.ny,' type:',self.numTypeStokes)
#print(' new point numbers: ne=',_ne,' nx=',_nx,' ny=',_ny,' type:',_typeStokes)
#nTot = _ne*_nx*_ny #array length to one Stokes component
#if s0needed:
# del self.arS0
# self.arS0 = array(_typeStokes, [0]*nTot)
#if s1needed:
# del self.arS1
# self.arS1 = array(_typeStokes, [0]*nTot)
#if s2needed:
# del self.arS2
# self.arS2 = array(_typeStokes, [0]*nTot)
#if s3needed:
# del self.arS3
# self.arS3 = array(_typeStokes, [0]*nTot)
nTot = _ne*_nx*_ny
if _mutual > 0:
nTot *= nTot
nTot *= 4 #array length of all Stokes components
#eventually allow for storage of less than 4 Stokes components!
self.arS = array(_typeStokes, [0]*nTot)
self.numTypeStokes = _typeStokes
self.mesh.ne = _ne
self.mesh.nx = _nx
self.mesh.ny = _ny
self.mutual = _mutual
def add_stokes(self, _st, _n_comp=4, _mult=1, _meth=0):
"""Add Another Stokes structure
:param _st: Stokes structure to be added
:param _n_comp: number of components to treat
:param _mult: multiplier
:param _meth: method of adding the Stokes structure _st:
0- simple addition assuming _wfr to have same mesh as this wavefront
1- add using bilinear interpolation (taking into account meshes of the two wavefronts)
2- add using bi-quadratic interpolation (taking into account meshes of the two wavefronts)
3- add using bi-cubic interpolation (taking into account meshes of the two wavefronts)
"""
nTot = _n_comp*self.mesh.ne*self.mesh.nx*self.mesh.ny #eventually allow for storage of less than 4 Stokes components!
if(self.mutual > 0): nTot *= nTot
if(_meth == 0):
if((self.mesh.ne != _st.mesh.ne) or (self.mesh.nx != _st.mesh.nx) or (self.mesh.ny != _st.mesh.ny)):
raise Exception("Stokes parameters addition can not be performed by this method because of unequal sizes of the two Stokes structures")
st_arS = _st.arS
if(_mult == 1):
for i in range(nTot):
#for some reason, this increases memory requirements in Py(?):
self.arS[i] += st_arS[i]
else:
for i in range(nTot):
#for some reason, this increases memory requirements in Py(?):
self.arS[i] += _mult*st_arS[i]
elif(_meth == 1):
#to implement
raise Exception("This Stokes parameters addition method is not implemented yet")
elif(_meth == 2):
#to implement
raise Exception("This Stokes parameters addition method is not implemented yet")
elif(_meth == 3):
#to implement
raise Exception("This Stokes parameters addition method is not implemented yet")
def avg_update_same_mesh(self, _more_stokes, _iter, _n_stokes_comp=4, _mult=1.):
""" Update this Stokes data structure with new data, contained in the _more_stokes structure, calculated on the same mesh, so that this structure would represent estimation of average of (_iter + 1) structures
:param _more_stokes: Stokes data structure to "add" to the estimation of average
:param _iter: number of Stokes structures already "added" previously
:param _n_stokes_comp: number of Stokes components to treat (1 to 4)
:param _mult: optional multiplier of the _more_stokes
"""
#DEBUG
#print('avg_update_same_mesh: iter=', _iter, _mult)
#nStPt = self.mesh.ne*self.mesh.nx*self.mesh.ny*_n_stokes_comp
nStPt = self.mesh.ne*self.mesh.nx*self.mesh.ny
if self.mutual > 0:
nStPt *= nStPt
nStPt *= _n_stokes_comp
if(_mult == 1.):
for ir in range(nStPt):
self.arS[ir] = (self.arS[ir]*_iter + _more_stokes.arS[ir])/(_iter + 1)
else:
for ir in range(nStPt):
self.arS[ir] = (self.arS[ir]*_iter + _mult*_more_stokes.arS[ir])/(_iter + 1)
def avg_update_interp(self, _more_stokes, _iter, _ord, _n_stokes_comp=4, _mult=1.):
""" Update this Stokes data structure with new data, contained in the _more_stokes structure, calculated on a different 2D mesh, so that it would represent estimation of average of (_iter + 1) structures
:param _more_stokes: Stokes data structure to "add" to the estimation of average
:param _iter: number of Stokes structures already "added" previously
:param _ord: order of 2D interpolation to use (1- bilinear, ..., 3- bi-cubic)
:param _n_stokes_comp: number of Stokes components to treat (1 to 4)
:param _mult: optional multiplier of the _more_stokes
"""
#DEBUG
#print('avg_update_interp: iter=', _iter, _mult)
eNpMeshRes = self.mesh.ne
xNpMeshRes = self.mesh.nx
xStartMeshRes = self.mesh.xStart
xStepMeshRes = 0
if(xNpMeshRes > 1):
xStepMeshRes = (self.mesh.xFin - xStartMeshRes)/(xNpMeshRes - 1)
yNpMeshRes = self.mesh.ny
yStartMeshRes = self.mesh.yStart
yStepMeshRes = 0
if(yNpMeshRes > 1):
yStepMeshRes = (self.mesh.yFin - yStartMeshRes)/(yNpMeshRes - 1)
eNpWfr = _more_stokes.mesh.ne
xStartWfr = _more_stokes.mesh.xStart
xNpWfr = _more_stokes.mesh.nx
xStepWfr = 0
if(xNpWfr > 1):
xStepWfr = (_more_stokes.mesh.xFin - xStartWfr)/(xNpWfr - 1)
yStartWfr = _more_stokes.mesh.yStart
yNpWfr = _more_stokes.mesh.ny
yStepWfr = 0
if(yNpWfr > 1):
yStepWfr = (_more_stokes.mesh.yFin - yStartWfr)/(yNpWfr - 1)
#DEBUG
#print('avg_update_interp: iter=', _iter)
#END DEBUG
nRadWfr = eNpWfr*xNpWfr*yNpWfr
iOfstSt = 0
ir = 0
for iSt in range(_n_stokes_comp):
for iy in range(yNpMeshRes):
yMeshRes = yStartMeshRes + iy*yStepMeshRes
for ix in range(xNpMeshRes):
xMeshRes = xStartMeshRes + ix*xStepMeshRes
for ie in range(eNpMeshRes):
#calculate Stokes parameters of propagated wavefront on the resulting mesh
#fInterp = srwl_uti_interp_2d(xMeshRes, yMeshRes, xStartWfr, xStepWfr, xNpWfr, yStartWfr, yStepWfr, yNpWfr, workArStokes, 1, eNpWfr, iOfstStokes)
fInterp = 0
loc_ix_ofst = iOfstSt + ie
nx_ix_per = xNpWfr*eNpWfr
if(_ord == 1): #bi-linear interpolation based on 4 points
ix0 = int(trunc((xMeshRes - xStartWfr)/xStepWfr + 1.e-09))
if((ix0 < 0) or (ix0 >= xNpWfr - 1)):
self.arS[ir] = self.arS[ir]*_iter/(_iter + 1); ir += 1
continue
ix1 = ix0 + 1
tx = (xMeshRes - (xStartWfr + xStepWfr*ix0))/xStepWfr
iy0 = int(trunc((yMeshRes - yStartWfr)/yStepWfr + 1.e-09))
if((iy0 < 0) or (iy0 >= yNpWfr - 1)):
self.arS[ir] = self.arS[ir]*_iter/(_iter + 1); ir += 1
continue
iy1 = iy0 + 1
ty = (yMeshRes - (yStartWfr + yStepWfr*iy0))/yStepWfr
iy0_nx_ix_per = iy0*nx_ix_per