Mercurial > repos > astroteam > crbeam_astro_tool
comparison mc_rotate.py @ 0:f40d05521dca draft default tip
planemo upload for repository https://github.com/esg-epfl-apc/tools-astro/tree/main/tools commit de01e3c02a26cd6353a6b9b6f8d1be44de8ccd54
| author | astroteam |
|---|---|
| date | Fri, 25 Apr 2025 19:33:20 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:f40d05521dca |
|---|---|
| 1 import math | |
| 2 | |
| 3 import numpy as np | |
| 4 | |
| 5 | |
| 6 def mc_rotate( | |
| 7 mc_file, jet_half_size_deg, jet_direction_deg, psf_deg=180, rotated_file=None | |
| 8 ): | |
| 9 if rotated_file is None: | |
| 10 rotated_file = f"{mc_file}_{jet_half_size_deg}_{jet_direction_deg}_{psf_deg}" | |
| 11 | |
| 12 jet_half_size = ( | |
| 13 np.longdouble(jet_half_size_deg) / 180.0 * math.pi | |
| 14 ) # jet openning angle | |
| 15 jet_direction = ( | |
| 16 np.longdouble(jet_direction_deg) / 180.0 * math.pi | |
| 17 ) # angle between direction to source and jet axes | |
| 18 | |
| 19 if jet_half_size > math.pi or jet_half_size <= 0: | |
| 20 jet_half_size = math.pi | |
| 21 # here we assume that jet axis is located in y-z plane if angle=0, then jet points towards observer | |
| 22 jet_axes = np.array( | |
| 23 [0, np.sin(jet_direction), np.cos(jet_direction)], dtype=np.longdouble | |
| 24 ) | |
| 25 | |
| 26 if jet_direction > math.pi or jet_direction < 0: | |
| 27 raise ValueError( | |
| 28 "invalid jet_direction_deg param [0, 180] range expected") | |
| 29 | |
| 30 with open(mc_file, "r") as lines, open(rotated_file, "w") as rotated: | |
| 31 column_names = [] | |
| 32 for line in lines: | |
| 33 columns = line.split() | |
| 34 if len(columns) == 0: | |
| 35 continue | |
| 36 if columns[0] == "#": | |
| 37 if "weight" in columns: | |
| 38 column_names = ( | |
| 39 columns[1:3] | |
| 40 + ["Theta", "Phi", "dT_raw/T", "dT_calc/T"] | |
| 41 + columns[9:] | |
| 42 ) | |
| 43 print("# " + "\t".join(column_names), file=rotated) | |
| 44 else: | |
| 45 print(line.strip(), file=rotated) | |
| 46 else: | |
| 47 particle = np.array(list(map(np.longdouble, columns))) | |
| 48 beta = phi = 0 #dTcalc = 0 | |
| 49 | |
| 50 r = particle[2:5] | |
| 51 dz = r[ | |
| 52 2 | |
| 53 # dz is roughly equal to time delay in units of full path (assuming very small deflection angle) | |
| 54 ] | |
| 55 | |
| 56 # negative dz may apper in data as a result of rounding errors | |
| 57 dz = max(dz, 0) # replacing negative values by zeros | |
| 58 | |
| 59 r[2] = 1.0 - dz # now r is position of particle | |
| 60 | |
| 61 # direction of momentum | |
| 62 n = particle[5:8] | |
| 63 n[2] = 1.0 - n[2] | |
| 64 # compensate rounding errors for n | |
| 65 mod_n = np.dot(n, n) ** 0.5 | |
| 66 n = 1 / mod_n * n | |
| 67 t = 0 # calculated delay | |
| 68 if dz > 0: # there is some delay | |
| 69 r2 = np.dot(r, r) | |
| 70 # assert r2 < 1 | |
| 71 if r2 > 1: | |
| 72 r *= 1.0 / np.sqrt(r2) | |
| 73 r2 = 1.0 | |
| 74 # we continue trajectory in the direction of momentun until it reaches the sphere of radius 1. | |
| 75 # to find the delay t, we need to solve the following quadratic equation with respect to variable t: | |
| 76 # [(x + V_x)*t]^2 + [(y + V_y)*t]^2 + [(z + V_z)*t]^2 = 1 | |
| 77 # with extra condition t > 0 | |
| 78 # below is the solution: | |
| 79 rV = np.dot(n, r) | |
| 80 t = np.sqrt(1.0 - r2 + rV * rV) - rV | |
| 81 | |
| 82 # calculating the end point of the trajectory | |
| 83 r = r + t * n | |
| 84 | |
| 85 # normalizing r to 1 to fix the rounding errors | |
| 86 modR = np.dot(r, r) ** 0.5 | |
| 87 r = 1.0 / modR * r | |
| 88 | |
| 89 # now we will construct the rotation of coordinates which converts vector r to 0,0,1 | |
| 90 # to find the starting direction of the particle momentum | |
| 91 dr2 = r[0] ** 2 + r[1] ** 2 | |
| 92 if dr2 > 0: | |
| 93 # building rotation matrix to move x,y,z to 0,0,1 | |
| 94 norm = 1.0 / (dr2**0.5) | |
| 95 # rotation axes: | |
| 96 ux = norm * r[1] | |
| 97 uy = -norm * r[0] | |
| 98 # uz=0 | |
| 99 cos = r[2] | |
| 100 sin = (1 - cos**2) ** 0.5 | |
| 101 | |
| 102 toCenter = np.array( | |
| 103 [ | |
| 104 [cos + ux * ux * (1 - cos), ux * | |
| 105 uy * (1 - cos), uy * sin], | |
| 106 [ux * uy * (1 - cos), cos + uy * | |
| 107 uy * (1 - cos), -ux * sin], | |
| 108 [-uy * sin, ux * sin, cos], | |
| 109 ] | |
| 110 ) | |
| 111 | |
| 112 zAxes = np.array([0.0, 0.0, 1.0], dtype=np.longdouble) | |
| 113 # test | |
| 114 error = np.fabs(np.dot(toCenter, r) - zAxes).max() | |
| 115 assert error < 1e-7 | |
| 116 # finding starting direction of the momentum | |
| 117 nInitial = np.dot(toCenter, zAxes) | |
| 118 norm_nInitial = np.sqrt(np.dot(nInitial, nInitial)) | |
| 119 nInitial *= ( | |
| 120 1.0 / norm_nInitial | |
| 121 ) # normalize after rotation to avoid rounding errors | |
| 122 # calculating angle between starting direction and jet axis | |
| 123 startCos = np.dot(nInitial, jet_axes) | |
| 124 startAngle = 0 | |
| 125 if ( | |
| 126 startCos < 1 | |
| 127 ): # avoid roundout errors if angle is very close to zero | |
| 128 startAngle = np.arccos(startCos) | |
| 129 # TODO: here instead of using step function we can modify weight depending | |
| 130 # on angular distance from jet axes | |
| 131 if ( | |
| 132 startAngle < jet_half_size | |
| 133 ): # if the particle is inside the cone (passes cut) | |
| 134 # calculate angle between arrival direction and direction to source | |
| 135 cosBeta = np.dot( | |
| 136 r, n | |
| 137 ) # this angle is invariant (doesn't depend on turns) | |
| 138 if ( | |
| 139 cosBeta < 1 | |
| 140 ): # avoid rounding errors if angle is very close to zero | |
| 141 beta = np.arccos(cosBeta) | |
| 142 nFinal = np.dot(toCenter, n).transpose() | |
| 143 cosPhi = nFinal[0] / np.sin( | |
| 144 beta | |
| 145 # second angle (could be useful in assymetric tasks) if beta=0 it is undefined (could be any) | |
| 146 ) | |
| 147 if cosPhi < 1 and cosPhi > -1: | |
| 148 phi = np.arccos( | |
| 149 cosPhi | |
| 150 ) # returns value in interval [0,pi] | |
| 151 if nFinal[1] < 0: | |
| 152 phi = 2 * math.pi - phi | |
| 153 | |
| 154 # s = math.sin(startAngle+beta) | |
| 155 # if s>0.: | |
| 156 # dTcalc = (math.sin(startAngle)+math.sin(beta))/s - 1. | |
| 157 # dTcalc = (1.-min(modR, 1.))/cosBeta # condition modR > 1 can only occur due to rounding error | |
| 158 else: | |
| 159 continue | |
| 160 elif ( | |
| 161 jet_direction > jet_half_size | |
| 162 ): # no deflection: check if jet is pointing towards observer | |
| 163 continue | |
| 164 | |
| 165 if beta * 180.0 / math.pi > psf_deg: | |
| 166 continue | |
| 167 | |
| 168 output = [ | |
| 169 particle[0], | |
| 170 particle[1], | |
| 171 beta * 180.0 / math.pi, | |
| 172 phi * 180.0 / math.pi, | |
| 173 dz, | |
| 174 t, | |
| 175 ] + list(particle[8:]) | |
| 176 output = ["{0:g}".format(i) for i in output] | |
| 177 print(*output, file=rotated) | |
| 178 | |
| 179 return rotated_file |
