Exemple #1
0
class Simulation(object):
    def __init__(self,
                 universe_material,
                 nbins,
                 diameter=100.,
                 detector_width=100.,
                 detector='plane'):
        self.universe_material = universe_material
        self.geometry = Geometry()
        self.source = np.array([-diameter / 2., 0.])
        if detector == 'plane':
            self.detector = DetectorPlane([diameter / 2., 0.], detector_width,
                                          nbins)
        elif detector == 'arc':
            self.detector = DetectorArc(self.source, diameter,
                                        detector_width / 2.,
                                        -detector_width / 2., nbins)

    def get_intersecting_segments(self, start, end, ray=False):
        """
        Find intersection points and lixels which intersect ray (start, end).
        """
        intercepts, indexes = [], []
        intersect_segment = np.array([start, end])

        for i, segment in enumerate(self.geometry.mesh.segments):
            intercept = math2d.intersect(segment, intersect_segment, ray)
            if intercept is not None:
                intercepts.append(intercept)
                indexes.append(i)

        return intercepts, indexes

    def attenuation_length(self, start, end):
        """
        Calculate attenuation length for geometry.

        Can account for starting and ending in any position.
        """
        intercepts, indexes = self.get_intersecting_segments(start, end)

        if len(intercepts) == 0:
            ray_intercepts, ray_indexes = self.get_intersecting_segments(
                start, end, ray=True)
            if len(ray_intercepts) == 0:
                return np.linalg.norm(start -
                                      end) * self.universe_material.attenuation

            distances = np.linalg.norm(np.add(ray_intercepts, -start), axis=1)
            distances_argmin = np.argmin(distances)
            closest_index = ray_indexes[distances_argmin]
            closest_intercept = ray_intercepts[distances_argmin]
            closest_normal = math2d.normal(
                self.geometry.mesh.segments[closest_index])
            start_sign = np.sign(
                np.dot(start - closest_intercept, closest_normal))

            if start_sign > 0:
                outer_atten = self.geometry.outer_materials[
                    closest_index].attenuation
                atten_length = np.linalg.norm(start - end) * outer_atten
            else:
                inner_atten = self.geometry.inner_materials[
                    closest_index].attenuation
                atten_length = np.linalg.norm(start - end) * inner_atten

            return atten_length

        distances = np.linalg.norm(np.add(intercepts, -start), axis=1)
        distances_argmin = np.argmin(distances)
        closest_index = indexes[distances_argmin]
        closest_intercept = intercepts[distances_argmin]
        closest_normal = math2d.normal(
            self.geometry.mesh.segments[closest_index])
        start_sign = np.sign(np.dot(start - closest_intercept, closest_normal))

        if start_sign > 0:
            outer_atten = self.geometry.outer_materials[
                closest_index].attenuation
            atten_length = np.linalg.norm(start - end) * outer_atten
        else:
            inner_atten = self.geometry.inner_materials[
                closest_index].attenuation
            atten_length = np.linalg.norm(start - end) * inner_atten

        for intercept, index in zip(intercepts, indexes):
            normal = math2d.normal(self.geometry.mesh.segments[index])
            start_sign = np.sign(np.dot(start - intercept, normal))
            end_sign = np.sign(np.dot(end - intercept, normal))
            inner_atten = self.geometry.inner_materials[index].attenuation
            outer_atten = self.geometry.outer_materials[index].attenuation

            atten_length += start_sign * np.linalg.norm(intercept - end) * (
                inner_atten - outer_atten)

        return atten_length

    def fission_segments(self, start, end):
        """
        Return list of line segments where fissions may occur.
        """
        segments, cross_sections = [], []

        intercepts, indexes = self.get_intersecting_lixels(start, end)

        # otherwise
        fission_indexes = []
        fission_intercepts = []
        for index, intercept in zip(indexes, intercepts):
            # test if lixel is border of fissionable material(s)
            inner_material = self.geometry.get_inner_material(index)
            outer_material = self.geometry.get_outer_material(index)
            if inner_material.is_fissionable or outer_material.is_fissionable:
                fission_indexes.append(index)
                fission_intercepts.append(intercept)

        # account for no intersections with fissionable materials
        if len(fission_intercepts) == 0:
            return segments, cross_sections

        # sort fission_indexes and fission_intercepts by distance from start
        distances = np.linalg.norm(np.add(fission_intercepts, -start), axis=1)
        distance_order = [
            index_
            for (distance_,
                 index_) in sorted(zip(distances, range(len(distances))))
        ]

        sorted_fission_indexes = [fission_indexes[i] for i in distance_order]
        sorted_fission_intercepts = [
            fission_intercepts[i] for i in distance_order
        ]

        for i in xrange(len(sorted_fission_indexes)):
            f_ind = sorted_fission_indexes[i]
            f_int = sorted_fission_intercepts[i]

            if i == 0:
                # test if start to first fission lixel is fissionable
                normal = self.geometry.mesh.lixel_normal(f_ind)
                sign = np.sign(np.dot(start - f_int, normal))

                inner_material = self.geometry.get_inner_material(f_ind)
                outer_material = self.geometry.get_outer_material(f_ind)

                if sign > 0 and outer_material.is_fissionable:
                    segments.append([start, f_int])
                    cross_sections.append(outer_material.macro_fission)
                elif sign < 0 and inner_material.is_fissionable:
                    segments.append([start, f_int])
                    cross_sections.append(inner_material.macro_fission)
            elif i == len(sorted_fission_indexes) - 1:
                # test if end to last fission lixel is fissionable
                normal = self.geometry.mesh.lixel_normal(f_ind)
                sign = np.sign(np.dot(end - f_int, normal))

                inner_material = self.geometry.get_inner_material(f_ind)
                outer_material = self.geometry.get_outer_material(f_ind)

                if sign > 0 and outer_material.is_fissionable:
                    segments.append([f_int, end])
                    cross_sections.append(outer_material.macro_fission)
                elif sign < 0 and inner_material.is_fissionable:
                    segments.append([f_int, end])
                    cross_sections.append(inner_material.macro_fission)
                continue

            # test all intervening segments
            normal_1 = self.geometry.mesh.lixel_normal(f_ind)
            sign_1 = np.sign(np.dot(start - f_int, normal_1))

            f_ind2 = sorted_fission_indexes[i + 1]
            f_int2 = sorted_fission_intercepts[i + 1]

            normal_2 = self.geometry.mesh.lixel_normal(f_ind2)
            sign_2 = np.sign(np.dot(start - f_int2, normal_2))

            if sign_1 > 0:
                mat_1 = self.geometry.get_inner_material(f_ind)
            else:
                mat_1 = self.geometry.get_outer_material(f_ind)

            if sign_2 < 0:
                mat_2 = self.geometry.get_inner_material(f_ind)
            else:
                mat_2 = self.geometry.get_outer_material(f_ind)

            if mat_1.is_fissionable and mat_2.is_fissionable:
                if mat_1.macro_fission != mat_2.macro_fission:
                    raise NotImplementedError
                segments.append([f_int, f_int2])
                cross_sections.append(mat_1.macro_fission)

        return segments, cross_sections

    def scan(self, angles=[0], nbins=100):
        atten_length = np.zeros((nbins, len(angles)))

        detector_bins = self.detector.create_bins(nbins)
        source = self.source

        for i, angle in enumerate(angles):
            rot = angle_matrix(angle)
            rot_source = np.inner(source, rot)
            rot_detector_bins = np.inner(detector_bins, rot)
            for j, detector_bin in enumerate(rot_detector_bins):
                atten_length[j, i] = self.attenuation_length(
                    rot_source, detector_bin)

        return atten_length

    def radon_transform(self, angles=[0]):
        if type(self.detector) is not DetectorPlane:
            raise TypeError('self.detector is not DetectorPlane')

        detector_centers = (self.detector.segments[:, 0, :] +
                            self.detector.segments[:, 1, :]) / 2.

        radon = np.zeros((np.size(detector_centers, 0), len(angles)))

        source_bins = np.inner(detector_centers,
                               math2d.angle_matrix(180.))[::-1]

        for i, angle in enumerate(angles):
            print i, len(angles)
            rot = math2d.angle_matrix(angle)
            rot_source = np.inner(source_bins, rot)
            rot_detector = np.inner(detector_centers, rot)
            for j in xrange(len(rot_detector)):
                radon[j, i] = self.attenuation_length(rot_source[j],
                                                      rot_detector[j])

        return radon

    def draw(self, draw_normals=False):
        self.geometry.draw(draw_normals)

        if self.source is not None:
            plt.scatter(self.source[0],
                        self.source[1],
                        color='red',
                        marker='x')

        self.detector.draw(draw_normals)

        plt.axis('equal')
        plt.xlabel('X (cm)')
        plt.ylabel('Y (cm)')
Exemple #2
0
class Simulation(object):
    def __init__(self, universe_material, nbins, diameter=100., detector_width=100., detector='plane'):
        self.universe_material = universe_material
        self.geometry = Geometry()
        self.source = np.array([-diameter / 2., 0.])
        if detector == 'plane':
            self.detector = DetectorPlane([diameter / 2., 0.], detector_width, nbins)
        elif detector == 'arc':
            self.detector = DetectorArc(self.source, diameter, detector_width / 2., -detector_width / 2., nbins)

    def get_intersecting_segments(self, start, end, ray=False):
        """
        Find intersection points and lixels which intersect ray (start, end).
        """
        intercepts, indexes = [], []
        intersect_segment = np.array([start, end])

        for i, segment in enumerate(self.geometry.mesh.segments):
            intercept = math2d.intersect(segment, intersect_segment, ray)
            if intercept is not None:
                intercepts.append(intercept)
                indexes.append(i)

        return intercepts, indexes
   
    def attenuation_length(self, start, end):
        """
        Calculate attenuation length for geometry.

        Can account for starting and ending in any position.
        """
        intercepts, indexes = self.get_intersecting_segments(start, end)

        if len(intercepts) == 0:
            ray_intercepts, ray_indexes = self.get_intersecting_segments(start, end, ray=True)
            if len(ray_intercepts) == 0:
                return np.linalg.norm(start - end) * self.universe_material.attenuation

            distances = np.linalg.norm(np.add(ray_intercepts, -start), axis=1)
            distances_argmin = np.argmin(distances)
            closest_index = ray_indexes[distances_argmin]
            closest_intercept = ray_intercepts[distances_argmin]
            closest_normal = math2d.normal(self.geometry.mesh.segments[closest_index])
            start_sign = np.sign(np.dot(start - closest_intercept, closest_normal))

            if start_sign > 0:
                outer_atten = self.geometry.outer_materials[closest_index].attenuation
                atten_length = np.linalg.norm(start - end) * outer_atten
            else:
                inner_atten = self.geometry.inner_materials[closest_index].attenuation
                atten_length = np.linalg.norm(start - end) * inner_atten

            return atten_length

        distances = np.linalg.norm(np.add(intercepts, -start), axis=1)
        distances_argmin = np.argmin(distances)
        closest_index = indexes[distances_argmin]
        closest_intercept = intercepts[distances_argmin]
        closest_normal = math2d.normal(self.geometry.mesh.segments[closest_index])
        start_sign = np.sign(np.dot(start - closest_intercept, closest_normal))

        if start_sign > 0:
            outer_atten = self.geometry.outer_materials[closest_index].attenuation
            atten_length = np.linalg.norm(start - end) * outer_atten
        else:
            inner_atten = self.geometry.inner_materials[closest_index].attenuation
            atten_length = np.linalg.norm(start - end) * inner_atten

        for intercept, index in zip(intercepts, indexes):
            normal =  math2d.normal(self.geometry.mesh.segments[index])
            start_sign = np.sign(np.dot(start - intercept, normal))
            end_sign = np.sign(np.dot(end - intercept, normal))
            inner_atten = self.geometry.inner_materials[index].attenuation
            outer_atten = self.geometry.outer_materials[index].attenuation

            atten_length += start_sign * np.linalg.norm(intercept - end) * (inner_atten - outer_atten)
        
        return atten_length

    def fission_segments(self, start, end):
        """
        Return list of line segments where fissions may occur.
        """
        segments, cross_sections = [], []

        intercepts, indexes = self.get_intersecting_lixels(start, end)

        # otherwise
        fission_indexes = []
        fission_intercepts = []
        for index, intercept in zip(indexes, intercepts):
            # test if lixel is border of fissionable material(s)
            inner_material = self.geometry.get_inner_material(index)
            outer_material = self.geometry.get_outer_material(index)
            if inner_material.is_fissionable or outer_material.is_fissionable:
                fission_indexes.append(index)
                fission_intercepts.append(intercept)

        # account for no intersections with fissionable materials
        if len(fission_intercepts) == 0:
            return segments, cross_sections

        # sort fission_indexes and fission_intercepts by distance from start
        distances = np.linalg.norm(np.add(fission_intercepts, -start), axis=1)
        distance_order = [index_ for (distance_, index_) in sorted(zip(distances, range(len(distances))))]

        sorted_fission_indexes = [fission_indexes[i] for i in distance_order]
        sorted_fission_intercepts = [fission_intercepts[i] for i in distance_order]

        for i in xrange(len(sorted_fission_indexes)):
            f_ind = sorted_fission_indexes[i]
            f_int = sorted_fission_intercepts[i]

            if i == 0:
                # test if start to first fission lixel is fissionable
                normal = self.geometry.mesh.lixel_normal(f_ind)
                sign = np.sign(np.dot(start - f_int, normal))

                inner_material = self.geometry.get_inner_material(f_ind)
                outer_material = self.geometry.get_outer_material(f_ind)

                if sign > 0 and outer_material.is_fissionable:
                    segments.append([start, f_int])
                    cross_sections.append(outer_material.macro_fission)
                elif sign < 0 and inner_material.is_fissionable:
                    segments.append([start, f_int])
                    cross_sections.append(inner_material.macro_fission)
            elif i == len(sorted_fission_indexes)-1:
                # test if end to last fission lixel is fissionable
                normal = self.geometry.mesh.lixel_normal(f_ind)
                sign = np.sign(np.dot(end - f_int, normal))

                inner_material = self.geometry.get_inner_material(f_ind)
                outer_material = self.geometry.get_outer_material(f_ind)

                if sign > 0 and outer_material.is_fissionable:
                    segments.append([f_int, end])
                    cross_sections.append(outer_material.macro_fission)
                elif sign < 0 and inner_material.is_fissionable:
                    segments.append([f_int, end])
                    cross_sections.append(inner_material.macro_fission)
                continue

            # test all intervening segments
            normal_1 = self.geometry.mesh.lixel_normal(f_ind)
            sign_1 = np.sign(np.dot(start - f_int, normal_1))

            f_ind2 = sorted_fission_indexes[i+1]
            f_int2 = sorted_fission_intercepts[i+1]

            normal_2 = self.geometry.mesh.lixel_normal(f_ind2)
            sign_2 = np.sign(np.dot(start - f_int2, normal_2))

            if sign_1 > 0:
                mat_1 = self.geometry.get_inner_material(f_ind)
            else:
                mat_1 = self.geometry.get_outer_material(f_ind)

            if sign_2 < 0:
                mat_2 = self.geometry.get_inner_material(f_ind)
            else:
                mat_2 = self.geometry.get_outer_material(f_ind)

            if mat_1.is_fissionable and mat_2.is_fissionable:
                if mat_1.macro_fission != mat_2.macro_fission:
                    raise NotImplementedError
                segments.append([f_int, f_int2])
                cross_sections.append(mat_1.macro_fission)

        return segments, cross_sections
    
    def scan(self, angles=[0], nbins=100):
        atten_length = np.zeros((nbins, len(angles)))
        
        detector_bins = self.detector.create_bins(nbins)
        source = self.source
        
        for i, angle in enumerate(angles):
            rot = angle_matrix(angle)
            rot_source = np.inner(source, rot)
            rot_detector_bins = np.inner(detector_bins, rot)
            for j, detector_bin in enumerate(rot_detector_bins):
                atten_length[j, i] = self.attenuation_length(rot_source, detector_bin)
        
        return atten_length
    
    def radon_transform(self, angles=[0]):
        if type(self.detector) is not DetectorPlane:
            raise TypeError('self.detector is not DetectorPlane')

        detector_centers = (self.detector.segments[:, 0, :] + self.detector.segments[:, 1, :]) / 2.

        radon = np.zeros((np.size(detector_centers, 0), len(angles)))
        
        source_bins = np.inner(detector_centers, math2d.angle_matrix(180.))[::-1]
        
        for i, angle in enumerate(angles):
            print i, len(angles)
            rot = math2d.angle_matrix(angle)
            rot_source = np.inner(source_bins, rot)
            rot_detector = np.inner(detector_centers, rot)
            for j in xrange(len(rot_detector)):
                radon[j, i] = self.attenuation_length(rot_source[j], rot_detector[j])
        
        return radon

    def draw(self, draw_normals=False):
        self.geometry.draw(draw_normals)
        
        if self.source is not None:
            plt.scatter(self.source[0], self.source[1], color='red', marker='x')
        
        self.detector.draw(draw_normals)

        plt.axis('equal')
        plt.xlabel('X (cm)')
        plt.ylabel('Y (cm)')