Esempio n. 1
0
def get_middle(self):
    """Return the point at the middle of the arc

    Parameters
    ----------
    self : Arc1
        An Arc1 object

    Returns
    -------
    Zmid: complex
        Complex coordinates of the middle of the Arc1
    """

    # We use the complex representation of the point
    z1 = self.begin
    z2 = self.end
    zc = self.get_center()

    # Geometric transformation : center is the origine, angle(begin) = 0
    Zstart = (z1 - zc) * exp(-1j * np_angle(z1 - zc))

    # Generation of the point by rotation
    alpha = self.get_angle()
    Zmid = Zstart * exp(1j * alpha / 2)

    # Geometric transformation : return to the main axis
    Zmid = Zmid * exp(1j * np_angle(z1 - zc)) + zc

    # Return (0,0) if the point is too close from 0
    if np_abs(Zmid) < 1e-6:
        Zmid = 0

    return Zmid
Esempio n. 2
0
    def get_hole(self):
        """Generate the HoleUD object corresponding to the selected surfaces

        Parameters
        ----------
        self : DXF_Hole
            a DXF_Hole object

        Returns
        -------
        hole : HoleUD
            User defined hole according to selected surfaces
        """

        hole = HoleUD(surf_list=self.surf_list)
        # Set labels
        Nmag = 0
        for ii in range(self.w_surface_list.rowCount()):
            if self.w_surface_list.cellWidget(ii,
                                              TYPE_COL).currentIndex() == 0:
                hole.surf_list[ii].label = "Hole"
            else:
                hole.surf_list[ii].label = "HoleMagnet"
                Nmag += 1
        # Create magnet objects
        hole.magnet_dict = dict()
        for ii in range(Nmag):
            hole.magnet_dict["magnet_" + str(ii)] = Magnet()

        # Sort the surfaces
        angles = [np_angle(surf.point_ref) for surf in hole.surf_list]
        idx = sorted(range(len(angles)), key=lambda k: angles[k])
        surf_list_sorted = [hole.surf_list[ii] for ii in idx]
        hole.surf_list = surf_list_sorted

        # Rotation
        Zref = sum([surf.point_ref for surf in hole.surf_list])
        for surf in hole.surf_list:
            surf.rotate(-1 * np_angle(Zref))

        # Set metadata
        hole.Zh = self.si_Zh.value()
        for magnet in hole.magnet_dict.values():
            magnet.Lmag = self.lf_mag_len.value()

        # Remove all materials => To be set in GUI
        hole.mat_void = None
        for magnet in hole.magnet_dict.values():
            magnet.mat_type = None

        return hole
Esempio n. 3
0
def discretize(self, nb_point=ARC_NPOINT_D):
    """Return the discretize version of the Arc.
    Begin and end are always returned

    Parameters
    ----------
    self : Arc1
        The Arc1 object to discretize
    nb_point :
        Number of points to add to discretize the arc (Default value = ARC_NPOINT_D)

    Returns
    -------
    list_point: list
        list of complex coordinate of the points

    Raises
    ------
    NbPointArc1DError
        nb_point must be an integer >=0
    """

    # Check that the Arc is correct
    self.check()
    if not isinstance(nb_point, int):
        raise NbPointArc1DError("discretize : nb_point must be an integer")
    if nb_point < 0:
        raise NbPointArc1DError("nb_point must be >=0")

    # We use the complex representation of the point
    z1 = self.begin
    z2 = self.end
    zc = self.get_center()

    # Geometric transformation : center is the origin, angle(begin) = 0
    Zstart = (z1 - zc) * exp(-1j * np_angle(z1 - zc))
    Zend = (z2 - zc) * exp(-1j * np_angle(z1 - zc))

    # Generation of the point by rotation
    if self.is_trigo_direction:
        sign = 1
    else:
        sign = -1
    t = linspace(0, self.get_angle(), nb_point + 2)
    list_point = Zstart * exp(1j * t)

    # Geometric transformation : return to the main axis
    list_point = list_point * exp(1j * np_angle(z1 - zc)) + zc

    return list_point
Esempio n. 4
0
def get_middle(self):
    """Return the point at the middle of the arc

    Parameters
    ----------
    self : Arc3
        An Arc3 object

    Returns
    -------
    Zmid: complex
        Complex coordinates of the middle of the Arc3
    """

    # We use the complex representation of the point
    z1 = self.begin
    zc = self.get_center()
    R = self.comp_radius()

    # Generation of the point by rotation
    if self.is_trigo_direction:  # Top
        Zmid = R * exp(1j * pi / 2.0)
    else:  # Bottom
        Zmid = R * exp(-1j * pi / 2.0)

    # Geometric transformation : return to the main axis
    Zmid = Zmid * exp(1j * np_angle(z1 - zc)) + zc

    # Return (0,0) if the point is too close from 0
    if np_abs(Zmid) < 1e-6:
        Zmid = 0

    return Zmid
Esempio n. 5
0
    def get_hole(self):
        """Generate the HoleUD object corresponding to the selected surfaces

        Parameters
        ----------
        self : DXF_Surf
            a DXF_Surf object

        Returns
        -------
        hole : HoleUD
            User defined hole according to selected surfaces
        """

        if self.lf_scaling.value() == 0:  # Avoid error
            self.lf_scaling.setValue(1)
        surf = self.get_surface()
        surf.scale(self.lf_scaling.value())
        surf.label = HOLEV_LAB
        hole = HoleUD(surf_list=[surf])

        # Translate
        if self.Zcenter != 0:
            surf.translate(-self.Zcenter * self.lf_scaling.value())

        # Rotation
        surf.rotate(-1 * np_angle(surf.point_ref))

        # Set metadata
        hole.Zh = self.si_Zh.value()

        # Remove materials => To be set in GUI
        hole.mat_void = None

        return hole
def comp_angle_d_axis(self, is_plot=False):
    """Compute the angle between the X axis and the first d+ axis
    By convention a "Tooth" is centered on the X axis

    Parameters
    ----------
    self : LamSlotWind
        A LamSlotWind object
    is_plot : bool
        True to plot d axis position regarding unit mmf

    Returns
    -------
    d_angle : float
        angle between the X axis and the first d+ axis
    """

    if self.winding is None or self.winding.qs == 0 or self.winding.conductor is None:
        return 0

    p = self.get_pole_pair_number()

    MMF, _ = self.comp_mmf_unit(Nt=1, Na=400 * p)

    # Get angle values
    results1 = MMF.get_along("angle[oneperiod]")
    angle_stator = results1["angle"]

    # Get the unit mmf FFT and wavenumbers
    results = MMF.get_along("wavenumber")
    wavenumber = results["wavenumber"]
    mmf_ft = results[MMF.symbol]

    # Find the fundamental harmonic of MMF
    indr_fund = np_abs(wavenumber - p).argmin()
    phimax = np_angle(mmf_ft[indr_fund])
    magmax = np_abs(mmf_ft[indr_fund])

    # Reconstruct fundamental MMF wave
    mmf_waveform = magmax * cos(p * angle_stator + phimax)

    # Get the first angle where mmf is max
    d_angle = angle_stator[argmax(mmf_waveform)]

    if is_plot:
        import matplotlib.pyplot as plt
        from numpy import squeeze

        fig, ax = plt.subplots()
        ax.plot(angle_stator,
                squeeze(MMF.get_along("angle[oneperiod]")[MMF.symbol]), "k")
        ax.plot(angle_stator, mmf_waveform, "r")
        ax.plot([d_angle, d_angle], [-magmax, magmax], "--k")
        plt.show()

    return d_angle
Esempio n. 7
0
def discretize(self, nb_point=ARC_NPOINT_D):
    """Return the discretize version of the Arc.
    Begin and end are always returned

    Parameters
    ----------
    self : Arc2
        An Arc2 object
    nb_point : int
        Number of points to add to discretize the arc (Default value = ARC_NPOINT_D)

    Returns
    -------
    list_point: list
        list of complex coordinate of the points

    Raises
    ------
    NbPointArc2DError
        nb_point must be an integer >=0
    """

    self.check()
    if not isinstance(nb_point, int):
        raise NbPointArc2DError("discretize : nb_point must be an integer")
    if nb_point < 0:
        raise NbPointArc2DError("nb_point must be >=0")

    # We use the complex representation of the point
    z1 = self.begin
    zc = self.center

    # Geometric transformation : center is the origine, angle(begin) = 0
    Zstart = (z1 - zc) * exp(-1j * np_angle(z1 - zc))

    # Generation of the point by rotation
    t = linspace(0, self.angle, nb_point + 2)
    list_point = Zstart * exp(1j * t)

    # Geometric transformation : return to the main axis
    list_point = list_point * exp(1j * np_angle(z1 - zc)) + zc

    return list_point
Esempio n. 8
0
def get_center(self):
    """Return the center of the arc

    Parameters
    ----------
    self : Arc1
        An Arc1 object

    Returns
    -------
    Zc: complex
        Complex coordinates of the center of the Arc1
    """

    self.check()

    # The center is on the bisection of [begin, end]
    z1 = self.begin
    z2 = self.end
    R = self.radius

    # Centre at the middle of begin and end (distance(Z1, Z2) = diameter )
    if abs(abs(z2 - z1) - abs(2 * R)) < 1e-6:
        Zc = (z2 + z1) / 2.0
    else:
        # Alpha is the opening angle (Begin-Center-End)
        alpha = 2 * arcsin(abs(z2 - z1) / (2 * R))
        if R > 0:
            Zc = z2 + R * exp(1j *
                              (np_angle(z2 - z1) %
                               (2 * pi))) * exp(1j *
                                                (pi / 2 + np_abs(alpha) / 2))
        else:
            Zc = z1 - R * exp(1j *
                              (np_angle(z1 - z2) %
                               (2 * pi))) * exp(1j *
                                                (pi / 2 + np_abs(alpha) / 2))

    # Return (0,0) if the point is too close from 0
    if np_abs(Zc) < 1e-6:
        Zc = 0

    return Zc
def comp_phase(values):
    """Computes the phase of the Fourier Transform
    Parameters
    ----------
    values: ndarray
        ndarray of the field
    Returns
    -------
    Phase of the Fourier Transform
    """
    return np_angle(_comp_fft(values))
Esempio n. 10
0
def comp_angle_d_axis(self):
    """Compute the angle between the X axis and the first d+ axis
    By convention a "Tooth" is centered on the X axis

    Parameters
    ----------
    self : LamSlotWind
        A LamSlotWind object

    Returns
    -------
    d_angle : float
        angle between the X axis and the first d+ axis
    """

    if self.winding is None:
        return 0

    p = self.get_pole_pair_number()

    MMF = self.comp_mmf_unit(Nt=1, Na=400 * p)

    # Get angle values
    results1 = MMF.get_along("angle[oneperiod]")
    angle_stator = results1["angle"]

    # Get the unit mmf FFT and wavenumbers
    results = MMF.get_along("wavenumber")
    wavenumber = results["wavenumber"]
    mmf_ft = results[MMF.symbol]

    # Find the angle where the FFT is max
    indr_fund = np_abs(wavenumber - p).argmin()
    phimax = np_angle(mmf_ft[indr_fund])
    magmax = np_abs(mmf_ft[indr_fund])

    # Reconstruct fundamental MMF wave
    mmf_waveform = magmax * cos(p * angle_stator + phimax)

    # Get the first angle where mmf is max
    d_angle = angle_stator[argmax(mmf_waveform)]

    # fig, ax = plt.subplots()
    # ax.plot(angle_stator, squeeze(MMF.get_along("angle[oneperiod]")[MMF.symbol]), "k")
    # ax.plot(angle_stator, mmf_waveform, "r")
    # ax.plot([d_angle, d_angle], [-magmax, magmax], "--k")
    # plt.show()

    return d_angle
Esempio n. 11
0
def discretize(self, nb_point=ARC_NPOINT_D):
    """Return the discretize version of the Arc.
    Begin and end are always return

    Parameters
    ----------
    self : Arc3
        An Arc3 object
    nb_point : int
        Number of points to add to discretize the arc (Default value = ARC_NPOINT_D)

    Returns
    -------
    list_point: list
        list of complex coordinate of the points

    Raises
    ------
    NbPointArc1DError
        nb_point must be an integer >=0
    """

    # Check that the Arc is correct
    self.check()
    if not isinstance(nb_point, int):
        raise NbPointArc3DError("discretize : nb_point must be an integer")
    if nb_point < 0:
        raise NbPointArc3DError("nb_point must be >=0")

    # We use the complex representation of the point
    z1 = self.begin
    zc = self.get_center()
    R = self.comp_radius()

    # Generation of the point by rotation
    if self.is_trigo_direction:  # Top
        t = linspace(0, pi, nb_point + 2)
    else:  # Bottom
        t = linspace(0, -pi, nb_point + 2)
    list_point = R * exp(1j * t)

    # Geometric transformation : return to the main axis
    list_point = list_point * exp(1j * np_angle(z1 - zc)) + zc

    return list_point
def get_phase_along(self, *args, unit="SI", is_norm=False, axis_data=[]):
    """Returns the ndarray of the magnitude of the FT, 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
    -------
    list of 1Darray of axes values, ndarray of magnitude values
    """
    if len(args) == 1 and type(args[0]) == tuple:
        args = args[0]  # if called from another script with *args
    return_dict = self.get_along(args, axis_data=axis_data)
    values = return_dict[self.symbol]
    # Compute magnitude
    values = np_angle(values)
    # Convert into right unit (apart because of degree conversion)
    if unit == self.unit or unit == "SI":
        if is_norm:
            try:
                values = values / self.normalizations.get("ref")
            except:
                raise NormError(
                    "ERROR: Reference value not specified for normalization"
                )
    elif unit == "°":
        values = convert(values, "rad", "°")
    elif unit in self.normalizations:
        values = values / self.normalizations.get(unit)
    else:
        values = convert(values, self.unit, unit)
    return_dict[self.symbol] = values
    return return_dict
Esempio n. 13
0
def xyz_to_rphiz(values):
    """Converts axis values from cartesian coordinates into cylindrical coordinates

    Parameters
    ----------
    values: array
        Values of the axis to convert (Nx3)
    Returns
    -------
    ndarray of the axis (Nx3)
    """

    x = values[:, 0]
    y = values[:, 1]
    z = values[:, 2]

    affixe = x + 1j * y
    r = np_abs(affixe)
    phi = (np_angle(affixe) + 2 * pi) % (2 * pi)

    return column_stack((r, phi, z))
Esempio n. 14
0
def get_center(self):
    """Return the center of the arc

    Parameters
    ----------
    self : Arc1
        An Arc1 object

    Returns
    -------
    Zc: complex
        Complex coordinates of the center of the Arc1
    """

    self.check()

    # The center is on the bisection of [begin, end]
    z1 = self.begin
    z2 = self.end
    R = self.radius
    D12 = np_abs(z2 - z1)  # length of segment [begin,end]

    # Centre at the middle of begin and end (distance(Z1, Z2) = diameter )
    if np_abs(D12 - np_abs(2 * R)) < 1e-6:
        Zc = (z2 + z1) / 2.0
    else:
        # In the coordinate system begin on center and end on X > 0 axis
        if R > 0:  # Center is above the segment
            Zc = D12 / 2 + 1j * sqrt(R**2 - (D12 / 2)**2)
        else:
            Zc = D12 / 2 - 1j * sqrt(R**2 - (D12 / 2)**2)
        # Go back to the original coordinate system
        Zc = Zc * exp(1j * np_angle(z2 - z1)) + z1

    # Return (0,0) if the point is too close from 0
    if np_abs(Zc) < 1e-6:
        Zc = 0

    return Zc
Esempio n. 15
0
def comp_angle_d_axis(self):
    """Compute the angle between the X axis and the first d+ axis
    By convention a "Tooth" is centered on the X axis

    Parameters
    ----------
    self : LamSlotWind
        A LamSlotWind object

    Returns
    -------
    d_angle : float
        angle between the X axis and the first d+ axis
    """

    MMF = self.comp_mmf_unit()
    p = self.get_pole_pair_number()

    # Get the unit mmf FFT and angle values
    results = MMF.get_along("angle")
    angle_rotor = results["angle"]
    results = MMF.get_along("wavenumber")
    wavenumber = results["wavenumber"]
    mmf_ft = results[MMF.symbol]

    # Find the angle where the FFT is max
    indr_fund = np_abs(wavenumber - p).argmin()
    phimax = np_angle(mmf_ft[indr_fund])
    magmax = np_abs(mmf_ft[indr_fund])
    mmf_waveform = magmax * cos(p * angle_rotor + phimax)
    ind_max = argmax(mmf_waveform)
    d_angle = angle_rotor[ind_max]

    # Get the first angle according to symmetry
    (sym, _) = self.comp_sym()
    return d_angle % (2 * pi / sym)
Esempio n. 16
0
    def get_slot(self):
        """Generate the SlotUD object corresponding to the selected lines

        Parameters
        ----------
        self : DXF_Slot
            a DXF_Slot object

        Returns
        -------
        sot : SlotUD
            User defined slot according to selected lines
        """

        if self.lf_scaling.value() == 0:  # Avoid error
            self.lf_scaling.setValue(1)

        # Get all the selected lines
        line_list = list()
        point_list = list()
        for ii, line in enumerate(self.line_list):
            if self.selected_list[ii]:
                line_list.append(line.copy())
                line_list[-1].scale(self.lf_scaling.value())
                point_list.append(line_list[-1].get_begin())
                point_list.append(line_list[-1].get_end())

        # Find begin point
        single_list = list()
        for p1 in point_list:
            count = 0
            for p2 in point_list:
                if abs(p1 - p2) < Z_TOL:
                    count += 1
            if count == 1:
                single_list.append(p1)
        assert len(single_list) == 2
        Zbegin = single_list[argmin(np_angle(array(single_list)))]
        # Get begin line
        id_list = list()
        id_list.extend([
            ii for ii, line in enumerate(line_list)
            if abs(line.get_begin() - Zbegin) < Z_TOL or abs(line.get_end() -
                                                             Zbegin) < Z_TOL
        ])
        # Sort the lines (begin = end)
        curve_list = list()
        curve_list.append(line_list.pop(id_list[0]))
        if abs(curve_list[0].get_end() - Zbegin) < Z_TOL:
            # Reverse begin line if line end matches with begin point
            curve_list[0].reverse()
        while len(line_list) > 0:
            end = curve_list[-1].get_end()
            for ii in range(len(line_list)):
                if abs(line_list[ii].get_begin() - end) < Z_TOL:
                    break
                if abs(line_list[ii].get_end() - end) < Z_TOL:
                    line_list[ii].reverse()
                    break
            curve_list.append(line_list.pop(ii))

        # Create the Slot object
        slot = SlotUD(line_list=curve_list)
        slot.type_line_wind = self.c_type_line.currentIndex()
        begin_id = self.si_wind_begin_index.value()
        end_id = self.si_wind_end_index.value()
        if (begin_id < len(curve_list) and end_id < len(curve_list)
                and begin_id < end_id):
            slot.wind_begin_index = begin_id
            slot.wind_end_index = end_id
        else:
            slot.wind_begin_index = None
            slot.wind_end_index = None

        # Translate
        if self.Zcenter != 0:
            for line in curve_list:
                line.translate(-self.Zcenter * self.lf_scaling.value())

        # Rotation
        Z1 = curve_list[0].get_begin()
        Z2 = curve_list[-1].get_end()
        alpha = (np_angle(Z2) + np_angle(Z1)) / 2
        for line in curve_list:
            line.rotate(-1 * alpha)

        # Set metadata
        slot.Zs = self.si_Zs.value()

        return slot
Esempio n. 17
0
def solve_FEA(self, output, sym, angle, time, angle_rotor, Is, Ir):
    """
    Solve Elmer model to calculate airgap flux density, torque instantaneous/average/ripple values,
    flux induced in stator windings and flux density, field and permeability maps

    Parameters
    ----------
    self: MagElmer
        A MagElmer object
    output: Output
        An Output object
    sym: int
        Spatial symmetry factor
    time: ndarray
        Time vector for calculation
    angle: ndarray
        Angle vector for calculation
    Is : ndarray
        Stator current matrix (qs,Nt) [A]
    Ir : ndarray
        Stator current matrix (qs,Nt) [A]
    angle_rotor: ndarray
        Rotor angular position vector (Nt,)
    """

    project_name = self.get_path_save_fea(output)
    elmermesh_folder = project_name
    mesh_names_file = join(project_name, "mesh.names")
    boundaries = {}
    bodies = {}
    machine = output.simu.machine
    BHs = output.geo.stator.BH_curve  # Stator B(H) curve
    BHr = output.geo.rotor.BH_curve  # Rotor B(H) curve
    # Is = output.elec.Is  # Stator currents waveforms
    # Ir = output.elec.Ir  # Rotor currents waveforms
    Speed = output.elec.N0
    rotor_mat_file = join(project_name, "rotor_material.pmf")
    stator_mat_file = join(project_name, "stator_material.pmf")

    # TO-DO: Time vector must be greater than one
    timesize_str = np.array2string(
        np.diff(time),
        separator=" ",
        formatter={"float_kind": lambda x: "%.2e" % x})
    timelen = len(time) - 1
    ones_str = np.array2string(np.ones(timelen),
                               separator=" ",
                               formatter={"int": lambda x: "%d" % x})
    timeinterval_str = ones_str.replace(".", "")

    with open(mesh_names_file, "rt") as f:
        for line in f:
            fields = line.strip().split()
            if fields[0] == "$":
                field_name = fields[1]
                field_value = fields[3]
                # update dictionary
                # _settings['Geometry'][field_name] = field_value
                if field_name.count("BOUNDARY"):
                    boundaries[field_name] = field_value
                else:
                    bodies[field_name] = {
                        "id": field_value,
                        "mat": 1,  # Air by Default
                        "eq": 1,  # RigidMeshMapper by Default
                        "bf": None,
                        "tg": None,
                    }

    with open(rotor_mat_file, "wt") as ro:
        ro.write("! File Generated by pyleecan v{0}\n".format(__version__))
        ro.write("! Material Name: {0}\n"
                 "! B-H Curve Rotor Material\n"
                 "Electric Conductivity = 0\n"
                 "H-B Curve = Variable coupled iter\n"
                 " Real\t\tCubic Monotone\n".format(
                     machine.rotor.mat_type.name))
        for ii in range(BHr.shape[0]):
            ro.write("   {0}\t\t{1}\n".format(BHr[ii][1], BHr[ii][0]))
        ro.write("End\n")

    with open(stator_mat_file, "wt") as ro:
        ro.write("! File Generated by pyleecan v{0}\n".format(__version__))
        ro.write("! Material Name: {0}\n"
                 "! B-H Curve Stator Material\n"
                 "Electric Conductivity = 0\n"
                 "H-B Curve = Variable coupled iter\n"
                 " Real\t\tCubic Monotone\n".format(
                     machine.stator.mat_type.name))
        for ii in range(BHs.shape[0]):
            ro.write("   {0}\t\t{1}\n".format(BHs[ii][1], BHs[ii][0]))
        ro.write("End\n")

    elmer_sim_file = join(project_name, "pyleecan_elmer.sif")
    pp = machine.stator.winding.p
    wind_mat = machine.stator.winding.comp_connection_mat(
        machine.stator.slot.Zs)
    Swire = machine.stator.winding.conductor.comp_surface()
    ror = machine.rotor.comp_radius_mec()
    sir = machine.stator.comp_radius_mec()
    with open(elmer_sim_file, "wt") as fo:
        fo.write("! File Generated by pyleecan v{0}\n".format(__version__))
        fo.write("$ WM = 2*pi*{0}/60        ! Mechanical Frequency [rad/s]\n".
                 format(Speed))
        fo.write("$ PP = {0}                ! Pole pairs\n".format(pp))
        fo.write("$ WE = PP*WM              ! Electrical Frequency [Hz]\n")

        magnet_dict = machine.rotor.hole[0].get_magnet_dict()
        magnet_0 = magnet_dict["magnet_0"]
        surf_list = machine.build_geometry(sym=1)
        pm_index = 6
        Mangle = list()
        Ncond_Aplus = 1
        Ncond_Aminus = 1
        Ncond_Bplus = 1
        Ncond_Bminus = 1
        Ncond_Cplus = 1
        Ncond_Cminus = 1
        Ncond_Dplus = 1
        Ncond_Dminus = 1
        Ncond_Eplus = 1
        Ncond_Eminus = 1
        Ncond_Fplus = 1
        Ncond_Fminus = 1
        for surf in surf_list:
            label = surface_label.get(surf.label, "UNKNOWN")
            if "MAGNET" in label:
                point_ref = surf.point_ref
                if "RAD" in label and "_N_" in label:  # Radial magnetization
                    mag = "theta"  # North pole magnet
                    magnetization_type = "radial"
                elif "RAD" in label:
                    mag = "theta + 180"  # South pole magnet
                    magnetization_type = "radial"
                elif "PAR" in label and "_N_" in label:
                    mag = np_angle(point_ref) * 180 / pi  # North pole magnet
                    magnetization_type = "parallel"
                elif "PAR" in label:
                    mag = np_angle(
                        point_ref) * 180 / pi + 180  # South pole magnet
                    magnetization_type = "parallel"
                elif "HALL" in label:
                    Zs = machine.rotor.slot.Zs
                    mag = str(-(Zs / 2 - 1)) + " * theta + 90 "
                    magnetization_type = "hallback"
                else:
                    continue
                if bodies.get(label, None) is not None:
                    Mangle.append(mag)
                    bodies[label]["mat"] = pm_index
                    bodies[label]["eq"] = 1
                    bodies[label]["bf"] = 1
                    bodies[label]["tg"] = 1
                    pm_index = pm_index + 1
            elif "W_STA" in label:
                st = label.split("_")
                Nrad_id = int(st[2][1:])  # zone radial coordinate
                Ntan_id = int(st[3][1:])  # zone tangential coordinate
                Zs_id = int(st[4][1:])  # Zone slot number coordinate
                # Get the phase value in the correct slot zone
                q_id = get_phase_id(wind_mat, Nrad_id, Ntan_id, Zs_id)
                Ncond = wind_mat[Nrad_id, Ntan_id, Zs_id, q_id]
                s = sign(Ncond)
                if bodies.get(label, None) is not None:
                    bodies[label]["mat"] = 5
                    bodies[label]["eq"] = 1
                    if q_id == 0 and s == 1:
                        bodies[label]["bf"] = 2
                        Ncond_Aplus = abs(Ncond)
                    elif q_id == 0 and s == -1:
                        bodies[label]["bf"] = 3
                        Ncond_Aminus = abs(Ncond)
                    elif q_id == 1 and s == 1:
                        bodies[label]["bf"] = 4
                        Ncond_Bplus = abs(Ncond)
                    elif q_id == 1 and s == -1:
                        bodies[label]["bf"] = 5
                        Ncond_Bminus = abs(Ncond)
                    elif q_id == 2 and s == 1:
                        bodies[label]["bf"] = 6
                        Ncond_Cplus = abs(Ncond)
                    elif q_id == 2 and s == -1:
                        bodies[label]["bf"] = 7
                        Ncond_Cminus = abs(Ncond)
                    elif q_id == 3 and s == 1:
                        bodies[label]["bf"] = 8
                        Ncond_Dplus = abs(Ncond)
                    elif q_id == 3 and s == -1:
                        bodies[label]["bf"] = 9
                        Ncond_Dminus = abs(Ncond)
                    elif q_id == 4 and s == 1:
                        bodies[label]["bf"] = 10
                        Ncond_Eplus = abs(Ncond)
                    elif q_id == 4 and s == -1:
                        bodies[label]["bf"] = 11
                        Ncond_Eminus = abs(Ncond)
                    elif q_id == 5 and s == 1:
                        bodies[label]["bf"] = 12
                        Ncond_Fplus = abs(Ncond)
                    elif q_id == 5 and s == -1:
                        bodies[label]["bf"] = 13
                        Ncond_Fminus = abs(Ncond)
                    else:
                        pass
            elif "ROTOR_LAM" in label and bodies.get(label, None) is not None:
                bodies[label]["mat"] = 4
                bodies[label]["eq"] = 1
                bodies[label]["bf"] = 1
                bodies[label]["tg"] = 1
            elif "STATOR_LAM" in label and bodies.get(label, None) is not None:
                bodies[label]["mat"] = 3
                bodies[label]["eq"] = 1
            elif "SHAFT" in label and bodies.get(label, None) is not None:
                bodies[label]["mat"] = 1
                bodies[label]["eq"] = 1
                bodies[label]["bf"] = 1
                bodies[label]["tg"] = 1
            elif "H_ROTOR" in label and bodies.get(label, None) is not None:
                bodies[label]["mat"] = 1
                bodies[label]["eq"] = 1
                bodies[label]["bf"] = 1
                bodies[label]["tg"] = 1
            else:
                pass

        # The following bodies are not in the dictionary
        bodies["AG_INT"]["bf"] = 1
        bodies["SB_INT"]["bf"] = 1

        No_Magnets = pm_index - 6
        magnet_temp = 20.0  # Magnet Temperature Fixed for now
        Hcm20 = magnet_0.mat_type.mag.Hc
        Brm20 = magnet_0.mat_type.mag.Brm20
        kt = 0.01  # Br Temperature Coefficient fixed for now
        Br = Brm20 * (1 + kt * 0.01 * (magnet_temp - 20.0))
        magnet_permeability = magnet_0.mat_type.mag.mur_lin
        rho20_m = magnet_0.mat_type.elec.rho
        kt_m = 0.01  # Rho Temperature Coefficient fixed for now
        rho_m = rho20_m * (1 + kt_m * (magnet_temp - 20.0))
        conductivity_m = 1.0 / rho_m

        skip_steps = 1  # Fixed for now
        degrees_step = 1  # Fixed for now
        current_angle = 0 - pp * degrees_step * skip_steps
        angle_shift = self.angle_rotor_shift - self.angle_stator_shift
        rotor_init_pos = angle_shift - degrees_step * skip_steps
        Ncond = 1  # Fixed for Now
        Cp = 1  # Fixed for Now
        qs = len(machine.stator.get_name_phase())

        fo.write("$ H_PM = {0}              ! Magnetization [A/m]\n".format(
            round(Hcm20, 2)))
        fo.write(
            "$ Shift = 2*pi/{0}        ! N-phase machine [rad]\n".format(qs))
        fo.write("$ Gamma = {0}*pi/180      ! Current Angle [rad]\n".format(
            round(current_angle, 2)))
        fo.write(
            "$ Ncond = {0}             ! Conductors per coil\n".format(Ncond))
        fo.write("$ Cp = {0}                ! Parallel paths\n".format(Cp))
        fo.write(
            "$ Is = {0}                ! Stator current [A]\n".format(0.0))
        fo.write("$ Aaxis = {0}             ! Axis Coil A [deg]\n".format(0.0))
        fo.write("$ Carea = {0}             ! Coil Side Conductor Area [m2]\n".
                 format(Swire))

        for mm in range(1, No_Magnets + 1):
            fo.write(
                "$ Mangle{0} = {1}      ! Magnetization Angle [deg]\n".format(
                    mm, round(Mangle[mm - 1], 2)))

        fo.write("$ Nsteps = {0}            !\n".format(2))
        fo.write("$ StepDegrees = {0}       !\n".format(degrees_step))
        fo.write("$ DegreesPerSec = WM*180.0/pi  !\n")
        fo.write("$ RotorInitPos = Aaxis - 360 / (4*PP) + {}!\n".format(
            round(rotor_init_pos, 2)))

        fo.write("\nHeader\n"
                 "\tCHECK KEYWORDS Warn\n"
                 '\tMesh DB "{0}"\n'
                 '\tInclude Path "."\n'
                 '\tResults Directory "{1}"\n'
                 "End\n".format(elmermesh_folder, elmermesh_folder))

        fo.write("\nConstants\n"
                 "\tPermittivity of Vacuum = 8.8542e-12\n"
                 "End\n")

        fo.write(
            "\nSimulation\n"
            "\tMax Output Level = 4\n"
            "\tCoordinate System = Cartesian 2D\n"
            "\tCoordinate Scaling = {0}\n"
            "\tSimulation Type = Transient\n"
            "\tTimestepping Method = BDF\n"
            "\tBDF Order = 2\n"
            #                 "\tTimestep Sizes = $ (StepDegrees / DegreesPerSec)  ! sampling time\n"
            #                 "\tTimestep Intervals = $ Nsteps              ! steps\n"
            #                 "\tOutput Intervals = 1\n"
            "\tTimestep Sizes({1}) = {2}\n"
            "\tTimestep Intervals({1}) = {3}\n"
            "\tUse Mesh Names = Logical True\n"
            "End\n".format(1.0, timelen, timesize_str[1:-1],
                           timeinterval_str[1:-1]))

        fo.write("\n!--- MATERIALS ---\n")
        fo.write("Material 1\n"
                 '\tName = "Air"\n'
                 "\tRelative Permeability = 1\n"
                 "\tElectric Conductivity = 0\n"
                 "End\n")

        fo.write("\nMaterial 2\n"
                 '\tName = "Insulation"\n'
                 "\tRelative Permeability = 1\n"
                 "\tElectric Conductivity = 0\n"
                 "End\n")

        fo.write("\nMaterial 3\n"
                 '\tName = "StatorMaterial"\n'
                 '\tInclude "{0}"\n'
                 "End\n".format(stator_mat_file))

        fo.write("\nMaterial 4\n"
                 '\tName = "RotorMaterial"\n'
                 '\tInclude "{0}"\n'
                 "End\n".format(rotor_mat_file))

        winding_temp = 20.0  # Fixed for Now
        rho20 = machine.stator.winding.conductor.cond_mat.elec.rho
        kt = 0.01  # Br Temperature Coefficient fixed for now
        rho = rho20 * (1 + kt * (winding_temp - 20.0))
        conductivity = 1.0 / rho

        fo.write("\nMaterial 5\n"
                 '\tName = "Copper"\n'
                 "\tRelative Permeability = 1\n"
                 "\tElectric Conductivity = {0}\n"
                 "End\n".format(round(conductivity, 2)))

        magnets_per_pole = No_Magnets  # TO-DO: Assumes only one pole drawn
        for m in range(1, magnets_per_pole + 1):
            mat_number = 5 + m
            if magnetization_type == "parallel":
                fo.write(
                    "\nMaterial {0}\n"
                    '\tName = "PM_{1}"\n'
                    "\tRelative Permeability = {2}\n"
                    "\tMagnetization 1 = Variable time, timestep size\n"
                    '\t\tReal MATC  "H_PM*cos(WM*(tx(0)-tx(1)) + {3}*pi/PP + {3}*pi + Aaxis*pi/180 + (Mangle{1}*pi/180))"\n'
                    "\tMagnetization 2 = Variable time, timestep size\n"
                    '\t\tReal MATC "H_PM*sin(WM*(tx(0)-tx(1)) + {3}*pi/PP + {3}*pi + Aaxis*pi/180 + (Mangle{1}*pi/180))"\n'
                    "\tElectric Conductivity = {4}\n"
                    "End\n".format(
                        mat_number,
                        m,
                        magnet_permeability,
                        int((m - 1) / magnets_per_pole),
                        round(conductivity_m, 2),
                    ))
            elif magnetization_type == "radial":
                fo.write(
                    "\nMaterial {0}\n"
                    '\tName = "PM_{1}"\n'
                    "\tRelative Permeability = {2}\n"
                    "\tMagnetization 1 = Variable Coordinate\n"
                    '\t\tReal MATC  "H_PM*cos(atan2(tx(1),tx(0)) + {3}*pi)"\n'
                    "\tMagnetization 2 = Variable Coordinate\n"
                    '\t\tReal MATC "H_PM*sin(atan2(tx(1),tx(0)) + {3}*pi)"\n'
                    "\tElectric Conductivity = {4}\n"
                    "End\n".format(
                        mat_number,
                        m,
                        magnet_permeability,
                        m - 1,
                        round(conductivity_m, 2),
                    ))
            elif magnetization_type == "perpendicular":
                fo.write(
                    "\nMaterial {0}\n"
                    '\tName = "PM_{1}"\n'
                    "\tRelative Permeability = {2}\n"
                    "\tMagnetization 1 = Variable time, timestep size\n"
                    '\t\tReal MATC  "H_PM*cos(WM*(tx(0)-tx(1)) + {3}*pi/PP + {3}*pi + Aaxis*pi/180 + (Mangle{1}*pi/180))"\n'
                    "\tMagnetization 2 = Variable time, timestep size\n"
                    '\t\tReal MATC "H_PM*sin(WM*(tx(0)-tx(1)) + {3}*pi/PP + {3}*pi + Aaxis*pi/180 + (Mangle{1}*pi/180))"\n'
                    "\tElectric Conductivity = {4}\n"
                    "End\n".format(
                        mat_number,
                        m,
                        magnet_permeability,
                        int((m - 1) / magnets_per_pole),
                        round(conductivity_m, 2),
                    ))
            else:
                fo.write("\nMaterial {0}\n"
                         '\tName = "PM_{1}"\n'
                         "\tRelative Permeability = {2}\n"
                         "\tElectric Conductivity = {4}\n"
                         "End\n".format(mat_number, m, magnet_permeability,
                                        round(conductivity_m, 2)))

        fo.write("\n!--- BODY FORCES ---\n")

        # fo.write("Body Force 1\n"
        #          "\tName = \"BodyForce_Rotation\"\n"
        #          "\t$omega = (180/pi)*WM\n"
        #          "\tMesh Rotate 3 = Variable time, timestep size\n"
        #          "\t\tReal MATC \"omega*(tx(0)-tx(1)) + RotorInitPos\"\n"
        #          "End\n")
        fo.write("Body Force 1\n"
                 '\tName = "BodyForce_Rotation"\n'
                 "\tMesh Rotate 3 = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt],
                                                     angle[tt] * 180.0 / pi))
        fo.write("\tEnd\n" "End\n")

        # fo.write("Body Force 2\n"
        #          "\tName = \"J_A_PLUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) + Gamma)\"\n"
        #          "End\n".format(Ncond_Aplus))

        # fo.write("Body Force 3\n"
        #          "\tName = \"J_A_MINUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"-(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) + Gamma)\"\n"
        #          "End\n".format(Ncond_Aminus))

        # fo.write("Body Force 4\n"
        #          "\tName = \"J_B_PLUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Bplus))
        #
        # fo.write("Body Force 5\n"
        #          "\tName = \"J_B_MINUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"-(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Bminus))
        #
        # fo.write("Body Force 6\n"
        #          "\tName = \"J_C_PLUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 2*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Cplus))
        #
        # fo.write("Body Force 7\n"
        #          "\tName = \"J_C_MINUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"-(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 2*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Cminus))
        #
        # fo.write("Body Force 8\n"
        #          "\tName = \"J_D_PLUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 3*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Dplus))
        #
        # fo.write("Body Force 9\n"
        #          "\tName = \"J_D_MINUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"-(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 3*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Dminus))
        #
        # fo.write("Body Force 10\n"
        #          "\tName = \"J_E_PLUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 4*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Eplus))
        #
        # fo.write("Body Force 11\n"
        #          "\tName = \"J_E_MINUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"-(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 4*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Eminus))
        #
        # fo.write("Body Force 12\n"
        #          "\tName = \"J_F_PLUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 5*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Fplus))
        #
        # fo.write("Body Force 13\n"
        #          "\tName = \"J_F_MINUS\"\n"
        #          "\tCurrent Density = Variable time, timestep size\n"
        #          "\t\tReal MATC \"-(Is/Carea) * ({0}/Cp) * sin(WE * (tx(0)-tx(1)) - 5*Shift + Gamma)\"\n"
        #          "End\n".format(Ncond_Fminus))

        fo.write("Body Force 2\n"
                 '\tName = "J_A_PLUS"\n'
                 "\tCurrent Density = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt], Is[0, tt]))
        fo.write("\tEnd\n" "End\n")

        fo.write("Body Force 3\n"
                 '\tName = "J_A_MINUS"\n'
                 "\tCurrent Density = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt], -Is[0, tt]))
        fo.write("\tEnd\n" "End\n")

        fo.write("Body Force 4\n"
                 '\tName = "J_B_PLUS"\n'
                 "\tCurrent Density = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt], Is[1, tt]))
        fo.write("\tEnd\n" "End\n")

        fo.write("Body Force 5\n"
                 '\tName = "J_B_MINUS"\n'
                 "\tCurrent Density = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt], -Is[1, tt]))
        fo.write("\tEnd\n" "End\n")

        fo.write("Body Force 6\n"
                 '\tName = "J_C_PLUS"\n'
                 "\tCurrent Density = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt], Is[2, tt]))
        fo.write("\tEnd\n" "End\n")

        fo.write("Body Force 7\n"
                 '\tName = "J_C_MINUS"\n'
                 "\tCurrent Density = Variable time\n"
                 "\t\tReal\n")
        for tt in range(0, timelen + 1):
            fo.write("\t\t{:.2e}\t\t{:.3f}\n".format(time[tt], -Is[2, tt]))
        fo.write("\tEnd\n" "End\n")

        fo.write("\n!--- BODIES ---\n")
        for k, v in bodies.items():
            bid = bodies[k]["id"]
            beq = bodies[k]["eq"]
            bmat = bodies[k]["mat"]
            bf = bodies[k]["bf"]
            btg = bodies[k]["tg"]
            fo.write("Body {0}\n"
                     "\tName = {1}\n"
                     "\tEquation = {2}\n"
                     "\tMaterial = {3}\n".format(bid, k, beq, bmat))
            if bf is not None:
                fo.write("\tBody Force = {0}\n".format(bf))
            if btg is not None:
                fo.write("\tTorque Groups = Integer {0}\n".format(btg))
            if k == "SB_INT":
                fo.write("\tR Inner = Real {0}\n"
                         "\tR Outer = Real {1}\n".format(ror, sir))
            fo.write("End\n\n")

        fo.write("Equation 1\n"
                 '\tName = "Model_Domain"\n'
                 "\tActive Solvers(6) = 1 2 3 4 5 6\n"
                 "End\n")

        fo.write("\n!--- SOLVERS ---\n")
        fo.write("Solver 1\n"
                 "\tExec Solver = Before Timestep\n"
                 "\tEquation = MeshDeform\n"
                 '\tProcedure = "RigidMeshMapper" "RigidMeshMapper"\n'
                 "End\n")

        fo.write("\nSolver 2\n"
                 "\tEquation = MgDyn2D\n"
                 '\tProcedure = "MagnetoDynamics2D" "MagnetoDynamics2D"\n'
                 "\tExec Solver = Always\n"
                 "\tVariable = A\n")
        fo.write(
            "\tNonlinear System Convergence Tolerance = {0}\n".format(1e-6))
        fo.write("\tNonlinear System Max Iterations = {0}\n".format(100))
        fo.write("\tNonlinear System Min Iterations = {0}\n".format(1))
        fo.write(
            "\tNonlinear System Newton After Iterations = {0}\n".format(5))
        fo.write("\tNonlinear System Relaxation Factor = {0}\n".format(0.9))
        fo.write("\tNonlinear System Convergence Without Constraints = {0}\n".
                 format("Logical True"))
        fo.write("\tExport Lagrange Multiplier = {0}\n".format("Logical True"))
        fo.write("\tLinear System Abort Not Converged = {0}\n".format(
            "Logical False"))
        fo.write("\tLinear System Solver = {0}\n".format("Direct"))
        fo.write("\tLinear System Direct Method = {0}\n".format("umfpack"))
        fo.write("\tOptimize Bandwidth = {0}\n".format("Logical True"))
        fo.write("\tLinear System Preconditioning =  {0}\n".format("ILU2"))
        fo.write("\tLinear System Max Iterations =  {0}\n".format(5000))
        fo.write("\tLinear System Residual Output =  {0}\n".format(20))
        fo.write("\tLinear System Convergence Tolerance =  {0}\n".format(1e-7))
        fo.write("\tMortar BCs Additive =  {0}\n".format("Logical True"))
        fo.write("End\n")

        fo.write(
            "\nSolver 3\n"
            "\tExec Solver = Always\n"
            "\tEquation = CalcFields\n"
            '\tPotential Variable = "A"\n'
            '\tProcedure = "MagnetoDynamics" "MagnetoDynamicsCalcFields"\n'
            "\tCalculate Nodal Forces = Logical True\n"
            "\tCalculate Magnetic Vector Potential = Logical True\n"
            "\tCalculate Winding Voltage = Logical True\n"
            "\tCalculate Current Density = Logical True\n"
            "\tCalculate Maxwell Stress = Logical True\n"
            "\tCalculate JxB = Logical True\n"
            "\tCalculate Magnetic Field Strength = Logical True\n"
            "End\n")

        fo.write("\nSolver 4\n"
                 "\tExec Solver = After Timestep\n"
                 '\tProcedure = "ResultOutputSolve" "ResultOutputSolver"\n'
                 '\tOutput File Name = "{0}"\n'
                 "\tVtu Format = True\n"
                 "\tBinary Output = True\n"
                 "\tSingle Precision = True\n"
                 "\tSave Geometry Ids = True\n"
                 "\tShow Variables = True\n"
                 "End\n".format("step"))

        fo.write("\nSolver 5\n"
                 "\tExec Solver = After Timestep\n"
                 "\tEquation = SaveLine\n"
                 '\tFilename = "{0}"\n'
                 '\tProcedure = "SaveData" "SaveLine"\n'
                 "\tVariable 1 = Magnetic Flux Density 1\n"
                 "\tVariable 2 = Magnetic Flux Density 2\n"
                 "\tVariable 3 = Magnetic Flux Density 3\n"
                 "\tVariable 4 = Magnetic Flux Density e 1\n"
                 "\tVariable 5 = Magnetic Flux Density e 2\n"
                 "\tVariable 6 = Magnetic Flux Density e 3\n"
                 "End\n".format("lines.dat"))

        fo.write("\nSolver 6\n"
                 "\tExec Solver = After Timestep\n"
                 '\tFilename = "{0}"\n'
                 '\tProcedure = "SaveData" "SaveScalars"\n'
                 "\tShow Norm Index = 1\n"
                 "End\n".format("scalars.dat"))

        fo.write("\n!--- BOUNDARIES ---\n")
        for k, v in boundaries.items():
            if k == "VP0_BOUNDARY":
                fo.write("Boundary Condition {0}\n"
                         "\tName = {1}\n"
                         "\tA = Real 0\n"
                         "End\n\n".format(v, k))
            elif k == "MASTER_STATOR_BOUNDARY":
                for k1, v1 in boundaries.items():
                    if k1 == "SLAVE_STATOR_BOUNDARY":
                        slave = v1
                        break
                if not self.is_periodicity_a:
                    fo.write("Boundary Condition {0}\n"
                             "\tName = {1}\n"
                             "\tMortar BC = Integer {2}\n"
                             "\tMortar BC Static = Logical True\n"
                             "\tRadial Projector = Logical True\n"
                             "\tGalerkin Projector = Logical True\n"
                             "End\n\n".format(v, k, slave))
                else:
                    fo.write("Boundary Condition {0}\n"
                             "\tName = {1}\n"
                             "\tMortar BC = Integer {2}\n"
                             "\tMortar BC Static = Logical True\n"
                             "\tAnti Radial Projector = Logical True\n"
                             "\tGalerkin Projector = Logical True\n"
                             "End\n\n".format(v, k, slave))
            elif k == "MASTER_ROTOR_BOUNDARY":
                for k1, v1 in boundaries.items():
                    if k1 == "SLAVE_ROTOR_BOUNDARY":
                        slave = v1
                        break
                if not self.is_periodicity_a:
                    fo.write("Boundary Condition {0}\n"
                             "\tName = {1}\n"
                             "\tMortar BC = Integer {2}\n"
                             "\tMortar BC Static = Logical True\n"
                             "\tRadial Projector = Logical True\n"
                             "\tGalerkin Projector = Logical True\n"
                             "End\n\n".format(v, k, slave))
                else:
                    fo.write("Boundary Condition {0}\n"
                             "\tName = {1}\n"
                             "\tMortar BC = Integer {2}\n"
                             "\tMortar BC Static = Logical True\n"
                             "\tAnti Radial Projector = Logical True\n"
                             "\tGalerkin Projector = Logical True\n"
                             "End\n\n".format(v, k, slave))
            elif k == "SB_STATOR_BOUNDARY":
                for k1, v1 in boundaries.items():
                    if k1 == "SB_ROTOR_BOUNDARY":
                        slave = v1
                        break
                if not self.is_periodicity_a:
                    fo.write("Boundary Condition {0}\n"
                             "\tName = {1}\n"
                             "\tMortar BC = Integer {2}\n"
                             "\tRotational Projector = Logical True\n"
                             "\tGalerkin Projector = Logical True\n"
                             "End\n\n".format(v, k, slave))
                else:
                    fo.write("Boundary Condition {0}\n"
                             "\tName = {1}\n"
                             "\tMortar BC = Integer {2}\n"
                             "\tAnti Rotational Projector = Logical True\n"
                             "\tGalerkin Projector = Logical True\n"
                             "End\n\n".format(v, k, slave))
            elif k == "AIRGAP_ARC_BOUNDARY":
                fo.write("Boundary Condition {0}\n"
                         "\tName = {1}\n"
                         "\tSave Line = True\n"
                         "End\n\n".format(v, k))
            else:
                fo.write("Boundary Condition {0}\n"
                         "\tName = {1}\n"
                         "End\n\n".format(v, k))

    # setup Elmer solver
    # ElmerSolver v8.4 must be installed and in the PATH

    elmer_settings = join(project_name, "pyleecan_elmer.sif")
    ElmerSolver_binary = get_path_binary("ElmerSolver")
    cmd_elmersolver = [
        ElmerSolver_binary,
        elmer_settings,
    ]
    self.get_logger().info("Calling ElmerSolver: " +
                           " ".join(map(str, cmd_elmersolver)))
    elmersolver = subprocess.Popen(cmd_elmersolver,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
    (stdout, stderr) = elmersolver.communicate()
    elmersolver.wait()
    self.get_logger().info(stdout.decode("UTF-8"))
    if elmersolver.returncode != 0:
        self.get_logger().info("ElmerSolver [Error]: " +
                               stderr.decode("UTF-8"))
        return False
    elmersolver.terminate()
    self.get_logger().info("ElmerSolver call complete!")

    self.get_meshsolution()

    Na = angle.size
    Nt = time.size

    # Loading parameters for readibility
    L1 = output.simu.machine.stator.comp_length()
    save_path = self.get_path_save(output)
    # FEM_dict = output.mag.FEM_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
        Phi_wind_stator = zeros((Nt, qs))
    else:
        Phi_wind_stator = None

    # Initialize results matrix
    Br = zeros((Nt, Na))
    Bt = zeros((Nt, Na))
    Bz = zeros((Nt, Na))
    Tem = zeros((Nt))
    # Phi_wind_stator = zeros((Nt, qs))

    # compute the data for each time step
    # TODO Other than FEMM, in Elmer I think it's possible to compute
    #      all time steps at once
    self.get_logger().debug("Solving Simulation")

    # run the computation
    if self.nb_worker > 1:
        # TODO run solver in parallel
        pass
    else:
        # TODO run solver 'normal'
        pass

    # get the air gap flux result
    # TODO add function (or method)
    # ii -> Time, jj -> Angle
    # Br[ii, jj], Bt[ii, jj] = get_airgap_flux()

    # get the torque
    # TODO add function (or method)
    # Tem[ii] = comp_Elmer_torque(FEM_dict, sym=sym)

    # flux linkage computation
    # if Phi_wind_stator is not None:
    #     # TODO
    #     # Phi_wind[ii, :] = comp_Elmer_Phi_wind()
    #     pass

    return Br, Bt, Bz, Tem, Phi_wind_stator
Esempio n. 18
0
    def get_hole(self):
        """Generate the HoleUD object corresponding to the selected surfaces

        Parameters
        ----------
        self : DXF_Hole
            a DXF_Hole object

        Returns
        -------
        hole : HoleUD
            User defined hole according to selected surfaces
        """

        if self.lf_scaling.value() == 0:  # Avoid error
            self.lf_scaling.setValue(1)
        hole = HoleUD(surf_list=[])
        bottom_list = list()
        offset_list = list()
        # Set labels
        Nmag = 0
        for ii in range(self.w_surface_list.rowCount()):
            hole.surf_list.append(self.surf_list[ii].copy())
            hole.surf_list[ii].scale(self.lf_scaling.value())
            if self.w_surface_list.cellWidget(ii,
                                              TYPE_COL).currentIndex() == 0:
                hole.surf_list[ii].label = "Hole"
            else:
                hole.surf_list[ii].label = "HoleMagnet"
                Nmag += 1
            bottom_list.append(self.line_list[int(
                self.w_surface_list.cellWidget(ii, REF_COL).currentText())])
            offset_list.append(
                self.w_surface_list.cellWidget(ii, OFF_COL).value())
        # Create magnet objects
        hole.magnet_dict = dict()
        for ii in range(Nmag):
            hole.magnet_dict["magnet_" +
                             str(ii)] = Magnet(type_magnetization=1)

        # Sort the surfaces
        angles = [np_angle(surf.point_ref) for surf in hole.surf_list]
        idx = sorted(range(len(angles)), key=lambda k: angles[k])
        surf_list_sorted = [hole.surf_list[ii] for ii in idx]
        bottom_list_sorted = [bottom_list[ii] for ii in idx]
        offset_list_sorted = [offset_list[ii] for ii in idx]
        hole.surf_list = surf_list_sorted

        # Rotation
        Zref = sum([surf.point_ref for surf in hole.surf_list])
        for surf in hole.surf_list:
            surf.rotate(-1 * np_angle(Zref))

        # Magnetization dict
        mag_dict = dict()
        Nmag = 0
        for ii in range(len(hole.surf_list)):
            if "Magnet" in hole.surf_list[ii].label:
                line = bottom_list_sorted[ii].copy()
                line.rotate(-1 * np_angle(Zref))
                mag_dict["magnet_" + str(Nmag)] = line.comp_normal()
                mag_dict["magnet_" +
                         str(Nmag)] += offset_list_sorted[ii] * pi / 180
                Nmag += 1
        hole.magnetization_dict_offset = mag_dict

        # Set metadata
        hole.Zh = self.si_Zh.value()
        for magnet in hole.magnet_dict.values():
            magnet.Lmag = self.lf_mag_len.value()

        # Remove all materials => To be set in GUI
        hole.mat_void = None
        for magnet in hole.magnet_dict.values():
            magnet.mat_type = None

        return hole
Esempio n. 19
0
 def Phase(self):
     return np_unwrap(np_angle(self.amp))
Esempio n. 20
0
 def PhaseTrim(self):
     return np_unwrap(np_angle(self.ampTrim))
Esempio n. 21
0
def xy_to_rphi(x, y):
    affixe = x + 1j * y
    r = np_abs(affixe)
    phi = (np_angle(affixe) + 2 * pi) % (2 * pi)

    return (r, phi)
Esempio n. 22
0
    def get_slot(self):
        """Generate the SlotUD object corresponding to the selected lines

        Parameters
        ----------
        self : DXF_Slot
            a DXF_Slot object

        Returns
        -------
        sot : SlotUD
            User defined slot according to selected lines
        """

        if self.lf_scaling.value() == 0:  # Avoid error
            self.lf_scaling.setValue(1)

        # Get all the selected lines
        line_list = list()
        point_list = list()
        for ii, line in enumerate(self.line_list):
            if self.selected_list[ii]:
                line_list.append(line.copy())
                line_list[-1].scale(self.lf_scaling.value())
                point_list.append(line_list[-1].get_begin())
                point_list.append(line_list[-1].get_end())

        # Find begin point
        single_list = list()
        for p1 in point_list:
            count = 0
            for p2 in point_list:
                if abs(p1 - p2) < Z_TOL:
                    count += 1
            if count == 1:
                single_list.append(p1)
        assert len(single_list) == 2
        Zbegin = single_list[argmin(np_angle(array(single_list)))]
        # Get begin line
        id_list = list()
        id_list.extend(
            [
                ii
                for ii, line in enumerate(line_list)
                if abs(line.get_begin() - Zbegin) < Z_TOL
                or abs(line.get_end() - Zbegin) < Z_TOL
            ]
        )
        # Sort the lines (begin = end)
        curve_list = list()
        curve_list.append(line_list.pop(id_list[0]))
        if abs(curve_list[0].get_end() - Zbegin) < Z_TOL:
            # Reverse begin line if line end matches with begin point
            curve_list[0].reverse()
        while len(line_list) > 0:
            end = curve_list[-1].get_end()
            for ii in range(len(line_list)):
                if abs(line_list[ii].get_begin() - end) < Z_TOL:
                    break
                if abs(line_list[ii].get_end() - end) < Z_TOL:
                    line_list[ii].reverse()
                    break
            curve_list.append(line_list.pop(ii))

        # Translate
        if self.Zcenter != 0:
            for line in curve_list:
                line.translate(-self.Zcenter * self.lf_scaling.value())

        # Check the first and last point are matching Rint
        Rbo = self.lam.get_Rbo()
        Zbegin = curve_list[0].get_begin()
        Zend = curve_list[-1].get_end()
        if abs(abs(Zbegin) - Rbo) > Z_TOL:
            QMessageBox().critical(
                self,
                self.tr("Error"),
                self.tr(
                    "First point of the slot is not on the bore radius:\nBore radius="
                    + str(Rbo)
                    + ", abs(First point)="
                    + str(abs(Zbegin))
                ),
            )
            return None
        if abs(abs(Zend) - Rbo) > Z_TOL:
            QMessageBox().critical(
                self,
                self.tr("Error"),
                self.tr(
                    "Last point of the slot is not on the bore radius:\nBore radius="
                    + str(Rbo)
                    + ", abs(Last point)="
                    + str(abs(Zend))
                ),
            )
            return None

        # Rotation
        Z1 = curve_list[0].get_begin()
        Z2 = curve_list[-1].get_end()
        alpha = (np_angle(Z2) + np_angle(Z1)) / 2
        for line in curve_list:
            line.rotate(-1 * alpha)

        # Enforce perfect match with Bore radius by adding Segments in needed
        Zbegin = curve_list[0].get_begin()
        Zb2 = Rbo * exp(1j * angle(Zbegin))
        if abs(Zb2 - Zbegin) > 1e-9:
            curve_list.insert(0, Segment(begin=Zb2, end=Zbegin))

        Zend = curve_list[-1].get_end()
        Ze2 = Rbo * exp(1j * angle(Zend))
        if abs(Ze2 - Zend) > 1e-9:
            curve_list.append(Segment(begin=Zend, end=Ze2))

        # Create the Slot object
        slot = SlotUD(line_list=curve_list)
        slot.type_line_wind = self.c_type_line.currentIndex()
        begin_id = self.si_wind_begin_index.value()
        end_id = self.si_wind_end_index.value()
        if (
            begin_id < len(curve_list)
            and end_id < len(curve_list)
            # and begin_id < end_id
        ):
            slot.wind_begin_index = begin_id
            slot.wind_end_index = end_id
        else:
            slot.wind_begin_index = None
            slot.wind_end_index = None
        slot.Zs = self.si_Zs.value()

        return slot
Esempio n. 23
0
    def get_hole(self):
        """Generate the HoleUD object corresponding to the selected surfaces

        Parameters
        ----------
        self : DXF_Hole
            a DXF_Hole object

        Returns
        -------
        hole : HoleUD
            User defined hole according to selected surfaces
        """

        if self.lf_scaling.value() == 0:  # Avoid error
            self.lf_scaling.setValue(1)
        hole = HoleUD(surf_list=[])
        bottom_list = list()
        offset_list = list()
        # Set labels
        Nmag = 0
        for ii in range(self.w_surface_list.rowCount()):
            hole.surf_list.append(self.surf_list[ii].copy())
            hole.surf_list[ii].scale(self.lf_scaling.value())
            if self.w_surface_list.cellWidget(ii,
                                              TYPE_COL).currentIndex() == 0:
                hole.surf_list[ii].label = "Hole"
            else:
                hole.surf_list[ii].label = "HoleMagnet"
                Nmag += 1
            bottom_list.append(self.line_list[int(
                self.w_surface_list.cellWidget(ii, REF_COL).currentText())])
            offset_list.append(
                self.w_surface_list.cellWidget(ii, OFF_COL).value())
        # Create magnet objects
        hole.magnet_dict = dict()
        for ii in range(Nmag):
            hole.magnet_dict["magnet_" +
                             str(ii)] = Magnet(type_magnetization=1)

        # Sort the surfaces
        angles = [np_angle(surf.point_ref) for surf in hole.surf_list]
        idx = sorted(range(len(angles)), key=lambda k: angles[k])
        surf_list_sorted = [hole.surf_list[ii] for ii in idx]
        bottom_list_sorted = [bottom_list[ii] for ii in idx]
        offset_list_sorted = [offset_list[ii] for ii in idx]
        hole.surf_list = surf_list_sorted

        # Rotation
        Zref = sum([surf.point_ref for surf in hole.surf_list])
        for surf in hole.surf_list:
            surf.rotate(-1 * np_angle(Zref))

        # Magnetization dict
        mag_dict = dict()
        Nmag = 0
        for ii in range(len(hole.surf_list)):
            if "Magnet" in hole.surf_list[ii].label:
                line = bottom_list_sorted[ii].copy()
                line.rotate(-1 * np_angle(Zref))
                mag_dict["magnet_" + str(Nmag)] = line.comp_normal()
                mag_dict["magnet_" +
                         str(Nmag)] += offset_list_sorted[ii] * pi / 180
                Nmag += 1
        hole.magnetization_dict_offset = mag_dict

        # Set metadata
        hole.Zh = self.si_Zh.value()
        for magnet in hole.magnet_dict.values():
            magnet.Lmag = self.lf_mag_len.value()

        # Remove all materials => To be set in GUI
        hole.mat_void = None
        for magnet in hole.magnet_dict.values():
            magnet.mat_type = None

        # Sort Hole then magnets
        # (for plot when Magnets are inside Hole surface)
        mag_list = list()
        hole_list = list()
        for surf in hole.surf_list:
            if "HoleMagnet" in surf.label:
                mag_list.append(surf)
            else:
                hole_list.append(surf)
        hole.surf_list = hole_list + mag_list

        # Correct hole ref_point (when Magnets are inside Hole surface)
        for surf in hole.surf_list:
            if "HoleMagnet" not in surf.label:
                line_list = surf.get_lines()
                # Get middle list
                middle_array = array([line.get_middle() for line in line_list])
                # Get the extrema line on the top or bottom of the hole
                if np_min(middle_array.imag) > 0 and np_max(
                        middle_array.imag) > 0:
                    start_idx = argmax(middle_array.imag)
                else:
                    start_idx = argmin(middle_array.imag)
                # Get the two lines middle besides the extrema line middle
                if start_idx == 0:
                    ref_mid = [
                        middle_array[-1], middle_array[0], middle_array[1]
                    ]
                elif start_idx == len(line_list) - 1:
                    ref_mid = [
                        middle_array[-2], middle_array[-1], middle_array[0]
                    ]
                else:
                    ref_mid = [
                        middle_array[start_idx - 1],
                        middle_array[start_idx],
                        middle_array[start_idx + 1],
                    ]
                # Barycenter of these middles as new reference
                surf.point_ref = sum(ref_mid) / 3

        return hole
Esempio n. 24
0
def get_yoke_side_line(self,
                       sym,
                       vent_surf_list,
                       ZBR=None,
                       ZTR=None,
                       ZBL=None,
                       ZTL=None):
    """Define the Yoke Side lines of a Lamination by taking into account sym and vent

    Parameters:
    self: Lamination
        a Lamination object
    sym : int
        Symmetry factor (1= full machine, 2= half of the machine...)
    vent_surf_list :
        List of the ventilation surfaces
    ZBR : Complex
        Yoke Side Limit point Bottom Right
    ZTR : Complex
        Yoke Side Limit point Top Right
    ZBL : Complex
        Yoke Side Limit point Bottom Left
    ZTL : Complex
        Yoke Side Limit point Top Left

    Returns:
    right_list, left_list: ([Line], [Line])
        List of the lines to draw the left and right side of the yoke
    """

    # Find the ventilation lines that collide with the Yoke Side
    inter_line_list_R, inter_line_list_L = list(), list()
    for surf in vent_surf_list:
        for line in surf.get_lines():
            if (line.prop_dict is not None
                    and BOUNDARY_PROP_LAB in line.prop_dict
                    and YS_LAB in line.prop_dict[BOUNDARY_PROP_LAB]):
                # Find if the line collide on right or left
                if abs(np_angle(line.get_middle())) < DELTA:
                    inter_line_list_R.append(line)
                else:
                    inter_line_list_L.append(line)

    # Yoke Limit point
    if ZBR is None:
        alpha = 2 * pi / sym
        ZBR = self.Rint
        ZTR = self.Rext
        ZBL = ZBR * exp(1j * alpha)
        ZTL = ZTR * exp(1j * alpha)

    lam_lab = self.get_label()
    if self.is_internal:
        right_list = merge_line_list(ZBR, ZTR, lam_lab + "_" + YSR_LAB,
                                     inter_line_list_R)
        left_list = merge_line_list(ZTL, ZBL, lam_lab + "_" + YSL_LAB,
                                    inter_line_list_L)
    else:
        left_list = merge_line_list(ZBL, ZTL, lam_lab + "_" + YSL_LAB,
                                    inter_line_list_L)
        right_list = merge_line_list(ZTR, ZBR, lam_lab + "_" + YSR_LAB,
                                     inter_line_list_R)

    return right_list, left_list
Esempio n. 25
0
def plot_schematics(
    self,
    is_default=False,
    is_add_point_label=False,
    is_add_schematics=True,
    is_add_main_line=True,
    type_add_active=True,
    save_path=None,
    is_show_fig=True,
):
    """Plot the schematics of the slot

    Parameters
    ----------
    self : VentilationPolar
        A VentilationPolar object
    is_default : bool
        True: plot default schematics, else use current slot values
    is_add_point_label : bool
        True to display the name of the points (Z1, Z2....)
    is_add_schematics : bool
        True to display the schematics information (W0, H0...)
    is_add_main_line : bool
        True to display "main lines" (slot opening and 0x axis)
    type_add_active : int
        0: No active surface, 1: active surface as winding, 2: active surface as magnet
    save_path : str
        full path including folder, name and extension of the file to save if save_path is not None
    is_show_fig : bool
        To call show at the end of the method
    """

    # Use some default parameter
    if is_default:
        hole = type(self)(
            H0=0.125,
            Zh=8,
            Alpha0=0.3,
            D0=0.05,
            W1=2 * pi / 16,
        )
        lam = LamHole(Rint=0.1,
                      Rext=0.2,
                      is_internal=True,
                      is_stator=False,
                      hole=[hole])
        hole.plot_schematics(
            is_default=False,
            is_add_point_label=is_add_point_label,
            is_add_schematics=is_add_schematics,
            is_add_main_line=is_add_main_line,
            type_add_active=type_add_active,
            save_path=save_path,
            is_show_fig=is_show_fig,
        )
    else:
        # Getting the main plot
        if self.parent is None:
            raise ParentMissingError(
                "Error: The hole is not inside a Lamination")
        lam = self.parent
        lam.plot(
            alpha=0,
            is_show_fig=False,
            is_lam_only=True,  # No magnet
        )  # center hole on Ox axis
        fig = plt.gcf()
        ax = plt.gca()
        point_dict = self._comp_point_coordinate()

        # Adding point label
        if is_add_point_label:
            for name, Z in point_dict.items():
                ax.text(
                    Z.real,
                    Z.imag,
                    name,
                    fontsize=P_FONT_SIZE,
                    bbox=TEXT_BOX,
                )

        # Adding schematics
        if is_add_schematics:
            # H0
            line = Segment(0, point_dict["Z1"])
            line.plot(
                fig=fig,
                ax=ax,
                color=ARROW_COLOR,
                linewidth=ARROW_WIDTH,
                label="H0",
                offset_label=self.D0 / 4 * 1j,
                is_arrow=True,
                fontsize=SC_FONT_SIZE,
            )
            # D0
            line = Segment(
                point_dict["Z1"] * exp(1j * 2 * pi / self.Zh),
                point_dict["Z2"] * exp(1j * 2 * pi / self.Zh),
            )
            line.plot(
                fig=fig,
                ax=ax,
                color=ARROW_COLOR,
                linewidth=ARROW_WIDTH,
                label="D0",
                offset_label=self.D0 / 4,
                is_arrow=True,
                fontsize=SC_FONT_SIZE,
            )
            # Alpha0
            line = Arc1(
                begin=self.H0 + self.D0 / 2,
                end=point_dict["Zc"],
                radius=self.H0 + self.D0 / 2,
                is_trigo_direction=True,
            )
            line.plot(
                fig=fig,
                ax=ax,
                color=ARROW_COLOR,
                linewidth=ARROW_WIDTH,
                label="Alpha0",
                offset_label=self.D0 / 4 * (1 - 1.5j),
                fontsize=SC_FONT_SIZE,
            )
            # W1
            Rtop = self.H0 + self.D0
            Rarc = (Rtop + lam.Rext) / 2
            line = Arc1(
                begin=Rarc * exp(1j * (self.Alpha0 - self.W1 / 2)),
                end=Rarc * exp(1j * (self.Alpha0 + self.W1 / 2)),
                radius=Rarc,
                is_trigo_direction=True,
            )
            line.plot(
                fig=fig,
                ax=ax,
                color=ARROW_COLOR,
                linewidth=ARROW_WIDTH,
                label="W1",
                offset_label=self.D0 / 4,
                fontsize=SC_FONT_SIZE,
            )

        if is_add_main_line:
            # Ox axis
            line = Segment(0, lam.Rext * 1.5)
            line.plot(
                fig=fig,
                ax=ax,
                color=MAIN_LINE_COLOR,
                linestyle=MAIN_LINE_STYLE,
                linewidth=MAIN_LINE_WIDTH,
            )
            # Center axis
            line = Segment(0, lam.Rext * 1.5 * exp(1j * self.Alpha0))
            line.plot(
                fig=fig,
                ax=ax,
                color=MAIN_LINE_COLOR,
                linestyle=MAIN_LINE_STYLE,
                linewidth=MAIN_LINE_WIDTH,
            )
            # H0 radius
            line = Arc1(
                begin=self.H0 * exp(-1j * pi / 2 * 0.9),
                end=self.H0 * exp(1j * pi / 2 * 0.9),
                radius=self.H0,
                is_trigo_direction=True,
            )
            line.plot(
                fig=fig,
                ax=ax,
                color=MAIN_LINE_COLOR,
                linestyle=MAIN_LINE_STYLE,
                linewidth=MAIN_LINE_WIDTH,
            )
            # H0+D0 radius
            line = Arc1(
                begin=(self.H0 + self.D0) * exp(-1j * pi / 2 * 0.9),
                end=(self.H0 + self.D0) * exp(1j * pi / 2 * 0.9),
                radius=(self.H0 + self.D0),
                is_trigo_direction=True,
            )
            line.plot(
                fig=fig,
                ax=ax,
                color=MAIN_LINE_COLOR,
                linestyle=MAIN_LINE_STYLE,
                linewidth=MAIN_LINE_WIDTH,
            )
            # Vent side 1 axis
            line = Segment(
                0, lam.Rext * 1.5 * exp(1j * np_angle(point_dict["Z1"])))
            line.plot(
                fig=fig,
                ax=ax,
                color=MAIN_LINE_COLOR,
                linestyle=MAIN_LINE_STYLE,
                linewidth=MAIN_LINE_WIDTH,
            )
            # Vent side 2 axis
            line = Segment(
                0, lam.Rext * 1.5 * exp(1j * np_angle(point_dict["Z3"])))
            line.plot(
                fig=fig,
                ax=ax,
                color=MAIN_LINE_COLOR,
                linestyle=MAIN_LINE_STYLE,
                linewidth=MAIN_LINE_WIDTH,
            )

        # Zooming and cleaning
        W = abs(point_dict["Z2"].imag) * 1.3
        Rint = self.parent.Rint
        Rext = self.parent.Rext

        plt.axis("equal")
        ax.set_ylim(-Rext / 10, Rext * 0.9)
        ax.set_xlim(Rext / 10, Rext)
        manager = plt.get_current_fig_manager()
        if manager is not None:
            manager.set_window_title(type(self).__name__ + " Schematics")
        ax.set_title("")
        ax.get_legend().remove()
        ax.set_axis_off()

        # Save / Show
        if save_path is not None:
            fig.savefig(save_path)
            plt.close()

        if is_show_fig:
            fig.show()