Mercurial > repos > astroteam > crbeam_astro_tool
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mc_rotate.py Fri Apr 25 19:33:20 2025 +0000 @@ -0,0 +1,179 @@ +import math + +import numpy as np + + +def mc_rotate( + mc_file, jet_half_size_deg, jet_direction_deg, psf_deg=180, rotated_file=None +): + if rotated_file is None: + rotated_file = f"{mc_file}_{jet_half_size_deg}_{jet_direction_deg}_{psf_deg}" + + jet_half_size = ( + np.longdouble(jet_half_size_deg) / 180.0 * math.pi + ) # jet openning angle + jet_direction = ( + np.longdouble(jet_direction_deg) / 180.0 * math.pi + ) # angle between direction to source and jet axes + + if jet_half_size > math.pi or jet_half_size <= 0: + jet_half_size = math.pi + # here we assume that jet axis is located in y-z plane if angle=0, then jet points towards observer + jet_axes = np.array( + [0, np.sin(jet_direction), np.cos(jet_direction)], dtype=np.longdouble + ) + + if jet_direction > math.pi or jet_direction < 0: + raise ValueError( + "invalid jet_direction_deg param [0, 180] range expected") + + with open(mc_file, "r") as lines, open(rotated_file, "w") as rotated: + column_names = [] + for line in lines: + columns = line.split() + if len(columns) == 0: + continue + if columns[0] == "#": + if "weight" in columns: + column_names = ( + columns[1:3] + + ["Theta", "Phi", "dT_raw/T", "dT_calc/T"] + + columns[9:] + ) + print("# " + "\t".join(column_names), file=rotated) + else: + print(line.strip(), file=rotated) + else: + particle = np.array(list(map(np.longdouble, columns))) + beta = phi = 0 #dTcalc = 0 + + r = particle[2:5] + dz = r[ + 2 + # dz is roughly equal to time delay in units of full path (assuming very small deflection angle) + ] + + # negative dz may apper in data as a result of rounding errors + dz = max(dz, 0) # replacing negative values by zeros + + r[2] = 1.0 - dz # now r is position of particle + + # direction of momentum + n = particle[5:8] + n[2] = 1.0 - n[2] + # compensate rounding errors for n + mod_n = np.dot(n, n) ** 0.5 + n = 1 / mod_n * n + t = 0 # calculated delay + if dz > 0: # there is some delay + r2 = np.dot(r, r) + # assert r2 < 1 + if r2 > 1: + r *= 1.0 / np.sqrt(r2) + r2 = 1.0 + # we continue trajectory in the direction of momentun until it reaches the sphere of radius 1. + # to find the delay t, we need to solve the following quadratic equation with respect to variable t: + # [(x + V_x)*t]^2 + [(y + V_y)*t]^2 + [(z + V_z)*t]^2 = 1 + # with extra condition t > 0 + # below is the solution: + rV = np.dot(n, r) + t = np.sqrt(1.0 - r2 + rV * rV) - rV + + # calculating the end point of the trajectory + r = r + t * n + + # normalizing r to 1 to fix the rounding errors + modR = np.dot(r, r) ** 0.5 + r = 1.0 / modR * r + + # now we will construct the rotation of coordinates which converts vector r to 0,0,1 + # to find the starting direction of the particle momentum + dr2 = r[0] ** 2 + r[1] ** 2 + if dr2 > 0: + # building rotation matrix to move x,y,z to 0,0,1 + norm = 1.0 / (dr2**0.5) + # rotation axes: + ux = norm * r[1] + uy = -norm * r[0] + # uz=0 + cos = r[2] + sin = (1 - cos**2) ** 0.5 + + toCenter = np.array( + [ + [cos + ux * ux * (1 - cos), ux * + uy * (1 - cos), uy * sin], + [ux * uy * (1 - cos), cos + uy * + uy * (1 - cos), -ux * sin], + [-uy * sin, ux * sin, cos], + ] + ) + + zAxes = np.array([0.0, 0.0, 1.0], dtype=np.longdouble) + # test + error = np.fabs(np.dot(toCenter, r) - zAxes).max() + assert error < 1e-7 + # finding starting direction of the momentum + nInitial = np.dot(toCenter, zAxes) + norm_nInitial = np.sqrt(np.dot(nInitial, nInitial)) + nInitial *= ( + 1.0 / norm_nInitial + ) # normalize after rotation to avoid rounding errors + # calculating angle between starting direction and jet axis + startCos = np.dot(nInitial, jet_axes) + startAngle = 0 + if ( + startCos < 1 + ): # avoid roundout errors if angle is very close to zero + startAngle = np.arccos(startCos) + # TODO: here instead of using step function we can modify weight depending + # on angular distance from jet axes + if ( + startAngle < jet_half_size + ): # if the particle is inside the cone (passes cut) + # calculate angle between arrival direction and direction to source + cosBeta = np.dot( + r, n + ) # this angle is invariant (doesn't depend on turns) + if ( + cosBeta < 1 + ): # avoid rounding errors if angle is very close to zero + beta = np.arccos(cosBeta) + nFinal = np.dot(toCenter, n).transpose() + cosPhi = nFinal[0] / np.sin( + beta + # second angle (could be useful in assymetric tasks) if beta=0 it is undefined (could be any) + ) + if cosPhi < 1 and cosPhi > -1: + phi = np.arccos( + cosPhi + ) # returns value in interval [0,pi] + if nFinal[1] < 0: + phi = 2 * math.pi - phi + + # s = math.sin(startAngle+beta) + # if s>0.: + # dTcalc = (math.sin(startAngle)+math.sin(beta))/s - 1. + # dTcalc = (1.-min(modR, 1.))/cosBeta # condition modR > 1 can only occur due to rounding error + else: + continue + elif ( + jet_direction > jet_half_size + ): # no deflection: check if jet is pointing towards observer + continue + + if beta * 180.0 / math.pi > psf_deg: + continue + + output = [ + particle[0], + particle[1], + beta * 180.0 / math.pi, + phi * 180.0 / math.pi, + dz, + t, + ] + list(particle[8:]) + output = ["{0:g}".format(i) for i in output] + print(*output, file=rotated) + + return rotated_file