{ "cells": [ { "cell_type": "markdown", "id": "1ecb329a-8614-42a5-82a8-78a3d5dacada", "metadata": {}, "source": [ "# Ex 1: Automatic Topological Generation & Surrogate Optimization (2D / 4-DOF)\n", "\n", "This notebook represents the complete methodology for imprecise tolerance analysis on the 2D baseline assembly. \n", "\n", "Building on the manual vector loops constructed previously, this notebook introduces the streamlined workflow required to scale the methodology to high-dimensional systems:\n", "1. **Automatic Graph-Based Assembly Modeling:** Automatically constructing the `SystemOfConstraintsAssemblyModel` from a high-level topological dictionary.\n", "2. **Multi-Layer Perceptron (MLP) Surrogate:** Replacing expensive direct geometric optimizations with a PyTorch neural network.\n", "3. **Automated Tracking & Constraint Injection:** Using `otaf.optimization.OptimizationTracker` and the model's native constraint generator to manage the credal space boundaries.\n", "4. **Min-Max Bounds Resolution:** Finding a feasible warm-start ($x_0$) and optimizing across threshold levels ($\\alpha_k$) to compute the P-Box." ] }, { "cell_type": "code", "execution_count": 1, "id": "bd254d60-fd5b-4c8e-9f4a-1de5de524e2b", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipy\n", "import openturns as ot\n", "import matplotlib.pyplot as plt\n", "import torch\n", "from scipy.optimize import minimize, NonlinearConstraint, Bounds\n", "\n", "import otaf\n", "from otaf.example_models import model1\n", "from gldpy import GLD\n", "\n", "ot.Log.Show(ot.Log.NONE)\n", "np.set_printoptions(suppress=True)" ] }, { "cell_type": "markdown", "id": "2bcde591-85fa-4662-992b-78fca659bfb0", "metadata": {}, "source": [ "## 1. High-Level Topological Dictionary & Automated Loops\n", "\n", "Instead of manually defining deviation and gap matrices, we define the assembly as a topological graph. The `otaf.AssemblyDataProcessor` interprets the interactions and expands the first-order loops.\n", "\n", "*(Note: For the surrogate and optimization phases, we will dynamically pull the parameters directly from `otaf.example_models.model1`, but this dictionary illustrates how the physics of `model1` are constructed).*" ] }, { "cell_type": "code", "execution_count": 2, "id": "c637ce58-ba04-4d7d-a03f-441b1d8e60d9", "metadata": {}, "outputs": [], "source": [ "# Nominal dimensions and base frames\n", "X1, X2, X3 = 99.8, 100.0, 10.0\n", "R0 = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])\n", "x_, y_, z_ = R0[0], R0[1], R0[2]\n", "\n", "# Part 1 and 2 points\n", "P1A0, P1A1, P1A2 = np.array((0, X3/2, 0.0)), np.array((0, X3, 0.0)), np.array((0, 0, 0.0))\n", "P1B0, P1B1, P1B2 = np.array((X1, X3/2, 0.0)), np.array((X1, X3, 0.0)), np.array((X1, 0, 0.0))\n", "P1C0, P1C1, P1C2 = np.array((X1/2, 0, 0.0)), np.array((0, 0, 0.0)), np.array((X1, 0, 0.0))\n", "P2A0, P2A1, P2A2 = np.array((0, X3/2, 0.0)), np.array((0, X3, 0.0)), np.array((0, 0, 0.0))\n", "P2B0, P2B1, P2B2 = np.array((X2, X3/2, 0.0)), np.array((X2, X3, 0.0)), np.array((X2, 0, 0.0))\n", "P2C0, P2C1, P2C2 = np.array((X2/2, 0, 0.0)), np.array((0, 0, 0.0)), np.array((X2, 0, 0.0))" ] }, { "cell_type": "markdown", "id": "f5ed640e-40fc-48fe-ab9a-b46b5fd97b61", "metadata": {}, "source": [ "## 2. High-Level Topological Dictionary\n", "\n", "Instead of manually defining deviation and gap matrices, we define the assembly as a topological graph. The `otaf.AssemblyDataProcessor` interprets the interactions and automatically expands the first-order loops." ] }, { "cell_type": "code", "execution_count": 3, "id": "8e1248a3-5053-4030-b165-c48863d8d2b9", "metadata": {}, "outputs": [], "source": [ "system_data = {\n", " \"PARTS\" : {\n", " '1' : {\n", " \"a\" : {\"FRAME\": np.array([-1*x_, -1*y_, z_]), \"POINTS\": {'A0': P1A0, 'A1': P1A1, 'A2': P1A2}, \"TYPE\": \"plane\", \"INTERACTIONS\": ['P2a'], \"CONSTRAINTS_D\": [\"PERFECT\"], \"CONSTRAINTS_G\": [\"FLOATING\"]},\n", " \"b\" : {\"FRAME\": R0, \"POINTS\": {'B0': P1B0, 'B1': P1B1, 'B2': P1B2}, \"TYPE\": \"plane\", \"INTERACTIONS\": ['P2b'], \"CONSTRAINTS_D\": [\"NONE\"], \"CONSTRAINTS_G\": [\"FLOATING\"]},\n", " \"c\" : {\"FRAME\": np.array([-y_, x_, z_]), \"POINTS\": {'C0': P1C0, 'C1': P1C1, 'C2': P1C2}, \"TYPE\": \"plane\", \"INTERACTIONS\": ['P2c'], \"CONSTRAINTS_D\": [\"PERFECT\"], \"CONSTRAINTS_G\": [\"SLIDING\"]},\n", " },\n", " '2' : {\n", " \"a\" : {\"FRAME\": R0, \"POINTS\": {'A0': P2A0, 'A1': P2A1, 'A2': P2A2}, \"TYPE\": \"plane\", \"INTERACTIONS\": ['P1a'], \"CONSTRAINTS_D\": [\"PERFECT\"], \"CONSTRAINTS_G\": [\"FLOATING\"]},\n", " \"b\" : {\"FRAME\": np.array([-1*x_, -1*y_, z_]), \"POINTS\": {'B0': P2B0, 'B1': P2B1, 'B2': P2B2}, \"TYPE\": \"plane\", \"INTERACTIONS\": ['P1b'], \"CONSTRAINTS_D\": [\"NONE\"], \"CONSTRAINTS_G\": [\"FLOATING\"]},\n", " \"c\" : {\"FRAME\": np.array([y_, -1*x_, z_]), \"POINTS\": {'C0': P2C0, 'C1': P2C1, 'C2': P2C2}, \"TYPE\": \"plane\", \"INTERACTIONS\": ['P1c'], \"CONSTRAINTS_D\": [\"PERFECT\"], \"CONSTRAINTS_G\": [\"SLIDING\"]},\n", " } \n", " },\n", " \"LOOPS\": {\n", " \"COMPATIBILITY\": {\n", " \"L0\": \"P1cC0 -> P2cC0 -> P2aA0 -> P1aA0\",\n", " \"L1\": \"P1cC0 -> P2cC0 -> P2bB0 -> P1bB0\",\n", " },\n", " },\n", " \"GLOBAL_CONSTRAINTS\": \"2D_NZ\",\n", "}\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "eda4b960-047d-47fa-a3ae-0a131bef54a3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total DOFs: 4\n", "Variables: [u_d_4, gamma_d_4, u_d_5, gamma_d_5]\n" ] } ], "source": [ "# Process the topology\n", "SDA = otaf.AssemblyDataProcessor(system_data)\n", "SDA.generate_expanded_loops()\n", "\n", "# Extract constraints automatically and embed the slack variable (s)\n", "CLH = otaf.CompatibilityLoopHandling(SDA)\n", "ILH = otaf.InterfaceLoopHandling(SDA, CLH, circle_resolution=20)\n", "SOCAM = otaf.SystemOfConstraintsAssemblyModel(\n", " CLH.get_compatibility_expressions(), \n", " ILH.get_interface_expressions()\n", ")\n", "SOCAM.embedOptimizationVariable()\n", "\n", "print(f\"Total DOFs: {len(SOCAM.deviation_symbols)}\")\n", "print(f\"Variables: {SOCAM.deviation_symbols}\")" ] }, { "cell_type": "markdown", "id": "ba18adf2-2501-4ab0-a3d4-d02d6ff30ed5", "metadata": {}, "source": [ "## 2. Surrogate Training (MLP)\n", "\n", "To bypass the expensive direct optimizations, we fit a PyTorch surrogate. We draw the distribution parameters directly from the `model1` definition, taking advantage of the `multiply_composed_distribution_standard_with_constants` utility to push the sampling into the failure space for robust training." ] }, { "cell_type": "code", "execution_count": 5, "id": "296e3160-d1fe-4f84-ab1b-804aac69c5bb", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "54fed309236b4be4a43f71765491f50a", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/100000 [00:00