Beispiel #1
0
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 * np.pi * 1e-7
    angle = output.force.angle
    Na_tot = output.force.Na_tot
    time = output.force.time
    Nt_tot = output.force.Nt_tot

    # Load magnetic flux
    Br = output.mag.Br.values
    Bt = output.mag.Bt.values

    # Compute AGSF with MT formula
    Prad = (Br * Br - Bt * Bt) / (2 * mu_0)
    Ptan = Br * Bt / mu_0

    # Store the results
    Time = DataLinspace(
        name="time",
        unit="s",
        symmetries={},
        initial=time[0],
        final=time[-1],
        number=Nt_tot,
    )

    Angle = DataLinspace(
        name="angle",
        unit="rad",
        symmetries={},
        initial=angle[0],
        final=angle[-1],
        number=Na_tot,
    )
    output.force.Prad = DataTime(
        name="Airgap radial surface force",
        unit="T",
        symbol="P_r",
        axes=[Time, Angle],
        values=Prad,
    )
    output.force.Ptan = DataTime(
        name="Airgap radial surface force",
        unit="T",
        symbol="P_t",
        axes=[Time, Angle],
        values=Ptan,
    )
Beispiel #2
0
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
Beispiel #4
0
def get_wave(self):
    """Return the wave generated by the Drive

    Parameters
    ----------
    self : DriveWave
        A DriveWave object

    Returns
    -------
    wave : DataTime
        Voltage / current waveform (Nt, qs)
    """

    wave = self.wave.get_data()

    Phase = Data1D(
        name="phase",
        unit="",
        values=gen_name(wave.shape[0], is_add_phase=True),
        is_components=True,
    )
    # Ouput.Simulation.Electrical.EEC.Drive
    if (check_parent(self, 4)
            and self.parent.parent.parent.parent.elec.time is not None
            and len(self.parent.parent.parent.parent.elec.time) > 1):
        Time = Data1D(name="time",
                      unit="s",
                      values=self.parent.parent.parent.parent.elec.time)
    else:
        Nt = wave.shape[1]
        Time = DataLinspace(
            name="time",
            unit="s",
            symmetries={},
            initial=0,
            final=Nt,
            number=Nt,
            include_endpoint=False,
        )

    if self.is_current:
        return DataTime(name="Current",
                        unit="A",
                        symbol="I",
                        axes=[Phase, Time],
                        values=wave)
    else:
        return DataTime(name="Voltage",
                        unit="V",
                        symbol="U",
                        axes=[Phase, Time],
                        values=wave)
Beispiel #5
0
def freq_to_time(self):
    """Performs the inverse Fourier Transform and stores the resulting field in a DataTime object.
    Parameters
    ----------
    self : DataFreq
        a DataFreq object
    Returns
    -------
    a DataTime object
    """
    
    axes_str = [axis.name for axis in self.axes]
    axes_str = ["time" if axis_name == "freqs" else axis_name for axis_name in axes_str]
    axes_str = ["angle" if axis_name == "wavenumber" else axis_name for axis_name in axes_str]
    
    if axes_str == [axis.name for axis in self.axes]:
        raise AxisError(
            "ERROR: No available axis is compatible with fft (should be time or angle)"
        )
    else:
        results = self.get_along(*axes_str)
        values = results.pop(self.symbol)
        Axes = []
        for axis in results.keys():
            Axes.append(Data1D(name=axis, values=results[axis]))
        return DataTime(
            name=self.name,
            unit=self.unit,
            symbol=self.symbol,
            axes=Axes,
            values=values,
        )
Beispiel #6
0
def build_solution_data(field, axis_list, name="", symbol="", unit="", is_real=True):
    """Build the MeshSolution objets from FEMM outputs.

    Parameters
    ----------
    field : ndarray
        a data field
    axis_list : list
        a list of SciDataTool axis

    Returns
    -------
    solution: SolutionData
        a SolutionData object
    """

    data = DataTime(
        name=name,
        unit=unit,
        symbol=symbol,
        axes=axis_list,
        values=field,
        is_real=is_real,
    )

    return SolutionData(field=data, label=symbol)
Beispiel #7
0
def solve_EEC(self, output):
    """Compute the parameters dict for the equivalent electrical circuit
    cf "Advanced Electrical Drives, analysis, modeling, control"
    Rik de doncker, Duco W.J. Pulle, Andre Veltman, Springer edition
    
                 <---                               --->
     -----R-----wsLqIq----              -----R-----wsLdId----
    |                     |            |                     |
    |                     |            |                    BEMF
    |                     |            |                     |
     ---------Id----------              ---------Iq----------
             
             --->                               ---> 
              Ud                                 Uq
              
    Parameters
    ----------
    self : EEC_PMSM
        an EEC_PMSM object
    output : Output
        an Output object
    """

    qs = output.simu.machine.stator.winding.qs
    freq0 = self.freq0
    ws = 2 * pi * freq0
    rot_dir = output.get_rot_dir()
    time = output.elec.time

    # Prepare linear system
    XR = array([
        [self.parameters["R20"], -ws * self.parameters["Lq"]],
        [ws * self.parameters["Ld"], self.parameters["R20"]],
    ])
    XE = array([0, self.parameters["BEMF"]])
    XU = array([self.parameters["Ud"], self.parameters["Uq"]])
    Idq = solve(XR, XU - XE)

    # dq to abc transform
    Is = dq2n(Idq, -rot_dir * 2 * pi * freq0 * time, n=qs)

    # Store currents into a Data object
    Time = Data1D(name="time", unit="s", values=time)
    phases_names = gen_name(qs, is_add_phase=True)
    Phases = Data1D(name="phases",
                    unit="dimless",
                    values=phases_names,
                    is_components=True)
    output.elec.Currents = DataTime(
        name="Stator currents",
        unit="A",
        symbol="I_s",
        axes=[Phases, Time],
        values=transpose(Is),
    )
    output.elec.Is = Is
    output.elec.Ir = None
Beispiel #8
0
def plot_mmf_unit(self, Na=2048, fig=None):
    """Plot the winding unit mmf as a function of space
    Parameters
    ----------
    self : LamSlotWind
        an LamSlotWind object
    Na : int
        Space discretization
    fig : Matplotlib.figure.Figure
        existing figure to use if None create a new one
    """

    # Create an empty Output object to use the generic plot methods
    module = __import__("pyleecan.Classes.Output", fromlist=["Output"])
    Output = getattr(module, "Output")
    out = Output()

    # Compute the winding function and mmf
    wf = self.comp_wind_function(Na=Na)
    qs = self.winding.qs
    mmf_u = self.comp_mmf_unit(Na=Na)

    # Create a Data object
    Phase = Data1D(
        name="phase",
        unit="",
        values=gen_name(qs, is_add_phase=True),
        is_components=True,
    )
    Angle = DataLinspace(
        name="angle",
        unit="rad",
        symmetries={},
        initial=0,
        final=2 * pi,
        number=Na,
        include_endpoint=False,
    )
    out.mag.Br = DataTime(name="WF",
                          unit="p.u.",
                          symbol="Magnitude",
                          axes=[Phase, Angle],
                          values=wf)

    color_list = config_dict["PLOT"]["COLOR_DICT"]["PHASE_COLORS"][:qs + 1]
    out.plot_A_space(
        "mag.Br",
        is_fft=True,
        index_list=[0, 1, 2],
        data_list=[mmf_u],
        fig=fig,
        color_list=color_list,
    )
Beispiel #9
0
    def compute_loudness(self, field_type='free'):
        """ Method to compute the loudness according to Zwicker's method
        
        Parameter
        ----------
        field-type: string
            'free' by default or 'diffuse'      
               
        """
        if self.third_spec == None:
            self.comp_3oct_spec()

        if self.is_stationary == True:
            N, N_specific = loudness_zwicker_stationary(
                self.third_spec.values, self.third_spec.axes[0].values,
                field_type)
        elif self.is_stationary == False:
            N, N_specific = loudness_zwicker_time(self.third_spec.values,
                                                  field_type)

        barks = Data1D(name='Frequency Bark scale',
                       unit='Bark',
                       values=np.linspace(0.1, 24, int(24 / 0.1)))

        if self.is_stationary == True:
            self.loudness_zwicker = Data1D(values=[N],
                                           name="Loudness",
                                           unit="Sones")
            self.loudness_zwicker_specific = DataFreq(symbol="N'",
                                                      axes=[barks],
                                                      values=N_specific,
                                                      name="Specific loudness",
                                                      unit="Sones")
        elif self.is_stationary == False:
            time = Data1D(symbol="T",
                          name="Time axis",
                          unit="s",
                          values=np.linspace(0,
                                             len(self.signal.values) / self.fs,
                                             num=N.size))

            self.loudness_zwicker = DataTime(symbol="N",
                                             axes=[time],
                                             values=N,
                                             name="Loudness",
                                             unit="Sones")
            self.loudness_zwicker_specific = DataFreq(symbol="N'",
                                                      axes=[barks, time],
                                                      values=N_specific,
                                                      name="Specific loudness",
                                                      unit="Sones")
Beispiel #10
0
def get_data(self):
    """Generate Data objects

    Parameters
    ----------
    self : ImportData
        An ImportData object

    Returns
    -------
    Data: DataND
        The generated Data object

    """

    axes_list = []
    is_freq = False
    for axis in self.axes:
        if axis.name == "freqs" or axis.name == "wavenumber":
            is_freq = True
        axes_list.append(
            Data1D(
                values=axis.field.get_data(),
                name=axis.name,
                unit=axis.unit,
                symmetries=axis.symmetries,
            ))

    if is_freq:
        Data = DataFreq(
            axes=axes_list,
            values=self.field.get_data(),
            name=self.name,
            symbol=self.symbol,
            unit=self.unit,
            normalizations=self.normalizations,
            symmetries=self.symmetries,
        )
    else:
        Data = DataTime(
            axes=axes_list,
            values=self.field.get_data(),
            name=self.name,
            symbol=self.symbol,
            unit=self.unit,
            normalizations=self.normalizations,
            symmetries=self.symmetries,
        )

    return Data
Beispiel #11
0
def plot_mmf_unit(self, fig=None):
    """Plot the winding unit mmf as a function of space
    Parameters
    ----------
    self : LamSlotWind
        an LamSlotWind object
    Na : int
        Space discretization
    fig : Matplotlib.figure.Figure
        existing figure to use if None create a new one
    """

    # Compute the winding function and mmf
    wf = self.comp_wind_function()
    qs = self.winding.qs
    mmf_u = self.comp_mmf_unit(Nt=1, Na=wf.shape[1])

    if len(mmf_u.values.shape) == 1:
        mmf_u.values = mmf_u.values[
            None, :]  # TODO: correct bug in SciDataTool

    # Create a Data object
    Phase = Data1D(
        name="phase",
        unit="",
        values=gen_name(qs, is_add_phase=True),
        is_components=True,
    )
    Angle = mmf_u.axes[1]
    WF = DataTime(
        name="WF",
        unit="p.u.",
        symbol="Magnitude",
        axes=[Phase, Angle],
        values=wf,
        symmetries=mmf_u.symmetries,
    )

    color_list = config_dict["PLOT"]["COLOR_DICT"]["PHASE_COLORS"][:qs + 1]
    plot_A_space(
        WF,
        is_fft=True,
        index_list=[0, 1, 2],
        data_list=[mmf_u],
        fig=fig,
        color_list=color_list,
    )
Beispiel #12
0
    def compute_roughness(self, overlap=0):
        """ Method to compute roughness according to the Daniel and Weber implementation
        
        Parameter
        ---------
        overlap: float
            overlapping coefficient for the time windows of 200ms 
        """
        roughness = comp_roughness(self.signal.values, self.fs, overlap)

        time = Data1D(name='Time', unit='s', values=roughness['time'])

        self.roughness = DataTime(symbol="R",
                                  axes=[time],
                                  values=roughness['values'],
                                  name="Roughness",
                                  unit="Asper")
Beispiel #13
0
def comp_loss(self, output, part_label):
    """Compute the Losses"""
    # get logger
    logger = self.get_logger()

    # check inpurt
    if not "Stator" in part_label and not "Rotor" in part_label:
        logger.warning(f"LossModelWinding.comp_loss(): 'part_label'" +
                       f" {part_label} not implemented yet.")
        return None, None

    # get the simulation and the lamination
    simu = output.simu
    lam = simu.machine.get_lam_by_label(part_label)

    # check that lamination has a winding
    if hasattr(lam, "winding") and lam.winding is not None:
        R = lam.comp_resistance_wind(T=self.temperature)
        if lam.is_stator:
            current = output.elec.get_Is()
        else:
            current = output.elec.get_Ir()

        axes_names = [axis.name for axis in current.axes]
        data_dict = current.get_along(*axes_names)

        data = DataTime(
            name=self.name,
            unit="W",
            symbol="Loss",
            axes=current.axes,
            values=R * data_dict[current.symbol]**2,
        )

        return data, None

    else:
        logger.warning(
            "LossModelWinding.comp_loss(): Lamination has no winding.")
        return None, None
Beispiel #14
0
def comp_mmf_unit(self, Na=2048, Nt=50):
    """Compute the winding Unit magnetomotive force

    Parameters
    ----------
    self : LamSlotWind
        an LamSlotWind object
    Na : int
        Space discretization for offline computation (otherwise use out.elec.angle)
    Nt : int
        Time discretization for offline computation (otherwise use out.elec.time)
    Returns
    -------
    mmf_unit : SciDataTool.Classes.DataND.DataND
        Unit magnetomotive force (Na)
    """

    # Check if the lamination is within an output object
    is_out = check_parent(self, 3)

    # Check if the result is already available
    if is_out and self.parent.parent.parent.elec.mmf_unit is not None:
        return self.parent.parent.parent.elec.mmf_unit

    # Define the space dicretization
    if (is_out and self.parent.parent.parent.elec.angle is not None
            and self.parent.parent.parent.elec.angle.size > 0):
        # Use Electrical module discretization
        angle = self.parent.parent.parent.elec.angle
        Na = angle.size
    else:
        angle = linspace(0, 2 * pi, Na, endpoint=False)

    # Define the time dicretization
    if (is_out and self.parent.parent.parent.elec.time is not None
            and self.parent.parent.parent.elec.time.size > 1):
        time = self.parent.parent.parent.elec.time
        Nt = time.size
    else:
        time = linspace(0, 1 / 50, Nt, endpoint=False)  # freq = 50Hz

    # Compute the winding function and mmf
    wf = self.comp_wind_function(angle=angle)
    qs = self.winding.qs

    # Compute unit mmf
    Idq = zeros((Nt, 2))
    Idq[:, 0] = ones(Nt)
    I = dq2n(Idq, 0, n=qs)
    mmf_u = squeeze(dot(I, wf))

    # Create a Data object
    Time = Data1D(name="time", unit="s", values=time)
    Angle = DataLinspace(
        name="angle",
        unit="rad",
        symmetries={},
        initial=0,
        final=2 * pi,
        number=Na,
        include_endpoint=False,
    )
    MMF = DataTime(
        name="Unit MMF",
        unit="p.u.",
        symbol="Magnitude",
        axes=[Time, Angle],
        values=mmf_u,
    )

    if is_out:  # Store the result if the Output is available
        self.parent.parent.parent.elec.mmf_unit = MMF
    return MMF
Beispiel #15
0
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()
Beispiel #16
0
def comp_loss(self, output, part_label):
    """Compute the Losses"""
    # get logger
    logger = self.get_logger()

    # check inpurt
    if not "Stator" in part_label and not "Rotor" in part_label:
        logger.warning(f"LossModelBertotti.comp_loss(): 'part_label'" +
                       f" {part_label} not implemented yet.")
        return None, None

    # get the simulation and the lamination
    simu = output.simu
    lam = simu.machine.get_lam_by_label(part_label)

    # get length, material and speed
    L1 = lam.L1
    mat_type = lam.mat_type
    rho = mat_type.struct.rho
    N0 = output.elec.N0
    group_name = part_label.lower() + " " + self.group  # TODO unifiy FEA names

    # setup meshsolution and solution list
    meshsolution = output.mag.meshsolution.get_group(group_name)

    # compute needed model parameter from material data
    success = self.comp_coeff_Bertotti(mat_type)
    if not success:
        logger.warning(
            "LossModelBertotti: Unable to estimate model coefficents.")

    if success:
        # compute loss density
        LossDens, LossDensComps = self.comp_loss_density(meshsolution)

        # compute sum over frequencies
        axes_list = [axis.name for axis in LossDens.axes]
        freqs_idx = axes_list.index("freqs")

        loss_dens_freq_sum = LossDens.get_along(*axes_list)["LossDens"].sum(
            axis=freqs_idx)

        time = Data1D(name="time", unit="", values=array([0, 1]))
        # time = Data1D(name="time", unit="", values=array([0]), ) # TODO squeeze issue
        axes = [
            axis for axis in LossDens.axes
            if axis.name not in ["time", "freqs"]
        ]

        # TODO utilize periodicity or use DataFreqs to reduce memory usage
        LossDensSum = DataTime(
            name="Losses sum",
            unit="W/kg",
            symbol="LossDensSum",
            axes=[time, *axes],
            values=tile(loss_dens_freq_sum, (2, 1)),
            # values=loss_freqs_sum[newaxis,:], # TODO squeeze issue
        )

        # Set the symmetry factor according to the machine
        if simu.mag.is_periodicity_a:
            sym, is_antiper_a, _, _ = output.get_machine_periodicity()
            sym *= is_antiper_a + 1
        else:
            sym = 1

        # compute the sum of the losses
        area = meshsolution.get_mesh().get_cell_area()
        N0_list = self.N0 if self.N0 else [N0]
        k_freq = [n / N0 for n in N0_list]

        Time = output.elec.Time
        Speed = Data1D(name="speed", unit="rpm", symbol="N0", values=N0_list)

        loss_sum = _comp_loss_sum(self, LossDensComps, area,
                                  k_freq)[newaxis, :]
        loss_sum = (loss_sum * ones(
            (Time.get_length(), 1))[:, newaxis])  # TODO use periodicity
        loss_sum *= L1 * rho * sym
        data = DataTime(name=self.name,
                        unit="W",
                        symbol="Loss",
                        axes=[Time, Speed],
                        values=loss_sum)
        if self.get_meshsolution:
            solution = []
            meshsolution.solution = solution
            solution.append(SolutionData(field=LossDens, label="LossDens"))
            solution.append(
                SolutionData(field=LossDensComps, label="LossDensComps"))
            solution.append(
                SolutionData(field=LossDensSum, label="LossDensSum"))
            return data, meshsolution
        else:
            return data, None
    else:
        return None, None
def dqh2n_DataTime(data_dqh, n, is_n_rms=False, phase_dir=None):
    """dqh to n phase coordinate transformation of DataTime object

    Parameters
    ----------
    data_dqh : DataTime
        data object containing values over time in dqh frame
    n: int
        number of phases
    is_n_rms : boolean
        True to return n currents in rms value, False to return peak values (Pyleecan convention)
    phase_dir: int
        direction of phase distribution: +/-1 (-1 clockwise) to enforce

    Returns
    -------
    data_n : DataTime
        data object containing values over time and phase axes
    """

    # Check if input data object is compliant with dqh transformation
    _check_data(data_dqh)

    if "angle_elec" not in data_dqh.axes[0].normalizations:
        raise Exception("Time axis should contain angle_elec normalization")

    # Get values for one time period converted in electrical angle and for all phases
    angle_elec = data_dqh.axes[0].get_values(normalization="angle_elec",
                                             is_oneperiod=True)
    data_dqh_val = data_dqh.get_along("time[oneperiod]",
                                      "phase",
                                      is_squeeze=False)[data_dqh.symbol]

    # Convert values to dqh frame
    data_n_val = dqh2n(data_dqh_val, angle_elec, n, is_n_rms, phase_dir)

    # Get time axis on one period
    per_t, is_aper_t = data_dqh.axes[0].get_periodicity()
    per_t = int(per_t / 2) if is_aper_t else per_t
    Time = data_dqh.axes[0].get_axis_periodic(per_t, is_aper=False)

    # Create DQH axis
    Phase = Data1D(
        name="phase",
        unit="",
        values=gen_name(n),
        is_components=True,
    )

    # Get normalizations
    normalizations = dict()
    if data_dqh.normalizations is not None and len(
            data_dqh.normalizations) > 0:
        for key, val in data_dqh.normalizations.items():
            normalizations[key] = val.copy()

    # Create DataTime object in dqh frame
    data_n = DataTime(
        name=data_dqh.name.replace(" in DQH frame", ""),
        unit=data_dqh.unit,
        symbol=data_dqh.symbol,
        values=data_n_val,
        axes=[Time, Phase],
        normalizations=normalizations,
        is_real=data_dqh.is_real,
    )

    return data_n
Beispiel #18
0
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
Beispiel #19
0
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
Beispiel #20
0
def gen_drive(self, output):
    """Generate the drive for the equivalent electrical circuit (only PWM drive for now)

    Parameters
    ----------
    self : Electrical
        an Electrical object
    output : Output
        an Output object
    """

    self.get_logger().info("Calculating PWM voltage")

    p = output.simu.machine.get_pole_pair_number()

    # Get PWM object
    PWM = output.elec.PWM

    if PWM.U0 in [0, None]:
        raise Exception("Cannot calculate PWM voltage if PWM.U0 is None or 0")

    # Get operating point
    OP = output.elec.OP

    if PWM.is_star:
        # Calculate modulation index to account for quick variations
        M_I = PWM.get_modulation_index()

        # Number of points depends on modulation index
        PWM.fs /= max([M_I, 0.05])

    # Number of points depends on modulation index
    Nt_tot = int(PWM.fs * PWM.duration)

    # Get time axis
    input_pwm = type(output.simu.input)(
        OP=OP,
        Nt_tot=Nt_tot,
        t_final=PWM.duration,
        current_dir=output.elec.current_dir,
        rot_dir=output.geo.rot_dir,
    )
    Time_PWM = input_pwm.comp_axis_time(p, per_t=1, is_antiper_t=False)

    # Generate PWM signal
    Uabc = PWM.get_data(is_norm=False, Time=Time_PWM)[0]

    # Get phase axis
    stator_label = output.simu.machine.stator.get_label()
    Phase = output.elec.axes_dict["phase_" + stator_label]

    # Create DataTime object
    Us_dt = DataTime(
        name="Stator voltage",
        symbol="U_s",
        unit="V",
        axes=[Time_PWM, Phase],
        values=Uabc,
    )

    # Get DataFreq object and frequency axis by taking FFT
    Us_df = Us_dt.get_data_along("freqs<" + str(self.freq_max), "phase")
    Freqs = Us_df.axes[0]
    freqs = Freqs.get_values()

    # Filter frequencies with low amplitude and create new frequency axis
    Un_norm = np.linalg.norm(Us_df.values, axis=-1)
    Iamp_n = Un_norm > 1e-2 * Un_norm.max()
    Us_df.axes[0] = Data1D(
        name=Freqs.name,
        unit=Freqs.unit,
        symbol=Freqs.symbol,
        values=freqs[Iamp_n],
        normalizations=Freqs.normalizations,
        is_components=False,
        symmetries=Freqs.symmetries,
    )
    Us_df.values = Us_df.values[Iamp_n, :]

    # Store voltage spectrum in OutElec
    output.elec.Us = Us_dt.to_datadual(datafreq=Us_df)

    # Store Time_PWM axis in axes_dict
    output.elec.axes_dict["time"] = Time_PWM
Beispiel #21
0
def comp_mmf_unit(self,
                  Na=None,
                  Nt=None,
                  felec=1,
                  current_dir=None,
                  phase_dir=None):
    """Compute the winding unit magnetomotive force for given inputs

    Parameters
    ----------
    self : LamSlotWind
        an LamSlotWind object
    Na : int
        Space discretization for offline computation
    Nt : int
        Time discretization for offline computation
    felec : float
        Stator current frequency to consider
    current_dir: int
        Stator current rotation direction +/-1
    phase_dir: int
        Stator winding phasor rotation direction +/-1

    Returns
    -------
    MMF_U : DataTime
        Unit magnetomotive force (Na,Nt)
    WF : DataTime
        Winding functions (qs,Na)

    """

    # Check that parent machine is not None
    if self.parent is None:
        raise Exception("Cannot calculate mmf unit if parent machine is None")
    else:
        machine = self.parent

    # Compute the winding function and mmf
    if self.winding is None:
        raise Exception("Cannot calculate mmf unit if winding is None")
    else:
        # Get stator winding number of phases
        qs = self.winding.qs

    if current_dir is None:
        current_dir = CURRENT_DIR_REF
    elif current_dir not in [-1, 1]:
        raise Exception("Cannot enforce current_dir other than +1 or -1")

    if phase_dir is None:
        phase_dir = PHASE_DIR_REF
    elif phase_dir not in [-1, 1]:
        raise Exception("Cannot enforce phase_dir other than +1 or -1")

    if machine.is_synchronous():
        OPclass = OPdq
    else:
        OPclass = OPslip
    InputVoltage = import_class("pyleecan.Classes", "InputVoltage")
    input = InputVoltage(
        Na_tot=Na,
        Nt_tot=Nt,
        OP=OPclass(felec=felec),
        current_dir=current_dir,
        rot_dir=ROT_DIR_REF,  # rotor rotating dir has not impact on unit mmf
    )

    axes_dict = input.comp_axes(
        axes_list=["time", "angle", "phase_S", "phase_R"],
        machine=machine,
        is_periodicity_t=True,
        is_periodicity_a=True,
        is_antiper_t=False,
        is_antiper_a=False,
    )

    # Compute winding function
    angle = axes_dict["angle"].get_values(is_oneperiod=True)
    per_a, _ = axes_dict["angle"].get_periodicity()
    wf = self.comp_wind_function(angle=angle, per_a=per_a)

    # Compute unit current function of time applying constant Id=1 Arms, Iq=Ih=0
    angle_elec = axes_dict["time"].get_values(is_oneperiod=True,
                                              normalization="angle_elec")
    Idq = zeros((angle_elec.size, 3))
    Idq[:, 0] = ones(angle_elec.size)
    I = dqh2n(Idq, angle_elec, n=qs, is_n_rms=False, phase_dir=phase_dir)

    # Compute unit mmf
    mmf_u = squeeze(dot(I, wf))

    # Create a Data object
    MMF_U = DataTime(
        name="Overall MMF",
        unit="A",
        symbol="MMF",
        axes=[axes_dict["time"], axes_dict["angle"]],
        values=mmf_u,
    )

    WF = DataTime(
        name="Phase MMF",
        unit="A",
        symbol="MMF",
        axes=[axes_dict["angle"], axes_dict["phase_" + self.get_label()]],
        values=wf.T,
    )

    return MMF_U, WF
Beispiel #22
0
def solve_EEC(self, output):
    """Compute the parameters dict for the equivalent electrical circuit
    TODO find ref. to cite
    cf "Title"
    Autor, Publisher

                  --->                     ---->
     -----Rs------XsIs---- --- -----Rr'----Xr'Ir'----
    |                     |   |                       |
    |                     Rfe Xm                      Rr'*(s-1)/s
    |                     |   |                       |
     ---------Is---------- --- ---------Ir------------

             --->
              Us

    Parameters
    ----------
    self : EEC_SCIM
        an EEC_SCIM object
    output : Output
        an Output object
    """
    Rs = self.parameters["Rs"]
    Rr = self.parameters["Rr_norm"]
    Rfe = self.parameters["Rfe"]
    Ls = self.parameters["Ls"]
    Lr = self.parameters["Lr_norm"]
    Lm = self.parameters["Lm"]
    norm = self.parameters["norm"]

    slip = self.parameters["slip"]

    felec = output.elec.felec
    ws = 2 * pi * felec

    Xs = ws * Ls
    Xm = ws * Lm
    Xr = ws * Lr

    Rr_s = Rr / slip if slip != 0 else 1e16  # TODO modify system instead

    # Prepare linear system

    # Solve system
    if "Ud" in self.parameters:
        Us = self.parameters["Ud"] + 1j * self.parameters["Uq"]
        # input vector
        b = array([real(Us), imag(Us), 0, 0, 0, 0, 0, 0, 0, 0])
        # system matrix (unknowns order: Um, Is, Im, Ir', Ife each real and imagine parts)
        # TODO simplify system for less unknows (only calculate them afterwards, e.g. Um, Im, Ife)
        # fmt: off
        A = array(
            [ 
                # sum of (real and imagine) voltages equals the input voltage Us
                [ 1,  0, Rs, -Xs,  0,   0,    0,    0,   0,   0, ], 
                [ 0,  1, Xs,  Rs,  0,   0,    0,    0,   0,   0, ], 
                # sum of (real and imagine) currents are zeros
                [ 0,  0, -1,   0,  1,   0,    1,    0,   1,   0, ], 
                [ 0,  0,  0,  -1,  0,   1,    0,    1,   0,   1, ], 
                # j*Xm*Im = Um
                [-1,  0,  0,   0,  0, -Xm,    0,    0,   0,   0, ], 
                [ 0, -1,  0,   0, Xm,   0,    0,    0,   0,   0, ],
                # (Rr'/s + j*Xr')*Ir' = Um
                [-1,  0,  0,   0,  0,   0, Rr_s,  -Xr,   0,   0, ], 
                [ 0, -1,  0,   0,  0,   0,   Xr, Rr_s,   0,   0, ],
                # Rfe*Ife = Um
                [-1,  0,  0,   0,  0,   0,    0,    0, Rfe,   0, ], 
                [ 0, -1,  0,   0,  0,   0,    0,    0,   0, Rfe, ],
            ]
        ) 
        # fmt: on
        # delete last row and column if Rfe is None
        if Rfe is None:
            A = A[:-2, :-2]
            b = b[:-2]

        # print(b)
        # print(A)
        X = solve(A.astype(float), b.astype(float))

        Ir_norm = array([X[6], X[7]])

        # TODO use logger for output of some quantities

        output.elec.Id_ref = X[2]  # use Id_ref / Iq_ref for now
        output.elec.Iq_ref = X[3]
    else:
        pass
        # TODO

    # Compute stator currents
    output.elec.Is = None
    output.elec.Is = output.elec.get_Is()

    # Compute stator voltage
    output.elec.Us = None
    output.elec.Us = output.elec.get_Us()

    # Compute rotor currents
    time = output.elec.Time.get_values(is_oneperiod=True)
    Nt = time.size
    qsr = output.simu.machine.rotor.winding.qs
    sym = output.simu.machine.comp_periodicity()[0]

    Ir_ = tile(Ir_norm, (Nt, 1)) * norm

    w_slip = ws * slip

    # Get rotation direction
    rot_dir = output.get_rot_dir()

    # compute actual rotor bar currents
    # TODO fix: initial rotor pos. is disregarded for now
    Ir = dq2n(Ir_, w_slip * time, n=qsr // sym, rot_dir=rot_dir, is_n_rms=False)
    Ir = tile(Ir, (1, sym))

    Phase = Data1D(
        name="phase",
        unit="",
        values=gen_name(qsr),
        is_components=True,
    )
    output.elec.Ir = DataTime(
        name="Rotor current",
        unit="A",
        symbol="Ir",
        axes=[Phase, output.elec.Time.copy()],
        values=Ir.T,
    )
Beispiel #23
0
def comp_mmf_unit(self, Na=None, Nt=None, freq=1):
    """Compute the winding Unit magnetomotive force

    Parameters
    ----------
    self : LamSlotWind
        an LamSlotWind object
    Na : int
        Space discretization for offline computation (otherwise use out.elec.angle)
    Nt : int
        Time discretization for offline computation (otherwise use out.elec.time)
    freq : float
        Stator current frequency to consider

    Returns
    -------
    mmf_unit : SciDataTool.Classes.DataND.DataND
        Unit magnetomotive force (Na)
    """

    # Check if the lamination is within an output object
    is_out = check_parent(self, 3)

    # Get stator winding number of phases
    qs = self.winding.qs

    # Get spatial symmetry
    per_a, _, _, _ = self.comp_periodicity()

    # Check if the result is already available and that requested size is the same as stored data
    if (is_out and self.parent.parent.parent.elec.mmf_unit is not None
            and Nt is not None and Na is not None):
        if self.parent.parent.parent.elec.mmf_unit.values.shape == (Nt, Na):
            return self.parent.parent.parent.elec.mmf_unit

    # Define the space dicretization
    if Na is None and is_out and self.parent.parent.parent.elec.angle is not None:
        # Use Electrical module discretization
        angle = self.parent.parent.parent.elec.angle
        Na = angle.size
    else:
        angle = linspace(0, 2 * pi / per_a, Na, endpoint=False)

    # Define the time dicretization
    if Nt is None and is_out and self.parent.parent.parent.elec.time is not None:
        # Use Electrical module discretization
        time = self.parent.parent.parent.elec.time
        freq = self.parent.parent.parent.elec.felec
        Nt = time.size
    else:
        time = linspace(0, 1 / freq, Nt, endpoint=False)

    # Compute the winding function and mmf
    wf = self.comp_wind_function(angle=angle, per_a=per_a)

    # Compute unit current function of time applying constant Id=1 Arms, Iq=0
    Idq = zeros((Nt, 2))
    Idq[:, 0] = ones(Nt)
    I = dq2n(Idq, 2 * pi * freq * time, n=qs, is_n_rms=False)

    # Compute unit mmf
    mmf_u = squeeze(dot(I, wf))

    # Create a Data object
    Time = Data1D(name="time", unit="s", values=time)
    Angle = DataLinspace(
        name="angle",
        unit="rad",
        symmetries={"angle": {
            "period": per_a
        }},
        initial=0,
        final=2 * pi / per_a,
        number=Na,
        include_endpoint=False,
    )
    MMF = DataTime(
        name="Unit MMF",
        unit="p.u.",
        symbol="Magnitude",
        axes=[Time, Angle],
        values=mmf_u,
        symmetries={"angle": {
            "period": per_a
        }},
    )

    if is_out:  # Store the result if the Output is available
        self.parent.parent.parent.elec.mmf_unit = MMF

    return MMF
def n2dqh_DataTime(data_n, is_dqh_rms=True, phase_dir=None):
    """n phases to dqh equivalent coordinate transformation of DataTime object

    Parameters
    ----------
    data_n : DataTime
        data object containing values over time and phase axes
    is_dqh_rms : boolean
        True to return dq currents in rms value (Pyleecan convention), False to return peak values
    phase_dir: int
        direction of phase distribution: +/-1 (-1 clockwise) to enforce

    Returns
    -------
    data_dqh : DataTime
        data object transformed in dqh frame

    """

    if phase_dir is None:
        # Check if input data object is compliant with dqh transformation
        # and get phase_dir from data object
        phase_dir = get_phase_dir_DataTime(data_n)
    else:
        # Only check if input data object is compliant with dqh transformation
        _check_data(data_n)

    if "angle_elec" not in data_n.axes[0].normalizations:
        raise Exception("Time axis should contain angle_elec normalization")

    # Get values for one time period converted in electrical angle and for all phases
    angle_elec = data_n.axes[0].get_values(normalization="angle_elec",
                                           is_oneperiod=True)
    data_n_val = data_n.get_along("time[oneperiod]", "phase",
                                  is_squeeze=False)[data_n.symbol]

    # Convert values to dqh frame
    data_dqh_val = n2dqh(data_n_val, angle_elec, is_dqh_rms, phase_dir)

    # Get time axis on one period
    per_t, is_aper_t = data_n.axes[0].get_periodicity()
    per_t = int(per_t / 2) if is_aper_t else per_t
    Time = data_n.axes[0].get_axis_periodic(per_t, is_aper=False)

    # Create DQH axis
    axis_dq = Data1D(
        name="phase",
        unit="",
        values=["direct", "quadrature", "homopolar"],
        is_components=True,
    )

    # Get normalizations
    normalizations = dict()
    if data_n.normalizations is not None and len(data_n.normalizations) > 0:
        for key, val in data_n.normalizations.items():
            normalizations[key] = val.copy()

    # Create DataTime object in dqh frame
    data_dqh = DataTime(
        name=data_n.name + " in DQH frame",
        unit=data_n.unit,
        symbol=data_n.symbol,
        values=data_dqh_val,
        axes=[Time, axis_dq],
        normalizations=normalizations,
        is_real=data_n.is_real,
    )

    return data_dqh
Beispiel #25
0
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
Beispiel #26
0
def gen_input(self):
    """Generate the input for the magnetic module (electrical output)

    Parameters
    ----------
    self : InputCurrent
        An InputCurrent object
    """

    # Get the simulation
    if isinstance(self.parent, Simulation):
        simu = self.parent
    elif isinstance(self.parent.parent, Simulation):
        simu = self.parent.parent
    else:
        raise InputError(
            "ERROR: InputCurrent object should be inside a Simulation object")

    # Create the correct Output object
    output = OutElec()

    # Set discretization
    Time, Angle = self.comp_axes(simu.machine, self.N0)
    output.time = Time
    output.angle = Angle

    # Number of winding phases for stator/rotor
    qs = len(simu.machine.stator.get_name_phase())
    qr = len(simu.machine.rotor.get_name_phase())

    output.N0 = self.N0
    output.felec = self.comp_felec()  # TODO introduce set_felec(slip)

    # Load and check Is
    if qs > 0:
        if self.Is is None:
            if self.Id_ref is None and self.Iq_ref is None:
                raise InputError(
                    "ERROR: InputCurrent.Is, InputCurrent.Id_ref, and InputCurrent.Iq_ref missing"
                )
            else:
                output.Id_ref = self.Id_ref
                output.Iq_ref = self.Iq_ref
                output.Is = None
        else:
            Is = self.Is.get_data()
            if not isinstance(Is, ndarray) or Is.shape != (self.Nt_tot, qs):
                raise InputError(
                    "ERROR: InputCurrent.Is must be a matrix with the shape " +
                    str((self.Nt_tot, qs)) +
                    " (len(time), stator phase number), " + str(Is.shape) +
                    " returned")
            # Creating the data object
            Phase = Data1D(
                name="phase",
                unit="",
                values=gen_name(qs, is_add_phase=True),
                is_components=True,
            )
            output.Is = DataTime(
                name="Stator current",
                unit="A",
                symbol="Is",
                axes=[Phase, Time],
                values=transpose(Is),
            )
            # Compute corresponding Id/Iq reference
            Idq = n2dq(
                transpose(output.Is.values),
                2 * pi * output.felec *
                output.time.get_values(is_oneperiod=False),
                is_dq_rms=True,
            )
            output.Id_ref = mean(Idq[:, 0])
            output.Iq_ref = mean(
                Idq[:, 1])  # TODO use of mean has to be documented

    # Load and check Ir is needed
    if qr > 0:
        if self.Ir is None:
            raise InputError("ERROR: InputCurrent.Ir missing")
        else:
            Ir = self.Ir.get_data()
            if not isinstance(Ir, ndarray) or Ir.shape != (self.Nt_tot, qr):
                raise InputError(
                    "ERROR: InputCurrent.Ir must be a matrix with the shape " +
                    str((self.Nt_tot, qr)) +
                    " (len(time), rotor phase number), " + str(Ir.shape) +
                    " returned")
            # Creating the data object
            Phase = Data1D(
                name="phase",
                unit="",
                values=gen_name(qr, is_add_phase=True),
                is_components=True,
            )
            output.Ir = DataTime(
                name="Rotor current",
                unit="A",
                symbol="Ir",
                axes=[Phase, Time],
                values=transpose(Ir),
            )

    # Load and check alpha_rotor and N0
    if self.angle_rotor is None and self.N0 is None:
        raise InputError(
            "ERROR: InputCurrent.angle_rotor and InputCurrent.N0 can't be None at the same time"
        )
    if self.angle_rotor is not None:
        output.angle_rotor = self.angle_rotor.get_data()
        if (not isinstance(output.angle_rotor, ndarray)
                or len(output.angle_rotor.shape) != 1
                or output.angle_rotor.size != self.Nt_tot):
            # angle_rotor should be a vector of same length as time
            raise InputError(
                "ERROR: InputCurrent.angle_rotor should be a vector of the same length as time, "
                + str(output.angle_rotor.shape) + " shape found, " +
                str(self.Nt_tot) + " expected")

    if self.rot_dir is None or self.rot_dir not in [-1, 1]:
        # Enforce default rotation direction
        simu.parent.geo.rot_dir = None
    else:
        simu.parent.geo.rot_dir = self.rot_dir

    if self.angle_rotor_initial is None:
        # Enforce default initial position
        output.angle_rotor_initial = 0
    else:
        output.angle_rotor_initial = self.angle_rotor_initial

    if self.Tem_av_ref is not None:
        output.Tem_av_ref = self.Tem_av_ref

    if simu.parent is None:
        raise InputError(
            "ERROR: The Simulation object must be in an Output object to run")

    # Save the Output in the correct place
    simu.parent.elec = output
Beispiel #27
0
def comp_mmf_unit(self, Na=None, Nt=None, freq=1):
    """Compute the winding Unit magnetomotive force

    Parameters
    ----------
    self : LamSlotWind
        an LamSlotWind object
    Na : int
        Space discretization for offline computation (otherwise use out.elec.angle)
    Nt : int
        Time discretization for offline computation (otherwise use out.elec.time)
    freq : float
        Stator current frequency to consider

    Returns
    -------
    MMF_U : SciDataTool.Classes.DataND.DataND
        Unit magnetomotive force (Na,Nt)
    WF : SciDataTool.Classes.DataND.DataND
        Winding functions (qs,Na)

    """

    # Get stator winding number of phases
    qs = self.winding.qs

    # Get spatial symmetry
    per_a, _, _, _ = self.comp_periodicity()

    # Define the space dicretization
    angle = linspace(0, 2 * pi / per_a, Na, endpoint=False)

    # Define the time dicretization
    time = linspace(0, 1 / freq, Nt, endpoint=False)

    # Compute the winding function and mmf
    if self.winding is None or self.winding.conductor is None:
        wf = zeros((qs, Na))
    else:
        wf = self.comp_wind_function(angle=angle, per_a=per_a)

    # Compute unit current function of time applying constant Id=1 Arms, Iq=0
    Idq = zeros((Nt, 2))
    Idq[:, 0] = ones(Nt)
    I = dq2n(Idq, 2 * pi * freq * time, n=qs, is_n_rms=False)

    # Compute unit mmf
    mmf_u = squeeze(dot(I, wf))

    # Create a Data object
    Time = Data1D(name="time", unit="s", values=time)
    Angle = Data1D(
        name="angle",
        unit="rad",
        symmetries={"period": per_a},
        values=angle,
        normalizations={"space_order": self.get_pole_pair_number()},
    )
    Phase = Data1D(
        name="phase",
        unit="",
        values=gen_name(qs),
        is_components=True,
    )
    MMF_U = DataTime(
        name="Unit MMF",
        unit="A",
        symbol="Magnitude",
        axes=[Time, Angle],
        values=mmf_u,
    )

    WF = DataTime(
        name="Winding Functions",
        unit="A",
        symbol="Magnitude",
        axes=[Phase, Angle],
        values=wf,
    )

    return MMF_U, WF
Beispiel #28
0
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 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 comp_BEMF_harmonics(Phi_A, Phi_B, Phi_C, delta, time):
    """
    Compute the back electromotive force harmonics from magnet fluxes (PMSM)

    Parameters
    ----------
    Phi_A: Magnetic flux of phase A
    Phi_B: Magnetic flux of phase B
    Phi_C: Magnetic flux of phase C
    delta: Rotor angular position
    time: time vector

    """

    # Park transformation (keep the amplitude factor=2/3)
    Phi_d = (
        2
        / 3
        * (
            Phi_A * cos(delta)
            + Phi_B * cos(delta - 2 * pi / 3)
            + Phi_C * cos(delta + 2 * pi / 3)
        )
    )
    Phi_q = (
        2
        / 3
        * (
            -Phi_A * sin(delta)
            - Phi_B * sin(delta - 2 * pi / 3)
            - Phi_C * sin(delta + 2 * pi / 3)
        )
    )
    Phi_h = 2 / 3 * 1 / 2 * (Phi_A + Phi_B + Phi_C)

    # Create time vector in form of DataLinspace
    time_axis = DataLinspace(
        name="time",
        unit="s",
        initial=0,
        final=time[-1],
        number=len(time),
        include_endpoint=True,
    )

    # Load Phi into DataTime
    Phi_A_data = DataTime(
        name="Phi_A",
        symbol="Phi_A",
        unit="Wb",
        normalizations=None,
        axes=[time_axis],
        values=Phi_A,
    )

    Phi_B_data = DataTime(
        name="Phi_B",
        symbol="Phi_B",
        unit="Wb",
        normalizations=None,
        axes=[time_axis],
        values=Phi_B,
    )
    Phi_C_data = DataTime(
        name="Phi_C",
        symbol="Phi_C",
        unit="Wb",
        normalizations=None,
        axes=[time_axis],
        values=Phi_C,
    )

    Phi_d_data = DataTime(
        name="Phi_d",
        symbol="Phi_d",
        unit="Wb",
        normalizations=None,
        axes=[time_axis],
        values=Phi_d,
    )

    Phi_q_data = DataTime(
        name="Phi_q",
        symbol="Phi_q",
        unit="Wb",
        normalizations=None,
        axes=[time_axis],
        values=Phi_q,
    )
    Phi_h_data = DataTime(
        name="Phi_h",
        symbol="Phi_h",
        unit="Wb",
        normalizations=None,
        axes=[time_axis],
        values=Phi_h,
    )

    # Phi_q_data.plot_2D_Data("time")
    # Phi_q_data.plot_2D_Data("freqs", type_plot='curve')
    # plt.show()

    # Calculate FFT for Phi on the dq0 frame
    d = Phi_d_data.get_along("freqs")
    freqs_d = d["freqs"]
    complx_d = d["Phi_d"]

    q = Phi_q_data.get_along("freqs")
    freqs_q = q["freqs"]
    complx_q = q["Phi_q"]

    h = Phi_h_data.get_along("freqs")
    freqs_h = h["freqs"]
    complx_h = h["Phi_h"]

    # Calculate back-emf (E) on dq0 frame
    E_d = -2 * pi * freqs_q * complx_q + 2 * pi * freqs_d * complx_d * 1j
    E_q = 2 * pi * freqs_d * complx_d + 2 * pi * freqs_q * complx_q * 1j
    E_h = 2 * pi * freqs_h * complx_h * 1j

    return E_d, E_q, E_h, freqs_d, freqs_q, freqs_h