# No. 425 (Brown 1868): "One of the many forms of rotary engine. A is the cylinder having the shaft, B, pass centrally through it. The piston,
# C, is simply an eccentric fast on the shaft and working in contact with the cylinder at one point. The induction and eduction of steam take
# place as indicated by arrows, and the pressure of the steam on one side of the piston produces its rotation and that of the shaft. The
# sliding abutment, D, between the induction and eduction ports moves out of the way of the piston to let it pass."
# Model (cm, Brown's section, gravity on; only the back half of the cut cylinder is drawn). The cylinder A (6.5 cm bore) has the shaft B
# through its centre; the piston C is an eccentric 10.62 cm across, 1.15 cm off the shaft, fast on it, and so just meets the bore at one
# point all round. The abutment D slides up and down in a guide at the top of the cylinder, between the induction port (right) and the eduction
# (left), and rests on the piston by its own weight (the only contact: C against D); the piston lifts it as its crown comes round and it falls
# after. The steam (the one input) presses on the piston's face between the point of contact and D, on the side open to the induction
# port: a force through the piston's centre, let in and shut off by the ports as the shaft turns (feet.ports, timed by j_B); the shaft is
# otherwise free, against a load (0.06 N m s). Generator: opencode-runs/steam/gen425.py.
number: 425
slug: rotary-engine-eccentric-piston
name: Rotary engine with eccentric piston
brown: "One of the many forms of rotary engine. A is the cylinder having the shaft, B, pass centrally through it. The piston, C, is simply an eccentric fast on the shaft and working in contact with the cylinder at one point. The induction and eduction of steam take place as indicated by arrows, and the pressure of the steam on one side of the piston produces its rotation and that of the shaft. The sliding abutment, D, between the induction and eduction ports moves out of the way of the piston to let it pass."
units: cm
sim:
  timestep: 0.0002
  integrator: implicitfast
  iterations: 100
  solver: PGS
  duration_s: 10
  floor: false
bodies:
  - id: piston
    shape: part
    pos: [0, 0, 9]
    density: 7800
    friction: 0.05
    color: "#a39d90"
    pieces:
      - {kind: disc, center: [0, 7.85], radius: 5.31, y: [0.4, 2.0], collide: 1}   # the eccentric piston C
      - {kind: disc, center: [0, 9], radius: 1.1, y: [-0.2, 2.8], color: "#6f6a60"}   # the shaft B
      - {kind: disc, center: [0, 9], radius: 9, y: [3.2, 7.2], collide: 0, color: "#6f6a60"}   # the fly-wheel, behind the cylinder (carries the shaft past the dead point)
      - {kind: poly, points: [[-0.25, 2.56], [0.25, 2.56], [0.25, 3.04], [-0.25, 3.04]], y: [0.3, 2.1], color: "#7d776c"}   # the packing strip where C meets the bore
  - id: abutment
    shape: part
    pos: [0, 0, 13.16]
    density: 7800
    friction: 0.05
    color: "#7d776c"
    pieces:
      - {kind: poly, points: [[-0.65, 13.16], [0.65, 13.16], [0.65, 19.36], [-0.65, 19.36]], y: [0.4, 2.0], collide: 2}   # the sliding abutment D
  - id: frame
    shape: part
    pos: [0, 0, 0]
    fixed: true
    color: "#cfc6b4"
    pieces:
      - {kind: poly, points: [[-0.9, 15.4374], [-0.9969, 16.1306], [-2.8306, 15.6202], [-2.5554, 14.9766]], y: [0, 2.4]}
      - {kind: poly, points: [[2.5554, 14.9766], [2.8306, 15.6202], [0.9969, 16.1306], [0.9, 15.4374]], y: [0, 2.4]}
      - {kind: poly, points: [[-4.2515, 13.9168], [-4.7094, 14.4463], [-5.708, 13.3885], [-5.153, 12.9618]], y: [0, 2.4]}
      - {kind: poly, points: [[-5.153, 12.9618], [-5.708, 13.3885], [-6.4736, 12.1516], [-5.8442, 11.8452]], y: [0, 2.4]}
      - {kind: poly, points: [[-5.8442, 11.8452], [-6.4736, 12.1516], [-6.975, 10.786], [-6.2968, 10.6124]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.2968, 10.6124], [-6.975, 10.786], [-7.1916, 9.3475], [-6.4924, 9.3138]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.4924, 9.3138], [-7.1916, 9.3475], [-7.1147, 7.8949], [-6.423, 8.0023]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.423, 8.0023], [-7.1147, 7.8949], [-6.7473, 6.4873], [-6.0913, 6.7316]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.0913, 6.7316], [-6.7473, 6.4873], [-6.1046, 5.1824], [-5.5111, 5.5535]], y: [0, 2.4]}
      - {kind: poly, points: [[-5.5111, 5.5535], [-6.1046, 5.1824], [-5.2126, 4.0332], [-4.7058, 4.5161]], y: [0, 2.4]}
      - {kind: poly, points: [[-4.7058, 4.5161], [-5.2126, 4.0332], [-4.1079, 3.0868], [-3.7085, 3.6617]], y: [0, 2.4]}
      - {kind: poly, points: [[-3.7085, 3.6617], [-4.1079, 3.0868], [-2.8354, 2.3818], [-2.5598, 3.0252]], y: [0, 2.4]}
      - {kind: poly, points: [[-2.5598, 3.0252], [-2.8354, 2.3818], [-1.4472, 1.947], [-1.3065, 2.6327]], y: [0, 2.4]}
      - {kind: poly, points: [[-1.3065, 2.6327], [-1.4472, 1.947], [-0, 1.8], [-0, 2.5]], y: [0, 2.4]}
      - {kind: poly, points: [[-0, 2.5], [-0, 1.8], [1.4472, 1.947], [1.3065, 2.6327]], y: [0, 2.4]}
      - {kind: poly, points: [[1.3065, 2.6327], [1.4472, 1.947], [2.8354, 2.3818], [2.5598, 3.0252]], y: [0, 2.4]}
      - {kind: poly, points: [[2.5598, 3.0252], [2.8354, 2.3818], [4.1079, 3.0868], [3.7085, 3.6617]], y: [0, 2.4]}
      - {kind: poly, points: [[3.7085, 3.6617], [4.1079, 3.0868], [5.2126, 4.0332], [4.7058, 4.5161]], y: [0, 2.4]}
      - {kind: poly, points: [[4.7058, 4.5161], [5.2126, 4.0332], [6.1046, 5.1824], [5.5111, 5.5535]], y: [0, 2.4]}
      - {kind: poly, points: [[5.5111, 5.5535], [6.1046, 5.1824], [6.7473, 6.4873], [6.0913, 6.7316]], y: [0, 2.4]}
      - {kind: poly, points: [[6.0913, 6.7316], [6.7473, 6.4873], [7.1147, 7.8949], [6.423, 8.0023]], y: [0, 2.4]}
      - {kind: poly, points: [[6.423, 8.0023], [7.1147, 7.8949], [7.1916, 9.3475], [6.4924, 9.3138]], y: [0, 2.4]}
      - {kind: poly, points: [[6.4924, 9.3138], [7.1916, 9.3475], [6.975, 10.786], [6.2968, 10.6124]], y: [0, 2.4]}
      - {kind: poly, points: [[6.2968, 10.6124], [6.975, 10.786], [6.4736, 12.1516], [5.8442, 11.8452]], y: [0, 2.4]}
      - {kind: poly, points: [[5.8442, 11.8452], [6.4736, 12.1516], [5.708, 13.3885], [5.153, 12.9618]], y: [0, 2.4]}
      - {kind: poly, points: [[5.153, 12.9618], [5.708, 13.3885], [4.7094, 14.4463], [4.2515, 13.9168]], y: [0, 2.4]}
      - {kind: poly, points: [[4.1313, 1.2301], [4.46, 0.612], [5.5302, 1.2755], [5.1227, 1.8447]], y: [0, 2.4]}
      - {kind: poly, points: [[5.1227, 1.8447], [5.5302, 1.2755], [6.5032, 2.0748], [6.024, 2.5851]], y: [0, 2.4]}
      - {kind: poly, points: [[6.024, 2.5851], [6.5032, 2.0748], [7.362, 2.9957], [6.8195, 3.4381]], y: [0, 2.4]}
      - {kind: poly, points: [[6.8195, 3.4381], [7.362, 2.9957], [8.0914, 4.0221], [7.4952, 4.3889]], y: [0, 2.4]}
      - {kind: poly, points: [[7.4952, 4.3889], [8.0914, 4.0221], [8.6787, 5.136], [8.0392, 5.4207]], y: [0, 2.4]}
      - {kind: poly, points: [[8.0392, 5.4207], [8.6787, 5.136], [9.1135, 6.3178], [8.442, 6.5154]], y: [0, 2.4]}
      - {kind: poly, points: [[8.442, 6.5154], [9.1135, 6.3178], [9.3882, 7.5466], [8.6964, 7.6537]], y: [0, 2.4]}
      - {kind: poly, points: [[8.6964, 7.6537], [9.3882, 7.5466], [9.4979, 8.801], [8.7981, 8.8157]], y: [0, 2.4]}
      - {kind: poly, points: [[8.7981, 8.8157], [9.4979, 8.801], [9.4408, 10.059], [8.7452, 9.9809]], y: [0, 2.4]}
      - {kind: poly, points: [[8.7452, 9.9809], [9.4408, 10.059], [9.2178, 11.2983], [8.5386, 11.1289]], y: [0, 2.4]}
      - {kind: poly, points: [[8.5386, 11.1289], [9.2178, 11.2983], [8.8329, 12.4972], [8.182, 12.2395]], y: [0, 2.4]}
      - {kind: poly, points: [[8.182, 12.2395], [8.8329, 12.4972], [8.2928, 13.6347], [7.6817, 13.2932]], y: [0, 2.4]}
      - {kind: poly, points: [[7.6817, 13.2932], [8.2928, 13.6347], [7.6069, 14.6907], [7.0464, 14.2714]], y: [0, 2.4]}
      - {kind: poly, points: [[7.0464, 14.2714], [7.6069, 14.6907], [6.7875, 15.6468], [6.2874, 15.157]], y: [0, 2.4]}
      - {kind: poly, points: [[6.2874, 15.157], [6.7875, 15.6468], [5.8488, 16.4861], [5.4178, 15.9345]], y: [0, 2.4]}
      - {kind: poly, points: [[5.4178, 15.9345], [5.8488, 16.4861], [4.8073, 17.1939], [4.4531, 16.5901]], y: [0, 2.4]}
      - {kind: poly, points: [[4.4531, 16.5901], [4.8073, 17.1939], [3.6814, 17.7577], [3.4101, 17.1124]], y: [0, 2.4]}
      - {kind: poly, points: [[3.4101, 17.1124], [3.6814, 17.7577], [2.4908, 18.1677], [2.3073, 17.4921]], y: [0, 2.4]}
      - {kind: poly, points: [[2.3073, 17.4921], [2.4908, 18.1677], [1.2564, 18.4165], [1.1639, 17.7227]], y: [0, 2.4]}
      - {kind: poly, points: [[1.1639, 17.7227], [1.2564, 18.4165], [-0, 18.5], [-0, 17.8]], y: [0, 2.4]}
      - {kind: poly, points: [[-0, 17.8], [-0, 18.5], [-1.2564, 18.4165], [-1.1639, 17.7227]], y: [0, 2.4]}
      - {kind: poly, points: [[-1.1639, 17.7227], [-1.2564, 18.4165], [-2.4908, 18.1677], [-2.3073, 17.4921]], y: [0, 2.4]}
      - {kind: poly, points: [[-2.3073, 17.4921], [-2.4908, 18.1677], [-3.6814, 17.7577], [-3.4101, 17.1124]], y: [0, 2.4]}
      - {kind: poly, points: [[-3.4101, 17.1124], [-3.6814, 17.7577], [-4.8073, 17.1939], [-4.4531, 16.5901]], y: [0, 2.4]}
      - {kind: poly, points: [[-4.4531, 16.5901], [-4.8073, 17.1939], [-5.8488, 16.4861], [-5.4178, 15.9345]], y: [0, 2.4]}
      - {kind: poly, points: [[-5.4178, 15.9345], [-5.8488, 16.4861], [-6.7875, 15.6468], [-6.2874, 15.157]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.2874, 15.157], [-6.7875, 15.6468], [-7.6069, 14.6907], [-7.0464, 14.2714]], y: [0, 2.4]}
      - {kind: poly, points: [[-7.0464, 14.2714], [-7.6069, 14.6907], [-8.2928, 13.6347], [-7.6817, 13.2932]], y: [0, 2.4]}
      - {kind: poly, points: [[-7.6817, 13.2932], [-8.2928, 13.6347], [-8.8329, 12.4972], [-8.182, 12.2395]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.182, 12.2395], [-8.8329, 12.4972], [-9.2178, 11.2983], [-8.5386, 11.1289]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.5386, 11.1289], [-9.2178, 11.2983], [-9.4408, 10.059], [-8.7452, 9.9809]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.7452, 9.9809], [-9.4408, 10.059], [-9.4979, 8.801], [-8.7981, 8.8157]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.7981, 8.8157], [-9.4979, 8.801], [-9.3882, 7.5466], [-8.6964, 7.6537]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.6964, 7.6537], [-9.3882, 7.5466], [-9.1135, 6.3178], [-8.442, 6.5154]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.442, 6.5154], [-9.1135, 6.3178], [-8.6787, 5.136], [-8.0392, 5.4207]], y: [0, 2.4]}
      - {kind: poly, points: [[-8.0392, 5.4207], [-8.6787, 5.136], [-8.0914, 4.0221], [-7.4952, 4.3889]], y: [0, 2.4]}
      - {kind: poly, points: [[-7.4952, 4.3889], [-8.0914, 4.0221], [-7.362, 2.9957], [-6.8195, 3.4381]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.8195, 3.4381], [-7.362, 2.9957], [-6.5032, 2.0748], [-6.024, 2.5851]], y: [0, 2.4]}
      - {kind: poly, points: [[-6.024, 2.5851], [-6.5032, 2.0748], [-5.5302, 1.2755], [-5.1227, 1.8447]], y: [0, 2.4]}
      - {kind: poly, points: [[-5.1227, 1.8447], [-5.5302, 1.2755], [-4.46, 0.612], [-4.1313, 1.2301]], y: [0, 2.4]}
      - {kind: poly, points: [[-1.35, 15.3], [-0.75, 15.3], [-0.75, 21.1], [-1.35, 21.1]], y: [0, 2.4]}   # the abutment's guide
      - {kind: poly, points: [[0.75, 15.3], [1.35, 15.3], [1.35, 21.1], [0.75, 21.1]], y: [0, 2.4]}
      - {kind: poly, points: [[-1.35, 21.1], [1.35, 21.1], [1.35, 21.7], [-1.35, 21.7]], y: [0, 2.4]}
      - {kind: poly, points: [[-4.3445, 14.5123], [-2.5445, 14.5123], [-2.5445, 18.9], [-4.3445, 18.9]], y: [2.4, 2.6], color: "#bdb4a2"}
      - {kind: disc, center: [0, 9], radius: 9.5, y: [2.4, 2.9], color: "#bdb4a2"}   # the cylinder's back
      - {kind: poly, points: [[-10.5, -0.8], [10.5, -0.8], [10.5, 0.2], [-10.5, 0.2]], y: [-1.5, 3.5], color: "#bdb4a2"}   # the bed
      - {kind: poly, points: [[-6.5, 0.2], [6.5, 0.2], [4, 0.5], [-4, 0.5]], y: [0, 2.9], color: "#bdb4a2"}
joints:
  - {id: j_B, type: hinge, body: piston, axis: [0, -1, 0], damping: 0.06}   # the shaft and piston; + : anticlockwise as drawn (Brown's arrow)
  - {id: j_D, type: slide, body: abutment, axis: [0, 0, 1], range: [-0.3, 2.6], damping: 0.02}   # the abutment, up and down in its guide
contact: {solref: [0.002, 1.0], solimp: [0.95, 0.99, 0.0005], margin: 0}
driver:
  joint: j_B
  kind: feet
  max_torque: 1.0
  # The steam, let in by the induction port and shut off at the eduction (the one input): while the point
  # of contact is between the induction port and the eduction (the steam side, behind it, open to the
  # boiler), the pressure on the piston's face from that point back to D pushes the piston, a force
  # through its centre of F(1 - sin a, cos a) (a the point of contact's angle from B, from the right,
  # anticlockwise; D at 90); timed by the shaft's own angle (a = q + 270). The shaft is otherwise free.
  feet:
    force: 40
    timed: {joint: j_B, gain: 1, offset: -4.7124}
    ports:
      - {body: piston, at: [0, 1.2, 7.85], dir: [1, 0, 0], frame: world, open: [130.85, 409.15], profile: [1, 0, -1]}
      - {body: piston, at: [0, 1.2, 7.85], dir: [0, 0, 1], frame: world, open: [130.85, 409.15], profile: [0, 1, 0]}
      - {body: piston, at: [0, 1.2, 7.85], dir: [0, 0, -1], frame: world, open: [130.85, 409.15], profile: [0, -1, 0]}
checks:
  - {type: monotonic, joint: j_B, direction: 1, max_back: 0.05, settle_s: 0.0, window_s: 9.5}
  - {type: turning, joint: j_B, direction: 1, min_mean: 3.5, min_fraction: 0.9, settle_s: 3.0, window_s: 6.5}
  - {type: amplitude, joint: j_D, min: 0.0109, max: 0.0118, sustain: 0.95, settle_s: 3.0, window_s: 6.5}   # D rides the piston: up and down by twice the eccentricity
  - {type: penetration, max: 0.002}
camera: {target: [0, 0, 7.0], position: [0, -95, 7.0], fov: 20}
display:
  output: j_B
  labels: {j_B: "Shaft B and piston C (steam)", j_D: "Abutment D"}
  proof: {label: "Abutment out of contact", sub: "the shaft turns but D lies on its stop"}
