LossyGear

model LossyGear "Gear with mesh efficiency and bearing friction (stuck/rolling possible)"
    extends Modelica.Mechanics.Rotational.Icons.Gear;
    extends Modelica.Mechanics.Rotational.Interfaces.PartialElementaryTwoFlangesAndSupport2;

    parameter Real ratio(start = 1) "Transmission ratio (flange_a.phi/flange_b.phi)";
    parameter Real lossTable[:,5] = [0,1,1,0,0] "Array for mesh efficiencies and bearing friction depending on speed";

    extends Modelica.Thermal.HeatTransfer.Interfaces.PartialElementaryConditionalHeatPortWithoutT;

    Modelica.SIunits.Angle phi_a "Angle between left shaft flange and support";
    Modelica.SIunits.Angle phi_b "Angle between right shaft flange and support";
    Real sa(final unit = "1") "Path parameter for acceleration and torque loss";
    SI.AngularVelocity w_a "Angular velocity of flange_a with respect to support";
    SI.AngularAcceleration a_a "Angular acceleration of flange_a with respect to support";
    Real interpolation_result[1,4] "Result of interpolation in lossTable (= [eta_mf1, eta_mf2, tau_bf1, tau_bf2])";
    Real eta_mf1(unit = "1") "Mesh efficiency in case that flange_a is driving";
    Real eta_mf2(unit = "1") "Mesh efficiency in case that flange_b is driving";
    SI.Torque tau_bf_a "Bearing friction torque on flange_a side";
    SI.Torque tau_eta "Torque that determines the driving side (= if forwardSliding then flange_a.tau-tau_bf_a else if backwardSliding then flange_a.tau+tau_bf_a else flange_a.tau)";
    SI.Torque tau_bf1 "Absolute resultant bearing friction torque with respect to flange_a in case that flange_a is driving (= |tau_bf_a*eta_mf1 + tau_bf_b/i|)";
    SI.Torque tau_bf2 "Absolute resultant bearing friction torque with respect to flange_a in case that flange_b is driving (= |tau_bf_a/eta_mf2 + tau_bf_b/i|)";
    SI.Torque quadrant1 "Torque loss if w_a > 0 and flange_a.tau >= 0";
    SI.Torque quadrant2 "Torque loss if w_a > 0 and flange_a.tau < 0";
    SI.Torque quadrant3 "Torque loss if w_a < 0 and flange_a.tau >= 0";
    SI.Torque quadrant4 "Torque loss if w_a < 0 and flange_a.tau < 0";
    SI.Torque quadrant1_p "Torque loss at w_a = 0+ to determine driving side (flange_a.tau >= 0)";
    SI.Torque quadrant2_p "Torque loss at w_a = 0+ to determine driving side (flange_a.tau < 0)";
    SI.Torque quadrant3_m "Torque loss at w_a = 0- to determine driving side (flange_a.tau >=0)";
    SI.Torque quadrant4_m "Torque loss at w_a = 0- to determine driving side (flange_a.tau < 0)";
    SI.Torque tauLoss "Torque loss due to friction in the gear teeth and in the bearings";
    SI.Torque tauLossMax "Torque loss for positive speed";
    SI.Torque tauLossMin "Torque loss for negative speed";
    SI.Torque tauLossMax_p "Torque loss for positive speed";
    SI.Torque tauLossMin_m "Torque loss for negative speed";
    Boolean tau_aPos(start = true) "Only for backwards compatibility (was previously: true, if torque of flange_a is not negative)";
    Boolean tau_etaPos(start = true) "true, if torque tau_eta is not negative";
    Boolean startForward(start = false) "true, if starting to roll forward";
    Boolean startBackward(start = false) "true, if starting to roll backward";
    Boolean locked(start = false) "true, if gear is locked";
    Boolean ideal "= true, if losses are neglected (that is lossTable = [0, 1, 1, 0, 0])";
    constant Integer Unknown = 3 "Value of mode is not known";
    constant Integer Free = 2 "Element is not active";
    constant Integer Forward = 1 "w_a > 0 (forward rolling)";
    constant Integer Stuck = 0 "w_a = 0 (forward rolling, locked or backward rolling)";
    constant Integer Backward = -1 "w_a < 0 (backward rolling)";
    Integer mode(final min = Backward, final max = Unknown, start = Free, fixed = true) "Mode of friction element (unknown, not active, forward/backward rolling, stuck)";
    SI.Torque tau_eta_p "tau_eta assuming positive omega";
    SI.Torque tau_eta_m "tau_eta assuming negative omega";
protected
    constant SI.AngularAcceleration unitAngularAcceleration = 1;
    constant SI.Torque unitTorque = 1;
    parameter Real eta_mf1_0(unit = "1") = Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,2], 0, 1);
    parameter Real eta_mf2_0(unit = "1") = Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,3], 0, 1);
    parameter SI.Torque tau_bf1_0 = abs(Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,4], 0, 1));
    parameter SI.Torque tau_bf2_0 = abs(Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,5], 0, 1));
    parameter SI.Torque tau_bf_a_0 = if Modelica.Math.isEqual(eta_mf1_0, 1, Modelica.Constants.eps) and Modelica.Math.isEqual(eta_mf2_0, 1, Modelica.Constants.eps) then 0.5 * tau_bf1_0 else (tau_bf1_0 - tau_bf2_0) / (eta_mf1_0 - eta_mf2_0 ^ (-1));
equation
    if Modelica.Math.isEqual(eta_mf1, 1, Modelica.Constants.eps) and Modelica.Math.isEqual(eta_mf2, 1, Modelica.Constants.eps) then 
        tau_bf_a = 0.5 * tau_bf1;
    else 
        tau_bf_a = (tau_bf1 - tau_bf2) / (eta_mf1 - eta_mf2 ^ (-1));
    end if;
    if ideal then 
        interpolation_result = [1,1,0,0];
        eta_mf1 = 1;
        eta_mf2 = 1;
        tau_bf1 = 0;
        tau_bf2 = 0;
    else 
        interpolation_result = [Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,2], noEvent(abs(w_a)), 1),Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,3], noEvent(abs(w_a)), 1),Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,4], noEvent(abs(w_a)), 1),Modelica.Math.Vectors.interpolate(lossTable[:,1], lossTable[:,5], noEvent(abs(w_a)), 1)];
        eta_mf1 = interpolation_result[1,1];
        eta_mf2 = interpolation_result[1,2];
        tau_bf1 = noEvent(abs(interpolation_result[1,3]));
        tau_bf2 = noEvent(abs(interpolation_result[1,4]));
    end if;
    assert(0 < abs(ratio), "Error in initialization of LossyGear: ratio may not be zero");
    0 = flange_b.tau + ratio * (flange_a.tau - tauLoss);
    a_a = unitAngularAcceleration * (if locked then 0 else sa - tauLoss / unitTorque);
    a_a = der(w_a);
    ideal = Modelica.Math.Matrices.isEqual(lossTable, [0,1,1,0,0], Modelica.Constants.eps);
    locked = not (ideal or pre(mode) == Forward or startForward or pre(mode) == Backward or startBackward);
    lossPower = tauLoss * w_a;
    mode = if ideal then Free else if (pre(mode) == Forward or startForward) and 0 < w_a then Forward else if (pre(mode) == Backward or startBackward) and w_a < 0 then Backward else Stuck;
    phi_a = ratio * phi_b;
    phi_a = flange_a.phi - phi_support;
    phi_b = flange_b.phi - phi_support;
    quadrant1 = (1 - eta_mf1) * flange_a.tau + tau_bf1;
    quadrant2 = (1 - eta_mf2 ^ (-1)) * flange_a.tau + tau_bf2;
    quadrant3 = (1 - eta_mf1) * flange_a.tau - tau_bf1;
    quadrant4 = (1 - eta_mf2 ^ (-1)) * flange_a.tau - tau_bf2;
    tauLoss = if ideal then 0 else if locked then sa * unitTorque else if startForward or pre(mode) == Forward then tauLossMax else tauLossMin;
    tau_aPos = tau_etaPos;
    tau_eta = if ideal then flange_a.tau else if locked then flange_a.tau else if startForward or pre(mode) == Forward then flange_a.tau - tau_bf_a else flange_a.tau + tau_bf_a;
    tau_eta_m = flange_a.tau + tau_bf_a_0;
    tau_eta_p = flange_a.tau - tau_bf_a_0;
    w_a = der(phi_a);
    quadrant1_p = (1 - eta_mf1_0) * flange_a.tau + tau_bf1_0;
    quadrant2_p = (1 - eta_mf2_0 ^ (-1)) * flange_a.tau + tau_bf2_0;
    quadrant3_m = (1 - eta_mf1_0) * flange_a.tau - tau_bf1_0;
    quadrant4_m = (1 - eta_mf2_0 ^ (-1)) * flange_a.tau - tau_bf2_0;
    startBackward = pre(mode) == Stuck and sa < tauLossMin_m / unitTorque or initial() and w_a < 0;
    startForward = pre(mode) == Stuck and tauLossMax_p / unitTorque < sa or initial() and 0 < w_a;
    tauLossMax = if tau_etaPos then quadrant1 else quadrant2;
    tauLossMax_p = if noEvent(0 < tau_eta_p) then quadrant1_p else quadrant2_p;
    tauLossMin = if tau_etaPos then quadrant4 else quadrant3;
    tauLossMin_m = if noEvent(0 < tau_eta_m) then quadrant4_m else quadrant3_m;
    tau_etaPos = 0 <= tau_eta;

    annotation (
        Documentation(info = "<html>\n<p>\nThis component models the gear ratio and the <strong>losses</strong> of\na standard gear box in a <strong>reliable</strong> way including the stuck phases\nthat may occur at zero speed. The gear boxes that can\nbe handled are fixed in the ground or on a moving support, have one input and one\noutput shaft, and are essentially described by the equations:\n</p>\n<blockquote><pre>\n             flange_a.phi  = i*flange_b.phi;\n-(flange_b.tau - tau_bf_b) = i*eta_mf*(flange_a.tau - tau_bf_a);\n\n// or        -flange_b.tau = i*eta_mf*(flange_a.tau - tau_bf_a - tau_bf_b/(i*eta_mf));\n</pre></blockquote>\n<p>\nwhere\n</p>\n\n<ul>\n<li> <strong>i</strong> is the constant <strong>gear ratio</strong>,</li>\n\n<li> <strong>eta_mf</strong> = eta_mf(w_a) is the <strong>mesh efficiency</strong> due to the\n     friction between the teeth of the gear wheels,</li>\n\n<li> <strong>tau_bf_a</strong> = tau_bf_a(w_a) is the <strong>bearing friction torque</strong>\n     on the flange_a side,</li>\n\n<li> <strong>tau_bf_b</strong> = tau_bf_b(w_a) is the <strong>bearing friction torque</strong>\n     on the flange_b side, and</li>\n\n<li><strong>w_a</strong> = der(flange_a.phi) is the speed of flange_a</li>\n</ul>\n\n<p>\nThe loss terms \"eta_mf\", \"tau_bf_a\" and \"tau_bf_b\" are functions of the\n<em>absolute value</em> of the input shaft speed w_a and of the energy\nflow direction. They are defined by parameter <strong>lossTable[:,5]\n</strong> where the columns of this table have the following\nmeaning:\n</p>\n\n<table border=1 cellspacing=0 cellpadding=2>\n  <tbody>\n    <tr>\n      <td>|w_a|</td>\n      <td>eta_mf1</td>\n      <td>eta_mf2</td>\n      <td>|tau_bf1|</td>\n      <td>|tau_bf2|</td>\n    </tr>\n    <tr>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n    </tr>\n    <tr>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n      <td align=\"center\">...</td>\n    </tr>\n  </tbody>\n</table>\n\n<p>with</p>\n<table border=1 cellspacing=0 cellpadding=2>\n  <tbody>\n    <tr>\n      <td>|w_a|</td>\n      <td>Absolute value of angular velocity of input shaft flange_a</td>\n    </tr>\n    <tr>\n      <td>eta_mf1</td>\n      <td>Mesh efficiency in case that flange_a is driving</td>\n    </tr>\n    <tr>\n      <td>eta_mf2</td>\n      <td>Mesh efficiency in case that flange_b is driving</td>\n    </tr>\n    <tr>\n      <td>|tau_bf1|</td>\n      <td> Absolute resultant bearing friction torque with respect to flange_a\n                        in case that flange_a is driving<br>\n                        (= |tau_bf_a*eta_mf1 + tau_bf_b/i|)\n                        </td>\n    </tr>\n    <tr>\n      <td>|tau_bf2|</td>\n      <td> Absolute resultant bearing friction torque with respect to flange_a\n                        in case that flange_b is driving<br>\n                        (= |tau_bf_a/eta_mf2 + tau_bf_b/i|)\n                        </td>\n    </tr>\n  </tbody>\n</table>\n<p>\nWith these variables, the mesh efficiency and the bearing friction\nare formally defined as:\n</p>\n\n<blockquote><pre>\n<strong>if</strong> (flange_a.tau - tau_bf_a)*w_a &gt; 0 <strong>or</strong>\n   (flange_a.tau - tau_bf_a) == 0 <strong>and</strong> w_a &gt; 0 <strong>then</strong>\n   eta_mf := eta_mf1\n   tau_bf := tau_bf1\n<strong>elseif</strong> (flange_a.tau - tau_bf_a)*w_a &lt; 0 <strong>or</strong>\n       (flange_a.tau - tau_bf_a) == 0 <strong>and</strong> w_a &lt; 0 <strong>then</strong>\n   eta_mf := 1/eta_mf2\n   tau_bf := tau_bf2\n<strong>else</strong> // w_a == 0\n   eta_mf and tau_bf are computed such that <strong>der</strong>(w_a) = 0\n<strong>end if</strong>;\n-flange_b.tau = i*(eta_mf*flange_a.tau - tau_bf);\n</pre></blockquote>\n\n<p>\nNote, that the losses are modeled in a physically meaningful way taking\ninto account that at zero speed the movement may be locked due\nto the friction in the gear teeth and/or in the bearings.\nDue to this important property, this component can be used in\nsituations where the combination of the components\nModelica.Mechanics.Rotational.IdealGear and\nModelica.Mechanics.Rotational.GearEfficiency will fail because,\ne.g., chattering occurs when using the\nModelica.Mechanics.Rotational.GearEfficiency model.\n</p>\n\n<h4>Acknowledgement:</h4>\n<ul>\n<li> The essential idea to model efficiency\n     in this way is from Christoph Pelchen, ZF Friedrichshafen.</li>\n<li> The article (Pelchen et.al. 2002), see Literature below,\n     and the first implementation of LossyGear (up to version 3.1 of package Modelica)\n     contained a bug leading to a non-converging solution in cases where the\n     driving side is not obvious.\n     This was pointed out by Christian Bertsch and Max Westenkirchner, Bosch,\n     and Christian Bertsch proposed a concrete solution how to fix this\n     bug, see Literature below.</li>\n</ul>\n\n<h4>Literature</h4>\n\n<ul>\n<li>Pelchen C.,\nSchweiger C.,\nand <a href=\"http://www.robotic.dlr.de/Martin.Otter/\">Otter M.</a>:\n&quot;<a href=\"https://www.modelica.org/events/Conference2002/papers/p33_Pelchen.pdf\">Modeling\nand Simulating the Efficiency of Gearboxes and of Planetary Gearboxes</a>,&quot; in\n<em>Proceedings of the 2nd International Modelica Conference, Oberpfaffenhofen, Germany,</em>\npp. 257-266, The Modelica Association and Institute of Robotics and Mechatronics,\nDeutsches Zentrum f&uuml;r Luft- und Raumfahrt e. V., March 18-19, 2002.</li>\n\n<li>Bertsch C. (2009):\n&quot;<a href=\"modelica://Modelica/Resources/Documentation/Mechanics/Lossy-Gear-Bug_Solution.pdf\">Problem\nwith model LossyGear and a proposed solution</a>&quot;,\nTicket <a href=\"https://trac.modelica.org/Modelica/ticket/108\">#108</a>,\nSept. 11, 2009.</li>\n</ul>\n\n</html>"),
        Icon(
            coordinateSystem(
                preserveAspectRatio = true,
                extent = {
                    {-100, -100}, 
                    {100, 100}}),
            graphics = {
                Polygon(
                    fillColor = {161, 35, 41},
                    pattern = LinePattern.None,
                    fillPattern = FillPattern.Solid,
                    points = {
                        {-110, 50}, 
                        {-80, 50}, 
                        {-80, 80}, 
                        {-90, 80}, 
                        {-70, 100}, 
                        {-50, 80}, 
                        {-60, 80}, 
                        {-60, 30}, 
                        {-110, 30}, 
                        {-110, 50}}), 
                Line(points = {
                    {-80, 20}, 
                    {-60, 20}}), 
                Text(
                    lineColor = {0, 0, 255},
                    extent = {
                        {-148, 105}, 
                        {152, 145}},
                    textString = "%name"), 
                Text(
                    extent = {
                        {-145, -79}, 
                        {155, -49}},
                    textString = "ratio=%ratio"), 
                Line(
                    visible = useHeatPort,
                    points = {
                        {-100, -100}, 
                        {-100, -30}, 
                        {0, -30}, 
                        {0, 0}},
                    color = {191, 0, 0},
                    pattern = LinePattern.Dot)}));
end LossyGear;