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):
…

# 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}
)