示例#1
0
    def render_task(self, task_index: int,
                    bucket_index: int,
                    bucket_order_info: BucketOrderInfo, sample, renderer: Renderer):

        sampler = self.main_sampler.get_sub_sampler(bucket_index, bucket_order_info)

        if sampler is None:
            return

        print("start render task : id(" + str(task_index) + ") (" + str(sampler.bucket_extend.start_x) + "," + str(
            sampler.bucket_extend.start_y) + ") " + "(" + str(sampler.bucket_extend.end_x - 1) + "," + str(
            sampler.bucket_extend.end_y - 1) + ")")

        self.draw_bucket_extend(sampler.bucket_extend)

        pixels = []

        maxSamples = sampler.get_maximum_sample_count()
        samples = []
        rays = []
        Ls = []
        Ts = []
        intersections = []
        for n in range(maxSamples):
            samples.append(deepcopy(sample))
            rays.append(Ray())
            Ls.append(Spectrum(0.0))
            Ts.append(Spectrum(0.0))
            intersections.append(Intersection())

            # preallocate array for each pixels
        # rays = [Ray()]*maxSamples
        # Ls = [Spectrum(0.0)]*maxSamples
        # Ts = [Spectrum(0.0)]*maxSamples
        #        intersections = [Intersection()]*maxSamples

        while True:

            if sampler.get_more_samples(samples) == 0:
                break

            for i in range(len(samples)):
                rays[i] = self.camera.generate_ray(samples[i])
                Ls[i] = renderer.get_li(self.scene, rays[i], intersections[i], samples[i])

            s = Spectrum(0.0)
            for i in range(len(samples)):
                s += Ls[i]

            s /= float(len(samples))
            s = s.get_clamp(0.0, 1.0)
            pixels.append(s)

        print("end render task " + str(task_index))

        return pixels, sampler.bucket_extend
示例#2
0
 def Le(self, w):
     """Return the light emitted by the object."""
     if self.primitive is None:
         logger.error("Intersect.Le() called with no primitive.")
         return Spectrum(0.0)
     area = self.primitive.get_area_light()
     if area:
         return area.L(self.dg.p, self.dg.nn, w)
     else:
         return Spectrum(0.0)
示例#3
0
def EstimateDirect(scene: Scene, renderer: Renderer, light: Light, p: Point3d, n: Normal, wo: Vector3d,
                   time: float, bsdf: BSDF, lightSample: LightSample, bsdfSample: BSDFSample,
                   BxDFTypeFlag: int) -> Spectrum:
    Ld = Spectrum(0.0)

    visibility = VisibilityTester()
    wi, Li, lightPdf = light.Sample_L1(p, lightSample, time, visibility)

    if lightPdf > 0.0 and not Li.get_is_black():
            f = bsdf.f(wo, wi, BxDFTypeFlag)
            if not f.get_is_black() and visibility.Unoccluded(scene):
    # Add light's contribution to reflected radiance
     #Li *= visibility.Transmittance(scene, renderer, None)
                inv_lightPdf = 1.0 / lightPdf
                if light.get_is_delta_light():
                    Ld += f * Li * math.fabs(Vector3d.dot(wi, n)) * inv_lightPdf
                else:
                    bsdfPdf = bsdf.get_Pdf(wo, wi, BxDFTypeFlag)
                    weight = PowerHeuristic(1, lightPdf, 1, bsdfPdf)
                    Ld += f * Li * math.fabs(Vector3d.dot(wi, n)) * weight * inv_lightPdf

    # Sample BSDF with multiple importance sampling
    if not light.get_is_delta_light():
        wi, bsdfPdf, sampledType, f = bsdf.Sample_f(wo, bsdfSample, BxDFTypeFlag)
        if not f.get_is_black() and bsdfPdf > 0.0:
            weight = 1.0
            if not (BxDFTypeFlag & BxDFType.BSDF_SPECULAR):
                lightPdf = light.get_pdf(p, wi)
                if lightPdf == 0.0:
                    return Ld
                weight = PowerHeuristic(1, bsdfPdf, 1, lightPdf)
            # Add light contribution from BSDF sampling
            lightIsect = Intersection()
            Li = Spectrum(0.0)
            ray = Ray(p, wi, 1e-3, infinity_max_f, time)
            if scene.get_intersection(ray, lightIsect):
                if lightIsect.primitive.GetAreaLight() == light:
                    Li = lightIsect.Le(-wi)
            else:
                Li = light.Le(ray)
            if not Li.get_is_black():
                Li *= 1.0  #todo renderer.Transmittance(scene, ray, None)
                Ld += f * Li * math.fabs(Vector3d.dot(wi, n)) * weight / bsdfPdf
    return Ld
示例#4
0
    def Li(self, scene, ray, sample, rng, intersection=None, T=None):
        """Compute the incident radiance along a given ray."""
        # allocate local variables for isect and T if needed
        if not T:
            T = Spectrum(0.0)
        if not intersection:
            intersection = Intersection()
        Li = Spectrum(0.0)
        hit = scene.intersect(ray, intersection)
        if hit:
            Li = self.surface_integrator.Li(scene, self, ray, intersection,
                                            sample, rng)
        else:
            # handle ray that doesn't intersect any geometry
            for light in scene.lights:
                Li += light.Le(ray)

        # Lvi = self.volume_integrator.Li(scene, self, ray, sample, rng, T)
        # return T * Li + Lvi, intersection

        return Li, intersection, T
示例#5
0
    def Li(self, scene, renderer, ray, intersection, sample, rng):
        """Computes the radiance along a ray."""
        return Spectrum(1.0)

        L = Spectrum(0.0)

        # evaluate BSDF at hit point
        bsdf = intersection.get_bsdf(ray)

        # initialize common variables for Whitted integrator
        p = bsdf.dg_shading.p
        n = bsdf.dg_shading.nn
        wo = -ray.d

        # compute emitted light if ray hit an area light source
        L += intersection.Le(wo)

        # add contribution of each light source
        for light in self.scene.lights:
            light_sample = LightSample.from_rng(rng)
            Li, wi, pdf, visibility = light.sample_L(p,
                                                     intersection.ray_epsilon,
                                                     light_sample, ray.time)
            if Li.is_black() or pdf == 0.0:
                continue

            f = bsdf.f(wo, wi)
            if (not f.is_black()) and visibility.unoccluded(scene):
                L += f * Li * abs_dot(wi, n) * visibility.transmittance(
                    scene, renderer, sample, rng) / pdf
        if ray.depth + 1 < self.max_depth:
            # trace rays for specular reflection and refraction
            L += specular_reflect(ray, bsdf, rng, intersection, renderer,
                                  scene, sample)

            L += specular_transmit(ray, bsdf, rng, intersection, renderer,
                                   scene, sample)

        return L
示例#6
0
    def initialize(self, lattice):
        # read settings
        settings = Settings()['generator']['photons']
        self._enabled = settings['enabled']
        self._full_events = settings['full_events']
        self._nth_step = settings['nth_step']
        self._time = settings['time']
        self._energy_cutoff = settings['energy_cutoff']
        self._sigma_h = settings['sigma']['horizontal']
        self._sigma_v = settings['sigma']['vertical']
        self._stepsize_h = 2.0 * self._sigma_h / settings['steps']['horizontal']
        self._stepsize_v = 2.0 * self._sigma_v / settings['steps']['vertical']
        self._crossing_angle = Settings()['machine']['crossing_angle']
        
        self._region_enabled = settings['region']['enabled']
        if settings['region']['range'][0] < settings['region']['range'][1]:
            self._region_left = settings['region']['range'][0]
            self._region_right = settings['region']['range'][1]
        else:
            self._region_left = settings['region']['range'][1]
            self._region_right = settings['region']['range'][0]

        self._target_zone_enabled = settings['target_zone']['enabled']
        self._target_zone_radius = settings['target_zone']['radius']
        self._target_zone_boundary = settings['target_zone']['boundary']

        # create synchrotron radiation power spectrum PDF
        self._spectrum = Spectrum()
        self._spectrum.initialize(settings['spectrum']['resolution'],
                                  settings['spectrum']['cutoff'],
                                  settings['spectrum']['seed'],
                                  settings['spectrum']['interpolation'])

        # internal parameters
        self._lattice = lattice
        self._call_count = 0
        self._dl = 0.0

        # internal constants and pre-calculated factors
        self._alpha = 1.0 / 137.035999074
        self._speed_of_light = 2.99792458e8 # [m/s]
        self._hbar = 6.58211928e-25 # [GeV s]
        self._gamma = Settings()['machine']['beam_energy'] / 510.998928e-6
        self._current = Settings()['machine']['beam_current'] * 6.241508e18
        self._num_photon_factor = (5.0 / (2.0*math.sqrt(3))) * \
                                  self._gamma * self._alpha * \
                                  self._current * self._time
        self._crit_e_factor = 3.0 / 2.0 * self._speed_of_light * \
                              self._hbar * (self._gamma**3)
示例#7
0
    def __init__(self, cap=None):
        self.captures = []
        self.dataTypes = []
        self.x = []
        self.y = []

        self.xkey = ''
        self.ykey = ''
        self.freqMode = 'Frequency'
        self.mode = 'Combined'
        self.scansIncluded = []
        self.histMode = True
        self.binsize = 0.03

        self.spectra = []
        self.combSpectrum = Spectrum()

        if not cap == None:
            self.addCapture(cap)
示例#8
0
    def run(self):
        """Execute the task."""

        print "executing task %d/%d" % (self.task_num, self.task_count)

        # get sub-sampler for SamplerRendererTask
        sampler = self.main_sampler.get_sub_sampler(self.task_num,
                                                    self.task_count)
        if not sampler:
            return

        # Declare local variables used for rendering loop
        rng = RNG(self.task_num)

        # allocate space for samples and intersections
        max_samples = sampler.maximum_sample_count()
        samples = self.orig_sample.duplicate(max_samples)
        rays = [None] * max_samples
        Ls = [None] * max_samples
        Ts = [None] * max_samples
        isects = []  # Intersection[max_samples]
        for i in range(max_samples):
            isects.append(Intersection())

        # get samples from Sampler and update image
        while True:
            sample_count = sampler.get_more_samples(samples, rng)

            # if no more samples to compute, exit
            if sample_count <= 0:
                break

            # generate camera rays and compute radiance along rays
            for i in range(sample_count):
                # find camera ray for samples[i]
                ray_weight, ray_diff = self.camera.generate_ray_differential(
                    samples[i])

                rays[i] = ray_diff
                coeff = 1.0 / math.sqrt(sampler.samples_per_pixel)
                ray_diff.scale_differentials(coeff)

                # evaluate radiance along camera ray
                if ray_weight > 0.0:
                    radiance, intersection, Ts_i = \
                              self.renderer.Li(self.scene,
                                               ray_diff,
                                               samples[i],
                                               rng,
                                               isects[i])
                    Ls_i = ray_weight * radiance
                else:
                    Ls_i = Spectrum(0.0)
                    Ts_i = Spectrum(1.0)

                Ls[i] = Ls_i
                Ts[i] = Ts_i

                # check for unexpected radiance values
                if Ls_i.has_nan():
                    logger.error(
                        "Not-a-number radiance value returned for image sample.  Setting to black."
                    )
                    Ls_i = Spectrum(0.0)
                elif Ls_i.y() < -1e-5:
                    logger.error(
                        "Negative luminance value, %f, returned for image sample.  Setting to black."
                        % Ls_i.y())
                    Ls_i = Spectrum(0.0)
                elif Ls_i.y() == float('inf'):
                    logger.error(
                        "Infinite luminance value returned for image sample.  Setting to black."
                    )
                    Ls_i = Spectrum(0.0)

            # report sample results to Sampler, add contributions to image
            if sampler.report_results(samples, rays, Ls, isects, sample_count):
                for i in range(sample_count):
                    self.camera.film.add_sample(samples[i], Ls[i])

        # clean up after SamplerRendererTask is done with its image region
        pass
示例#9
0
文件: path.py 项目: neodyme60/raypy
    def Li(self, scene, renderer: Renderer, r: Ray, intersection: Intersection, sample: Sample) -> Spectrum:
        # Declare common path integration variables
        pathThroughput = Spectrum(1.0)
        L = Spectrum(0.0)
        ray = deepcopy(r)
        specularBounce = False
        localIsect = Intersection()
        isectp = intersection
        bounces = 0
        while True:
            # Possibly add emitted light at path vertex
            if bounces == 0 or specularBounce:
                L += pathThroughput * isectp.Le(-ray.direction)

            # Sample illumination from lights to find path contribution
            bsdf = isectp.get_bsdf(ray)
            p = bsdf.dgShading.point
            n = bsdf.dgShading.normal
            wo = -ray.direction
            if bounces < self.maxDepth:
                L += pathThroughput * UniformSampleOneLight(scene, renderer, p, n, wo, ray.time, bsdf, sample,
                                                            self.lightSampleOffsets[bounces],
                                                            self.bsdfSampleOffsets[bounces],
                                                            self.lightNumOffset[bounces])
            else:
                L += pathThroughput * UniformSampleOneLight(scene, renderer, p, n, wo, ray.time, bsdf, sample)

            # Sample BSDF to get new path direction

            # Get _outgoingBSDFSample_ for sampling new path direction
            if bounces < self.maxDepth:
                outgoingBSDFSample = BSDFSample.create_from_sample(sample, self.pathSampleOffsets[bounces], 0)
            else:
                outgoingBSDFSample = BSDFSample.create_from_random()

            wi, pdf, flags, f = bsdf.Sample_f(wo, outgoingBSDFSample, BxDFType.BSDF_ALL)
            if f.get_is_black() or pdf == 0.0:
                break

            specularBounce = (flags & BxDFType.BSDF_SPECULAR) != 0
            pathThroughput *= f * math.fabs(Vector3d.dot(wi, n)) / pdf
            ray = Ray(p, wi, 0.01, ray.max_t)

            # Possibly terminate the path
            if bounces > 3:
                continueProbability = min(0.5, pathThroughput.y())
                if random() > continueProbability:
                    break
                pathThroughput /= continueProbability

            if bounces == self.maxDepth:
                break

            # Find next vertex of path
            if not scene.get_intersection(ray, localIsect):
                if specularBounce:
                    for i in range(scene.lights.size()):
                        L += pathThroughput * scene.lights[i].get_Le(ray)
                break
            # pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
            isectp = localIsect

            bounces += 1

        return L
示例#10
0
    def enterParamSet(self, ctx):

        print("enter paramset")
        p = ctx.paramSetLeft()
        a = str(p.STRING()).replace('"', '').split(' ')
        param_type = a[0]
        param_name = a[1]
        print("-->" + param_name)
        if param_type == 'point':
            print("-->point")
            i = 0
            values = []
            while True:
                if ctx.NUMBER(i) is None:
                    break
                values.append(
                    Point3d(float(str(ctx.NUMBER(i))), float(str(ctx.NUMBER(i + 1))), float(str(ctx.NUMBER(i + 2)))))
                i += 3
            self.currentParamSet.add_point(param_name, values)
        elif param_type == 'float':
            print("-->float")
            i = 0
            values = []
            while True:
                if ctx.NUMBER(i) is None:
                    break
                values.append(float(str(ctx.NUMBER(i))))
                i += 1
            self.currentParamSet.add_float(param_name, values)
        elif param_type == 'integer':
            print("-->int")
            i = 0
            values = []
            while True:
                if ctx.NUMBER(i) is None:
                    break
                values.append(int(str(ctx.NUMBER(i))))
                i += 1
            self.currentParamSet.add_int(param_name, values)
        elif param_type == 'string':
            print("-->string")
            i = 0
            values = []
            while True:
                if ctx.STRING(i) is None:
                    break
                values.append(str(ctx.STRING(i)).replace("\"", ""))
                i += 1
            if len(values) == 1:
                self.currentParamSet.add_string(param_name, values[0])
            else:
                self.currentParamSet.add_string(param_name, values)
        elif param_type == 'color':
            print("-->color")
            i = 0
            values = []
            while True:
                if ctx.NUMBER(i) is None:
                    break
                values.append(float(str(ctx.NUMBER(i))))
                i += 1
            self.currentParamSet.add_spectrum(param_name, Spectrum.create_from_array(values))
示例#11
0
    def run(self):
        """Execute the task."""

        print "executing task %d/%d" % (self.task_num, self.task_count)
        
        # get sub-sampler for SamplerRendererTask
        sampler = self.main_sampler.get_sub_sampler(self.task_num,
                                                    self.task_count)
        if not sampler:
            return
        
        # Declare local variables used for rendering loop
        rng = RNG(self.task_num)

        # allocate space for samples and intersections
        max_samples = sampler.maximum_sample_count()
        samples = self.orig_sample.duplicate(max_samples)
        rays = [None] * max_samples
        Ls = [None] * max_samples
        Ts = [None] * max_samples
        isects = [] # Intersection[max_samples]
        for i in range(max_samples):
            isects.append(Intersection())

        # get samples from Sampler and update image
        while True:
            sample_count = sampler.get_more_samples(samples, rng)

            # if no more samples to compute, exit
            if sample_count <= 0:
                break
            
            # generate camera rays and compute radiance along rays
            for i in range(sample_count):
                # find camera ray for samples[i]
                ray_weight, ray_diff = self.camera.generate_ray_differential(samples[i])
                
                rays[i] = ray_diff
                coeff = 1.0 / math.sqrt(sampler.samples_per_pixel)
                ray_diff.scale_differentials(coeff)
                
                # evaluate radiance along camera ray
                if ray_weight > 0.0:
                    radiance, intersection, Ts_i = \
                              self.renderer.Li(self.scene,
                                               ray_diff,
                                               samples[i],
                                               rng,
                                               isects[i])
                    Ls_i = ray_weight * radiance
                else:
                    Ls_i = Spectrum(0.0)
                    Ts_i = Spectrum(1.0)

                Ls[i] = Ls_i
                Ts[i] = Ts_i
                
                # check for unexpected radiance values
                if Ls_i.has_nan():
                    logger.error(
                        "Not-a-number radiance value returned for image sample.  Setting to black.")
                    Ls_i = Spectrum(0.0)
                elif Ls_i.y() < -1e-5:
                    logger.error(
                        "Negative luminance value, %f, returned for image sample.  Setting to black." % Ls_i.y())
                    Ls_i = Spectrum(0.0)
                elif Ls_i.y() == float('inf'):
                    logger.error(
                        "Infinite luminance value returned for image sample.  Setting to black.")
                    Ls_i = Spectrum(0.0)

            # report sample results to Sampler, add contributions to image
            if sampler.report_results(samples, rays, Ls, isects, sample_count):
                for i in range(sample_count):
                    self.camera.film.add_sample(samples[i], Ls[i])

        # clean up after SamplerRendererTask is done with its image region
        pass
示例#12
0
class Photons():
    """
    Integrate over the beam and create the photons
    """
    def __init__(self):
        pass


    def initialize(self, lattice):
        # read settings
        settings = Settings()['generator']['photons']
        self._enabled = settings['enabled']
        self._full_events = settings['full_events']
        self._nth_step = settings['nth_step']
        self._time = settings['time']
        self._energy_cutoff = settings['energy_cutoff']
        self._sigma_h = settings['sigma']['horizontal']
        self._sigma_v = settings['sigma']['vertical']
        self._stepsize_h = 2.0 * self._sigma_h / settings['steps']['horizontal']
        self._stepsize_v = 2.0 * self._sigma_v / settings['steps']['vertical']
        self._crossing_angle = Settings()['machine']['crossing_angle']
        
        self._region_enabled = settings['region']['enabled']
        if settings['region']['range'][0] < settings['region']['range'][1]:
            self._region_left = settings['region']['range'][0]
            self._region_right = settings['region']['range'][1]
        else:
            self._region_left = settings['region']['range'][1]
            self._region_right = settings['region']['range'][0]

        self._target_zone_enabled = settings['target_zone']['enabled']
        self._target_zone_radius = settings['target_zone']['radius']
        self._target_zone_boundary = settings['target_zone']['boundary']

        # create synchrotron radiation power spectrum PDF
        self._spectrum = Spectrum()
        self._spectrum.initialize(settings['spectrum']['resolution'],
                                  settings['spectrum']['cutoff'],
                                  settings['spectrum']['seed'],
                                  settings['spectrum']['interpolation'])

        # internal parameters
        self._lattice = lattice
        self._call_count = 0
        self._dl = 0.0

        # internal constants and pre-calculated factors
        self._alpha = 1.0 / 137.035999074
        self._speed_of_light = 2.99792458e8 # [m/s]
        self._hbar = 6.58211928e-25 # [GeV s]
        self._gamma = Settings()['machine']['beam_energy'] / 510.998928e-6
        self._current = Settings()['machine']['beam_current'] * 6.241508e18
        self._num_photon_factor = (5.0 / (2.0*math.sqrt(3))) * \
                                  self._gamma * self._alpha * \
                                  self._current * self._time
        self._crit_e_factor = 3.0 / 2.0 * self._speed_of_light * \
                              self._hbar * (self._gamma**3)


    def create(self, step, beam, output, hepevt):
        if not self._enabled:
            return

        # accumulate steps only inside magnets and, if enabled,
        # inside the region
        if (not step.in_vacuum) and \
           (not self._region_enabled or \
            (self._region_enabled and \
             step.s0ip >= self._region_left and step.s0ip <= self._region_right)):
            self._dl += step.dl
            self._call_count += 1

        # radiate photons if the n-th step is reached,
        # the current step is on a magnet to vacuum boundary,
        # or the region is enabled and the step leaves the region
        if (self._call_count >= self._nth_step) or \
           (self._call_count > 0 and step.on_boundary) or \
           (self._region_enabled and self._call_count > 0 and step.ds < 0.0 and \
            step.s0ip <= self._region_left) or \
           (self._region_enabled and self._call_count > 0 and step.ds > 0.0 and \
            step.s0ip >= self._region_right):
            self._integrate_beam(math.fabs(self._dl),
                                 step, beam,
                                 output, hepevt)
            self._dl = 0.0
            self._call_count = 0


    def write_spectrum(self, output):
        self._spectrum.write(output)


    def _intersect_target_zone(self, vertex, direction):
        """
        Target zone is an z-axis aligned cylinder with an inner and an outer
        radius and a lower and upper boundary. This code intersects a line with
        the cylinder, not a ray! That's ok for the SyncRad Generator, as this
        shouldn't introduce too many false intersections as long as the generation
        is done in a way such that the photons travel towards the IP.
        """

        # calculate slopes
        if math.fabs(direction[2]) > 0.0000000001:
            slope_xz = direction[0] / direction[2]
            slope_yz = direction[1] / direction[2]
        else:
            slope_xz = 0.0
            slope_yz = 0.0

        # calculate the distance from the z-axis (radius) that the ray has at the
        # lower and upper z-boundary of the cylinder.
        def calc_radius2(boundary):
            x_b = slope_xz*(boundary-vertex[2]) + vertex[0]
            y_b = slope_yz*(boundary-vertex[2]) + vertex[1]
            return x_b**2 + y_b**2

        r_low = calc_radius2(self._target_zone_boundary[0])
        r_up  = calc_radius2(self._target_zone_boundary[1])

        # since it is a z-axis aligned cylinder, the radius can simply be compared
        # to the cylinder radii, in order to check for the intersection of the ray.
        radius2 = [self._target_zone_radius[0]**2, self._target_zone_radius[1]**2]
        return ((r_low < radius2[1]) and (r_up  > radius2[0])) or \
               ((r_up  < radius2[1]) and (r_low > radius2[0]))


    def _integrate_beam(self, dl, step, beam, output, hepevt):
        """
        integrate over the beam profile
        """
        total_number_photons = 0
        total_number_photons_cut = 0

        k1s = []
        for region in self._lattice.get(step.s0ip):
            idx = region.index(step.s0ip)
            k1s.append([region.k1(idx), region.sk1(idx)])

        hsize, vsize, ch, cv = beam.size()
        prob_norm_1 = 1.0 / (2.506628 * hsize * vsize)
        prob_norm_2 = 1.0 / (2.0*math.pi * hsize * vsize)
        weight_factor = self._stepsize_h * self._stepsize_v * hsize * vsize
        xstep = self._stepsize_h * hsize
        ystep = self._stepsize_v * vsize
        xs_max = self._sigma_h * hsize
        ys_max = self._sigma_v * vsize

        cx_s = math.sin(self._crossing_angle)
        cx_c = math.cos(self._crossing_angle)

        xs = -1.0 * self._sigma_h * hsize + 0.5*xstep
        while xs <= xs_max:
            ys = -1.0 * self._sigma_v * vsize + 0.5*ystep
            while ys <= ys_max:
                # calculate local radius
                local_gh = step.gh
                local_gv = step.gv
                for k1 in k1s:
                    local_gh += (k1[0] * xs) - (k1[1] * ys)
                    local_gv += (k1[0] * ys) + (k1[1] * xs)
                rho_inv = math.sqrt(local_gh**2 + local_gv**2)

                # calculate weight
                prob = 0.0
                nsigh = xs / hsize
                nsigv = ys / vsize

                # beam profile. Either Talman tails or Gaussian
                if (math.fabs(nsigv) > 5.0) and (beam.emitv / beam.emith < 0.2):
                    prob = prob_norm_1 * math.exp(-0.5*nsigh**2) * \
                                         math.exp(-7.4 -1.2*math.fabs(nsigv))
                else:
                    prob = prob_norm_2 * math.exp(-0.5*(nsigh**2 + nsigv**2))
                weight = prob * weight_factor

                # calculate number of radiated photons
                num_photons = int(self._num_photon_factor * rho_inv * weight * dl)
                total_number_photons += num_photons

                if num_photons > 0:

                    # calculate critical energy
                    crit_e = self._crit_e_factor * rho_inv

                    # calculate vertex
                    vx = (cx_c*(step.xip+xs)) + (cx_s*step.zip)
                    vy = step.yip+ys
                    vz = (cx_c*step.zip) - (cx_s*(step.xip+xs))

                    # calculate momentum
                    px_temp = -step.zip * ((math.pi - step.xip_prime) + (ch * xs))
                    py_temp = -step.zip * (step.yip_prime + (cv * ys))
                    pz_temp = -step.zip

                    # rotate momentum into Geant4 space
                    px = (cx_c*px_temp) + (cx_s*pz_temp)
                    py = py_temp
                    pz = (cx_c*pz_temp) - (cx_s*px_temp)
                    norm = 1.0/math.sqrt(px**2 + py**2 + pz**2)

                    # if the target zone feature is on, only write events if
                    # the photons will hit the zone.
                    if (not self._target_zone_enabled) or \
                       (self._target_zone_enabled and \
                        self._intersect_target_zone([vx, vy, vz], [px, py, pz])):

                        # if full event writing is turned on, get the energies
                        # for all radiated photons and write them into the
                        # event file
                        if self._full_events:
                            energies = self._spectrum.random(crit_e, num_photons,
                                                             self._energy_cutoff)
                            if len(energies) > 0:
                                evt = hepevt.event(vx, vy, vz)
                                for e in energies:
                                    evt.add(px*e*norm, py*e*norm, pz*e*norm)
                                evt.commit()
                            total_number_photons_cut += len(energies)
                        else:
                            evt = hepevt.event(vx, vy, vz,
                                               num_photons=num_photons,
                                               critical_e=crit_e)
                            evt.add(px*norm, py*norm, pz*norm)
                            evt.commit()

                ys += ystep
            xs += xstep
        output.write(["%f:%i:%i:%e:%e:%e:%e\n"%(step.s0ip,
                                       total_number_photons,
                                       total_number_photons_cut,
                                       step.x, step.y,
                                       step.xp, step.yp)])