Exemplo n.º 1
0
    def comp_3oct_spec(self):
        """ Method to compute third-octave spectrum according to ISO"""

        freqs = Data1D(name='freqs', unit='Hertz')

        if self.is_stationary == True:
            third_spec, freqs.values = oct3spec(self.signal.values, self.fs)
            np.squeeze(third_spec)
            self.third_spec = DataFreq(symbol="3oct",
                                       axes=[freqs],
                                       values=third_spec,
                                       name="Third-octave spectrum",
                                       unit="dB ref 2e-05")

        elif self.is_stationary == False:
            time = Data1D(name='time', unit='s')
            third_spec, freqs.values, time.values = calc_third_octave_levels(
                self.signal.values, self.fs)
            np.squeeze(third_spec)

            self.third_spec = DataFreq(symbol="3oct",
                                       axes=[freqs, time],
                                       values=third_spec,
                                       name="Third-octave spectrum",
                                       unit="dB ref 2e-05")
Exemplo n.º 2
0
def abc2dqh(Z_abc, freqs, current_dir, felec):
    """alpha-beta-gamma to dqh coordinate transformation

    Parameters
    ----------
    Z_abc : ndarray
        matrix (N x 3) of phases values in abc static frame
    freqs : ndarray
        frequency array in static frame [Hz]
    current_dir: int
        direction of current waveform: +/-1 (-1 clockwise) to enforce
    felec: float
        fundamental electrical frequency [Hz]

    Returns
    -------
    Z_dqh : ndarray
        transformed matrix (N x 3) of dqh equivalent values
    freqs_dqh : ndarray
        frequency array in dqh frame [Hz]
    """

    # Create Frequency axes
    Freqs = Data1D(name="freqs", unit="Hz", values=freqs)
    Freqs0 = Data1D(name="freqs", unit="Hz", values=np.array([felec]))

    # Build DataFreq for Za and Zb
    df_a = DataFreq(axes=[Freqs], values=Z_abc[:, 0])
    df_b = DataFreq(axes=[Freqs], values=Z_abc[:, 1])

    # Build DataFreq for cos(angle_elec) / sin(angle_elec)
    df_cos = DataFreq(axes=[Freqs0], values=np.array([1]))
    df_sin = DataFreq(axes=[Freqs0], values=np.array([-current_dir * 1j]))
    df_sin_neg = DataFreq(axes=[Freqs0], values=np.array([current_dir * 1j]))

    # Calculate Zd in spectrum domain (Zd = Za*cos + Zb*sin)
    df_d = df_a.conv(df_cos)
    df_d = df_d.sum(df_b.conv(df_sin))

    # Calculate Zq in spectrum domain (Zq = -Za*sin + Zb*cos)
    df_q = df_a.conv(df_sin_neg)
    df_q = df_q.sum(df_b.conv(df_cos))

    # Merge frequencies
    freqs_dqh, Ifdq, Ifh = union1d_tol(df_d.axes[0].values,
                                       freqs,
                                       return_indices=True)

    # Rebuild dqh values
    Z_dqh = np.zeros((freqs_dqh.size, 3), dtype=complex)
    Z_dqh[Ifdq, 0] = df_d.values
    Z_dqh[Ifdq, 1] = df_q.values
    Z_dqh[Ifh, 2] = Z_abc[:, 2]  # Homopolar axis same as c axis

    # Only return non zero values
    Z_dqh_norm = np.linalg.norm(Z_dqh, axis=1)
    I0 = Z_dqh_norm > 1e-10

    return Z_dqh[I0, :], freqs_dqh[I0]
Exemplo n.º 3
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
Exemplo n.º 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)
Exemplo n.º 5
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")
Exemplo n.º 6
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,
        )
Exemplo n.º 7
0
def get_solution(self, indice=None):
    """Return a copy of the solution with the option to only include specified indice.

    Parameters
    ----------
    self : SolutionData
        a SolutionData object
    indice : list
        list of indice, if list is empty or None all indice are included

    Returns
    -------
    solution: SolutionMat
        solution

    """
    logger = self.get_logger()

    # create copy to directly manipulate data
    solution = self.copy()

    if indice:
        axes = solution.field.axes
        axes_names = solution.get_axes_list()[0]
        ax_idx = axes_names.index("indice")

        org_indice = axes[ax_idx].get_values()

        if set(indice) - set(org_indice):
            logger.warning(
                "At least one input indice is not part of the solution. " +
                "Respective indice will be skipped.")

        # skip indice that are not part of the solution
        new_indice = [ii for ii in indice if ii in org_indice]

        # create requested axes list to get field values (see SciDataTool slicing ref.)
        args = [name for name in axes_names]
        args[ax_idx] += "=axis_data"

        # get the field values
        field_dict = solution.field.get_along(*args,
                                              axis_data={"indice": new_indice})

        # set new indice axis
        axes[ax_idx] = Data1D(
            values=new_indice,
            is_components=axes[ax_idx].is_components,
            symmetries=axes[ax_idx].symmetries,
            symbol=axes[ax_idx].symbol,
            name=axes[ax_idx].name,
            unit=axes[ax_idx].unit,
            normalizations=axes[ax_idx].normalizations,
        )

        # set new field data
        solution.field.values = field_dict[self.field.symbol]

    return solution
Exemplo n.º 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,
    )
Exemplo n.º 9
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
Exemplo n.º 10
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,
    )
Exemplo n.º 11
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")
Exemplo n.º 12
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,
        )
Exemplo n.º 13
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
Exemplo n.º 14
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
Exemplo n.º 15
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
Exemplo n.º 16
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
Exemplo n.º 17
0
def get_data_along(self, *args, unit="SI", is_norm=False, axis_data=[]):
    """Returns the sliced or interpolated version of the data, using conversions and symmetries if needed.
    Parameters
    ----------
    self: Data
        a Data object
    *args: list of strings
        List of axes requested by the user, their units and values (optional)
    unit: str
        Unit requested by the user ("SI" by default)
    is_norm: bool
        Boolean indicating if the field must be normalized (False by default)
    axis_data: list
        list of ndarray corresponding to user-input data
    Returns
    -------
    a DataND object
    """

    # Dynamic import to avoid loop
    module = __import__("SciDataTool.Classes.DataND", fromlist=["DataND"])
    DataND = getattr(module, "DataND")

    results = self.get_along(*args)
    values = results.pop(self.symbol)
    del results["axes_dict_other"]
    del results["axes_list"]
    Axes = []
    for axis_name in results.keys():
        if len(results[axis_name]) > 1:
            for axis in self.axes:
                if axis.name == axis_name:
                    name = axis.name
                    is_components = axis.is_components
                    axis_values = results[axis_name]
                    unit = axis.unit
                elif axis_name in axes_dict:
                    if axes_dict[axis_name][0] == axis.name:
                        name = axis_name
                        is_components = axis.is_components
                        axis_values = results[axis_name]
                        unit = axes_dict[axis_name][2]
                elif axis_name in rev_axes_dict:
                    if rev_axes_dict[axis_name][0] == axis.name:
                        name = axis_name
                        is_components = axis.is_components
                        axis_values = results[axis_name]
                        unit = rev_axes_dict[axis_name][2]
            Axes.append(
                Data1D(
                    name=name,
                    unit=unit,
                    values=axis_values,
                    is_components=is_components,
                )
            )
    return DataND(
        name=self.name,
        unit=self.unit,
        symbol=self.symbol,
        axes=Axes,
        values=values,
        is_real=self.is_real,
    )
Exemplo n.º 18
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
Exemplo n.º 19
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
Exemplo n.º 20
0
def solve_PWM(self, output, freq_max=None, is_dqh_freq=False):
    """Get stator current harmonics due to PWM harmonics
    TODO: validation with transient FEA simulation

    Parameters
    ----------
    self : EEC_PMSM
        an EEC_PMSM object
    output: Output
        An Output object
    freq_max: float
        Maximum frequency
    is_dqh_freq: bool
        True to consider frequencies in dqh frame

    Returns
    ------
    Is_PWM : DataFreq
        Stator current harmonics as DataFreq
    """
    self.get_logger().info("Calculating PWM current harmonics")

    # Get stator winding phase number
    qs = output.simu.machine.stator.winding.qs

    # Get PWM frequencies in abc frame
    freqs_n = output.elec.Us.axes_df[0].values

    # Get stator voltage harmonics in dqh frame
    Us_PWM = output.elec.get_Us(is_dqh=True, is_harm_only=True, is_freq=True)
    result_dqh = Us_PWM.get_along("freqs<" + str(freq_max), "phase")
    Udqh_val = result_dqh[Us_PWM.symbol]
    freqs_dqh = result_dqh["freqs"]

    # # Plot Us_n and U_dqh
    # output.elec.Us.plot_2D_Data("freqs=[0,10000]", "phase[0]")
    # Us_PWM.plot_2D_Data("freqs=[0,5000]", "phase[0]")

    # Filter Udqh_val zeros values
    Udqh_norm = np.linalg.norm(Udqh_val, axis=-1)
    Iamp = Udqh_norm > 1e-6 * Udqh_norm.max()
    freqs_dqh = freqs_dqh[Iamp]
    Udqh_val = Udqh_val[Iamp, :]

    # Get parameters from eec
    par = self.parameters

    # Init current harmonics matrix
    Idqh_val = np.zeros((freqs_dqh.size, qs), dtype=complex)

    if is_dqh_freq:
        # Take dqh frequency values
        fn_dqh = freqs_dqh
    else:
        # Look for frequency value in n frame for each frequency in dqh frame
        fn_dqh = np.zeros(freqs_dqh.size)
        fn_pos = freqs_dqh + par["felec"]
        fn_neg = freqs_dqh - par["felec"]
        for ii, (fpos, fneg) in enumerate(zip(fn_pos, fn_neg)):
            fn_ii = None
            jj = 0
            while fn_ii is None and jj < freqs_n.size:
                if (np.abs(fpos - freqs_n[jj]) < 1e-4
                        or np.abs(fneg - freqs_n[jj]) < 1e-4):
                    fn_ii = freqs_n[jj]
                elif (np.abs(fpos + freqs_n[jj]) < 1e-4
                      or np.abs(fneg + freqs_n[jj]) < 1e-4):
                    fn_ii = -freqs_n[jj]
                else:
                    jj += 1
            if fn_ii is None:
                raise Exception("Cannot map dqh frequency back to n frequency")
            else:
                fn_dqh[ii] = fn_ii

    # Calculate impedances
    we = 0 * 2 * np.pi * par["felec"]  # in static frame
    wh = 2 * np.pi * fn_dqh
    a = par["R1"] + 1j * wh * par["Ld"]
    b = -we * par["Lq"]
    c = we * par["Ld"]
    d = par["R1"] + 1j * wh * par["Lq"]
    det = a * d - c * b
    # Calculate current harmonics
    # Calculate Id
    Idqh_val[:, 0] = (d * Udqh_val[:, 0] - b * Udqh_val[:, 1]) / det
    # Calculate Iq
    Idqh_val[:, 1] = (-c * Udqh_val[:, 0] + a * Udqh_val[:, 1]) / det
    # if np.any(np.abs(Idqh_val[:, 0]) > 20) or np.any(np.abs(Idqh_val[:, 1]) > 20):
    #     print("problem")

    # # Check
    # Ud = a * Idqh_val[If, 0] + b * Idqh_val[If, 1]
    # Uq = c * Idqh_val[If, 0] + d * Idqh_val[If, 1]
    # check_d = Ud - Udqh_val[If, 0]
    # check_q = Uq - Udqh_val[If, 1]

    # Create frequency axis
    Freqs_PWM = Us_PWM.axes[0]

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

    Freqs = Data1D(
        name=Freqs_PWM.name,
        symbol=Freqs_PWM.symbol,
        unit=Freqs_PWM.unit,
        values=freqs_dqh,
        normalizations=norm_freq,
    )

    # Create DataFreq in DQH Frame
    Is_PWM_dqh = DataFreq(
        name="Stator current",
        unit="A",
        symbol="I_s",
        axes=[Freqs, Us_PWM.axes[1].copy()],
        values=Idqh_val,
    )

    # # Plot PWM current in dqh frame over frequency
    # Is_PWM_dqh.plot_2D_Data("freqs=[0,20000]", "phase[]")

    # Convert I_dqh spectrum back to stator frame
    Is_PWM_n = dqh2n_DataFreq(
        Is_PWM_dqh,
        n=qs,
        phase_dir=output.elec.phase_dir,
        current_dir=output.elec.current_dir,
        felec=output.elec.OP.felec,
        is_n_rms=False,
    )

    # Reduce current to original voltage frequencies
    Is_PWM_n = Is_PWM_n.get_data_along("freqs=" + str(freqs_n.tolist()),
                                       "phase")

    return Is_PWM_n
Exemplo n.º 21
0
def dqh2abc(Z_dqh, freqs, current_dir, felec):
    """dqh to alpha-beta-gamma coordinate transformation

    Parameters
    ----------
    Z_dqh : ndarray
        matrix (N x 3) of dqh - reference frame values
    freqs : ndarray
        frequency array in dqh frame [Hz]
    current_dir: int
        direction of current waveform: +/-1 (-1 clockwise) to enforce
    felec: float
        fundamental electrical frequency [Hz]

    Returns
    -------
    Z_abc : ndarray
        transformed array
    freqs_abc : ndarray
        frequency array in static frame [Hz]
    """

    # Create Frequency axes
    Freqs = Data1D(name="freqs", unit="Hz", values=freqs)
    Freqs0 = Data1D(name="freqs", unit="Hz", values=np.array([felec]))

    # Build DataFreq for Zd and Zq
    df_d = DataFreq(axes=[Freqs], values=Z_dqh[:, 0])
    df_q = DataFreq(axes=[Freqs], values=Z_dqh[:, 1])

    # Build DataFreq for cos(angle_elec) / sin(angle_elec)
    df_cos = DataFreq(axes=[Freqs0], values=np.array([1]))
    df_sin = DataFreq(axes=[Freqs0], values=np.array([-current_dir * 1j]))
    df_sin_neg = DataFreq(axes=[Freqs0], values=np.array([current_dir * 1j]))

    # Calculate Za with convolution (Za = Zd*cos - Zq*sin)
    df_a = df_d.conv(df_cos)
    df_a = df_a.sum(df_q.conv(df_sin_neg))

    # Calculate Zb with convolution (Zb = Zd*sin + Zq*cos)
    df_b = df_d.conv(df_sin)
    df_b = df_b.sum(df_q.conv(df_cos))

    # Merge frequencies
    freqs_abc, Ifab, Ifc = union1d_tol(df_a.axes[0].values,
                                       freqs,
                                       return_indices=True,
                                       tol=1e-4,
                                       is_abs_tol=False)

    # Rebuild abc values
    Z_abc = np.zeros((freqs_abc.size, 3), dtype=complex)
    Z_abc[Ifab, 0] = df_a.values
    Z_abc[Ifab, 1] = df_b.values
    Z_abc[Ifc, 2] = Z_dqh[:, 2]  # c axis same as homopolar axis

    # Only return non zero values
    Z_abc_norm = np.linalg.norm(Z_abc, axis=1)
    I0 = Z_abc_norm > 1e-10

    return Z_abc[I0, :], freqs_abc[I0]
Exemplo n.º 22
0
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
Exemplo n.º 23
0
def n2dqh_DataFreq(data_n,
                   current_dir,
                   felec,
                   is_dqh_rms=True,
                   phase_dir=None):
    """n phases to dqh equivalent coordinate transformation of DataFreq/DataTime object

    Parameters
    ----------
    data_n : DataFreq/DataTime
        data object containing values over freqs 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
    current_dir: int
        direction of current waveform: +/-1 (-1 clockwise) to enforce
    felec: float
        fundamental electrical frequency [Hz]

    Returns
    -------
    data_dqh : DataFreq
        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_DataFreq(data_n)
    else:
        # Only check if input data object is compliant with dqh transformation
        _check_data(data_n, is_freq=True)

    # Get values for one time period converted in electrical angle and for all phases
    result_n = data_n.get_along("freqs", "phase", is_squeeze=False)

    # Convert values to dqh frame
    Z_dqh, freqs_dqh = n2dqh(
        Z_n=result_n[data_n.symbol],
        freqs=result_n["freqs"],
        phase_dir=phase_dir,
        current_dir=current_dir,
        felec=felec,
        is_dqh_rms=is_dqh_rms,
    )

    # Create frequency axis
    norm_freq = dict()
    ax_freq = data_n.axes[0]
    if ax_freq.normalizations is not None and len(ax_freq.normalizations) > 0:
        for key, val in ax_freq.normalizations.items():
            norm_freq[key] = val.copy()
    Freqs = Data1D(name="freqs",
                   unit="Hz",
                   values=freqs_dqh,
                   normalizations=norm_freq)

    # 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 DataFreq object in dqh frame
    data_dqh = DataFreq(
        name=data_n.name + " in DQH frame",
        unit=data_n.unit,
        symbol=data_n.symbol,
        values=Z_dqh,
        axes=[Freqs, axis_dq],
        normalizations=normalizations,
        is_real=data_n.is_real,
    )

    return data_dqh
Exemplo n.º 24
0
def comp_flux_airgap(self, output, axes_dict, Is=None, Ir=None):
    """Build and solve FEMM model to calculate and store magnetic quantities

    Parameters
    ----------
    self : MagFEMM
        a MagFEMM object
    output : Output
        an Output object
    axes_dict: {Data}
        Dict of axes used for magnetic calculation

    Returns
    -------
    out_dict: dict
        Dict containing the following quantities:
            Br : ndarray
                Airgap radial flux density (Nt,Na) [T]
            Bt : ndarray
                Airgap tangential flux density (Nt,Na) [T]
            Tem : ndarray
                Electromagnetic torque over time (Nt,) [Nm]
            Phi_wind_stator : ndarray
                Stator winding flux (qs,Nt) [Wb]
            Phi_wind : dict
                Dict of winding fluxlinkage with respect to Machine.get_lam_list_label (qs,Nt) [Wb]
            meshsolution: MeshSolution
                MeshSolution object containing magnetic quantities B, H, mu for each time step
    """

    # Init output
    out_dict = dict()
    if output.mag.internal is None:
        output.mag.internal = OutMagFEMM()

    # Get time and angular axes
    Angle = axes_dict["angle"]
    Time = axes_dict["time"]

    # Set the angular symmetry factor according to the machine and check if it is anti-periodic
    sym, is_antiper_a = Angle.get_periodicity()

    # Import angular vector from Data object
    angle = Angle.get_values(
        is_oneperiod=self.is_periodicity_a,
        is_antiperiod=is_antiper_a and self.is_periodicity_a,
    )
    Na = angle.size

    # Check if the time axis is anti-periodic
    _, is_antiper_t = Time.get_periodicity()

    # Number of time steps
    time = Time.get_values(
        is_oneperiod=self.is_periodicity_t,
        is_antiperiod=is_antiper_t and self.is_periodicity_t,
    )
    Nt = time.size

    # Get rotor angular position
    angle_rotor = output.get_angle_rotor()[0:Nt]

    # Setup the FEMM simulation
    # Geometry building and assigning property in FEMM
    # Instanciate a new FEMM
    femm = _FEMMHandler()
    output.mag.internal.handler_list.append(femm)
    if self.import_file is None:
        self.get_logger().debug("Drawing machine in FEMM...")
        FEMM_dict = draw_FEMM(
            femm,
            output,
            is_mmfr=self.is_mmfr,
            is_mmfs=self.is_mmfs,
            sym=sym,
            is_antiper=is_antiper_a,
            type_calc_leakage=self.type_calc_leakage,
            is_remove_ventS=self.is_remove_ventS,
            is_remove_ventR=self.is_remove_ventR,
            is_remove_slotS=self.is_remove_slotS,
            is_remove_slotR=self.is_remove_slotR,
            type_BH_stator=self.type_BH_stator,
            type_BH_rotor=self.type_BH_rotor,
            kgeo_fineness=self.Kgeo_fineness,
            kmesh_fineness=self.Kmesh_fineness,
            user_FEMM_dict=self.FEMM_dict_enforced,
            path_save=self.get_path_save_fem(output),
            is_sliding_band=self.is_sliding_band,
            transform_list=self.transform_list,
            rotor_dxf=self.rotor_dxf,
            stator_dxf=self.stator_dxf,
        )
    else:
        self.get_logger().debug("Reusing the FEMM file: " + self.import_file)
        FEMM_dict = self.FEMM_dict_enforced

    # Init flux arrays in out_dict
    out_dict["Br"] = zeros((Nt, Na))
    out_dict["Bt"] = zeros((Nt, Na))
    # Init torque array in out_dict
    out_dict["Tem"] = zeros((Nt))
    # Init lamination winding flux list of arrays in out_dict
    machine = output.simu.machine
    out_dict["Phi_wind"] = {}
    for label, lam in zip(machine.get_lam_list_label(),
                          machine.get_lam_list()):
        if hasattr(lam, "winding") and lam.winding is not None:
            qs = lam.winding.qs  # Winding phase number
            out_dict["Phi_wind"][label] = zeros((Nt, qs))
    # delete 'Phi_wind' if empty
    if not out_dict["Phi_wind"]:
        out_dict.pop("Phi_wind")

    # Solve for all time step and store all the results in out_dict
    if self.nb_worker > 1:
        # A Femm handler will be created for each worker
        femm.closefemm()
        output.mag.internal.handler_list.remove(femm)
        # With parallelization
        B_elem, H_elem, mu_elem, A_node, meshFEMM, groups = self.solve_FEMM_parallel(
            femm,
            output,
            out_dict,
            FEMM_dict=FEMM_dict,
            sym=sym,
            Nt=Nt,
            angle=angle,
            Is=Is,
            Ir=Ir,
            angle_rotor=angle_rotor,
        )
    else:
        # Without parallelization
        B_elem, H_elem, mu_elem, A_node, meshFEMM, groups = self.solve_FEMM(
            femm,
            output,
            out_dict,
            FEMM_dict=FEMM_dict,
            sym=sym,
            Nt=Nt,
            angle=angle,
            Is=Is,
            Ir=Ir,
            angle_rotor=angle_rotor,
            is_close_femm=self.is_close_femm,
            filename=self.import_file,
        )

    # Store FEMM_dict in out_dict if FEMM file is not imported
    if self.import_file is None:
        output.mag.internal.FEMM_dict = FEMM_dict

    # Store stator winding flux
    if STATOR_LAB + "-0" in out_dict["Phi_wind"].keys():
        out_dict["Phi_wind_stator"] = out_dict["Phi_wind"][STATOR_LAB + "-0"]

    # Store mesh data & solution
    if self.is_get_meshsolution and B_elem is not None:

        # Define axis
        Time = Time.copy()
        indices_cell = meshFEMM[0].cell["triangle"].indice
        Indices_Cell = Data1D(name="indice",
                              values=indices_cell,
                              is_components=True)
        axis_list = [Time, Indices_Cell]

        B_sol = build_solution_vector(
            field=B_elem,
            axis_list=axis_list,
            name="Magnetic Flux Density",
            symbol="B",
            unit="T",
        )
        H_sol = build_solution_vector(
            field=H_elem,
            axis_list=axis_list,
            name="Magnetic Field",
            symbol="H",
            unit="A/m",
        )
        mu_sol = build_solution_data(
            field=mu_elem,
            axis_list=axis_list,
            name="Magnetic Permeability",
            symbol="\mu",
            unit="H/m",
        )

        indices_nodes = meshFEMM[0].node.indice
        Indices_Nodes = Data1D(name="indice",
                               values=indices_nodes,
                               is_components=True)
        axis_list_node = [Time, Indices_Nodes]

        A_sol = build_solution_data(
            field=A_node,
            axis_list=axis_list_node,
            name="Magnetic Potential Vector",
            symbol="A_z",
            unit="T.m",
        )
        A_sol.type_cell = "node"

        list_solution = [B_sol, H_sol, mu_sol, A_sol]

        out_dict["meshsolution"] = build_meshsolution(
            list_solution=list_solution,
            label="FEMM 2D Magnetostatic",
            list_mesh=meshFEMM,
            group=groups,
        )

    return out_dict
Exemplo n.º 25
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
Exemplo n.º 26
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()
Exemplo n.º 27
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
Exemplo n.º 28
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
Exemplo n.º 29
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
    """

    # Dynamic import to avoid loop
    module = __import__("SciDataTool.Classes.DataTime", fromlist=["DataTime"])
    DataTime = getattr(module, "DataTime")
    module = __import__("SciDataTool.Classes.DataPattern",
                        fromlist=["DataPattern"])
    DataPattern = getattr(module, "DataPattern")

    axes_str = []
    for i, axis in enumerate(self.axes):
        if axis.is_components:
            axis_str = axis.name + str(list(range(len(axis.values))))
        elif axis.name == "freqs":
            axis_str = "time[smallestperiod]"
        elif axis.name == "wavenumber":
            axis_str = "angle[smallestperiod]"
        elif isinstance(axis, DataPattern):
            axis_str = axis.name + "[pattern]"
        else:
            axis_str = axis.name + "[smallestperiod]"
        axes_str.append(axis_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 self.axes:
            if axis.name == "freqs":
                axis_new = Data1D(
                    name="time",
                    is_components=False,
                    values=results["time"],
                    unit="s",
                    symmetries=axis.symmetries.copy(),
                    normalizations=axis.normalizations.copy(),
                )
            elif axis.name == "wavenumber":
                axis_new = Data1D(
                    name="angle",
                    is_components=False,
                    values=results["angle"],
                    unit="rad",
                    symmetries=axis.symmetries.copy(),
                    normalizations=axis.normalizations.copy(),
                )
            else:
                axis_new = axis.copy()
            Axes.append(axis_new)

        return DataTime(
            name=self.name,
            unit=self.unit,
            symbol=self.symbol,
            axes=Axes,
            values=values,
            is_real=self.is_real,
            normalizations=self.normalizations.copy(),
        )
Exemplo n.º 30
0
def dqh2n_DataFreq(data_dqh,
                   n,
                   current_dir,
                   felec,
                   is_n_rms=False,
                   phase_dir=None):
    """dqh to n phase coordinate transformation of DataFreq object

    Parameters
    ----------
    data_dqh : DataFreq
        data object containing values over time in dqh frame
    n: int
        number of phases
    current_dir: int
        direction of current waveform: +/-1 (-1 clockwise) to enforce
    felec: float
        fundamental electrical frequency [Hz]
    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 : DataFreq
        data object containing values over time and phase axes
    """

    # Check if input data object is compliant with dqh transformation
    _check_data(data_dqh, is_freq=True)

    # Get values for one time period converted in electrical angle and for all phases
    result_dqh = data_dqh.get_along("freqs", "phase", is_squeeze=False)

    # Convert values to dqh frame
    Z_abc, freqs_abc = dqh2n(
        Z_dqh=result_dqh[data_dqh.symbol],
        freqs=result_dqh["freqs"],
        phase_dir=phase_dir,
        current_dir=current_dir,
        felec=felec,
        n=n,
        is_n_rms=is_n_rms,
    )

    # Create frequency axis
    norm_freq = dict()
    ax_freq = data_dqh.axes[0]
    if ax_freq.normalizations is not None and len(ax_freq.normalizations) > 0:
        for key, val in ax_freq.normalizations.items():
            norm_freq[key] = val.copy()
    Freqs = Data1D(name="freqs",
                   unit="Hz",
                   values=freqs_abc,
                   normalizations=norm_freq)

    # 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 DataFreq object in dqh frame
    data_n = DataFreq(
        name=data_dqh.name.replace(" in DQH frame", ""),
        unit=data_dqh.unit,
        symbol=data_dqh.symbol,
        values=Z_abc,
        axes=[Freqs, Phase],
        normalizations=normalizations,
        is_real=data_dqh.is_real,
    )

    return data_n