def test_Magnetic_FEMM_sym(self): """Test compute the Flux in FEMM, with and without symmetry and with MANATEE MMF analytical model """ out = Output(simu=simu) out.post.legend_name = "No symmetry" simu.run() out2 = Output(simu=simu_sym) out2.post.legend_name = "1/2 symmetry" out2.post.line_color = "r--" simu_sym.run() out3 = Output(simu=simu_load) out3.post.legend_name = "MANATEE MMF" out3.post.line_color = "g--" simu_load.run() # Plot the result by comparing the two simulation (sym / no sym) plt.close("all") out.plot_B_space(out_list=[out2]) fig = plt.gcf() fig.savefig(join(save_path, "test_EM_SCIM_NL_006_sym.png")) # Plot the result by comparing the two simulation (no sym / MANATEE) plt.close("all") out.plot_B_space(j_t0=0, is_deg=False, out_list=[out3]) fig = plt.gcf() fig.savefig(join(save_path, "test_EM_SCIM_NL_006_MMF.png"))
def test_Magnetic_FEMM(self): """Test compute the Flux in FEMM and import the mesh. """ out = Output(simu=simu) out.post.legend_name = "Slotless lamination" simu.run() # out.plot_mesh_field(meshsolution=out.mag.meshsolution, title="Permeability") out.plot_mesh_field( mesh=out.mag.meshsolution.mesh[0], title="Permeability", field=out.mag.meshsolution.solution[0].mu, ) fig = plt.gcf() fig.savefig(join(save_path, "test_CEFC_002_save_mag"))
def test_Magnetic_FEMM(self): """Test compute the Flux in FEMM without slots and without sliding band. """ out = Output(simu=simu) out.post.legend_name = "Slotless lamination" simu.run()
def test_Magnetic_FEMM(self): """Test compute the Flux in FEMM and import the mesh. """ out = Output(simu=simu) out.post.legend_name = "Slotless lamination" simu.run() out.plot_mesh_field( mesh=out.mag.meshsolution[0].mesh, field=out.mag.meshsolution[0].solution.get_field("mu"), title="Permeability", ) element_dict = ElementDict() element_dict.convert_element( out.mag.meshsolution[0].mesh.element) # It works !
def test_InCurrent_Error_test(self, test_dict): """Check that the input current raises the correct errors """ output = Output(simu=test_dict["test_obj"]) with self.assertRaises(InputError, msg="Expect: " + test_dict["exp"]) as context: output.simu.input.gen_input() self.assertEqual(test_dict["exp"], str(context.exception))
def test_Magnetic_FEMM_sym(self): """Test compute the Flux in FEMM, with and without symmetry """ out = Output(simu=simu) out.post.legend_name = "No symmetry" simu.run() out2 = Output(simu=simu_sym) out2.post.legend_name = "1/2 symmetry" out2.post.line_color = "r--" simu_sym.run() # Plot the result by comparing the two simulation plt.close("all") out.plot_B_space(out_list=[out2]) fig = plt.gcf() fig.savefig(join(save_path, "test_EM_IPMSM_FL_002_sym.png")) # Plot the surface magnetic forces plt.close("all") out.plot_force_space(j_t0=0, is_deg=False, out_list=[]) fig = plt.gcf() fig.savefig(join(save_path, "test_EM_IPMSM_FL_002_force.png"))
def test_Magnetic_FEMM_sym(self): """Test compute the Flux in FEMM, with and without symmetry and with MANATEE semi-analytical subdomain model """ out = Output(simu=simu) out.post.legend_name = "Symmetry" simu.run() out3 = Output(simu=simu_load) out3.post.legend_name = "MANATEE SDM" out3.post.line_color = "r--" simu_load.run() plt.close("all") out.plot_B_space(out_list=[out3]) fig = plt.gcf() fig.savefig(join(save_path, "test_EM_SPMSM_NL_001_SDM.png"))
def update(indiv): """Update the individual output after the mutation""" indiv.output = Output(simu=indiv.output.simu.as_dict()) for k in range(len(indiv.keys)): exec("indiv." + indiv.design_var[indiv.keys[k]].name + "=indiv[k]") indiv.is_simu_valid = False indiv.cstr_viol = 0 # Delete the fitness del indiv.fitness.values
def test_FEMM_sym(self): """Figure 9: Check that the FEMM can handle symmetry From pyleecan/Tests/Validation/Simulation/test_EM_SCIM_NL_006.py """ simu = Simu1(name="ICEM_2020", machine=SCIM_006) simu.machine.name = "fig_09_FEMM_sym" # Definition of the enforced output of the electrical module Nr = ImportMatrixVal(value=ones(1) * 1500) Is = ImportMatrixVal(value=array([[20, -10, -10]])) Ir = ImportMatrixVal(value=zeros((1, 28))) time = ImportGenVectLin(start=0, stop=0, num=1, endpoint=False) angle = ImportGenVectLin(start=0, stop=2 * pi, num=4096, endpoint=False) 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.2244, ) # Definition of the magnetic simulation # 2 sym + antiperiodicity = 1/4 Lamination simu.mag = MagFEMM( is_stator_linear_BH=2, is_rotor_linear_BH=2, is_symmetry_a=True, sym_a=2, is_antiper_a=True, ) # Stop after magnetic computation simu.struct = None # Run simulation out = Output(simu=simu) simu.run() # FEMM files (mesh and results) are available in Results folder copyfile( join(out.path_res, "Femm", "fig_09_FEMM_sym_model.ans"), join(save_path, "fig_09_FEMM_sym_model.ans"), ) copyfile( join(out.path_res, "Femm", "fig_09_FEMM_sym_model.fem"), join(save_path, "fig_09_FEMM_sym_model.fem"), )
def test_InCurrent_Ok(self): """Check that the input current can return a correct output """ test_obj = Simulation(machine=M3) output = Output(simu=test_obj) time = ImportGenVectLin(0, 1, 16) angle = ImportGenVectLin(0, 2 * pi, 20) Is = ImportGenMatrixSin(is_transpose=True) Is.init_vector(f=[2, 2, 2], A=[2, 2, 2], Phi=[pi / 2, 0, -pi / 2], N=16, Tf=1) S = sqrt(2) Is_exp = transpose( array([ [2, S, 0, -S, -2, -S, 0, S, 2, S, 0, -S, -2, -S, 0, S], [0, S, 2, S, 0, -S, -2, -S, 0, S, 2, S, 0, -S, -2, -S], [-2, -S, 0, S, 2, S, 0, -S, -2, -S, 0, S, 2, S, 0, -S], ])) Ir = ImportGenMatrixSin(is_transpose=True) Ir.init_vector(f=[2, 2], A=[2, 2], Phi=[0, -pi / 2], N=16, Tf=1) Ir_exp = transpose( array([ [0, S, 2, S, 0, -S, -2, -S, 0, S, 2, S, 0, -S, -2, -S], [-2, -S, 0, S, 2, S, 0, -S, -2, -S, 0, S, 2, S, 0, -S], ])) angle_rotor = ImportGenVectLin(0, 2 * pi, 16) Nr = ImportMatrixVal(value=ones(16) * 10) test_obj.input = InCurrent(time=time, angle=angle, Is=Is, Ir=Ir, angle_rotor=angle_rotor, Nr=Nr) test_obj.input.gen_input() assert_array_almost_equal(output.elec.time, linspace(0, 1, 16)) assert_array_almost_equal(output.elec.angle, linspace(0, 2 * pi, 20)) assert_array_almost_equal(output.elec.Is, Is_exp) assert_array_almost_equal(output.elec.Ir, Ir_exp) assert_array_almost_equal(output.elec.angle_rotor, linspace(0, 2 * pi, 16)) assert_array_almost_equal(output.elec.Nr, ones(16) * 10)
def create_indiv(create, output, design_var): """Create individual using DEAP tools Parameters ---------- creator : function function to create the individual output : pyleecan.Classes.Output output of the individual design_var : dict Design variables Returns: -------- indiv : list individual """ # Extract design variables keys = list(design_var.keys()) var = [] keys.sort() for key in keys: tmp = design_var[key].function(design_var[key].space) exec(design_var[key].name + "=tmp") var.append(tmp) ind = create(var) # Store the keys ind.keys = keys # Store the design variables ind.design_var = design_var # Store the simulation validity ind.is_simu_valid = False # Store the number of constraints violations ind.cstr_viol = 0 # Output with the design variables set ind.output = Output(simu=output.simu.as_dict()) return ind
def test_Magnetic_Phi0(self): """Test compute the Torque in FEMM as a function of Phi0 and compare the results with Syr-e r29 """ Tem_FEMM = zeros(Phi0.shape) for ii, I in enumerate(Is_list): simu.input.Is = ImportMatrixVal(value=I) out = Output(simu=simu) simu.run() print("test_EM_SynRM_FL_001: " + str(ii + 1) + "/16") Tem_FEMM[ii] = out.mag.Tem_av fig, ax = plt.subplots() ax.set_title("Torque as a function of Phi0") ax.set_xlabel("Phi0 [rad]") ax.set_ylabel("Torque [N.m]") ax.plot(Phi0, Tem_FEMM, "r", label="FEMM") ax.plot(Phi0, Tem, "b--", label="Syr-e") fig.savefig(join(save_path, "test_EM_SynRM_FL_001.png"))
def test_InCurrentDQ_Ok(self): """Check that the input current can return a correct output """ test_obj = Simulation(machine=M1) output = Output(simu=test_obj) time = ImportGenVectLin(0, 1, 7) angle = ImportGenVectLin(0, 2 * pi, 20) Is = ImportMatrixVal(value=transpose( array([ [2, 2, 2, 2, 2, 2, 2], [0, 0, 0, 0, 0, 0, 0], ]))) Is_exp = transpose( array([ [2, 1, -1, -2, -1, 1, 2], [-1, 1, 2, 1, -1, -2, -1], [-1, -2, -1, 1, 2, 1, -1], ])) zp = M1.stator.get_pole_pair_number() angle_rotor_initial = M1.comp_initial_angle() angle_rotor_exp = linspace(0, 2 * pi / zp, 7) + angle_rotor_initial Nr = ImportMatrixVal(value=ones(7) * 60 / zp) test_obj.input = InCurrentDQ(time=time, angle=angle, Is=Is, Ir=None, angle_rotor=None, Nr=Nr, angle_rotor_initial=angle_rotor_initial, rot_dir=1) test_obj.input.gen_input() assert_array_almost_equal(output.elec.time, linspace(0, 1, 7)) assert_array_almost_equal(output.elec.angle, linspace(0, 2 * pi, 20)) assert_array_almost_equal(output.elec.Is, Is_exp) assert_array_almost_equal(output.elec.angle_rotor, angle_rotor_exp) assert_array_almost_equal(output.elec.Nr, ones(7) * 60 / zp)
class OutputMulti(FrozenClass): # cf Methods.Output.OutputMulti.add_output if isinstance(add_output, ImportError): add_output = property(fget=lambda x: raise_( ImportError("Can't use OutputMulti method add_output: " + str( add_output)))) else: add_output = add_output # save method is available in all object save = save def __init__( self, output_ref=-1, outputs=list(), is_valid=[], design_var=[], design_var_names=[], init_dict=None, ): """Constructor of the class. Can be use in two ways : - __init__ (arg1 = 1, arg3 = 5) every parameters have name and default values for Matrix, None will initialise the property with an empty Matrix for pyleecan type, None will call the default constructor - __init__ (init_dict = d) d must be a dictionnary wiht every properties as keys ndarray or list can be given for Vector and Matrix object or dict can be given for pyleecan Object""" if output_ref == -1: output_ref = Output() if init_dict is not None: # Initialisation by dict check_init_dict( init_dict, [ "output_ref", "outputs", "is_valid", "design_var", "design_var_names" ], ) # Overwrite default value with init_dict content if "output_ref" in list(init_dict.keys()): output_ref = init_dict["output_ref"] if "outputs" in list(init_dict.keys()): outputs = init_dict["outputs"] if "is_valid" in list(init_dict.keys()): is_valid = init_dict["is_valid"] if "design_var" in list(init_dict.keys()): design_var = init_dict["design_var"] if "design_var_names" in list(init_dict.keys()): design_var_names = init_dict["design_var_names"] # Initialisation by argument self.parent = None # output_ref can be None, a Output object or a dict if isinstance(output_ref, dict): self.output_ref = Output(init_dict=output_ref) else: self.output_ref = output_ref # outputs can be None or a list of Output object self.outputs = list() if type(outputs) is list: for obj in outputs: if obj is None: # Default value self.outputs.append(Output()) elif isinstance(obj, dict): self.outputs.append(Output(init_dict=obj)) else: self.outputs.append(obj) elif outputs is None: self.outputs = list() else: self.outputs = outputs self.is_valid = is_valid self.design_var = design_var self.design_var_names = design_var_names # The class is frozen, for now it's impossible to add new properties self._freeze() def __str__(self): """Convert this objet in a readeable string (for print)""" OutputMulti_str = "" if self.parent is None: OutputMulti_str += "parent = None " + linesep else: OutputMulti_str += ("parent = " + str(type(self.parent)) + " object" + linesep) if self.output_ref is not None: tmp = (self.output_ref.__str__().replace(linesep, linesep + "\t").rstrip("\t")) OutputMulti_str += "output_ref = " + tmp else: OutputMulti_str += "output_ref = None" + linesep + linesep if len(self.outputs) == 0: OutputMulti_str += "outputs = []" + linesep for ii in range(len(self.outputs)): tmp = self.outputs[ii].__str__().replace(linesep, linesep + "\t") + linesep OutputMulti_str += "outputs[" + str( ii) + "] =" + tmp + linesep + linesep OutputMulti_str += ( "is_valid = " + linesep + str(self.is_valid).replace(linesep, linesep + "\t") + linesep) OutputMulti_str += ( "design_var = " + linesep + str(self.design_var).replace(linesep, linesep + "\t") + linesep) OutputMulti_str += ("design_var_names = " + linesep + str( self.design_var_names).replace(linesep, linesep + "\t") + linesep) return OutputMulti_str def __eq__(self, other): """Compare two objects (skip parent)""" if type(other) != type(self): return False if other.output_ref != self.output_ref: return False if other.outputs != self.outputs: return False if other.is_valid != self.is_valid: return False if other.design_var != self.design_var: return False if other.design_var_names != self.design_var_names: return False return True def as_dict(self): """Convert this objet in a json seriable dict (can be use in __init__) """ OutputMulti_dict = dict() if self.output_ref is None: OutputMulti_dict["output_ref"] = None else: OutputMulti_dict["output_ref"] = self.output_ref.as_dict() OutputMulti_dict["outputs"] = list() for obj in self.outputs: OutputMulti_dict["outputs"].append(obj.as_dict()) OutputMulti_dict["is_valid"] = self.is_valid OutputMulti_dict["design_var"] = self.design_var OutputMulti_dict["design_var_names"] = self.design_var_names # The class name is added to the dict fordeserialisation purpose OutputMulti_dict["__class__"] = "OutputMulti" return OutputMulti_dict def _set_None(self): """Set all the properties to None (except pyleecan object)""" if self.output_ref is not None: self.output_ref._set_None() for obj in self.outputs: obj._set_None() self.is_valid = None self.design_var = None self.design_var_names = None def _get_output_ref(self): """getter of output_ref""" return self._output_ref def _set_output_ref(self, value): """setter of output_ref""" check_var("output_ref", value, "Output") self._output_ref = value if self._output_ref is not None: self._output_ref.parent = self # Reference output of the multi simulation # Type : Output output_ref = property( fget=_get_output_ref, fset=_set_output_ref, doc=u"""Reference output of the multi simulation""", ) def _get_outputs(self): """getter of outputs""" for obj in self._outputs: if obj is not None: obj.parent = self return self._outputs def _set_outputs(self, value): """setter of outputs""" check_var("outputs", value, "[Output]") self._outputs = value for obj in self._outputs: if obj is not None: obj.parent = self # list of output from the multi-simulation # Type : [Output] outputs = property( fget=_get_outputs, fset=_set_outputs, doc=u"""list of output from the multi-simulation""", ) def _get_is_valid(self): """getter of is_valid""" return self._is_valid def _set_is_valid(self, value): """setter of is_valid""" check_var("is_valid", value, "list") self._is_valid = value # list to indicate if the corresponding output is valid # Type : list is_valid = property( fget=_get_is_valid, fset=_set_is_valid, doc=u"""list to indicate if the corresponding output is valid""", ) def _get_design_var(self): """getter of design_var""" return self._design_var def _set_design_var(self, value): """setter of design_var""" check_var("design_var", value, "list") self._design_var = value # list of design variables corresponding to the output # Type : list design_var = property( fget=_get_design_var, fset=_set_design_var, doc=u"""list of design variables corresponding to the output""", ) def _get_design_var_names(self): """getter of design_var_names""" return self._design_var_names def _set_design_var_names(self, value): """setter of design_var_names""" check_var("design_var_names", value, "list") self._design_var_names = value # list of str containing the design variables names sorted alphabetically # Type : list design_var_names = property( fget=_get_design_var_names, fset=_set_design_var_names, doc= u"""list of str containing the design variables names sorted alphabetically""", )
def __init__( self, output_ref=-1, outputs=list(), is_valid=[], design_var=[], design_var_names=[], init_dict=None, ): """Constructor of the class. Can be use in two ways : - __init__ (arg1 = 1, arg3 = 5) every parameters have name and default values for Matrix, None will initialise the property with an empty Matrix for pyleecan type, None will call the default constructor - __init__ (init_dict = d) d must be a dictionnary wiht every properties as keys ndarray or list can be given for Vector and Matrix object or dict can be given for pyleecan Object""" if output_ref == -1: output_ref = Output() if init_dict is not None: # Initialisation by dict check_init_dict( init_dict, [ "output_ref", "outputs", "is_valid", "design_var", "design_var_names" ], ) # Overwrite default value with init_dict content if "output_ref" in list(init_dict.keys()): output_ref = init_dict["output_ref"] if "outputs" in list(init_dict.keys()): outputs = init_dict["outputs"] if "is_valid" in list(init_dict.keys()): is_valid = init_dict["is_valid"] if "design_var" in list(init_dict.keys()): design_var = init_dict["design_var"] if "design_var_names" in list(init_dict.keys()): design_var_names = init_dict["design_var_names"] # Initialisation by argument self.parent = None # output_ref can be None, a Output object or a dict if isinstance(output_ref, dict): self.output_ref = Output(init_dict=output_ref) else: self.output_ref = output_ref # outputs can be None or a list of Output object self.outputs = list() if type(outputs) is list: for obj in outputs: if obj is None: # Default value self.outputs.append(Output()) elif isinstance(obj, dict): self.outputs.append(Output(init_dict=obj)) else: self.outputs.append(obj) elif outputs is None: self.outputs = list() else: self.outputs = outputs self.is_valid = is_valid self.design_var = design_var self.design_var_names = design_var_names # The class is frozen, for now it's impossible to add new properties self._freeze()
def test_zdt3(): # ### Defining reference Output # Definition of the enforced output of the electrical module Nt = 2 Nr = ImportMatrixVal(value=np.ones(Nt) * 3000) Is = ImportMatrixVal(value=np.array([ [6.97244193e-06, 2.25353053e02, -2.25353060e02], [-2.60215295e02, 1.30107654e02, 1.30107642e02], # [-6.97244208e-06, -2.25353053e02, 2.25353060e02], # [2.60215295e02, -1.30107654e02, -1.30107642e02], ])) Ir = ImportMatrixVal(value=np.zeros(30)) time = ImportGenVectLin(start=0, stop=0.015, num=Nt, endpoint=True) angle = ImportGenVectLin(start=0, stop=2 * np.pi, num=64, endpoint=False) # num=1024 # Definition of the simulation simu = Simu1(name="Test_machine", machine=SCIM_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.5216 + np.pi, ) # 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.Kmesh_fineness = 0.01 # simu.mag.Kgeo_fineness=0.02 simu.mag.sym_a = 4 simu.struct = None output = Output(simu=simu) # ### Design variable my_vars = {} for i in range(30): my_vars["var_" + str(i)] = OptiDesignVar( name="output.simu.input.Ir.value[" + str(i) + "]", type_var="interval", space=[0, 1], function=lambda space: np.random.uniform(*space), ) # ### Objectives objs = { "obj1": OptiObjFunc( description="Maximization of the torque average", func=lambda output: output.mag.Tem_av, ), "obj2": OptiObjFunc( description="Minimization of the torque ripple", func=lambda output: output.mag.Tem_rip, ), } # ### Evaluation def evaluate(output): x = output.simu.input.Ir.value f1 = lambda x: x[0] g = lambda x: 1 + (9 / 29) * np.sum(x[1:]) h = lambda f1, g: 1 - np.sqrt(f1 / g) - (f1 / g) * np.sin(10 * np.pi * f1) output.mag.Tem_av = f1(x) output.mag.Tem_rip = g(x) * h(f1(x), g(x)) # ### Defining the problem my_prob = OptiProblem(output=output, design_var=my_vars, obj_func=objs, eval_func=evaluate) solver = OptiGenAlgNsga2Deap( problem=my_prob, size_pop=40, nb_gen=100, p_mutate=0.5, ) res = solver.solve() def plot_pareto(self): """Plot every fitness values with the pareto front for 2 fitness Parameters ---------- self : OutputMultiOpti """ # TODO Add a feature to return the design_varibles of each indiv from the Pareto front # Get fitness and ngen is_valid = np.array(self.is_valid) fitness = np.array(self.fitness) ngen = np.array(self.ngen) # Keep only valid values indx = np.where(is_valid)[0] fitness = fitness[indx] ngen = ngen[indx] # Get pareto front pareto = list(np.unique(fitness, axis=0)) # Get dominated values to_remove = [] N = len(pareto) for i in range(N): for j in range(N): if all(pareto[j] <= pareto[i]) and any(pareto[j] < pareto[i]): to_remove.append(pareto[i]) break # Remove dominated values for i in to_remove: for l in range(len(pareto)): if all(i == pareto[l]): pareto.pop(l) break pareto = np.array(pareto) fig, axs = plt.subplots(1, 2, figsize=(16, 6)) # Plot Pareto front axs[0].scatter( pareto[:, 0], pareto[:, 1], facecolors="b", edgecolors="b", s=0.8, label="Pareto Front", ) axs[0].autoscale() axs[0].legend() axs[0].set_title("Pyleecan results") axs[0].set_xlabel(r"$f_1(x)$") axs[0].set_ylabel(r"$f_2(x)$") try: img_to_find = img.imread( "pyleecan\\Tests\\Validation\\Optimization\\zdt3.jpg", format="jpg") axs[1].imshow(img_to_find, aspect="auto") axs[1].axis("off") axs[1].set_title("Pareto front of the problem") except TypeError: print("Pillow is needed to import jpg files") return fig fig = plot_pareto(res) plt.savefig("pyleecan\\Tests\\Results\\Validation\\test_zdt3.png")
def test_Magnetic_FEMM(self): """Test compute the Flux in FEMM and import the mesh. """ out = Output(simu=simu) out.post.legend_name = "Slotless lamination" simu.run() out.plot_mesh(mesh=out.mag.meshsolution.mesh[0], title="FEA Mesh") # out.plot_mesh_field(meshsolution=out.mag.meshsolution, title="Permeability") out.plot_mesh_field( mesh=out.mag.meshsolution.mesh[0], title="Permeability", field=out.mag.meshsolution.solution[0].face["mu"], ) fig = plt.gcf() fig.savefig(join(save_path, "test_CEFC_002_save_mag")) # Test save with MeshSolution object in out out.save(save_path=save_path) load_path = join(save_path, "Output.json") # Test to load the Meshsolution object (inside the output): with open(load_path) as json_file: json_tmp = json.load(json_file) FEMM = Output(init_dict=json_tmp) # To test that the "mu" is still a ndarray after saving and loading out.plot_mesh_field( mesh=FEMM.mag.meshsolution.mesh[0], title="Permeability", field=FEMM.mag.meshsolution.solution[0].face["mu"], )
def test_ecc_FEMM(self): """Figure 19: transfrom_list in FEMM for eccentricities """ simu = Simu1(name="ICEM_2020", machine=SPMSM_015) simu.machine.name = "fig_19_Transform_list" # Modify stator Rext to get more convincing translation SPMSM_015.stator.Rext = SPMSM_015.stator.Rext * 0.9 gap = SPMSM_015.comp_width_airgap_mec() # Definition of the enforced output of the electrical module Nr = ImportMatrixVal(value=ones(1) * 3000) Is = ImportMatrixVal(value=array([[0, 0, 0]])) time = ImportGenVectLin(start=0, stop=0, num=1, endpoint=True) angle = ImportGenVectLin(start=0, stop=2 * 2 * pi / 9, num=2043, endpoint=False) simu.input = InCurrent( Is=Is, Ir=None, # No winding on the rotor Nr=Nr, angle_rotor=None, time=time, angle=angle, angle_rotor_initial=0, ) # Definition of the magnetic simulation (is_mmfr=False => no flux from the magnets) simu.mag = MagFEMM( is_stator_linear_BH=0, is_rotor_linear_BH=0, is_sliding_band=False, # Ecc => No sliding band is_symmetry_a=False, # No sym is_mmfs=False, is_get_mesh=True, is_save_FEA=True, sym_a=1, ) simu.struct = None # Set two transformations # First rotate 3rd Magnet transform_list = [{ "type": "rotate", "value": 0.08, "label": "MagnetRotorRadial_S_R0_T0_S3" }] # Then Translate the rotor transform_list.append({ "type": "translate", "value": gap * 0.75, "label": "Rotor" }) simu.mag.transform_list = transform_list # Run the simulation out = Output(simu=simu) simu.run() # FEMM files (mesh and results) are available in Results folder copyfile( join(out.path_res, "Femm", "fig_19_Transform_list_model.ans"), join(save_path, "fig_19_Transform_list_model.ans"), ) copyfile( join(out.path_res, "Femm", "fig_19_Transform_list_model.fem"), join(save_path, "fig_19_Transform_list_model.fem"), ) # Plot, check, save out.plot_mesh(mesh=out.mag.meshsolution.mesh[0], title="FEMM Mesh") fig = plt.gcf() fig.savefig(join(save_path, "fig_19_transform_list.png")) fig.savefig(join(save_path, "fig_19_transform_list.svg"), format="svg")
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", )
class OptiProblem(FrozenClass): """Multi-objectives optimization problem with some constraints""" VERSION = 1 # cf Methods.Optimization.OptiProblem.eval_pb if isinstance(eval_pb, ImportError): eval_pb = property(fget=lambda x: raise_( ImportError("Can't use OptiProblem method eval_pb: " + str(eval_pb) ))) else: eval_pb = eval_pb # save method is available in all object save = save def __init__( self, output=-1, design_var=dict(), obj_func=dict(), eval_func=None, constraint=dict(), init_dict=None, ): """Constructor of the class. Can be use in two ways : - __init__ (arg1 = 1, arg3 = 5) every parameters have name and default values for Matrix, None will initialise the property with an empty Matrix for pyleecan type, None will call the default constructor - __init__ (init_dict = d) d must be a dictionnary wiht every properties as keys ndarray or list can be given for Vector and Matrix object or dict can be given for pyleecan Object""" if output == -1: output = Output() if init_dict is not None: # Initialisation by dict check_init_dict( init_dict, [ "output", "design_var", "obj_func", "eval_func", "constraint" ], ) # Overwrite default value with init_dict content if "output" in list(init_dict.keys()): output = init_dict["output"] if "design_var" in list(init_dict.keys()): design_var = init_dict["design_var"] if "obj_func" in list(init_dict.keys()): obj_func = init_dict["obj_func"] if "eval_func" in list(init_dict.keys()): eval_func = init_dict["eval_func"] if "constraint" in list(init_dict.keys()): constraint = init_dict["constraint"] # Initialisation by argument self.parent = None # output can be None, a Output object or a dict if isinstance(output, dict): self.output = Output(init_dict=output) else: self.output = output # design_var can be None or a dict of OptiDesignVar object self.design_var = dict() if type(design_var) is dict: for key, obj in design_var.items(): if isinstance(obj, dict): self.design_var[key] = OptiDesignVar(init_dict=obj) else: self.design_var[key] = obj elif design_var is None: self.design_var = dict() else: self.design_var = design_var # Should raise an error # obj_func can be None or a dict of OptiObjFunc object self.obj_func = dict() if type(obj_func) is dict: for key, obj in obj_func.items(): if isinstance(obj, dict): self.obj_func[key] = OptiObjFunc(init_dict=obj) else: self.obj_func[key] = obj elif obj_func is None: self.obj_func = dict() else: self.obj_func = obj_func # Should raise an error self.eval_func = eval_func # constraint can be None or a dict of OptiConstraint object self.constraint = dict() if type(constraint) is dict: for key, obj in constraint.items(): if isinstance(obj, dict): self.constraint[key] = OptiConstraint(init_dict=obj) else: self.constraint[key] = obj elif constraint is None: self.constraint = dict() else: self.constraint = constraint # Should raise an error # The class is frozen, for now it's impossible to add new properties self._freeze() def __str__(self): """Convert this objet in a readeable string (for print)""" OptiProblem_str = "" if self.parent is None: OptiProblem_str += "parent = None " + linesep else: OptiProblem_str += ("parent = " + str(type(self.parent)) + " object" + linesep) if self.output is not None: tmp = self.output.__str__().replace(linesep, linesep + "\t").rstrip("\t") OptiProblem_str += "output = " + tmp else: OptiProblem_str += "output = None" + linesep + linesep if len(self.design_var) == 0: OptiProblem_str += "design_var = dict()" + linesep for key, obj in self.design_var.items(): tmp = (self.design_var[key].__str__().replace( linesep, linesep + "\t") + linesep) OptiProblem_str += "design_var[" + key + "] =" + tmp + linesep + linesep if len(self.obj_func) == 0: OptiProblem_str += "obj_func = dict()" + linesep for key, obj in self.obj_func.items(): tmp = ( self.obj_func[key].__str__().replace(linesep, linesep + "\t") + linesep) OptiProblem_str += "obj_func[" + key + "] =" + tmp + linesep + linesep if self._eval_func[1] is None: OptiProblem_str += "eval_func = " + str(self._eval_func[1]) else: OptiProblem_str += ("eval_func = " + linesep + str(self._eval_func[1]) + linesep + linesep) if len(self.constraint) == 0: OptiProblem_str += "constraint = dict()" + linesep for key, obj in self.constraint.items(): tmp = (self.constraint[key].__str__().replace( linesep, linesep + "\t") + linesep) OptiProblem_str += "constraint[" + key + "] =" + tmp + linesep + linesep return OptiProblem_str def __eq__(self, other): """Compare two objects (skip parent)""" if type(other) != type(self): return False if other.output != self.output: return False if other.design_var != self.design_var: return False if other.obj_func != self.obj_func: return False if other.eval_func != self.eval_func: return False if other.constraint != self.constraint: return False return True def as_dict(self): """Convert this objet in a json seriable dict (can be use in __init__) """ OptiProblem_dict = dict() if self.output is None: OptiProblem_dict["output"] = None else: OptiProblem_dict["output"] = self.output.as_dict() OptiProblem_dict["design_var"] = dict() for key, obj in self.design_var.items(): OptiProblem_dict["design_var"][key] = obj.as_dict() OptiProblem_dict["obj_func"] = dict() for key, obj in self.obj_func.items(): OptiProblem_dict["obj_func"][key] = obj.as_dict() if self.eval_func is None: OptiProblem_dict["eval_func"] = None else: OptiProblem_dict["eval_func"] = [ dumps(self._eval_func[0]).decode("ISO-8859-2"), self._eval_func[1], ] OptiProblem_dict["constraint"] = dict() for key, obj in self.constraint.items(): OptiProblem_dict["constraint"][key] = obj.as_dict() # The class name is added to the dict fordeserialisation purpose OptiProblem_dict["__class__"] = "OptiProblem" return OptiProblem_dict def _set_None(self): """Set all the properties to None (except pyleecan object)""" if self.output is not None: self.output._set_None() for key, obj in self.design_var.items(): obj._set_None() for key, obj in self.obj_func.items(): obj._set_None() self.eval_func = None for key, obj in self.constraint.items(): obj._set_None() def _get_output(self): """getter of output""" return self._output def _set_output(self, value): """setter of output""" check_var("output", value, "Output") self._output = value if self._output is not None: self._output.parent = self # Default output to define the default simulation. # Type : Output output = property( fget=_get_output, fset=_set_output, doc=u"""Default output to define the default simulation. """, ) def _get_design_var(self): """getter of design_var""" for key, obj in self._design_var.items(): if obj is not None: obj.parent = self return self._design_var def _set_design_var(self, value): """setter of design_var""" check_var("design_var", value, "{OptiDesignVar}") self._design_var = value # Dict of design variables # Type : {OptiDesignVar} design_var = property(fget=_get_design_var, fset=_set_design_var, doc=u"""Dict of design variables""") def _get_obj_func(self): """getter of obj_func""" for key, obj in self._obj_func.items(): if obj is not None: obj.parent = self return self._obj_func def _set_obj_func(self, value): """setter of obj_func""" check_var("obj_func", value, "{OptiObjFunc}") self._obj_func = value # Dict of objective functions # Type : {OptiObjFunc} obj_func = property(fget=_get_obj_func, fset=_set_obj_func, doc=u"""Dict of objective functions""") def _get_eval_func(self): """getter of eval_func""" return self._eval_func[0] def _set_eval_func(self, value): """setter of eval_func""" try: check_var("eval_func", value, "list") except CheckTypeError: check_var("eval_func", value, "function") if isinstance(value, list): # Load function from saved dict self._eval_func = [loads(value[0].encode("ISO-8859-2")), value[1]] elif value is None: self._eval_func = [None, None] elif callable(value): self._eval_func = [value, getsource(value)] else: raise TypeError( "Expected function or list from a saved file, got: " + str(type(value))) # Function to evaluate before computing obj function and constraints # Type : function eval_func = property( fget=_get_eval_func, fset=_set_eval_func, doc= u"""Function to evaluate before computing obj function and constraints""", ) def _get_constraint(self): """getter of constraint""" for key, obj in self._constraint.items(): if obj is not None: obj.parent = self return self._constraint def _set_constraint(self, value): """setter of constraint""" check_var("constraint", value, "{OptiConstraint}") self._constraint = value # Dict containing the constraints # Type : {OptiConstraint} constraint = property( fget=_get_constraint, fset=_set_constraint, doc=u"""Dict containing the constraints """, )
def solve(self): """Method to perform NSGA-II using DEAP tools Parameters ---------- self : OptiGenAlgNsga2Deap Solver to perform NSGA-II Returns ------- multi_output : OutputMultiOpti class containing the results """ if self.problem == None: raise MissingProblem( "The problem has not been defined, please add a problem to OptiGenAlgNsga2Deap" ) # Create the toolbox self.create_toolbox() # Add the design variable names self.multi_output.design_var_names = list(self.problem.design_var.keys()) self.multi_output.design_var_names.sort() # Add the fitness names self.multi_output.fitness_names = list(self.problem.obj_func.keys()) self.multi_output.fitness_names.sort() # Add the reference output to multi_output self.multi_output.output_ref = self.problem.output # Create the first population pop = self.toolbox.population(self.size_pop) # Start of the evaluation of the generation time_start_gen = datetime.now().strftime("%H:%M:%S") # Evaluate the population nb_error = 0 for i in range(0, self.size_pop): nb_error += evaluate(self, pop[i]) print( "\r{} gen {:>5}: {:>5.2f}%, {:>4} errors.".format( time_start_gen, 0, (i + 1) * 100 / self.size_pop, nb_error), end="", ) # Check the constraints violation nb_infeasible = 0 if len(self.problem.constraint) > 0: for indiv in pop: nb_infeasible += check_cstr(self, indiv) print("\r{} gen {:>5}: 100%, {:>4} errors,{:>4} infeasible.".format( time_start_gen, 0, nb_error, nb_infeasible - nb_error)) # Add pop to OutputMultiOpt for indiv in pop: # Check that at every fitness values is different from inf is_valid = indiv.fitness.valid and indiv.is_simu_valid and indiv.cstr_viol == 0 # Add the indiv to the multi_output self.multi_output.add_evaluation(indiv.output, is_valid, list(indiv), indiv.fitness.values, 0) if self.selector == None: pop = selNSGA2(pop, self.size_pop) else: parents = self.selector(pop, self.size_pop) ############################ # LOOP FOR EACH GENERATION # ############################ for ngen in range(1, self.nb_gen): time_start_gen = datetime.now().strftime("%H:%M:%S") # Extracting parents using parents = tournamentDCD(pop, self.size_pop) # Copy new indivuals children = [] for indiv in parents: child = self.toolbox.individual() for i in range(len(indiv)): child[i] = deepcopy(indiv[i]) child.output = Output(init_dict=indiv.output.as_dict()) child.fitness = deepcopy(indiv.fitness) children.append(child) for indiv1, indiv2 in zip(children[::2], children[::-2]): # Crossover is_cross = False if random.random() < self.p_cross: is_cross = True if self.crossover == None: cxOnePoint(indiv1, indiv2) # Mutation is_mutation = self.mutate(indiv1) if is_cross or is_mutation: update(indiv1) is_mutation = self.mutate(indiv2) if is_cross or is_mutation: update(indiv2) # Evaluate the children to_eval = [] for indiv in children: if indiv.fitness.valid == False: to_eval.append(indiv) nb_error = 0 for i in range(len(to_eval)): nb_error += evaluate(self, to_eval[i]) print( "\r{} gen {:>5}: {:>5.2f}%, {:>4} errors.".format( time_start_gen, ngen, (i + 1) * 100 / len(to_eval), nb_error), end="", ) # Check the constraints violation nb_infeasible = 0 if len(self.problem.constraint) > 0: for indiv in to_eval: nb_infeasible += check_cstr(self, indiv) print("\r{} gen {:>5}: 100%, {:>4} errors,{:>4} infeasible.".format( time_start_gen, ngen, nb_error, nb_infeasible - nb_error)) # Add children to OutputMultiOpti for indiv in children: # Check that at every fitness values is different from inf is_valid = (indiv.fitness.valid and indiv.is_simu_valid and indiv.cstr_viol == 0) # Add the indiv to the multi_output self.multi_output.add_evaluation( indiv.output, is_valid, list(indiv), indiv.fitness.values, ngen, ) # Sorting the population according to NSGA2 if self.selector == None: pop = selNSGA2(pop + children, self.size_pop) else: pop = self.selector(pop, self.size_pop) return self.multi_output