Пример #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,
    )
Пример #2
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,
    )
Пример #3
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)
Пример #4
0
    def import_signal(self,
                      is_stationary,
                      file,
                      calib=1,
                      mat_signal='',
                      mat_fs=''):
        """ Method to load the signal from a .wav .mat or .uff file
        
        Parameters
        ----------
        is_stationary : boolean
            TRUE if the signal is stationary, FALSE if it is time-varying
        file : string
            string path to the signal file
        calib : float
            calibration factor for the signal to be in [pa]
        mat_signal : string
            in case of a .mat file, name of the signal variable
        mat_fs : string
            in case of a .mat file, name of the sampling frequency variable

    
        Outputs
        -------
        signal : numpy.array
            time signal values
        fs : integer
            sampling frequency        
      
        """

        self.is_stationary = is_stationary
        values, self.fs = load(self.is_stationary, file, calib, mat_signal,
                               mat_fs)
        self.signal = Data1D(values=values, name="Audio signal", unit='Pa')

        self.time_axis = DataLinspace(
            initial=0,
            final=len(self.signal.values) / self.fs,
            step=1 / self.fs,
        )
Пример #5
0
def comp_axes(self, machine, N0=None):
    """Compute simulation axes, i.e. space DataObject including (anti)-periodicity
    and time DataObject including (anti)-periodicity and accounting for rotating speed
    and number of revolutions

    Parameters
    ----------
    self : Input
        an Input object
    machine : Machine
        a Machine object
    N0 : float
        rotating speed [rpm]


    Returns
    -------
    Time : DataLinspace
        Time axis including (anti)-periodicity and accounting for rotating speed and number of revolutions
    Angle : DataLinspace
        Angle axis including (anti)-periodicity

    """
    if self.time is None and N0 is None:
        raise InputError("ERROR: time and N0 can't be both None")

    # Get machine pole pair number
    p = machine.get_pole_pair_number()

    # Get electrical fundamental frequency
    f_elec = self.comp_felec()

    # Airgap radius
    Rag = machine.comp_Rgap_mec()

    # Setup normalizations for time and angle axes
    norm_time = {
        "elec_order": f_elec,
        "mech_order": f_elec / p,
    }
    if N0 is not None:
        norm_time["angle_rotor"] = 1 / (360 * N0 / 60)

    norm_angle = {"space_order": p, "distance": 1 / Rag}

    # Create time axis
    if self.time is None:
        # Create time axis as a DataLinspace
        Time = DataLinspace(
            name="time",
            unit="s",
            initial=0,
            final=60 / N0 * self.Nrev,
            number=self.Nt_tot,
            include_endpoint=False,
            normalizations=norm_time,
        )
    else:
        # Load time data
        time = self.time.get_data()
        self.Nt_tot = len(time)
        Time = Data1D(name="time",
                      unit="s",
                      values=time,
                      normalizations=norm_time)

    # Create angle axis
    if self.angle is None:
        # Create angle axis as a DataLinspace
        Angle = DataLinspace(
            name="angle",
            unit="rad",
            initial=0,
            final=2 * pi,
            number=self.Na_tot,
            include_endpoint=False,
            normalizations=norm_angle,
        )
    else:
        # Load angle data
        angle = self.angle.get_data()
        self.Na_tot = len(angle)
        Angle = Data1D(name="angle",
                       unit="rad",
                       values=angle,
                       normalizations=norm_angle)

    return Time, Angle
Пример #6
0
    def __init__(self, file, is_stationary=False, calib=1, mat_signal="", mat_fs=""):
        """Constructor of the class. Loads the signal from a .wav .mat or .uff file

        Parameters
        ----------
        self : Audio object
            Object from the Audio class
        file : string
            string path to the signal file
        is_stationary : boolean
            TRUE if the signal is stationary, FALSE if it is time-varying
        calib : float
            calibration factor for the signal to be in [pa]
        mat_signal : string
            in case of a .mat file, name of the signal variable
        mat_fs : string
            in case of a .mat file, name of the sampling frequency variable

        """

        # Import audio signal
        values, fs = load(
            is_stationary,
            file,
            calib=calib,
            mat_signal=mat_signal,
            mat_fs=mat_fs,
        )

        # Create Data object for time axis
        time_axis = DataLinspace(
            name="time",
            unit="s",
            initial=0,
            final=(len(values) - 1) / fs,
            number=len(values),
            include_endpoint=True,
        )

        # Create audio signal Data object and populate the object
        self.fs = fs
        self.is_stationary = is_stationary
        self.signal = DataTime(
            name="Audio signal",
            symbol="x",
            unit="Pa",
            normalizations={"ref": 2e-5},
            axes=[time_axis],
            values=values,
        )

        # Init physical metrics attributes
        self.third_spec = None
        self.level = None
        self.welch = None

        # Init physiological metrics attributes
        self.loudness_zwicker = None
        self.loudness_zwicker_specific = None
        self.sharpness = dict()
        self.roughness = dict()
        self.tonality = dict()
Пример #7
0
def comp_axis_time(self, p, per_t=None, is_antiper_t=None, Time_in=None):
    """Compute time axis, with or without periodicities and including normalizations

    Parameters
    ----------
    self : Input
        an Input object
    p: int
        Number of pole pairs
    per_t : int
        time periodicity
    is_antiper_t : bool
        if the time axis is antiperiodic
    Time_in: Data
        Input time axis

    Returns
    -------
    Time: Data
        Requested Time axis
    """

    f_elec = self.OP.get_felec(p=p)
    N0 = self.OP.get_N0(p=p)
    A0 = self.angle_rotor_initial

    # Setup normalizations for time and angle axes
    norm_time = {
        "elec_order":
        Norm_ref(ref=f_elec),
        "mech_order":
        Norm_ref(ref=N0 / 60),
        "angle_elec":
        Norm_ref(ref=self.current_dir / (2 * pi * f_elec)),
        "angle_rotor":
        Norm_affine(slope=self.rot_dir * N0 * 360 / 60, offset=A0 * 180 / pi),
    }

    # Compute Time axis based on input one
    if Time_in is not None:
        if per_t is None or is_antiper_t is None:
            # Get periodicity from input Time axis
            per_t, is_antiper_t = Time_in.get_periodicity()
            per_t = int(per_t / 2) if is_antiper_t else per_t
        # Get axis on given periodicities
        Time = Time_in.get_axis_periodic(Nper=per_t, is_aper=is_antiper_t)
        Time.normalizations = norm_time

    # Create time axis
    elif self.time is None:
        # Create time axis as a DataLinspace
        if self.t_final is not None:
            # Enforce final time
            t_final = self.t_final
        elif self.Nrev is not None:
            # Set final time depending on rotor speed and number of revolutions
            t_final = 60 / self.OP.N0 * self.Nrev
        else:
            # Set final time to p times the number of electrical periods
            t_final = p / f_elec
        # Create time axis as a DataLinspace
        Time = DataLinspace(
            name="time",
            unit="s",
            initial=0,
            final=t_final,
            number=self.Nt_tot,
            include_endpoint=False,
            normalizations=norm_time,
        )
        # Add time (anti-)periodicity
        if per_t > 1 or is_antiper_t:
            Time = Time.get_axis_periodic(per_t, is_antiper_t)
    else:
        # Load time data
        time = self.time.get_data()
        self.Nt_tot = time.size
        Time = Data1D(name="time",
                      unit="s",
                      values=time,
                      normalizations=norm_time)
        # Add time (anti-)periodicity
        sym_t = dict()
        if is_antiper_t:
            sym_t["antiperiod"] = per_t
        elif per_t > 1:
            sym_t["period"] = per_t
        Time.symmetries = sym_t
        Time = Time.to_linspace()

    return Time
Пример #8
0
def comp_axis_angle(self,
                    p,
                    Rag,
                    per_a=None,
                    is_antiper_a=None,
                    Angle_in=None):
    """Compute angle axis with or without periodicities and including normalizations

    Parameters
    ----------
    self : Input
        an Input object
    p : int
        Machine pole pair number
    Rag: float
        Airgap mean radius [m]
    per_a : int
        angle periodicity
    is_antiper_a : bool
        if the angle axis is antiperiodic
    Angle_in: Data
        Input axis angle

    Returns
    -------
    Timee_in: Data
        Requested axis angle

    """

    norm_angle = {
        "space_order": Norm_ref(ref=p),
        "distance": Norm_ref(ref=1 / Rag)
    }

    # Compute angle axis based on input one
    if Angle_in is not None:
        if per_a is None or is_antiper_a is None:
            # Get periodicity from input Angle axis
            per_a, is_antiper_a = Angle_in.get_periodicity()
            per_a = int(per_a / 2) if is_antiper_a else per_a
        # Get Angle axis on requested periodicities
        Angle = Angle_in.get_axis_periodic(Nper=per_a, is_aper=is_antiper_a)
        Angle.normalizations = norm_angle

    # Create angle axis
    elif self.angle is None:

        # Create angle axis as a DataLinspace
        Angle = DataLinspace(
            name="angle",
            unit="rad",
            initial=0,
            final=2 * pi,
            number=self.Na_tot,
            include_endpoint=False,
            normalizations=norm_angle,
        )
        # Add angle (anti-)periodicity
        if per_a > 1 or is_antiper_a:
            Angle = Angle.get_axis_periodic(per_a, is_antiper_a)

    else:
        # Load angle data
        angle = self.angle.get_data()
        self.Na_tot = angle.size
        Angle = Data1D(name="angle",
                       unit="rad",
                       values=angle,
                       normalizations=norm_angle)
        # Add angle (anti-)periodicity
        sym_a = dict()
        if is_antiper_a:
            sym_a["antiperiod"] = per_a
        elif per_a > 1:
            sym_a["period"] = per_a
        Angle.symmetries = sym_a
        Angle = Angle.to_linspace()

    return Angle
Пример #9
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
Пример #10
0
def comp_axes(self, machine, N0=None):
    """Compute simulation axes, i.e. space DataObject including (anti)-periodicity
    and time DataObject including (anti)-periodicity and accounting for rotating speed
    and number of revolutions

    Parameters
    ----------
    self : Input
        an Input object
    machine : Machine
        a Machine object
    N0 : float
        rotating speed [rpm]


    Returns
    -------
    Time : DataLinspace
        Time axis including (anti)-periodicity and accounting for rotating speed and number of revolutions
    Angle : DataLinspace
        Angle axis including (anti)-periodicity

    """

    # Time axis
    if self.time is None:
        if N0 is None:
            raise InputError("ERROR: time and N0 can't be both None")

        Time = DataLinspace(
            name="time",
            unit="s",
            initial=0,
            final=60 / N0 * self.Nrev,
            number=self.Nt_tot,
            include_endpoint=False,
        )
    else:
        # Load and check time
        time = self.time.get_data()
        self.Nt_tot = len(time)
        Time = Data1D(name="time", unit="s", values=time)

    # Angle axis
    if self.angle is None:
        # Create angle vector as a linspace
        Angle = DataLinspace(
            name="angle",
            unit="rad",
            initial=0,
            final=2 * pi,
            number=self.Na_tot,
            include_endpoint=False,
        )
    else:
        # Load angle data
        angle = self.angle.get_data()
        self.Na_tot = len(angle)
        Angle = Data1D(name="angle", unit="rad", values=angle)

    return Time, Angle
Пример #11
0
    def draw_FEMM(self):
        """Draw the Machine in FEMM"""

        save_file_path = self.get_save_path(ext=".fem",
                                            file_type="FEMM (*.fem)")
        # Avoid bug due to user closing the popup witout selecting a file
        if save_file_path is [None, ""]:
            return

        femm = _FEMMHandler()
        output = Output(simu=Simu1(machine=self.machine))
        # Periodicity
        sym, is_antiper, _, _ = self.machine.comp_periodicity()
        if is_antiper:
            sym *= 2
        # Set Current (constant J in a layer)
        S_slot = self.machine.stator.slot.comp_surface_active()
        (Nrad, Ntan) = self.machine.stator.winding.get_dim_wind()
        Ntcoil = self.machine.stator.winding.Ntcoil
        Sphase = S_slot / (Nrad * Ntan)
        J = 5e6
        if self.machine.is_synchronous():
            output.elec.OP = OPdq(felec=60)
        else:
            output.elec.OP = OPslip(felec=60)
        output.elec.OP.set_Id_Iq(Id=J * Sphase / Ntcoil, Iq=0)
        output.elec.Time = DataLinspace(
            name="time",
            unit="s",
            initial=0,
            final=60,
            number=20,
            include_endpoint=False,
        )
        time = output.elec.Time.get_values(
            is_oneperiod=False,
            is_antiperiod=False,
        )
        Is = output.elec.comp_I_mag(time, is_stator=True)
        alpha = output.get_angle_rotor_initial()
        try:
            # Draw the machine
            FEMM_dict = draw_FEMM(
                femm,
                output,
                is_mmfr=True,
                is_mmfs=True,
                sym=sym,
                is_antiper=is_antiper,
                type_calc_leakage=0,
                path_save=None,
                is_sliding_band=True,
            )
            # Set the current
            update_FEMM_simulation(
                femm=femm,
                circuits=FEMM_dict["circuits"],
                is_sliding_band=True,
                is_internal_rotor=self.machine.rotor.is_internal,
                angle_rotor=[alpha],
                Is=Is,
                Ir=None,
                ii=0,
            )
            femm.mi_saveas(save_file_path)  # Save
        except Exception as e:
            err_msg = ("Error while drawing machine " + self.machine.name +
                       " in FEMM:\n" + str(e))
            getLogger(GUI_LOG_NAME).error(err_msg)
            QMessageBox().critical(
                self,
                self.tr("Error"),
                self.tr(err_msg),
            )
        femm.closefemm()
Пример #12
0
def comp_axes(
    self,
    axes_values,
    machine=None,
    N0=None,
    per_a=1,
    is_antiper_a=False,
    per_t=1,
    is_antiper_t=False,
):
    """Compute simulation axes, i.e. space DataObject including (anti)-periodicity
    and time DataObject including (anti)-periodicity and accounting for rotating speed
    and number of revolutions -> overrides Input comp_axes method

    Parameters
    ----------
    self : InputFlux
        an InputFlux object
    axes_values : {ndarray}
        dict of axe values
    machine : Machine
        a Machine object
    N0 : float
        rotating speed [rpm]
    per_a : int
        angle periodicity
    is_antiper_a : bool
        if the angle axis is antiperiodic
    per_t : int
        time periodicity
    is_antiper_t : bool
        if the time axis is antiperiodic


    Returns
    -------
    axes_dict : {Data}
        dict of Data objects for each axis

    """

    norm_time = {}
    norm_angle = {}

    if machine is not None:
        # Get machine pole pair number
        p = machine.get_pole_pair_number()

        # Get electrical fundamental frequency
        f_elec = self.comp_felec()

        # Airgap radius
        Rag = machine.comp_Rgap_mec()

        # Setup normalizations for time and angle axes
        norm_time["elec_order"] = f_elec
        norm_time["mech_order"] = f_elec / p
        if N0 is not None:
            norm_time["angle_rotor"] = 1 / (360 * N0 / 60)

        norm_angle["space_order"] = p
        norm_angle["distance"] = 1 / Rag

    sym_t = {}
    if is_antiper_t:
        sym_t["antiperiod"] = per_t
    else:
        sym_t["period"] = per_t

    if self.time is not None:
        Time = Data1D(
            name="time",
            unit="s",
            values=self.time.get_data(),
            normalizations=norm_time,
            symmetries=sym_t,
        )
    elif "time" in axes_values:
        Time = Data1D(
            name="time",
            unit="s",
            values=axes_values["time"],
            normalizations=norm_time,
            symmetries=sym_t,
        )
    elif N0 is None:
        raise InputError("ERROR: time and N0 can't be both None")
    else:
        # Create time axis as a DataLinspace
        Time = DataLinspace(
            name="time",
            unit="s",
            initial=0,
            final=60 / N0 * self.Nrev,
            number=self.Nt_tot,
            include_endpoint=False,
            normalizations=norm_time,
            symmetries=sym_t,
        )

    sym_a = {}
    if is_antiper_a:
        sym_a["antiperiod"] = per_a
    else:
        sym_a["period"] = per_a

    if self.angle is not None:
        Angle = Data1D(
            name="angle",
            unit="rad",
            values=self.angle.get_data(),
            normalizations=norm_angle,
            symmetries=sym_a,
        )
    elif "angle" in axes_values:
        Angle = Data1D(
            name="angle",
            unit="rad",
            values=axes_values["angle"],
            normalizations=norm_angle,
            symmetries=sym_a,
        )
    else:
        # Create angle axis as a DataLinspace
        Angle = DataLinspace(
            name="angle",
            unit="rad",
            initial=0,
            final=2 * pi,
            number=self.Na_tot,
            include_endpoint=False,
            normalizations=norm_angle,
            symmetries=sym_a,
        )

    # Store in axes_dict
    axes_dict = {"Time": Time, "Angle": Angle}

    return axes_dict
Пример #13
0
def solve_FEMM(self, femm, output, sym):

    # 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)
    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_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):
        self.get_logger().debug("Solving step " + str(ii + 1) + " / " +
                                str(Nt_tot))
        # 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_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, 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_tot == 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_tot, meshFEMM[ii].cell["triangle"].nb_cell, 3])
                H_elem = np.zeros(
                    [Nt_tot, meshFEMM[ii].cell["triangle"].nb_cell, 3])
                mu_elem = np.zeros(
                    [Nt_tot, 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_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)
    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
    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_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()
Пример #14
0
def comp_axis_phase(self,
                    lamination,
                    per_a=None,
                    is_apera=None,
                    Phase_in=None):
    """Compute phase axes for given lamination

    Parameters
    ----------
    self : Input
        an Input object
    lamination: Lamination
        a Lamination object
    per_a : int
        time periodicity
    Phase_in: Data
        Input phase axis

    Returns
    -------
    Phase: Data
        Requested phase axis
    """

    Phase = None

    if Phase_in is not None:
        Phase = Phase_in.copy()

    else:
        name_phase = lamination.get_name_phase()

        if len(name_phase) > 0:

            if per_a is not None and is_apera is not None:
                sym_dict = dict()
                if is_apera:
                    per_a *= 2
                    sym_dict["antiperiod"] = per_a
                elif per_a > 1:
                    sym_dict["period"] = per_a

                # Creating the data object
                Phase = DataLinspace(
                    name="phase",
                    unit="rad",
                    initial=0,
                    final=2 * pi / per_a,
                    number=int(len(name_phase) / per_a),
                    include_endpoint=False,
                    symmetries=sym_dict,
                    normalizations={"bar_id": Norm_indices()},
                    is_overlay=True,
                    # filter={"Phase": []},
                )
            else:
                # Creating the data object
                Phase = Data1D(
                    name="phase",
                    unit="rad",
                    values=name_phase,
                    is_components=True,
                    is_overlay=True,
                    filter={"Phase": []},
                )

    return Phase
Пример #15
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
Пример #16
0
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
Пример #17
0
def solve_FEMM(self, output, sym, FEMM_dict):

    # Loading parameters for readibilitys
    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, 1))

    lam_int = output.simu.machine.get_lamination(True)
    lam_ext = output.simu.machine.get_lamination(False)
    Rgap_mec_int = lam_int.comp_radius_mec()
    Rgap_mec_ext = lam_ext.comp_radius_mec()

    if self.is_get_mesh or self.is_save_FEA:
        meshFEMM = [Mesh() for ii in range(Nt_tot)]
        solutionFEMM = [Solution() for ii in range(Nt_tot)]
    else:
        meshFEMM = [Mesh()]
        solutionFEMM = [Solution()]

    # 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:
            Rag = (Rgap_mec_ext + Rgap_mec_int) / 2
            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_get_mesh or self.is_save_FEA:
            meshFEMM[ii], solutionFEMM[ii] = self.get_meshsolution(
                self.is_get_mesh, self.is_save_FEA, save_path, ii)

    # 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,
    )
    Angle = DataLinspace(
        name="angle",
        unit="rad",
        symmetries={},
        initial=angle[0],
        final=angle[-1],
        number=Na_tot,
    )
    output.mag.Br = DataTime(
        name="Airgap radial flux density",
        unit="T",
        symbol="B_r",
        axes=[Time, Angle],
        values=Br,
    )
    output.mag.Bt = DataTime(
        name="Airgap tangential flux density",
        unit="T",
        symbol="B_t",
        axes=[Time, Angle],
        values=Bt,
    )
    output.mag.Tem = Tem
    output.mag.Tem_av = mean(Tem)
    if output.mag.Tem_av != 0:
        output.mag.Tem_rip = abs(
            (np_max(Tem) - np_min(Tem)) / output.mag.Tem_av)
    output.mag.Phi_wind_stator = Phi_wind_stator
    output.mag.FEMM_dict = FEMM_dict

    if self.is_get_mesh:
        cond = (not self.is_sliding_band) or (Nt_tot == 1)
        output.mag.meshsolution = MeshSolution(
            name="FEMM_magnetic_mesh",
            mesh=meshFEMM,
            solution=solutionFEMM,
            is_same_mesh=cond,
        )

    if self.is_save_FEA:
        save_path_fea = join(save_path, "MeshSolutionFEMM.json")
        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
Пример #18
0
def gen_input(self):
    """Generate the input for the structural module (magnetic output)

    Parameters
    ----------
    self : InFlux
        An InFlux object
    """

    output = OutMag()
    # Load and check time
    if self.time is None:
        raise InputError("ERROR: InFlux.time missing")
    output.time = self.time.get_data()

    if not isinstance(output.time, ndarray) or len(output.time.shape) != 1:
        # time should be a vector
        raise InputError("ERROR: InFlux.time should be a vector, " +
                         str(output.time.shape) + " shape found")
    Nt_tot = len(output.time)

    # Load and check angle
    if self.angle is None:
        raise InputError("ERROR: InFlux.angle missing")
    output.angle = self.angle.get_data()
    if not isinstance(output.angle, ndarray) or len(output.angle.shape) != 1:
        # angle should be a vector
        raise InputError("ERROR: InFlux.angle should be a vector, " +
                         str(output.angle.shape) + " shape found")
    Na_tot = len(output.angle)

    if self.Br is None:
        raise InputError("ERROR: InFlux.Br missing")
    Br = self.Br.get_data()
    Time = DataLinspace(
        name="time",
        unit="s",
        symmetries={},
        initial=output.time[0],
        final=output.time[-1],
        number=Nt_tot,
    )
    Angle = DataLinspace(
        name="angle",
        unit="rad",
        symmetries={},
        initial=output.angle[0],
        final=output.angle[-1],
        number=Na_tot,
    )
    output.Br = DataTime(
        name="Airgap radial flux density",
        unit="T",
        symbol="B_r",
        axes=[Time, Angle],
        values=Br,
    )
    if not isinstance(output.Br, DataND) or Br.shape != (Nt_tot, Na_tot):
        raise InputError("ERROR: InFlux.Br must be a matrix with the shape " +
                         str((Nt_tot, Na_tot)) +
                         " (len(time), stator phase number), " +
                         str(Br.shape) + " returned")

    if self.Bt is not None:
        Bt = self.Bt.get_data()
        output.Bt = DataTime(
            name="Airgap tangential flux density",
            unit="T",
            symbol="B_t",
            axes=[Time, Angle],
            values=Bt,
        )
        if not isinstance(output.Bt, DataND) or Bt.shape != (Nt_tot, Na_tot):
            raise InputError(
                "ERROR: InFlux.Bt must be a matrix with the shape " +
                str((Nt_tot, Na_tot)) + " (len(time), rotor phase number), " +
                str(Bt.shape) + " returned")
    else:
        output.Bt = None

    if self.parent.parent is None:
        raise InputError(
            "ERROR: The Simulation object must be in an Output object to run")
    # Save the Output in the correct place
    self.parent.parent.mag = output