def comp_force(self, output): """Compute the air-gap surface force based on Maxwell Tensor (MT). Parameters ---------- self : ForceMT A ForceMT object output : Output an Output object (to update) """ mu_0 = 4 * pi * 1e-7 # Load magnetic flux Brphiz = output.mag.B.get_rphiz_along("time", "angle") Br = Brphiz["radial"] Bt = Brphiz["tangential"] Bz = Brphiz["axial"] # Compute AGSF with MT formula Prad = (Br * Br - Bt * Bt - Bz * Bz) / (2 * mu_0) Ptan = Br * Bt / mu_0 Pz = Br * Bz / mu_0 # Store the results components = {} if not np_all((Prad == 0)): Prad_data = DataTime( name="Airgap radial surface force", unit="N/m2", symbol="P_r", axes=list(output.mag.B.components.values())[0].axes, values=Prad, ) components["radial"] = Prad_data if not np_all((Ptan == 0)): Ptan_data = DataTime( name="Airgap tangential surface force", unit="N/m2", symbol="P_t", axes=list(output.mag.B.components.values())[0].axes, values=Ptan, ) components["tangential"] = Ptan_data if not np_all((Pz == 0)): Pz_data = DataTime( name="Airgap axial surface force", unit="N/m2", symbol="P_z", axes=list(output.mag.B.components.values())[0].axes, values=Pz, ) components["axial"] = Pz_data output.force.P = VectorField(name="Magnetic airgap surface force", symbol="P", components=components)
def build_solution_vector(field, axis_list, name="", symbol="", unit="", is_real=True): """Build a SolutionVector object Parameters ---------- field : ndarray a vector vield axis_list : list a list of SciDataTool axis Returns ------- solution: SolutionVector a SolutionVector object """ components = {} x_data = DataTime( name=name, unit=unit, symbol=symbol + "x", axes=axis_list, values=field[..., 0], is_real=is_real, ) components["comp_x"] = x_data y_data = DataTime( name=name, unit=unit, symbol=symbol + "y", axes=axis_list, values=field[..., 1], is_real=is_real, ) components["comp_y"] = y_data if field.shape[-1] == 3 and not np_all((field[..., 2] == 0)): z_data = DataTime( name=name, unit=unit, symbol=symbol + "z", axes=axis_list, values=field[..., 2], is_real=is_real, ) components["comp_z"] = z_data vectorfield = VectorField(name=name, symbol=symbol, components=components) solution = SolutionVector(field=vectorfield, label=symbol) return solution
def comp_force_nodal(self, output, axes_dict): """Run the nodal forces calculation based on a tensor. from publications: Parameters ---------- self : ForceTensor A ForceTensor object output : Output an Output object (to update) """ dim = 2 Time = axes_dict["Time"] Nt_tot = Time.get_length() # Number of time step meshsolution_mag = output.mag.meshsolution # Comes from FEMM simulation # Select the target group (stator, rotor ...) meshsolution_group = meshsolution_mag.get_group(self.group) # TODO before: Check if is_same_mesh is True mesh = meshsolution_group.get_mesh() # New meshsolution object for output, that could be different from the one inputed meshsolution = MeshSolution(mesh=[mesh.copy()], is_same_mesh=True, dimension=dim) # Load magnetic flux B and H and mu objects B_sol = meshsolution_group.get_solution(label="B") H_sol = meshsolution_group.get_solution(label="H") mu_sol = meshsolution_group.get_solution(label="\mu") # Import time vector from Time Data object if self.is_periodicity_t is not None: is_periodicity_t = self.is_periodicity_t is_periodicity_t, is_antiper_t = Time.get_periodicity() time = Time.get_values( is_oneperiod=is_periodicity_t, is_antiperiod=is_antiper_t and is_periodicity_t, ) # Load magnetic flux B and H of size (Nt_tot, nb_elem, dim) and mu (Nt_tot, nb_elem) resultB = B_sol.field.get_xyz_along( "indice", "time=axis_data", axis_data={"time": time}, ) indice = resultB["indice"] # Store elements indices Bx = resultB["comp_x"] By = resultB["comp_y"] B = np.stack((Bx, By), axis=2) resultH = H_sol.field.get_xyz_along( "indice", "time=axis_data", axis_data={"time": time}, ) Hx = resultH["comp_x"] Hy = resultH["comp_y"] H = np.stack((Hx, Hy), axis=2) resultmu = mu_sol.field.get_along( "indice", "time=axis_data", axis_data={"time": time}, ) mu = resultmu["\\mu"] # Move time axis at the end for clarity purpose B = np.moveaxis(B, 0, -1) H = np.moveaxis(H, 0, -1) mu = np.moveaxis(mu, 0, -1) # Loop on elements and nodes for nodal forces f, connect = self.element_loop(mesh, B, H, mu, indice, dim, Nt_tot) indices_nodes = np.sort(np.unique(connect)) Indices_Point = Data1D(name="indice", values=indices_nodes, is_components=True) # Time axis goes back to first axis f = np.moveaxis(f, -1, 0) components = {} fx_data = DataTime( name="Nodal force (x)", unit="N", symbol="Fx", axes=[Time, Indices_Point], values=f[..., 0], ) components["comp_x"] = fx_data fy_data = DataTime( name="Nodal force (y)", unit="N", symbol="Fy", axes=[Time, Indices_Point], values=f[..., 1], ) components["comp_y"] = fy_data vec_force = VectorField(name="Nodal forces", symbol="F", components=components) solforce = SolutionVector(field=vec_force, type_cell="node", label="F") meshsolution.solution.append(solforce) out_dict = dict() out_dict["meshsolution"] = meshsolution return out_dict
def build_meshsolution(self): """Get the mesh and solution data from an Elmer VTU results file Parameters ---------- self : ElmerResultsVTU a ElmerResultsVTU object Returns ------- success: bool Information if meshsolution could be created """ # create meshsolution meshsol = MeshSolution(label=self.label) # get the mesh save_path, fn = split(self.file_path) file_name, file_ext = splitext(fn) if file_ext != ".vtu": raise ElmerResultsVTUError( "ElmerResultsVTU: Results file must be of type VTU.") meshvtk = MeshVTK(path=save_path, name=file_name, format="vtu") # TODO maybe convert to MeshMat before meshsol.mesh = [meshvtk] # get the solution data on the mesh meshsolvtu = read(self.file_path) pt_data = meshsolvtu.point_data # point_data is of type dict # setup axes indices = arange(meshsolvtu.points.shape[0]) Indices = Data1D(name="indice", values=indices, is_components=True) # store only data from store dict if available comp_ext = ["x", "y", "z"] sol_list = [] # list of solutions for key, value in pt_data.items(): # check if value should be stored if key in self.store_dict.keys(): siz = value.shape[1] # only regard max. 3 components if siz > 3: logger.warning( f'ElmerResultsVTU.build_meshsolution(): size of data "{key}" > 3' + " - " + "Data will be truncated.") siz = 3 components = [] comp_name = [] # loop though components for i in range(siz): # setup name, symbol and component name extension if siz == 1: ext = "" else: ext = comp_ext[i] # setup data object data = DataTime( name=self.store_dict[key]["name"] + " " + ext, unit=self.store_dict[key]["unit"], symbol=self.store_dict[key]["symbol"] + ext, axes=[Indices], values=value[:, i], normalizations={ "ref": Norm_ref(ref=self.store_dict[key]["norm"]) }, ) components.append(data) comp_name.append("comp_" + ext) # setup solution depending on number of field components if siz == 1: field = components[0] sol_list.append( SolutionData( field=field, type_cell="point", label=self.store_dict[key]["symbol"], )) else: comps = {} for i in range(siz): comps[comp_name[i]] = components[i] field = VectorField( name=self.store_dict[key]["name"], symbol=self.store_dict[key]["symbol"], components=comps, ) sol_list.append( SolutionVector( field=field, type_cell="point", label=self.store_dict[key]["symbol"], )) meshsol.solution = sol_list return meshsol
def build_meshsolution(self, Nt_tot, meshFEMM, Time, B, H, mu, groups): """Build the MeshSolution objets from FEMM outputs. Parameters ---------- self : MagFEMM a MagFEMM object is_get_mesh : bool 1 to load the mesh and solution into the simulation is_save_FEA : bool 1 to save the mesh and solution into a .json file j_t0 : int Targeted time step Returns ------- meshsol: MeshSolution a MeshSolution object with FEMM outputs at every time step """ sollist = list() cond = self.is_sliding_band or Nt_tot == 1 if cond: indices_cell = meshFEMM[0].cell["triangle"].indice Direction = Data1D(name="direction", values=["x", "y", "z"], is_components=True) Indices_Cell = Data1D(name="indice", values=indices_cell, is_components=True) Nodirection = Data1D(name="direction", values=["scalar"], is_components=False) # Store the results for B components = {} Bx_data = DataTime( name="Magnetic Flux Density Bx", unit="T", symbol="Bx", axes=[Time, Indices_Cell], values=B[:, :, 0], ) components["x"] = Bx_data By_data = DataTime( name="Magnetic Flux Density By", unit="T", symbol="By", axes=[Time, Indices_Cell], values=B[:, :, 1], ) components["y"] = By_data if not np.all((B[:, :, 2] == 0)): Bz_data = DataTime( name="Magnetic Flux Density Bz", unit="T", symbol="Bz", axes=[Time, Indices_Cell], values=B[:, :, 2], ) components["z"] = Bz_data solB = VectorField(name="Magnetic Flux Density", symbol="B", components=components) # Store the results for H componentsH = {} Hx_data = DataTime( name="Magnetic Field Hx", unit="A/m", symbol="Hx", axes=[Time, Indices_Cell], values=H[:, :, 0], ) componentsH["x"] = Hx_data Hy_data = DataTime( name="Magnetic Field Hy", unit="A/m", symbol="Hy", axes=[Time, Indices_Cell], values=H[:, :, 1], ) componentsH["y"] = Hy_data if not np.all((H[:, :, 2] == 0)): Hz_data = DataTime( name="Magnetic Field Hz", unit="A/m", symbol="Hz", axes=[Time, Indices_Cell], values=H[:, :, 2], ) componentsH["z"] = Hz_data solH = VectorField(name="Magnetic Field", symbol="H", components=componentsH) solmu = DataTime( name="Magnetic Permeability", unit="H/m", symbol="\mu", axes=[Time, Indices_Cell], values=mu, ) sollist.append( SolutionVector(field=solB, type_cell="triangle", label="B")) # Face solution sollist.append( SolutionVector(field=solH, type_cell="triangle", label="H")) sollist.append( SolutionData(field=solmu, type_cell="triangle", label="\mu")) meshsol = MeshSolution( label="FEMM_magnetotatic", mesh=meshFEMM, solution=sollist, is_same_mesh=cond, dimension=2, ) meshsol.group = groups[0] return meshsol
def get_meshsolution(self, output): """Build the MeshSolution objects from the FEA outputs. Parameters ---------- self : MagElmer a MagElmer object output: Output An Output object Returns ------- meshsol: MeshSolution a MeshSolution object with Elmer outputs at every time step """ project_name = self.get_path_save_fea(output) elmermesh_folder = project_name meshsol = MeshSolution(label="Elmer MagnetoDynamics") if not self.is_get_mesh or not self.is_save_FEA: self.get_logger().info( "MagElmer: MeshSolution is not stored by request.") return False meshvtk = MeshVTK(path=elmermesh_folder, name="step_t0002", format="vtu") meshsol.mesh = [meshvtk] result_filename = join(elmermesh_folder, "step_t0002.vtu") meshsolvtu = read(result_filename) #pt_data = meshsolvtu.point_data cell_data = meshsolvtu.cell_data #indices = arange(meshsolvtu.points.shape[0]) indices = arange(meshsolvtu.cells[0].data.shape[0] + meshsolvtu.cells[1].data.shape[0]) Indices = Data1D(name="indice", values=indices, is_components=True) # store_dict = { # "magnetic vector potential": { # "name": "Magnetic Vector Potential A", # "unit": "Wb", # "symbol": "A", # "norm": 1, # }, # "magnetic flux density": { # "name": "Magnetic Flux Density B", # "unit": "T", # "symbol": "B", # "norm": 1, # }, # "magnetic field strength": { # "name": "Magnetic Field H", # "unit": "A/m", # "symbol": "H", # "norm": 1, # }, # "current density": { # "name": "Current Density J", # "unit": "A/mm2", # "symbol": "J", # "norm": 1, # } # } store_dict = { "magnetic flux density e": { "name": "Magnetic Flux Density B", "unit": "T", "symbol": "B", "norm": 1, }, "magnetic vector potential e": { "name": "Magnetic Vector Potential A", "unit": "Wb", "symbol": "A", "norm": 1, }, "magnetic field strength e": { "name": "Magnetic Field H", "unit": "A/m", "symbol": "H", "norm": 1, }, "current density e": { "name": "Current Density J", "unit": "A/mm2", "symbol": "J", "norm": 1, }, } comp_ext = ["x", "y", "z"] sol_list = [] #for key, value in pt_data.items(): for key, value in cell_data.items(): if key in store_dict.keys(): #siz = value.shape[1] siz = value[0].shape[1] if siz > 3: print("Some Message") siz = 3 components = [] comp_name = [] values = np_append(value[0], value[1], axis=0) for i in range(siz): if siz == 1: ext = "" else: ext = comp_ext[i] data = DataTime( name=store_dict[key]["name"] + ext, unit=store_dict[key]["unit"], symbol=store_dict[key]["symbol"] + ext, axes=[Indices], #values=value[:, i], values=values[:, i], normalizations={"ref": store_dict[key]["norm"]}, ) components.append(data) comp_name.append("comp_" + ext) if siz == 1: field = components[0] sol_list.append( SolutionData( field=field, #type_cell="point", type_cell="triangle", label=store_dict[key]["symbol"], )) else: comps = {} for i in range(siz): comps[comp_name[i]] = components[i] field = VectorField(name=store_dict[key]["name"], symbol=store_dict[key]["symbol"], components=comps) sol_list.append( SolutionVector( field=field, #type_cell="point", type_cell="triangle", label=store_dict[key]["symbol"], )) meshsol.solution = sol_list output.mag.meshsolution = meshsol return True
def solve_FEMM(self, femm, output, sym): # Get time and angular axes Angle_comp, Time_comp = self.get_axes(output) _, Time_comp_Tem = self.get_axes(output, is_remove_apert=True) # Check if the angular axis is anti-periodic _, is_antiper_a = Angle_comp.get_periodicity() # Import angular vector from Data object angle = Angle_comp.get_values( is_oneperiod=self.is_periodicity_a, is_antiperiod=is_antiper_a and self.is_periodicity_a, ) # Number of angular steps Na_comp = angle.size # Check if the angular axis is anti-periodic _, is_antiper_t = Time_comp.get_periodicity() # Number of time steps Nt_comp = Time_comp.get_length( is_oneperiod=True, is_antiperiod=is_antiper_t and self.is_periodicity_t, ) # Loading parameters for readibility L1 = output.simu.machine.stator.comp_length() save_path = self.get_path_save(output) FEMM_dict = output.mag.FEMM_dict if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): qs = output.simu.machine.stator.winding.qs # Winding phase number Npcpp = output.simu.machine.stator.winding.Npcpp Phi_wind_stator = zeros((Nt_comp, qs)) else: Phi_wind_stator = None # Create the mesh femm.mi_createmesh() # Initialize results matrix Br = zeros((Nt_comp, Na_comp)) Bt = zeros((Nt_comp, Na_comp)) Tem = zeros((Nt_comp)) Rag = output.simu.machine.comp_Rgap_mec() # Compute the data for each time step for ii in range(Nt_comp): self.get_logger().debug("Solving step " + str(ii + 1) + " / " + str(Nt_comp)) # Update rotor position and currents update_FEMM_simulation( femm=femm, output=output, materials=FEMM_dict["materials"], circuits=FEMM_dict["circuits"], is_mmfs=self.is_mmfs, is_mmfr=self.is_mmfr, j_t0=ii, is_sliding_band=self.is_sliding_band, ) # try "previous solution" for speed up of FEMM calculation if self.is_sliding_band: try: base = basename(self.get_path_save_fem(output)) ans_file = splitext(base)[0] + ".ans" femm.mi_setprevious(ans_file, 0) except: pass # Run the computation femm.mi_analyze() femm.mi_loadsolution() # Get the flux result if self.is_sliding_band: for jj in range(Na_comp): Br[ii, jj], Bt[ii, jj] = femm.mo_getgapb("bc_ag2", angle[jj] * 180 / pi) else: for jj in range(Na_comp): B = femm.mo_getb(Rag * np.cos(angle[jj]), Rag * np.sin(angle[jj])) Br[ii, jj] = B[0] * np.cos(angle[jj]) + B[1] * np.sin(angle[jj]) Bt[ii, jj] = -B[0] * np.sin(angle[jj]) + B[1] * np.cos(angle[jj]) # Compute the torque Tem[ii] = comp_FEMM_torque(femm, FEMM_dict, sym=sym) if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): # Phi_wind computation Phi_wind_stator[ii, :] = comp_FEMM_Phi_wind( femm, qs, Npcpp, is_stator=True, Lfemm=FEMM_dict["Lfemm"], L1=L1, sym=sym, ) # Load mesh data & solution if (self.is_sliding_band or Nt_comp == 1) and (self.is_get_mesh or self.is_save_FEA): tmpmeshFEMM, tmpB, tmpH, tmpmu, tmpgroups = self.get_meshsolution( femm, save_path, ii) if ii == 0: meshFEMM = [tmpmeshFEMM] groups = [tmpgroups] B_elem = np.zeros( [Nt_comp, meshFEMM[ii].cell["triangle"].nb_cell, 3]) H_elem = np.zeros( [Nt_comp, meshFEMM[ii].cell["triangle"].nb_cell, 3]) mu_elem = np.zeros( [Nt_comp, meshFEMM[ii].cell["triangle"].nb_cell]) B_elem[ii, :, 0:2] = tmpB H_elem[ii, :, 0:2] = tmpH mu_elem[ii, :] = tmpmu # Shift to take into account stator position roll_id = int(self.angle_stator * Na_comp / (2 * pi)) Br = roll(Br, roll_id, axis=1) Bt = roll(Bt, roll_id, axis=1) # Store the results sym_dict = dict() # Define the periodicity if self.is_periodicity_t: sym_dict.update(Time_comp.symmetries) if self.is_periodicity_a: sym_dict.update(Angle_comp.symmetries) sym_dict_Tem = dict() if self.is_periodicity_t: sym_dict_Tem.update(Time_comp_Tem.symmetries) Br_data = DataTime( name="Airgap radial flux density", unit="T", symbol="B_r", axes=[Time_comp, Angle_comp], symmetries=sym_dict, values=Br, ) Bt_data = DataTime( name="Airgap tangential flux density", unit="T", symbol="B_t", axes=[Time_comp, Angle_comp], symmetries=sym_dict, values=Bt, ) output.mag.B = VectorField( name="Airgap flux density", symbol="B", components={ "radial": Br_data, "tangential": Bt_data }, ) output.mag.Tem = DataTime( name="Electromagnetic torque", unit="Nm", symbol="T_{em}", axes=[Time_comp_Tem], symmetries=sym_dict_Tem, values=Tem, ) output.mag.Tem_av = mean(Tem) self.get_logger().debug("Average Torque: " + str(output.mag.Tem_av) + " N.m") output.mag.Tem_rip_pp = abs(np_max(Tem) - np_min(Tem)) # [N.m] if output.mag.Tem_av != 0: output.mag.Tem_rip_norm = output.mag.Tem_rip_pp / output.mag.Tem_av # [] else: output.mag.Tem_rip_norm = None if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): Phase = Data1D( name="phase", unit="", values=gen_name(qs, is_add_phase=True), is_components=True, ) output.mag.Phi_wind_stator = DataTime( name="Stator Winding Flux", unit="Wb", symbol="Phi_{wind}", axes=[Time_comp, Phase], symmetries=sym_dict, values=Phi_wind_stator, ) output.mag.FEMM_dict = FEMM_dict if self.is_get_mesh: output.mag.meshsolution = self.build_meshsolution( Nt_comp, meshFEMM, Time_comp, B_elem, H_elem, mu_elem, groups) if self.is_save_FEA: save_path_fea = join(save_path, "MeshSolutionFEMM.h5") output.mag.meshsolution.save(save_path_fea) if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): # Electromotive forces computation (update output) self.comp_emf() else: output.mag.emf = None if self.is_close_femm: femm.closefemm()
def comp_AGSF_transfer(self, output, rnoise=None): """Method to compute Air-Gap Surface Force transfer from middle air-gap radius to stator bore radius. From publication: PILE, Raphaël, LE BESNERAIS, Jean, PARENT, Guillaume, et al. Analytical study of air-gap surface force–application to electrical machines. Open Physics, 2020, vol. 18, no 1, p. 658-673. https://www.degruyter.com/view/journals/phys/18/1/article-p658.xml Parameters ---------- self: Force a Force object output : Output an Output object """ Rag = output.force.Rag Rsbo = output.simu.machine.stator.Rint AGSF = output.force.AGSF arg_list = ["freqs", "wavenumber"] result_freq = AGSF.get_rphiz_along(*arg_list) Prad_wr = result_freq["radial"] Ptan_wr = result_freq["tangential"] wavenumber = result_freq["wavenumber"] freqs = result_freq["freqs"] Nf = len(freqs) Ratio = Rag / Rsbo # Transfer coefficients Eq. (46) Sn = (Ratio** 2) * (power(Ratio, wavenumber) + power(Ratio, -wavenumber)) / 2 Cn = (Ratio** 2) * (power(Ratio, wavenumber) - power(Ratio, -wavenumber)) / 2 # Noise filtering (useful with magnetic FEA) if rnoise is not None: Inoise = where(abs(wavenumber) > rnoise)[0] Sn[Inoise] = 1 Cn[Inoise] = 0 XSn = repeat(Sn[..., newaxis], Nf, axis=1).transpose() XCn = repeat(Cn[..., newaxis], Nf, axis=1).transpose() # Transfer law Eq. (45) Prad_wr_TR = multiply(XSn, Prad_wr) + 1j * multiply(XCn, Ptan_wr) Ptan_wr_TR = multiply(XSn, Ptan_wr) - 1j * multiply(XCn, Prad_wr) Datafreqs = Data1D(name="freqs", values=freqs) Datawavenumbers = Data1D(name="wavenumber", values=wavenumber) axes_list = [Datafreqs, Datawavenumbers] AGSF_TR = VectorField( name="Air gap Surface Force", symbol="AGSF", ) AGSF_TR.components["radial"] = DataFreq( name="Radial AGSF", unit="N/m²", symbol="AGSF_r", axes=axes_list, values=Prad_wr_TR, ) AGSF_TR.components["tangential"] = DataFreq( name="Tangential AGSF", unit="N/m²", symbol="AGSF_t", axes=axes_list, values=Ptan_wr_TR, ) # Replace original AGSF output.force.AGSF = AGSF_TR output.force.Rag = Rsbo
def comp_force(self, output, axes_dict): """Compute the air-gap surface force based on Maxwell Tensor (MT). Parameters ---------- self : ForceMT A ForceMT object output : Output an Output object (to update) axes_dict: {Data} Dict of axes used for force calculation """ # Get time and angular axes Angle = axes_dict["Angle"] Time = axes_dict["Time"] # Import angular vector from Angle Data object _, is_antiper_a = Angle.get_periodicity() angle = Angle.get_values( is_oneperiod=self.is_periodicity_a, is_antiperiod=is_antiper_a and self.is_periodicity_a, ) # Import time vector from Time Data object _, is_antiper_t = Time.get_periodicity() time = Time.get_values( is_oneperiod=self.is_periodicity_t, is_antiperiod=is_antiper_t and self.is_periodicity_t, ) # Load magnetic flux Brphiz = output.mag.B.get_rphiz_along( "time=axis_data", "angle=axis_data", axis_data={ "time": time, "angle": angle }, ) Br = Brphiz["radial"] Bt = Brphiz["tangential"] Bz = Brphiz["axial"] # Magnetic void permeability mu_0 = 4 * pi * 1e-7 # Compute AGSF with MT formula Prad = (Br * Br - Bt * Bt - Bz * Bz) / (2 * mu_0) Ptan = Br * Bt / mu_0 Pz = Br * Bz / mu_0 # Store Maxwell Stress tensor P in VectorField # Build axes list axes_list = list() for axe in output.mag.B.get_axes(): if axe.name == Angle.name: axes_list.append(Angle) elif axe.name == Time.name: axes_list.append(Time) else: axes_list.append(axe) # Build components list components = {} if not np_all((Prad == 0)): Prad_data = DataTime( name="Airgap radial surface force", unit="N/m2", symbol="P_r", axes=axes_list, values=Prad, ) components["radial"] = Prad_data if not np_all((Ptan == 0)): Ptan_data = DataTime( name="Airgap tangential surface force", unit="N/m2", symbol="P_t", axes=axes_list, values=Ptan, ) components["tangential"] = Ptan_data if not np_all((Pz == 0)): Pz_data = DataTime( name="Airgap axial surface force", unit="N/m2", symbol="P_z", axes=axes_list, values=Pz, ) components["axial"] = Pz_data # Store components in VectorField output.force.P = VectorField(name="Magnetic airgap surface force", symbol="P", components=components)
def solve_FEMM(self, output, sym, FEMM_dict): # Loading parameters for readibility angle = output.mag.angle L1 = output.simu.machine.stator.comp_length() Nt_tot = output.mag.Nt_tot # Number of time step Na_tot = output.mag.Na_tot # Number of angular step save_path = self.get_path_save(output) if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): qs = output.simu.machine.stator.winding.qs # Winding phase number Npcpp = output.simu.machine.stator.winding.Npcpp Phi_wind_stator = zeros((Nt_tot, qs)) else: Phi_wind_stator = None # Create the mesh femm.mi_createmesh() # Initialize results matrix Br = zeros((Nt_tot, Na_tot)) Bt = zeros((Nt_tot, Na_tot)) Tem = zeros((Nt_tot)) Rag = output.simu.machine.comp_Rgap_mec() # Compute the data for each time step for ii in range(Nt_tot): # Update rotor position and currents update_FEMM_simulation( output=output, materials=FEMM_dict["materials"], circuits=FEMM_dict["circuits"], is_mmfs=self.is_mmfs, is_mmfr=self.is_mmfr, j_t0=ii, is_sliding_band=self.is_sliding_band, ) # try "previous solution" for speed up of FEMM calculation if self.is_sliding_band: try: base = basename(self.get_path_save_fem(output)) ans_file = splitext(base)[0] + ".ans" femm.mi_setprevious(ans_file, 0) except: pass # Run the computation femm.mi_analyze() femm.mi_loadsolution() # Get the flux result if self.is_sliding_band: for jj in range(Na_tot): Br[ii, jj], Bt[ii, jj] = femm.mo_getgapb("bc_ag2", angle[jj] * 180 / pi) else: for jj in range(Na_tot): B = femm.mo_getb(Rag * np.cos(angle[jj]), Rag * np.sin(angle[jj])) Br[ii, jj] = B[0] * np.cos(angle[jj]) + B[1] * np.sin(angle[jj]) Bt[ii, jj] = -B[0] * np.sin(angle[jj]) + B[1] * np.cos(angle[jj]) # Compute the torque Tem[ii] = comp_FEMM_torque(FEMM_dict, sym=sym) if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): # Phi_wind computation Phi_wind_stator[ii, :] = comp_FEMM_Phi_wind( qs, Npcpp, is_stator=True, Lfemm=FEMM_dict["Lfemm"], L1=L1, sym=sym) # Load mesh data & solution if (self.is_sliding_band or Nt_tot == 1) and (self.is_get_mesh or self.is_save_FEA): tmpmeshFEMM, tmpB, tmpH, tmpmu, tmpgroups = self.get_meshsolution( save_path, ii) if ii == 0: meshFEMM = [tmpmeshFEMM] groups = [tmpgroups] B = np.zeros( [Nt_tot, meshFEMM[ii].cell["triangle"].nb_cell, 3]) H = np.zeros( [Nt_tot, meshFEMM[ii].cell["triangle"].nb_cell, 3]) mu = np.zeros([Nt_tot, meshFEMM[ii].cell["triangle"].nb_cell]) B[ii, :, 0:2] = tmpB H[ii, :, 0:2] = tmpH mu[ii, :] = tmpmu # Shift to take into account stator position roll_id = int(self.angle_stator * Na_tot / (2 * pi)) Br = roll(Br, roll_id, axis=1) Bt = roll(Bt, roll_id, axis=1) # Store the results Time = DataLinspace( name="time", unit="s", symmetries={}, initial=output.mag.time[0], final=output.mag.time[-1], number=Nt_tot, include_endpoint=True, ) Angle = DataLinspace( name="angle", unit="rad", symmetries={}, initial=angle[0], final=angle[-1], number=Na_tot, include_endpoint=True, ) Br_data = DataTime( name="Airgap radial flux density", unit="T", symbol="B_r", axes=[Time, Angle], values=Br, ) Bt_data = DataTime( name="Airgap tangential flux density", unit="T", symbol="B_t", axes=[Time, Angle], values=Bt, ) output.mag.B = VectorField( name="Airgap flux density", symbol="B", components={ "radial": Br_data, "tangential": Bt_data }, ) output.mag.Tem = DataTime( name="Electromagnetic torque", unit="Nm", symbol="T_{em}", axes=[Time], values=Tem, ) output.mag.Tem_av = mean(Tem) output.mag.Tem_rip_pp = abs(np_max(Tem) - np_min(Tem)) # [N.m] if output.mag.Tem_av != 0: output.mag.Tem_rip_norm = output.mag.Tem_rip_pp / output.mag.Tem_av # [] else: output.mag.Tem_rip_norm = None output.mag.Phi_wind_stator = Phi_wind_stator output.mag.FEMM_dict = FEMM_dict if self.is_get_mesh: output.mag.meshsolution = self.build_meshsolution( Nt_tot, meshFEMM, Time, B, H, mu, groups) if self.is_save_FEA: save_path_fea = join(save_path, "MeshSolutionFEMM.h5") output.mag.meshsolution.save(save_path_fea) if (hasattr(output.simu.machine.stator, "winding") and output.simu.machine.stator.winding is not None): # Electromotive forces computation (update output) self.comp_emf() else: output.mag.emf = None
def get_meshsolution(self, output): """Get and set the mesh data and solution data. Parameters ---------- self : StructElmer a StructElmer object save_path: str Full path to folder in which the results are saved Returns ------- success: bool Information if meshsolution could be created """ # logger logger = self.get_logger() # create meshsolution meshsol = MeshSolution(label="Elmer Structural") # if meshsoltion is not requested set empty MeshSolution if not self.is_get_mesh or not self.is_save_FEA: logger.info("StructElmer: MeshSolution is not stored by request.") output.struct.meshsolution = meshsol return False # get the mesh fea_path = self.get_path_save_fea(output) meshvtk = MeshVTK(path=join(fea_path, "Results"), name="case_t0001", format="vtu") # TODO maybe convert to MeshMat before meshsol.mesh = [meshvtk] # get the solution data on the mesh filename = join(fea_path, "Results", "case_t0001.vtu") meshsolvtu = read(filename) pt_data = meshsolvtu.point_data # point_data is of type dict # setup axes indices = arange(meshsolvtu.points.shape[0]) Indices = Data1D(name="indice", values=indices, is_components=True) # store only certain data store_dict = { "displacement": { "name": "Displacement", "unit": "mm", "symbol": "disp", "norm": 1e-3, }, "vonmises": { "name": "Von Mises Stress", "unit": "MPa", "symbol": "vonmises", "norm": 1e6, }, } comp_ext = ["x", "y", "z"] sol_list = [] # list of solutions for key, value in pt_data.items(): # check if value should be stored if key in store_dict.keys(): siz = value.shape[1] # only regard max. 3 components if siz > 3: logger.warning( f'StructElmer get_meshsolution: size of data "{key}" > 3' + " - " + "Data will be truncated." ) siz = 3 components = [] comp_name = [] # loop though components for i in range(siz): # setup name, symbol and component name extension if siz == 1: ext = "" else: ext = comp_ext[i] # setup data object data = DataTime( name=store_dict[key]["name"] + " " + ext, unit=store_dict[key]["unit"], symbol=store_dict[key]["symbol"] + ext, axes=[Indices], values=value[:, i], normalizations={"ref": store_dict[key]["norm"]}, ) components.append(data) comp_name.append("comp_" + ext) # setup solution depending on number of field components if siz == 1: field = components[0] sol_list.append( SolutionData( field=field, type_cell="point", label=store_dict[key]["symbol"], ) ) else: comps = {} for i in range(siz): comps[comp_name[i]] = components[i] field = VectorField( name=store_dict[key]["name"], symbol=store_dict[key]["symbol"], components=comps, ) sol_list.append( SolutionVector( field=field, type_cell="point", label=store_dict[key]["symbol"], ) ) meshsol.solution = sol_list output.struct.meshsolution = meshsol return True
def store_output(self, output, out_dict, axes_dict): """Store the standard outputs of Magnetics that are temporarily in out_dict as arrays into OutMag as Data object Parameters ---------- self : Magnetic a Magnetic object output : Output an Output object (to update) out_dict : dict Dict containing all magnetic quantities that have been calculated in comp_flux_airgap axes_dict: {Data} Dict of axes used for magnetic calculation """ # Get time axis Time = axes_dict["Time"] # Store airgap flux as VectorField object # Axes for each airgap flux component axis_list = [Time, axes_dict["Angle"]] # Create VectorField with empty components output.mag.B = VectorField( name="Airgap flux density", symbol="B", ) # Radial flux component if "Br" in out_dict: output.mag.B.components["radial"] = DataTime( name="Airgap radial flux density", unit="T", symbol="B_r", axes=axis_list, values=out_dict.pop("Br"), ) # Tangential flux component if "Bt" in out_dict: output.mag.B.components["tangential"] = DataTime( name="Airgap tangential flux density", unit="T", symbol="B_t", axes=axis_list, values=out_dict.pop("Bt"), ) # Axial flux component if "Bz" in out_dict: output.mag.B.components["axial"] = DataTime( name="Airgap axial flux density", unit="T", symbol="B_z", axes=axis_list, values=out_dict.pop("Bz"), ) # Store electromagnetic torque over time, and global values: average, peak to peak and ripple if "Tem" in out_dict: Tem = out_dict.pop("Tem") output.mag.Tem = DataTime( name="Electromagnetic torque", unit="Nm", symbol="T_{em}", axes=[axes_dict["Time_Tem"]], values=Tem, ) # Calculate average torque in Nm output.mag.Tem_av = mean(Tem) self.get_logger().debug("Average Torque: " + str(output.mag.Tem_av) + " N.m") # Calculate peak to peak torque in absolute value Nm output.mag.Tem_rip_pp = abs(np_max(Tem) - np_min(Tem)) # [N.m] # Calculate torque ripple in percentage if output.mag.Tem_av != 0: output.mag.Tem_rip_norm = output.mag.Tem_rip_pp / output.mag.Tem_av # [] else: output.mag.Tem_rip_norm = None # Store stator winding flux and calculate electromotive force if "Phi_wind_stator" in out_dict: # Store stator winding flux qs = self.parent.machine.stator.winding.qs Phase = Data1D( name="phase", unit="", values=gen_name(qs), is_components=True, ) output.mag.Phi_wind_stator = DataTime( name="Stator Winding Flux", unit="Wb", symbol="Phi_{wind}", axes=[Time, Phase], values=out_dict.pop("Phi_wind_stator"), ) # Electromotive force computation output.mag.comp_emf()