{
  "openapi": "3.1.1",
  "info": {
    "title": "Plate Stress Analysis with Holes (2-D FEA) API",
    "version": "1.0.0",
    "summary": "Stress map of a plate with holes under tension: von Mises, peaks and the concentration factor.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://prooflineplatestress.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Stress map of a plate with holes under tension: von Mises, peaks and the concentration factor.",
        "description": "Draw a plate as a rectangle or polygon with circular, rectangular or polygonal holes, fix edges or points, pull or press edges with a traction or pressure or add point forces, and get the plane-stress field from a finite-element model: a colour map of the von Mises stress or any stress component, the peak stress and where it sits, the stress concentration factor against the applied stress, the largest displacement, a utilisation check against the yield strength and an equilibrium check. The mesh is generated automatically (Delaunay triangulation, refined towards the holes). Part of the Proofline math notebook; a checking and teaching model, not certified design.\n\nMethodology:\n- Mesh: the outline and holes are split into boundary segments (circular holes stay on the circle), triangulated by the incremental Bowyer–Watson Delaunay algorithm, missing or encroached boundary segments are split (conforming Delaunay), and triangles that are too large or have angles below about 20° are refined by inserting their circumcentres (Ruppert). The element size grows from the hole size by a quarter of the distance to the nearest hole.\n- Elements: constant-strain triangles (CST) in plane stress, D = E/(1 − ν²)·[[1, ν, 0], [ν, 1, 0], [0, 0, (1 − ν)/2]], K = t·A·BᵀDB. Edge tractions and pressures become consistent nodal forces (half of each edge’s share to each end).\n- The sparse stiffness matrix is reordered with reverse Cuthill–McKee and solved by envelope Cholesky; a plate that can still move rigidly is reported.\n- Stresses are constant per element; von Mises = √(σx² − σxσy + σy² + 3τxy²). Area-weighted nodal averages give a smoother but lower peak.\n- Verified by the patch test (uniform stress exact on any mesh), equilibrium of reactions and loads, and convergence towards Kirsch’s factor 3 at a small hole in a wide plate as the elements at the hole shrink.\n\nNot certified engineering. Linear elastic plane stress with constant-strain triangles: peaks at holes and corners are only as good as the mesh there (refine until the peak stops changing), sharp re-entrant corners have no finite peak at all, and yielding, fatigue, buckling of thin plates and contact at bolt holes are not modelled.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "proofline_plate_stress",
        "x-owner-app": "proofline",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProoflinePlateStressInput"
              },
              "examples": {
                "example1": {
                  "summary": "Steel strip with a hole under tension (d/W = 0.3)",
                  "value": {
                    "model": "rect 200 100\nhole circle 100 50 15\nthickness 5\nmaterial E=210 nu=0.3 fy=250\nfix x=0 ux\nfix point 0 0 uy\ntraction x=200 tx=50"
                  }
                },
                "example2": {
                  "summary": "Small hole in a wide plate: towards Kirsch’s factor 3",
                  "value": {
                    "model": "# hole d = 2 mm in a 40 mm plate, pulled on both ends\noutline -20,-20 20,-20 20,20 -20,20\nhole circle 0 0 1\nthickness 1\nmaterial E=210 nu=0.3\nfix point -20 0 ux uy\nfix point 20 0 uy\ntraction x=20 tx=1\ntraction x=-20 tx=-1",
                    "elementSize": 3,
                    "holeElementSize": 0.12,
                    "show": "sx"
                  }
                },
                "example3": {
                  "summary": "Aluminium bracket with two bolt holes, pressure in one hole",
                  "value": {
                    "model": "outline 0,0 120,0 120,40 40,40 40,100 0,100\nhole circle 20 20 6\nhole circle 20 80 6\nthickness 4\nmaterial E=70 nu=0.33 fy=160\nfix hole 1 both\npressure hole 2 p=30",
                    "elementSize": 6,
                    "holeElementSize": 1.5,
                    "showMesh": true
                  }
                },
                "example4": {
                  "summary": "Uniform tension without a hole (patch check: σx = 10 MPa everywhere)",
                  "value": {
                    "model": "rect 100 50\nthickness 2\nmaterial E=210 nu=0.3 fy=235\nfix x=0 ux\nfix point 0 0 uy\ntraction x=100 tx=10",
                    "elementSize": 10,
                    "show": "sx"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProoflinePlateStressResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ProoflinePlateStressInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "model": {
            "title": "Plate (one statement per line)",
            "description": "Units mm, N, MPa; E in GPa. rect W H or outline x,y x,y … · hole circle cx cy r | hole rect x0 y0 x1 y1 | hole polygon x,y … · thickness t · material E=210 nu=0.3 fy=250 · fix x=0 ux (or y=…, edge N, hole N, point x y; ux, uy or both) · traction x=200 tx=50 ty=0 (MPa on that edge) · pressure hole 1 p=20 (MPa, pushing on the surface) · force x y Fx=… Fy=… (N at the nearest node). Edges are numbered from the first outline point.",
            "type": "string",
            "maxLength": 50000
          },
          "elementSize": {
            "title": "Element size",
            "description": "Target triangle edge length away from the holes. Empty: 1/10 of the shorter plate side.",
            "type": "number",
            "minimum": 0,
            "x-unit": "mm"
          },
          "holeElementSize": {
            "title": "Element size at the holes",
            "description": "Smaller elements at the hole edges capture the peak stress; the size grows with the distance from the holes. Empty: a quarter of the smallest hole radius.",
            "type": "number",
            "minimum": 0,
            "x-unit": "mm"
          },
          "show": {
            "title": "Stress map",
            "type": "string",
            "enum": [
              "vonMises",
              "sx",
              "sy",
              "txy"
            ],
            "default": "vonMises"
          },
          "showMesh": {
            "title": "Draw the element edges",
            "type": "boolean",
            "default": false
          }
        },
        "required": [
          "model"
        ]
      },
      "ProoflinePlateStressResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "nodes",
              "elements",
              "minAngleDeg",
              "elementSize",
              "holeElementSize",
              "truncated",
              "maxVonMises",
              "maxVonMisesNodal",
              "maxSx",
              "minSx",
              "maxDisplacement",
              "utilisation",
              "nominalStress",
              "concentrationFactor",
              "equilibrium",
              "solver"
            ],
            "properties": {
              "nodes": {
                "type": "integer"
              },
              "elements": {
                "type": "integer"
              },
              "minAngleDeg": {
                "type": "number"
              },
              "elementSize": {
                "type": "number"
              },
              "holeElementSize": {
                "type": "number"
              },
              "truncated": {
                "type": "boolean"
              },
              "maxVonMises": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "value",
                  "x",
                  "y"
                ],
                "properties": {
                  "value": {
                    "type": "number"
                  },
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "number"
                  }
                },
                "description": "Peak element von Mises stress (MPa) at the element centroid (mm)."
              },
              "maxVonMisesNodal": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "value",
                  "x",
                  "y"
                ],
                "properties": {
                  "value": {
                    "type": "number"
                  },
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "number"
                  }
                },
                "description": "Peak of the area-weighted nodal averages (MPa)."
              },
              "maxSx": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "value",
                  "x",
                  "y"
                ],
                "properties": {
                  "value": {
                    "type": "number"
                  },
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "number"
                  }
                }
              },
              "minSx": {
                "type": "number"
              },
              "maxDisplacement": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "value",
                  "x",
                  "y"
                ],
                "properties": {
                  "value": {
                    "type": "number"
                  },
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "number"
                  }
                },
                "description": "Largest displacement (mm) and the node where it occurs."
              },
              "utilisation": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "nominalStress": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "concentrationFactor": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Peak von Mises stress divided by the single applied traction or pressure."
              },
              "equilibrium": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "appliedFx",
                  "appliedFy",
                  "reactionFx",
                  "reactionFy",
                  "maxError"
                ],
                "properties": {
                  "appliedFx": {
                    "type": "number"
                  },
                  "appliedFy": {
                    "type": "number"
                  },
                  "reactionFx": {
                    "type": "number"
                  },
                  "reactionFy": {
                    "type": "number"
                  },
                  "maxError": {
                    "type": "number"
                  }
                }
              },
              "solver": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "unknowns",
                  "nonZeros",
                  "bandwidthBefore",
                  "bandwidthAfter",
                  "relativeResidual"
                ],
                "properties": {
                  "unknowns": {
                    "type": "integer"
                  },
                  "nonZeros": {
                    "type": "integer"
                  },
                  "bandwidthBefore": {
                    "type": "integer"
                  },
                  "bandwidthAfter": {
                    "type": "integer"
                  },
                  "relativeResidual": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}