def test_load_error_wrong_class(self):
     """Test that the load function can detect wrong __class__
     """
     with self.assertRaises(LoadWrongDictClassError) as context:
         load(load_file_2)
     self.assertEqual("SlotDoesntExist is not a pyleecan class",
                      str(context.exception))
 def test_load_error_missing_class(self):
     """Test that the load function can detect missing __class__
     """
     with self.assertRaises(LoadWrongDictClassError) as context:
         load(load_file_1)
     self.assertEqual('Key "__class__" missing in loaded file',
                      str(context.exception))
예제 #3
0
    def s_load(self):
        """Slot to load a machine from a .m file (triggered by b_load)

        Parameters
        ----------
        self : DMachineSetup
            A DMachineSetup object
        """

        # Ask the user to select a .m file to load
        load_path = str(
            QFileDialog.getOpenFileName(
                self, self.tr("Load file"), self.machine_path, "Json (*.json)"
            )[0]
        )
        if load_path != "":
            try:
                self.machine = load(load_path)
                self.machineChanged.emit()
                self.is_save_needed = False
            except Exception as e:
                QMessageBox().critical(
                    self,
                    self.tr("Error"),
                    self.tr(
                        "The machine file is incorrect:\n",
                        "Please keep the \n, another " "message is following this one",
                    )
                    + type(e).__name__
                    + ": "
                    + str(e),
                )
                return
            self.update_nav()
    def test_save_load_machine(self):
        """Check that you can save and load a machine object
        """
        # SetUp
        test_obj = MachineSIPMSM(name="test", desc="test\non\nseveral lines")
        test_obj.stator = LamSlotWind(L1=0.45)
        test_obj.stator.slot = SlotW10(Zs=10, H0=0.21, W0=0.23)
        test_obj.stator.winding = WindingDW1L(qs=5)
        test_obj.rotor = LamSlotMag(L1=0.55)
        test_obj.rotor.slot = SlotMPolar(W0=pi / 4)
        test_obj.rotor.slot.magnet = [MagnetType11(Wmag=pi / 4, Hmag=3)]
        test_obj.shaft = Shaft(Lshaft=0.65)
        test_obj.frame = None

        # Save Test
        file_path = join(save_dir, "test_machine.json")
        if isfile(file_path):
            remove(file_path)
        self.assertFalse(isfile(file_path))
        test_obj.save(file_path)
        self.assertTrue(isfile(file_path))

        # Load Test
        result = load(file_path)
        self.assertTrue(type(result) is MachineSIPMSM)
        self.assertEqual(result.name, "test")
        self.assertEqual(result.desc, "test\non\nseveral lines")

        self.assertTrue(type(result.stator) is LamSlotWind)
        self.assertEqual(result.stator.L1, 0.45)

        self.assertTrue(type(result.stator.slot) is SlotW10)
        self.assertEqual(result.stator.slot.Zs, 10)
        self.assertEqual(result.stator.slot.H0, 0.21)
        self.assertEqual(result.stator.slot.W0, 0.23)

        self.assertTrue(type(result.stator.winding) is WindingDW1L)
        self.assertEqual(result.stator.winding.qs, 5)

        self.assertTrue(type(result.rotor) is LamSlotMag)
        self.assertEqual(result.rotor.L1, 0.55)

        self.assertTrue(type(result.rotor.slot) is SlotMPolar)
        self.assertEqual(result.rotor.slot.W0, pi / 4)

        self.assertTrue(type(result.rotor.slot.magnet) is list)
        self.assertTrue(type(result.rotor.slot.magnet[0]) is MagnetType11)
        self.assertEqual(len(result.rotor.slot.magnet), 1)
        self.assertEqual(result.rotor.slot.magnet[0].Wmag, pi / 4)
        self.assertEqual(result.rotor.slot.magnet[0].Hmag, 3)

        self.assertTrue(type(result.shaft) is Shaft)
        self.assertEqual(result.shaft.Lshaft, 0.65)

        self.assertEqual(result.frame, None)
    def test_save_load_just_name(self):
        """Save with a folder path
        """

        test_obj = SlotW10(Zs=10)

        file_path = join(getcwd(), "test_slot.json")
        if isfile(file_path):
            remove(file_path)
        self.assertFalse(isfile(file_path))
        test_obj.save("test_slot")
        self.assertTrue(isfile(file_path))

        result = load("test_slot")
        self.assertTrue(type(result) is SlotW10)
        self.assertEqual(result.Zs, 10)
예제 #6
0
    def s_load(self):
        """Slot to load a machine from a .json file (triggered by b_load)

        Parameters
        ----------
        self : DMachineSetup
            A DMachineSetup object
        """
        ### TODO: handle material data, i.e. "connect", set new material, etc.

        # Ask the user to select a .json file to load
        load_path = str(
            QFileDialog.getOpenFileName(self, self.tr("Load file"),
                                        self.machine_path, "Json (*.json)")[0])
        if load_path != "":
            try:
                # Update the machine path to remember the last used folder
                self.machine_path = dirname(load_path)
                # Load and check type of instance
                machine = load(load_path)
                if isinstance(machine, Machine):
                    self.machine = machine
                else:
                    QMessageBox().critical(
                        self,
                        self.tr("Error"),
                        self.tr("The choosen file is not a machine file."),
                    )
                    return
                self.machineChanged.emit()
                self.is_save_needed = False
            except Exception as e:
                QMessageBox().critical(
                    self,
                    self.tr("Error"),
                    self.tr(
                        "The machine file is incorrect:\n",
                        "Please keep the \n, another "
                        "message is following this one",
                    ) + type(e).__name__ + ": " + str(e),
                )
                return
            self.update_nav()
 def test_load_error_missing(self):
     """Test that the load function can detect missing file
     """
     with self.assertRaises(LoadMissingFileError):
         load(save_dir)
예제 #8
0
    def test_load_save_several_file(self):
        """Check that you can load/save several machine files
        """
        # Copy the matlib
        mkdir(join(self.work_path, "Lamination"))
        mkdir(join(self.work_path, "Magnet"))
        copyfile(
            join(DATA_DIR, "Material", "Magnet1.json"),
            join(self.work_path, "Magnet", "Magnet1.json"),
        )
        cop_path = join(self.work_path, "Copper1.json")
        copyfile(join(DATA_DIR, "Material", "Copper1.json"), cop_path)
        copyfile(
            join(DATA_DIR, "Material", "Insulator1.json"),
            join(self.work_path, "Insulator1.json"),
        )
        lam_path = join(self.work_path, "Lamination", "M400-50A.json")
        copyfile(join(DATA_DIR, "Material", "M400-50A.json"), lam_path)
        # Check initial state
        nb_file = len(
            [
                name
                for name in listdir(self.work_path)
                if isfile(join(self.work_path, name)) and name[-5:] == ".json"
            ]
        )
        self.assertEqual(nb_file, 2)

        # Start the GUI
        self.widget = DMachineSetup(
            machine=None, machine_path=self.work_path, matlib_path=self.work_path
        )
        # Check load of the matlib
        self.assertEqual(len(self.widget.matlib), 4)
        self.assertEqual(
            ["Copper1", "Insulator1", "M400-50A", "Magnet1"],
            [mat.name for mat in self.widget.matlib],
        )
        self.assertEqual(self.widget.matlib[0].elec.rho, 1.73e-8)
        self.assertEqual(self.widget.matlib[0].HT.alpha, 0.00393)
        self.assertEqual(
            self.widget.matlib[0].path, join(self.work_path, "Copper1.json")
        )

        self.assertEqual(self.widget.matlib[2].mag.mur_lin, 2500)
        self.assertEqual(self.widget.matlib[2].struct.rho, 7650)
        self.assertEqual(self.widget.matlib[2].struct.Ex, 215000000000)
        self.assertEqual(
            self.widget.matlib[2].path,
            join(self.work_path, "Lamination", "M400-50A.json"),
        )
        # Change value of materials
        self.widget.matlib[0].elec.rho = 1.74e-8
        self.widget.matlib[0].HT.alpha = 0.00555
        self.widget.matlib[2].mag.mur_lin = 2501.2
        self.widget.matlib[2].struct.rho = 76
        # Save matlib
        for mat in self.widget.matlib:
            mat.save(mat.path)
            mat2 = load(mat.path)
            self.assertEqual(mat.as_dict(), mat2.as_dict())
예제 #9
0
    def test_Optimization_problem(self):
        """
        Figure19: Machine topology before optimization
        Figure20: Individuals in the fitness space
        Figure21: Pareto Front in the fitness space
        Figure22: Topology to maximize first torque harmonic
        Figure22: Topology to minimize second torque harmonic  

        WARNING: The computation takes 6 hours on a single 3GHz CPU core.
        The algorithm uses randomization at different steps so 
        the results won't be exactly the same as the one in the publication
        """
        # ------------------ #
        # DEFAULT SIMULATION #
        # ------------------ #

        # First, we need to define a default simulation.
        # This simulation will the base of every simulation during the optimization process

        # Load the machine
        SPMSM_001 = load("pyleecan/Tests/Validation/Machine/SPMSM_001.json")

        # Definition of the enforced output of the electrical module
        Na = 1024  # Angular steps
        Nt = 32  # Time step
        Is = ImportMatrixVal(value=np.array([
            [1.73191211247099e-15, 24.4948974278318, -24.4948974278318],
            [-0.925435413499285, 24.9445002597334, -24.0190648462341],
            [-1.84987984757817, 25.3673918959653, -23.5175120483872],
            [-2.77234338398183, 25.7631194935712, -22.9907761095894],
            [-3.69183822565029, 26.1312592975275, -22.4394210718773],
            [-4.60737975447626, 26.4714170945114, -21.8640373400352],
            [-5.51798758565886, 26.7832286350338, -21.2652410493749],
            [-6.42268661752422, 27.0663600234871, -20.6436734059628],
            [-7.32050807568877, 27.3205080756888, -20.0000000000000],
            [-8.21049055044714, 27.5454006435389, -19.3349100930918],
            [-9.09168102627374, 27.7407969064430, -18.6491158801692],
            [-9.96313590233562, 27.9064876291883, -17.9433517268527],
            [-10.8239220029239, 28.0422953859991, -17.2183733830752],
            [-11.6731175767218, 28.1480747505277, -16.4749571738058],
            [-12.5098132838389, 28.2237124515809, -15.7138991677421],
            [-13.3331131695549, 28.2691274944141, -14.9360143248592],
            [-14.1421356237309, 28.2842712474619, -14.1421356237310],
            [-14.9360143248592, 28.2691274944141, -13.3331131695549],
            [-15.7138991677420, 28.2237124515809, -12.5098132838389],
            [-16.4749571738058, 28.1480747505277, -11.6731175767219],
            [-17.2183733830752, 28.0422953859991, -10.8239220029240],
            [-17.9433517268527, 27.9064876291883, -9.96313590233564],
            [-18.6491158801692, 27.7407969064430, -9.09168102627375],
            [-19.3349100930918, 27.5454006435389, -8.21049055044716],
            [-20, 27.3205080756888, -7.32050807568879],
            [-20.6436734059628, 27.0663600234871, -6.42268661752424],
            [-21.2652410493749, 26.7832286350338, -5.51798758565888],
            [-21.8640373400352, 26.4714170945114, -4.60737975447627],
            [-22.4394210718772, 26.1312592975275, -3.69183822565031],
            [-22.9907761095894, 25.7631194935712, -2.77234338398184],
            [-23.5175120483872, 25.3673918959653, -1.84987984757819],
            [-24.0190648462341, 24.9445002597334, -0.925435413499304],
        ]))
        Nr = ImportMatrixVal(value=np.ones(Nt) * 400)
        Ir = ImportMatrixVal(value=np.zeros((Nt, 28)))
        time = ImportGenVectLin(start=0,
                                stop=1 / (400 / 60) / 24,
                                num=Nt,
                                endpoint=False)
        angle = ImportGenVectLin(start=0,
                                 stop=2 * np.pi,
                                 num=Na,
                                 endpoint=False)

        SPMSM_001.name = (
            "Default SPMSM machine"  # Rename the machine to have the good plot title
        )

        # Definition of the simulation
        simu = Simu1(name="Default simulation", machine=SPMSM_001)

        simu.input = InCurrent(
            Is=Is,
            Ir=Ir,  # zero current for the rotor
            Nr=Nr,
            angle_rotor=None,  # Will be computed
            time=time,
            angle=angle,
            angle_rotor_initial=0.39,
        )

        # Definition of the magnetic simulation
        simu.mag = MagFEMM(
            is_stator_linear_BH=2,
            is_rotor_linear_BH=2,
            is_symmetry_a=True,
            is_antiper_a=False,
        )

        simu.mag.sym_a = 4
        simu.struct = None

        # Default Output
        output = Output(simu=simu)

        # Modify magnet width and the slot opening height
        output.simu.machine.stator.slot.H0 = 0.001
        output.simu.machine.rotor.slot.magnet[0].Wmag *= 0.98

        # FIG21 Display default machine
        output.simu.machine.plot()
        fig = plt.gcf()
        fig.savefig(
            join(save_path, "fig_21_Machine_topology_before_optimization.png"))
        fig.savefig(
            join(save_path, "fig_21_Machine_topology_before_optimization.svg"),
            format="svg",
        )

        # -------------------- #
        # OPTIMIZATION PROBLEM #
        # -------------------- #

        # Objective functions

        def harm1(output):
            """Return the average torque opposite (opposite to be maximized)"""
            N = output.simu.input.time.num
            x = output.mag.Tem[:, 0]
            sp = np.fft.rfft(x)
            sp = 2 / N * np.abs(sp)
            return -sp[0] / 2

        def harm2(output):
            """Return the first torque harmonic """
            N = output.simu.input.time.num
            x = output.mag.Tem[:, 0]
            sp = np.fft.rfft(x)
            sp = 2 / N * np.abs(sp)
            return sp[1]

        objs = {
            "Opposite average torque (Nm)":
            OptiObjFunc(description="Maximization of the average torque",
                        func=harm1),
            "First torque harmonic (Nm)":
            OptiObjFunc(
                description="Minimization of the first torque harmonic",
                func=harm2),
        }

        # Design variables
        my_vars = {
            "design var 1":
            OptiDesignVar(
                name="output.simu.machine.stator.slot.W0",
                type_var="interval",
                space=[
                    0.2 * output.simu.machine.stator.slot.W2,
                    output.simu.machine.stator.slot.W2,
                ],
                function=lambda space: random.uniform(*space),
            ),
            "design var 2":
            OptiDesignVar(
                name="output.simu.machine.rotor.slot.magnet[0].Wmag",
                type_var="interval",
                space=[
                    0.5 * output.simu.machine.rotor.slot.W0,
                    0.99 * output.simu.machine.rotor.slot.W0,
                ],  # May generate error in FEMM
                function=lambda space: random.uniform(*space),
            ),
        }

        # Problem creation
        my_prob = OptiProblem(output=output, design_var=my_vars, obj_func=objs)

        # Solve problem with NSGA-II
        solver = OptiGenAlgNsga2Deap(problem=my_prob,
                                     size_pop=12,
                                     nb_gen=40,
                                     p_mutate=0.5)
        res = solver.solve()

        # ------------- #
        # PLOTS RESULTS #
        # ------------- #

        res.plot_generation()
        fig = plt.gcf()
        fig.savefig(join(save_path, "fig_20_Individuals_in_fitness_space.png"))
        fig.savefig(join(save_path, "fig_20_Individuals_in_fitness_space.svg"),
                    format="svg")

        res.plot_pareto()
        fig = plt.gcf()
        fig.savefig(join(save_path, "Pareto_front_in_fitness_space.png"))
        fig.savefig(join(save_path, "Pareto_front_in_fitness_space.svg"),
                    format="svg")

        # Extraction of best topologies for every objective
        pareto = res.get_pareto()  # Extraction of the pareto front

        out1 = [pareto[0]["output"], pareto[0]["fitness"]]  # First objective
        out2 = [pareto[0]["output"], pareto[0]["fitness"]]  # Second objective

        for pm in pareto:
            if pm["fitness"][0] < out1[1][0]:
                out1 = [pm["output"], pm["fitness"]]
            if pm["fitness"][1] < out2[1][1]:
                out2 = [pm["output"], pm["fitness"]]

        # Rename machine to modify the title
        name1 = "Machine that maximizes the average torque ({:.3f} Nm)".format(
            abs(out1[1][0]))
        out1[0].simu.machine.name = name1
        name2 = "Machine that minimizes the first torque harmonic ({:.4f}Nm)".format(
            abs(out1[1][1]))
        out2[0].simu.machine.name = name2

        # plot the machine
        out1[0].simu.machine.plot()
        fig = plt.gcf()
        fig.savefig(
            join(save_path, "fig_21_Topology_to_maximize_average_torque.png"),
            format="png",
        )
        fig.savefig(
            join(save_path, "fig_21_Topology_to_maximize_average_torque.svg"),
            format="svg",
        )

        out2[0].simu.machine.plot()
        fig = plt.gcf()
        fig.savefig(
            join(save_path,
                 "fig_21_Topology_to_minimize_first_torque_harmonic.png"),
            format="png",
        )
        fig.savefig(
            join(save_path,
                 "fig_21_Topology_to_minimize_first_torque_harmonic.svg"),
            format="svg",
        )