Exemplo n.º 1
0
    def make_helix(
        self,
        width,
        height,
        length,
        num_turns,
        radius,
        output=False,
        output_geometries=False,
    ):
        """
        produces square-helical 3d point array of n points.
        :param width:
        :param height:
        :param length:
        :param num_turns:
        :param radius:
        :param output:
        :return:
        """
        # declarations and piecewise function describe coil, arbitrary function could be used.
        num_per_turn = int(round(self.n / num_turns))
        perimeter = (
            2 * (height - 2 * radius) + 2 * (width - 2 * radius) + 2 * np.pi * radius
        )
        n_w = int(round((width - 2 * radius) * (num_per_turn / perimeter)))
        n_h = int(round((height - 2 * radius) * (num_per_turn / perimeter)))
        n_r = int(round((2 * np.pi * radius) * (num_per_turn / perimeter)))

        geometry_dict = {"n width": n_w, "n_height": n_h, "n radius": n_r}

        x = self.piecewise(height, radius, n_h, n_w, n_r, num_per_turn)
        y = self.piecewise(width, radius, n_w, n_h, n_r, num_per_turn, transpose=True)

        vector_list = [list(np.copy(x)), list(np.copy(y)), ""]
        for i in range(num_turns - 1):
            vector_list[0].extend(x)
            vector_list[1].extend(y)
            datahandling.progressbar(i, num_turns - 1, message="building coil")
        vector_list[2] = np.linspace(0, length, len(vector_list[0]))
        helix_array = np.array(
            [np.array(vector_list[0]), np.array(vector_list[1]), vector_list[2]]
        )
        if output and output_geometries:
            return helix_array, geometry_dict
        elif not output and not output_geometries:
            self.main_spiral = np.copy(helix_array)
        elif not output and output_geometries:
            self.main_spiral = np.copy(helix_array)
            return geometry_dict
        if output and not output_geometries:
            return helix_array
Exemplo n.º 2
0
    def map_points(self, unmapped_local_array, normal_array, trans_array, output=False):
        """
        will map point from global origin [0, 0, 0] to translated and rotated points
        :param unmapped_local_array: n-dimensional array of points to be mapped.
        :type unmapped_local_array: ndarray
        :param normal_array: n-dimensional array of normals to rotation point.
        :type normal_array: ndarray
        :param trans_array: n-dimensional array of translated origins.
        :type trans_array: ndarray
        :param output: kwarg conditional for making method static.
        :type output: bool
        :return: mapped_array: n-dimensional array of mapped points.
        """
        mapped_list = []

        def angle(p):
            angle_rad = np.angle(p[0] + p[1] * 1j, deg=False)
            return angle_rad

        for index, unmapped_vector in enumerate(unmapped_local_array):
            normal_vector = normal_array[index]
            unmapped_vector = unmapped_local_array[index]
            trans_vector = trans_array[index]
            rot_angle_y = np.pi / 2 - angle(
                np.array(
                    [
                        (normal_vector[0] ** 2 + normal_vector[1] ** 2) ** 0.5,
                        normal_vector[2],
                    ]
                )
            )
            rot_angle_z = angle(np.array([normal_vector[0], normal_vector[1]]))
            r_y = np.copy(
                np.array(
                    [
                        np.array([np.cos(rot_angle_y), 0, np.sin(rot_angle_y)]),
                        np.array([0, 1, 0]),
                        np.array([-np.sin(rot_angle_y), 0, np.cos(rot_angle_y)]),
                    ]
                )
            )
            r_z = np.copy(
                np.array(
                    [
                        np.array([np.cos(rot_angle_z), -np.sin(rot_angle_z), 0]),
                        np.array([np.sin(rot_angle_z), np.cos(rot_angle_z), 0]),
                        np.array([0, 0, 1]),
                    ]
                )
            )
            mapped_vector = np.copy(
                np.matmul(r_z, np.matmul(r_y, unmapped_vector)) + trans_vector
            )
            mapped_list.append(mapped_vector)
            datahandling.progressbar(
                index,
                len(unmapped_local_array),
                message="mapping profile points to coil path",
                exit_message="finished, coil built...",
            )
        if output:
            return np.array(mapped_list)
        else:
            self.point_array = np.transpose(np.array(mapped_list))
Exemplo n.º 3
0
    def reconstruct_coil(self, output=True):
        # for each turn create a lofted matrix of profile shapes. loft for the turning section of the path.
        n_h = self.loop_size["n height"]

        if n_h % 2 == 0:
            start_point = n_h // 2
            end_point = n_h // 2
        else:
            start_point = (n_h - 1) // 2 + 1
            end_point = (n_h - 1) // 2

        # find n gradients for j loops between start and end loops.
        def _find_grad(start_vector, end_vector, size):
            shift_vector = (end_vector - start_vector) / (size + 2)
            return shift_vector

        find_grad = np.vectorize(_find_grad)

        # will move nth point in jth loop by j*grad_i, for loft.
        def _shift(start, grad, posn):
            vector = start + (posn + 1) * grad
            return vector

        shift = np.vectorize(_shift)

        # for jth point in jth loop will create linear loft using start profile and gradient to loft.
        # j: 0 -> n  | j: 0 -> k
        def loft(gradient, k, start):
            lofted_loop_list = []
            for i in range(k):
                lofted_loop = shift(start, gradient, k)
                lofted_loop_list.append(lofted_loop)
            return np.array(lofted_loop_list)

        lofting_points = (int(self.lpht - (start_point + end_point)))

        turn_profile_list = []
        for turn in range(self.cross_sections - 1):
            # start and end profiles are (ppt x 3) arrays
            # k = lpht
            start_profile = self.coil_loop_dict[turn][1]
            end_profile = self.coil_loop_dict[turn + 1][1]
            profile_sections_list = [
                np.tile(start_profile, (start_point, 1, 1))
            ]
            gradient_array = find_grad(start_profile, end_profile,
                                       lofting_points)
            profile_sections_list.append(
                loft(gradient_array, lofting_points, start_profile))
            profile_sections_list.append(
                np.tile(end_profile, (end_point, 1, 1)))
            turn_profile = np.concatenate(profile_sections_list)
            turn_profile_list.append(turn_profile)
            datahandling.progressbar(turn, self.cross_sections - 1,
                                     "lofting sections")
        lofted_profile_matrix = np.concatenate(turn_profile_list)
        lofted_profile_array = lofted_profile_matrix.reshape(
            -1, lofted_profile_matrix.shape[-1])

        sub_gm = GeometryManipulation(lofted_profile_array.shape[0])
        mapping_normal_array = sub_gm.grad(self.path_array, output=True)
        sub_gm.path_ppt = int(self.lpht * 2)
        sub_gm.num_turns = self.path_array.shape[0] // sub_gm.path_ppt
        sub_gm.profile_ppt = int(self.profile_ppt)
        # self.pcd = o3d.geometry.PointCloud()
        # self.pcd.points = o3d.utility.Vector3dVector(lofted_profile_array)
        # o3d.visualization.draw_geometries([self.pcd])
        mapped_lofted_surface_array = sub_gm.map_points(lofted_profile_array,
                                                        mapping_normal_array,
                                                        self.path_array,
                                                        output=True)
        if output:
            return mapped_lofted_surface_array
        else:
            self.reconstructed_array = mapped_lofted_surface_array
Exemplo n.º 4
0
    def breakdown_coil(
        self,
        num_turns=None,
        profile_ppt=None,
        path_ppt=None,
        profile_array=None,
        path_array=None,
        loop_size_dict=None,
    ):
        """
        # breaks down coil profile and path components into storable dict for use in FEMM analysis.
        :param num_turns: number of coil turns
        :param profile_ppt: points per profile turn
        :param profile_array: unmapped profile array
        :param path_array: path_array
        :param loop_size_dict: dictionary containing allocation of turn points.
        :return:
        """
        # assign attributes to method variables if None.
        geom_dict = datahandling.read_dict("docs/dimensions.json")
        if num_turns is None:
            num_turns = geom_dict["num_of_turns"]
        if profile_ppt is None:
            profile_ppt = self.profile_ppt
        if path_ppt is None:
            path_ppt = self.path_ppt
        if profile_array is None:
            profile_array = self.coil_spiral
            equal = profile_array == np.zeros((3, self.n))
            if equal.all():
                profile_array = datahandling.fetch_coil_points(
                    'docs/coil_array_points.json')
        if path_array is None:
            path_array = np.transpose(self.main_spiral)
        if loop_size_dict is None:
            loop_size_dict = self.loop_size_dict
            if loop_size_dict is None:
                loop_size_dict = datahandling.read_dict(
                    "docs/loop_size_dict.json")

        # calc. coil parameters, for splitting up coil into useful profiles.
        loops = path_ppt * num_turns
        cross_sections = 2 * num_turns + 1
        loops_per_half_turn = loops / (num_turns * 2)
        print("loops: %s \ncross-sections: %s \nloops per half turn: %s" %
              (loops, cross_sections, loops_per_half_turn))
        # make dict with coil parameters
        header_dict = {
            "coil shape": geom_dict,
            "profile_ppt": profile_ppt,
            "path_ppt": path_ppt,
            "cross sections": cross_sections,
            "lpht": loops_per_half_turn,
            "loop size": loop_size_dict,
        }
        cross_sections_dict = {}
        profile_array = np.transpose(profile_array)
        # split coil into individual profile loops for 3D matrix.
        profile_matrix = np.reshape(profile_array,
                                    (int(loops), int(profile_ppt), 3))
        for i in range(cross_sections):
            index = int(i * loops_per_half_turn)
            if i == cross_sections - 1:
                cross_sections_dict[i] = (tuple(path_array[-1]),
                                          profile_matrix[-1])
            else:
                cross_sections_dict[i] = (tuple(path_array[index]),
                                          profile_matrix[index])

            datahandling.progressbar(i, cross_sections,
                                     'breaking down sections')
        compact_coil_dict = {
            "header": header_dict,
            "x-sec": cross_sections_dict,
            "path": path_array,
        }
        return compact_coil_dict
Exemplo n.º 5
0
    def map_points(self,
                   unmapped_profile_array,
                   normal_array,
                   trans_array,
                   output=False):
        """
        maps loops of unmapped profile array to path array, sweeps profile around paths
        k = n // ppt
        :param unmapped_profile_array: (nx3) profile array - unmapped
        :type unmapped_profile_array: ndarray
        :param normal_array: (kx3) gradient of path array - normal of unmapped plane
        :type normal_array: ndarray
        :param trans_array: (kx3) path array
        :type trans_array: ndarray
        :param output: kwarg conditional for making method static.
        :type output: bool
        :return: mapped_array: n-dimensional array of mapped points.
        """
        mapped_list = []
        k = int(self.path_ppt * self.num_turns)
        profile_matrix = np.reshape(unmapped_profile_array,
                                    (k, self.profile_ppt, 3))

        def angle(p):
            angle_rad = np.angle(p[0] + p[1] * 1j, deg=False)
            return angle_rad

        for index, path_point in enumerate(trans_array):
            normal_vector = normal_array[index]
            unmapped_profile_loop = profile_matrix[index]
            rot_angle_y = 1 * (np.pi / 2 - angle(
                np.array([
                    (normal_vector[0]**2 + normal_vector[1]**2)**0.5,
                    normal_vector[2],
                ])))
            rot_angle_z = -1 * angle(
                np.array([normal_vector[0], normal_vector[1]]))
            r_y = np.copy(
                np.array([
                    np.array([np.cos(rot_angle_y), 0,
                              np.sin(rot_angle_y)]),
                    np.array([0, 1, 0]),
                    np.array([-np.sin(rot_angle_y), 0,
                              np.cos(rot_angle_y)]),
                ]))
            r_z = np.copy(
                np.array([
                    np.array([np.cos(rot_angle_z), -np.sin(rot_angle_z), 0]),
                    np.array([np.sin(rot_angle_z),
                              np.cos(rot_angle_z), 0]),
                    np.array([0, 0, 1]),
                ]))
            mapped_matrix = np.copy(
                np.matmul(unmapped_profile_loop, np.matmul(r_y, r_z)) +
                path_point)
            mapped_list.append(mapped_matrix)

            datahandling.progressbar(
                index,
                len(trans_array),
                message="mapping profile points to coil path",
                exit_message="finished, coil built...",
            )
        if output:
            return np.concatenate(mapped_list)
        else:
            self.point_array = np.transpose(np.concatenate(mapped_list))
Exemplo n.º 6
0
    def make_helix(
        self,
        width,
        height,
        length,
        num_turns,
        radius,
        num_per_turn,
        output=False,
        output_geometries=False,
    ):
        """
        produces square-helical 3d point array of n points.
        :param width:
        :param height:
        :param length:
        :param num_turns:
        :param num_per_turn:
        :param radius:
        :param output:
        :param output_geometries:
        :return:
        """
        # declarations and piecewise function describe coil, arbitrary function could be used.
        perimeter = (2 * (height - 2 * radius) + 2 * (width - 2 * radius) +
                     2 * np.pi * radius)
        # calculate number of points per section of rectangle to make even point spacing.
        n_w = int(round((width - 2 * radius) * (num_per_turn / perimeter)))
        n_h = int(round((height - 2 * radius) * (num_per_turn / perimeter)))
        n_r = int(round((2 * np.pi * radius) * (num_per_turn / perimeter)))

        # create dictionary for optional output
        geometry_dict = {"n width": n_w, "n height": n_h, "n radius": n_r}

        # make x and y coordinate arrays for one turn
        x = self.piecewise(height, radius, n_h, n_w, n_r, num_per_turn)
        y = self.piecewise(width,
                           radius,
                           n_w,
                           n_h,
                           n_r,
                           num_per_turn,
                           transpose=True)

        # form 2D array of coordinates for one turn
        vector_list = [list(np.copy(x)), list(np.copy(y)), ""]
        # extend array for number of turns in coil
        for i in range(num_turns - 1):
            vector_list[0].extend(x)
            vector_list[1].extend(y)
            datahandling.progressbar(i, num_turns - 1, message="building coil")
        # add z-axis component
        vector_list[2] = np.linspace(0, length, len(vector_list[0]))
        helix_array = np.array([
            np.array(vector_list[0]),
            np.array(vector_list[1]), vector_list[2]
        ])
        if output and output_geometries:
            return helix_array, geometry_dict
        elif not output and not output_geometries:
            self.main_spiral = np.copy(helix_array)
        elif not output and output_geometries:
            self.main_spiral = np.copy(helix_array)
            return geometry_dict
        if output and not output_geometries:
            return helix_array