def compute_next_point(self, location, direction): #lam = int(dist / self.clumpNH) # generate random number of clumps to encounter (xi, yi, zi) = location (dist, beta, alpha) = direction k = numpy.random.poisson(self.nclumps) NHfull = k * self.clumpNH #d = dist / self.NH if dist > NHfull: # we land outside d = 10 elif dist < NHfull: # we go as far as we get j = int(dist / self.clumpNH) NHremainder = numpy.fmod(dist, self.clumpNH) d = NHfull / self.NH # how deep do we go into the k-th cloud d = (NHfull + NHremainder) / self.NH if self.verbose: print(' .. .. mean in nH units: ', d.mean()) # compute relative vector traveled xv, yv, zv = to_cartesian((d, beta, alpha)) # compute new position xf, yf, zf = xi + xv, yi + yv, zi + zv # compute spherical coordinates rad, phi, theta = to_spherical((xf, yf, zf)) # are we inside the cone? inside = rad < 1. return inside, (xf,yf,zf), (rad, phi, theta)
def compute_los_nh(self, beta, alpha): a, b, c = to_cartesian((1, beta, alpha)) x = a * 0 + self.center[0] y = b * 0 + self.center[1] z = c * 0 + self.center[2] NH = grid_raytrace_flat(self.rho_flat, self.rho.shape[0], x, y, z, a, b, c) return NH
def compute_next_point(self, location, direction): (xi, yi, zi) = location (dist, beta, alpha) = direction a, b, c = to_cartesian((1, beta, alpha)) t = sphere_raytrace_finite(self.x, self.y, self.z, self.r, self.rho, xi, yi, zi, a, b, c, dist) # are we still inside? inside = t >= 0 # compute new position xf, yf, zf = xi + a * t, yi + b * t, zi + c * t # compute spherical coordinates rad, phi, theta = to_spherical((xf, yf, zf)) return inside, (xf, yf, zf), (rad, phi, theta)
def compute_next_point(self, location, direction): (xi, yi, zi) = location (dist, beta, alpha) = direction d = dist / self.NH # distance in units of nH if self.verbose: print(' .. .. mean in nH units: ', d.mean()) # compute relative vector traveled xv, yv, zv = to_cartesian((d, beta, alpha)) # compute new position xf, yf, zf = xi + xv, yi + yv, zi + zv # compute spherical coordinates rad, phi, theta = to_spherical((xf, yf, zf)) # are we inside the disk inside = zf <= 0 return inside, (xf, yf, zf), (rad, phi, theta)
def compute_next_point(self, location, direction): (xi, yi, zi) = location (dist, beta, alpha) = direction a, b, c = to_cartesian((1, beta, alpha)) t = cone_raytrace_finite(self.Theta_tors, self.NHs, xi, yi, zi, a, b, c, dist) # are we still inside? inside = t >= 0 t = numpy.where(t < 0, dist, t) # compute new position xf, yf, zf = xi + a * t, yi + b * t, zi + c * t # compute spherical coordinates rad, theta, phi = to_spherical((xf, yf, zf)) return inside, (xf, yf, zf), (rad, theta, phi)
def compute_next_point(self, location, direction): (xi, yi, zi) = location (dist, beta, alpha) = direction a, b, c = to_cartesian((1, beta, alpha)) x = xi + self.center[0] y = yi + self.center[1] z = zi + self.center[2] t = grid_raytrace_finite_flat(self.rho_flat, self.rho.shape[0], x, y, z, a, b, c, dist) # are we still inside? inside = t >= 0 # compute new position t = numpy.where(t < 0, dist, t) xf, yf, zf = xi + a * t, yi + b * t, zi + c * t # compute spherical coordinates rad, phi, theta = to_spherical((xf, yf, zf)) return inside, (xf, yf, zf), (rad, phi, theta)
def compute_los_nh(self, beta, alpha): a, b, c = to_cartesian((1, beta, alpha)) mindistances = numpy.zeros(1) NH = sphere_raytrace(self.x, self.y, self.z, self.r, self.rho, a, b, c, mindistances)[0, :] return NH
def compute_next_point(self, location, direction): (xi, yi, zi) = location (dist, beta, alpha) = direction d = dist / self.NH # distance in units of nH if self.verbose: print(' .. .. mean in nH units: ', d.mean()) # compute relative vector traveled xv, yv, zv = to_cartesian((d, beta, alpha)) # compute new position xf, yf, zf = xi + xv, yi + yv, zi + zv # compute intersection with cone border a = zv**2 - (xv**2 + yv**2) * tan(0.5 * pi - self.Theta_tor)**2 b = 2. * zi * zv - (2. * xi * xv + 2. * yi * yv) * tan(0.5 * pi - self.Theta_tor)**2 c = zi**2 - (xi**2 + yi**2) * tan(0.5 * pi - self.Theta_tor)**2 quad = b**2 - 4. * a * c # compute the two solutions e1 = (-b - quad**0.5) / (2. * a) e2 = (-b + quad**0.5) / (2. * a) # if both are positive and e1<1 twosolmask = logical_and(logical_and(quad > 0., e1 > 0.), logical_and(e1 < 1., e2 > 0.)) #if self.verbose: print ' .. %d of %d have 2 solutions' % (twosolmask.sum(), len(twosolmask)) # compute the two possible new positions #print 'twosol:', twosolmask x1 = xi[twosolmask] + e1[twosolmask] * xv[twosolmask] x2 = xi[twosolmask] + e2[twosolmask] * xv[twosolmask] y1 = yi[twosolmask] + e1[twosolmask] * yv[twosolmask] y2 = yi[twosolmask] + e2[twosolmask] * yv[twosolmask] z1 = zi[twosolmask] + e1[twosolmask] * zv[twosolmask] z2 = zi[twosolmask] + e2[twosolmask] * zv[twosolmask] #print 'e2', e2 ltsol = e2[twosolmask] < 1. gtsol = ~ltsol asol = twosolmask.copy() asol[twosolmask] = ltsol bsol = twosolmask.copy() bsol[twosolmask] = gtsol xf[asol] = x2[ltsol] + xv[asol] - (x1[ltsol] - xi[asol]) yf[asol] = y2[ltsol] + yv[asol] - (y1[ltsol] - yi[asol]) zf[asol] = z2[ltsol] + zv[asol] - (z1[ltsol] - zi[asol]) xf[bsol] += (x2[gtsol] - x1[gtsol]) yf[bsol] += (y2[gtsol] - y1[gtsol]) zf[bsol] += (z2[gtsol] - z1[gtsol]) #print ' .. using symmetries' # use symmetries # bring to upper side of torus zf = numpy.abs(zf) # compute spherical coordinates rad, theta, phi = to_spherical((xf, yf, zf)) assert not numpy.isnan(rad).any() assert not numpy.isnan(theta).any() assert not numpy.isnan(phi).any() #if self.verbose: print ' .. checking if left cone' # are we inside the cone? inside = numpy.logical_and(rad < 1., theta > self.Theta_tor) return inside, (xf, yf, zf), (rad, phi, theta)
def pump(self): if self.verbose: print('photon iteration: %d free photons, %d scattering %s' % ((~self.stuck).sum(), self.stuck.sum(), '_' * 20)) phi, theta, rad, alpha, beta, energy, bin = self.get_free() # first half deals with free photons if len(energy) > 0: if self.verbose: print(' .. distance travelled') nphot = len(energy) r1 = rng.uniform(size=nphot) taur = -log(1. - r1) # effective tau dist = taur / xboth[bin] # distance travelled #if self.verbose: print ' .. .. tau min: %f mean: %f max: %f ' % (taur.min(), taur.mean(), taur.max()) if self.verbose: print(' .. .. dist min: %f mean: %f max: %f ' % (dist.min(), dist.mean(), dist.max())) phi0 = phi theta0 = theta rad0 = rad xi, yi, zi = to_cartesian((rad0, theta0, phi0)) #if self.verbose: print ' .. computing position' # compute position inside, (xf, yf, zf), (rad, phi, theta) = self.geometry.compute_next_point( (xi, yi, zi), (dist, beta, alpha)) outside = ~inside # emit if self.verbose: print(' .. emitting %d to outside, %d inside material' % ((~inside).sum(), inside.sum())) self.update_free(phi, theta, rad, alpha, beta, energy, bin) mask = ~self.stuck mask[mask] = outside self.cut_free(inside) emit = dict(phi=phi0[outside], theta=theta0[outside], rad=rad0[outside], beta=beta[outside], alpha=alpha[outside], energy=energy[outside], bin=bin[outside], x=xi[outside], y=yi[outside], z=zi[outside], mask=mask) #print ' ', rad.shape, theta.shape, len(inside) xf, yf, zf = xf[inside], yf[inside], zf[inside] phi, theta, rad, alpha, beta, energy, bin = self.get_free() nphot = len(energy) assert nphot == inside.sum() if self.verbose: print(' .. checking if absorbed') # what effect are we dealing with r2 = rng.uniform(size=nphot) # Photon-absorbed q = absorption_ratio[bin] photabsorbed = r2 < q #if self.verbose: print ' .. .. photon-abs prob min: %f mean: %f max: %f ' % (q.min(), q.mean(), q.max()) # Iron to photon-absorption effectiveness omega = xlines_cumulative[bin[ photabsorbed]] # Importance of lines compared to photon-absorption # omega *= 0 # disable fluorescent lines #print ' .. .. omega:', omega r3 = rng.uniform(size=photabsorbed.sum()) # Are we coming out as a line # This handles all lines (lines loaded in xsects) line_mask = omega > r3[:, None] iline = numpy.where(line_mask.any(axis=1), line_mask.argmax(axis=1), -1) is_line = iline >= 0 photabsorbed_line = photabsorbed.copy() photabsorbed_notline = photabsorbed.copy() # set the absorbed ones (where we have true) to the criterion photabsorbed_line[photabsorbed] = is_line photabsorbed_notline[photabsorbed] = ~is_line r4 = rng.uniform(size=photabsorbed_line.sum()) if is_line.any(): e2 = xlines_energies[iline[is_line]] e = energy2bin(e2) energy[photabsorbed_line] = e2 bin[photabsorbed_line] = e del e2, e # emit line in random direction if photabsorbed_line.any(): nline = photabsorbed_line.sum() alpha_random = rng.uniform(0, 2 * pi, size=nline) beta_random = acos(rng.uniform(-1, 1, size=nline)) alpha[photabsorbed_line] = alpha_random beta[photabsorbed_line] = beta_random self.update_free(phi, theta, rad, alpha, beta, energy, bin) # no, we are just being swallowed (photon-absorption). # photabsorbed & ~is_line # remove photabsorbed_line absorbed = photabsorbed_notline if self.verbose: print(' .. .. line emission: %3d' % (is_line.sum())) if self.verbose: print(' .. .. absorbed: %d (%d lost)' % (photabsorbed.sum(), absorbed.sum())) if self.verbose: print(' .. checking if scattered') # Were we compton-scattered? photscattered = (~photabsorbed) nphotscattered = photscattered.sum() #assert nphotscattered.shape == energy.shape if nphotscattered > 0: if self.verbose: print(' .. .. scattered: %d' % nphotscattered) #self.energy[free] = energy #self.bin[free] = bin if self.verbose: print(' .. checking if outside of energy range') # if in relevant energy range, find right bin #energy = self.energy # for unstuck and photabsorbed_line we have to check the energy # just do it for all energy_outside = bin <= 0 dropouts = numpy.logical_or(energy_outside, absorbed) remainders = ~dropouts if self.verbose: print(' .. .. outside of energy range: %d' % (energy_outside.sum())) if self.verbose: print(' .. .. %d left' % (remainders.sum())) # cut to remainders (so that we only have to compute new positions for few) #self.set(phi, theta, rad, alpha, beta, energy, bin) self.cut_free(remainders) phi, theta, rad, alpha, beta, energy, bin = self.get_free() # compute new positions for remainder xf, yf, zf = xf[remainders], yf[remainders], zf[remainders] rad, theta, phi = to_spherical((xf, yf, zf)) #rad = (xf**2+yf**2+zf**2)**0.5 #phi = atan2(yf, xf) #theta = numpy.where(rad == 0, 0., acos(zf / rad)) self.update_free(phi, theta, rad, alpha, beta, energy, bin) self.stuck[~self.stuck] = photscattered[remainders] if self.verbose: print(' .. .. %d stuck in scattering' % (self.stuck.sum())) else: emit = None # now deal with stuck photons, i.e. those undergoing compton # scattering alpha, beta, energy, bin = self.get_stuck() nphotscattered = len(energy) if nphotscattered > 0: if self.verbose: print(' scattering: %d' % nphotscattered) a = rng.uniform(size=nphotscattered) # compute new direction: alpha_new = a * 2. * pi # left-right angle uniform randomly # compute up-down angle beta0 = beta r5 = rng.uniform(size=nphotscattered) r5a = rng.uniform(size=nphotscattered) x = 2. * r5a - 1 mus = numpy.where(r5 > 0.75, numpy.sign(x) * numpy.abs(x)**(1 / 3.), x) betas = acos(mus) mu = cos(beta0) * cos(betas) + sin(beta0) * sin(betas) * cos( alpha_new - alpha) beta_new = acos(mu) # new energy #if self.verbose: print ' .. .. mus: %.2f' % (mus.mean()) loss = (1. + (1. - mus) * energy / electmass) if self.verbose: print(' .. .. energy loss: %.3f, %.3f, %.3f' % (loss.min(), loss.mean(), loss.max())) E = energy / loss if self.verbose: print(' .. .. new energy: %.3f, %.3f, %.3f' % (E.min(), E.mean(), E.max())) processed = E >= 0 self.update_and_free_stuck(alpha_new, beta_new, E, energy2bin(E), processed) if self.verbose: print(' .. finally checking all, if outside of energy range') phi, theta, rad, alpha, beta, energy, bin = self.get() energy_outside = numpy.logical_or(energy < 0.1, energy > 1800) dropouts = energy_outside remainders = ~dropouts if self.verbose: print(' .. .. outside of energy range: %d' % (energy_outside.sum())) if self.verbose: print(' .. .. %d left' % (remainders.sum())) # cut out if dropouts.any(): self.cut(remainders) # next round return emit, len(self.energy) > 0
def compute_next_point(self, location, direction): (xi, yi, zi) = location (dist, beta, alpha) = direction d = dist / self.NH # distance in units of nH if self.verbose: print(' .. .. mean in nH units: ', d.mean()) # compute relative vector traveled xv, yv, zv = to_cartesian((1, beta, alpha)) #print xv, yv, zv # compute intersection with cone border a = zv**2 - (xv**2 + yv**2) * tan(0.5 * pi - self.Theta_tor)**2 b = 2. * zi * zv - (2. * xi * xv + 2. * yi * yv) * tan(0.5 * pi - self.Theta_tor)**2 c = zi**2 - (xi**2 + yi**2) * tan(0.5 * pi - self.Theta_tor)**2 quad = b**2 - 4. * a * c # compute the two solutions with numpy.errstate(invalid='ignore'): e1 = (-b - quad**0.5) / (2. * a) e2 = (-b + quad**0.5) / (2. * a) # if both are positive and e1<1 #assert (quad >= 0).all() x1 = xi + e1 * xv x2 = xi + e2 * xv y1 = yi + e1 * yv y2 = yi + e2 * yv z1 = zi + e1 * zv z2 = zi + e2 * zv # check if those points are outside the sphere with numpy.errstate(invalid='ignore'): ri1 = x1**2 + y1**2 + z1**2 <= 1 ri2 = x2**2 + y2**2 + z2**2 <= 1 #print 'cone points radius', ri1, ri2 # compute intersection with sphere # a=xD2+yD2-zD2, b=2xExD+2yEyD-2zEzD, and c=xE2+yE2-zE2. # a=xD2+yD2+zD2, b=2xExD+2yEyD+2zEzD, and c=xE2+yE2+zE2-1 sa = 1 sb = 2. * zi * zv + 2. * xi * xv + 2. * yi * yv sc = zi**2 + xi**2 + yi**2 - 1 squad = sb**2 - 4. * sa * sc # compute the two solutions se1 = (-sb - squad**0.5) / (2. * sa) se2 = (-sb + squad**0.5) / (2. * sa) sx1 = xi + se1 * xv sx2 = xi + se2 * xv sy1 = yi + se1 * yv sy2 = yi + se2 * yv sz1 = zi + se1 * zv sz2 = zi + se2 * zv # check if inside cone with numpy.errstate(invalid='ignore'): r1 = sx1**2 + sy1**2 + sz1**2 r2 = sx2**2 + sy2**2 + sz2**2 theta1 = numpy.where(r1 == 0, 0., arccos(sz1 / r1)) theta2 = numpy.where(r2 == 0, 0., arccos(sz2 / r2)) theta1[theta1 > pi / 2] = pi - theta1[theta1 > pi / 2] theta2[theta2 > pi / 2] = pi - theta2[theta2 > pi / 2] si1 = theta1 >= self.Theta_tor si2 = theta2 >= self.Theta_tor #print 'circle points theta', theta1, theta2, si1, si2 #, (sx1, sy1, sz1), (sx2, sy2, sz2) # now we should go through them es = numpy.transpose([e1, e2, se1, se2]) #print es, 'es' good = numpy.transpose([ri1, ri2, si1, si2]) # they will be ignored, because we go in the pos direction with numpy.errstate(invalid='ignore'): good[~(es > 0)] = False es[~good] = -1 nsol = good.sum(axis=1) #assert nsol.shape == xi.shape, (nsol.shape, xi.shape) es.sort(axis=1) # now handle the different cases #print nsol, good #assert (nsol >= 0).all(), nsol.min() #assert (nsol <= 3).all(), nsol.min() #assert ((nsol == 0) + (nsol == 1) + (nsol == 3)).all(), nsol # default: xf, yf, zf = xi + d * xv, yi + d * yv, zi + d * zv # no solution, we are on the brink of stepping out # set to False inside = numpy.zeros(xf.shape, dtype=bool) es_1 = nsol == 1 t = es[es_1, -1] # go from where we are to there inside[es_1] = d[es_1] < t # if true we never left #print 't:', t, d[es_1], es_1 # 3 solutions es_2 = nsol == 3 if es_2.any(): #print 'BRANCH2' t1 = es[es_2, -3] t2 = es[es_2, -2] t3 = es[es_2, -1] # two cases: either we never make it to t1 inside_2 = d[es_2] < t1 # if true we are still inside #print inside.shape, inside_2.shape inside[es_2] = inside_2 #print 'es_2', es_2 #print 'inside_2', inside_2 if not inside_2.all(): #print 'BRANCH3' # alternatively, we make it to t1: # have to add to the distance we are allowed to travel # the distance between t1 and t2 es_3 = es_2 es_3[es_3] = ~inside_2 # select those cases #print 'd', d, es_3, d[es_3].shape, t2.shape, t1.shape, inside_2.shape #print 't:', t1, t2, t3 #assert es_3.shape == (len(xi),) #assert es_3.sum() == (~inside_2).sum(), (es_3, ~inside_2) #assert d[es_3].size == (~inside_2).sum(), (es_3, ~inside_2) dmod = d[es_3] + t2[~inside_2] - t1[~inside_2] xf[es_3] = xi[es_3] + dmod * xv[es_3] yf[es_3] = yi[es_3] + dmod * yv[es_3] zf[es_3] = zi[es_3] + dmod * zv[es_3] inside[es_3] = dmod < t3[~inside_2] # bring to upper side of torus #zf = numpy.abs(zf) # compute spherical coordinates rad, theta, phi = to_spherical((xf, yf, zf)) return inside, (xf, yf, zf), (rad, phi, theta)