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))
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)
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)
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())
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", )