Beispiel #1
0
	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)
Beispiel #2
0
 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
Beispiel #3
0
    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)
Beispiel #4
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((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)
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
 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
Beispiel #8
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((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)
Beispiel #9
0
    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
Beispiel #10
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)