addResponses Method

adds Response objects to the model.

The model object, containing the responses, is provided to the optimizer to do the optimization.

Example

For the four-bar model, add three responses:
  • The deviation of the X coordinate of CM of the coupler link from a path.
  • The deviation of the Y coordinate of CM of the coupler link from a path.
  • The deviation of the AZ coordinate of CM of the coupler link from a path.

The RMS2 response is used to compute the deviation.

def addResponses (self):

  m = self.mbsModel
  
  # Coupler-DX curve: Spline
  x1=(0   , 0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 , 0.45, 0.5 , 0.55, 0.6 , 0.65, 
      0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1   , 1.05, 1.1 , 1.15, 1.2 , 1.25, 1.3 , 1.35, 
     1.4 , 1.45, 1.5 , 1.55, 1.6 , 1.65, 1.7 , 1.75, 1.8 , 1.85, 1.9 , 1.95, 2)
  y1=(199.997, 141.431, 78.0588, 17.3066, -35.233, -75.736, -102.14, -114.06, -112.56,
       -99.81, -78.718, -52.477, -23.994, 5.10437, 36.8618, 82.4831, 157.392, 231.874, 
      260.213, 244.03, 199.997, 141.429, 78.0583, 17.3061, -35.234, -75.736, -102.14,     
      -114.06, -112.56, -99.81, -78.717, -52.477, -23.994, 5.10462, 36.8621, 82.4836, 
      157.392, 231.874, 260.213, 244.03, 199.999)
  xy  = list(zip(x1,y1))

  # Coupler-DX curve: The Measured value
  dx_coupler = "DX({marker})".format(marker=m.coupler.cm.id)

  # Coupler-DX curve: The x-deviation
  self.a2x = RMS2 ( label = "Coupler-DX", targetValue = xy, measuredValue = dx_coupler)  

  #############

  # Coupler-DY curve: Spline
  x2=(0   , 0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 , 0.45, 0.5 , 0.55, 0.6 , 0.65, 
      0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1   , 1.05, 1.1 , 1.15, 1.2 , 1.25, 1.3 , 1.35, 
      1.4 , 1.45, 1.5 , 1.55, 1.6 , 1.65, 1.7 , 1.75, 1.8 , 1.85, 1.9 , 1.95, 2)
  y2=(400    , 398.307, 382.665, 353.359, 312.589, 263.960, 211.853, 160.818, 115.093, 
      78.2737,  53.181, 41.9427, 46.3684, 68.8531, 113.983, 187.585, 275.874, 337.05, 
      369.449, 389.534, 400.001, 398.305, 382.665, 353.359, 312.589, 263.959, 211.853, 
      160.818, 115.093, 78.2735, 53.1808, 41.9426, 46.3685, 68.8534, 113.983, 187.586, 
      275.875, 337.05, 369.449, 389.534, 400)
  xy2 = list(zip(x2,y2))

  # Coupler-DY curve: The Measured value

  dy_coupler = "DY({marker})".format(marker=m.coupler.cm.id) 

  # Coupler-DX curve: The y-deviation
  self.a2y = RMS2 ( label = "Coupler-DY", targetValue = xy2, measuredValue = dy_coupler)

  #############
  
  # Coupler-PSI curve: Spline
  x3=(0   , 0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 , 0.45, 0.5 , 0.55, 0.6 , 0.65, 
      0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1   , 1.05, 1.1 , 1.15, 1.2 , 1.25, 1.3 , 1.35, 
      1.4 , 1.45, 1.5 , 1.55, 1.6 , 1.65, 1.7 , 1.75, 1.8 , 1.85, 1.9 , 1.95, 2)
  y3=(0.0    , -1.5036, -0.9390,  1.2498,  4.8231,  9.6362, 15.5739, 22.5081, 30.2735, 
      38.6522, 47.3546, 55.9772, 63.8940, 69.9926, 72.0582, 65.9705, 48.8237, 28.1874, 
      13.1512,  4.3989,  0.0   , -1.5036, -0.939 ,  1.2499,  4.8231,  9.6363, 15.574, 
      22.5081, 30.2736, 38.6522, 47.3547, 55.9772, 63.8941, 69.9927, 72.0582, 65.9704, 
      48.8235, 28.1873, 13.1511,  4.3988, 0.0)
  xy3 = list(zip(x3,y3))

  # Coupler-PSI curve: Measured value
  psi_coupler = "RTOD*AZ({marker})".format(marker=m.coupler.azMarker.id) 

  # Coupler-PSI curve: Z-rotation deviation
  self.a2psi = RMS2 ( label = "Coupler-PSI", targetValue = xy3, measuredValue = psi_coupler)
  return

Constraints are treated as a normal response in msolve. So if you want to add a constraint in your model, you can do that in the addResponses method as well. Note that adding constraints is very important in optimization; the optimizer could step into a design that is not feasible without proper constraints even if you start with a feasible point.

In msolve, we formulate the optimization problem in a positive null form. The first thing you should do is to convert your problem so that every inequality constraint has a positive value when it is valid. Then, write each constraint as a response in the addResponses method. We illustrate this process by imposing Grashof condition in four-bar model. There are a lot of ways to impose Grashof condition; the way provided here works well with msolve and its implementation is straightforward.

Example

To guarantee a crank-rocker motion in a four-bar mechanism, the model should satisfy the following Grashof condition:
  • The crank should be the shortest link.
  • The sum of the shortest and longest length should be less than the sum of the other two.
def addResponses (self):
# Start adding your objective function
# Adding objective function completed

# Start adding constraints
# Crank needs to be the shortest link
# This means crank is shorter than any other links
self.cons1 = ResponseExpression(
  label    = "crank < base",
  function = "(dx-ax)**2 + (dy-ay)**2 - (bx-ax)**2 - (by-ay)**2",
  mapping  = {"ax":m.ax, "ay":m.ay, "bx":m.bx, "by":m.by, "dx":m.dx, "dy":m.dy}
)
    self.cons2 = ResponseExpression(
      label    = "crank < follower",
      function = "(dx-cx)**2 + (dy-cy)**2 - (bx-ax)**2 - (by-ay)**2",
      mapping  = {"ax": m.ax, "ay": m.ay, "bx": m.bx, "by": m.by, 
                  "cx": m.cx, "cy": m.cy, "dx": m.dx, "dy": m.dy}
    )
    self.cons3 = ResponseExpression(
      label    = "crank < coupler",
      function = "(cx-bx)**2 + (cy-by)**2 - (bx-ax)**2 - (by-ay)**2",
      mapping  = {"ax": m.ax, "ay": m.ay, "bx": m.bx, "by": m.by, "cx":m.cx, "cy":m.cy}
)

# s + l < p + q
# The crank is always the shortest
# So the sum of crank and some other link is always shorter than the sum of the other two
    self.cons4 = ResponseExpression(
      label    = "crank + base < other two",
      function = "-(bx-ax)**2 - (by-ay)**2 - (dx-ax)**2 - (dy-ay)**2 + (cx-dx)**2 + (cy-dy)**2 + (bx-cx)**2 + (by-cy)**2",
      mapping  = {"ax": m.ax, "ay": m.ay, "bx": m.bx, "by": m.by, 
                  "cx": m.cx, "cy": m.cy, "dx": m.dx, "dy": m.dy}
)
    self.cons5 = ResponseExpression(
      label    = "crank + coupler < other two",
      function = "-(bx-ax)**2 - (by-ay)**2 - (cx-bx)**2 - (cy-by)**2 + (dx-cx)**2 + (dy-cy)**2 + (dx-ax)**2 + (dy-ay)**2",
      mapping  = {"ax": m.ax, "ay": m.ay, "bx": m.bx, "by": m.by,
                  "cx": m.cx, "cy": m.cy, "dx": m.dx, "dy": m.dy}
)
    self.cons6 = ResponseExpression(
      label    = "crank + follower < other two",
      function = "-(bx-ax)**2 - (by-ay)**2 - (dx-cx)**2 - (dy-cy)**2 + (cx-bx)**2 + (cy-by)**2 + (dx-ax)**2 + (dy-ay)**2",
      mapping  = {"ax": m.ax, "ay": m.ay, "bx": m.bx, "by": m.by, 
                  "cx": m.cx, "cy": m.cy, "dx": m.dx, "dy": m.dy}
    )