docs: Major documentation overhaul - restructure folders, update tagline, add Getting Started guide

- Restructure docs/ folder (remove numeric prefixes):
  - 04_USER_GUIDES -> guides/
  - 05_API_REFERENCE -> api/
  - 06_PHYSICS -> physics/
  - 07_DEVELOPMENT -> development/
  - 08_ARCHIVE -> archive/
  - 09_DIAGRAMS -> diagrams/

- Replace tagline 'Talk, don't click' with 'LLM-driven optimization framework' in 9 files

- Create comprehensive docs/GETTING_STARTED.md:
  - Prerequisites and quick setup
  - Project structure overview
  - First study tutorial (Claude or manual)
  - Dashboard usage guide
  - Neural acceleration introduction

- Rewrite docs/00_INDEX.md with correct paths and modern structure

- Archive obsolete files:
  - 01_PROTOCOLS.md -> archive/historical/01_PROTOCOLS_legacy.md
  - 03_GETTING_STARTED.md -> archive/historical/
  - ATOMIZER_PODCAST_BRIEFING.md -> archive/marketing/

- Update timestamps to 2026-01-20 across all key files

- Update .gitignore to exclude docs/generated/

- Version bump: ATOMIZER_CONTEXT v1.8 -> v2.0
This commit is contained in:
2026-01-20 10:03:45 -05:00
parent 37f73cc2be
commit ea437d360e
103 changed files with 8980 additions and 327 deletions

View File

@@ -0,0 +1,730 @@
# Atomizer Architecture Review
**Date**: January 2026
**Version**: 2.0 (AtomizerSpec unified configuration)
**Author**: Architecture Review
---
## Executive Summary
Atomizer is a structural optimization platform that enables engineers to optimize FEA (Finite Element Analysis) designs through a visual canvas interface and AI-powered assistance. The architecture follows a **single source of truth** pattern where all configuration flows through `atomizer_spec.json`.
### Key Strengths
- **Unified Configuration**: One JSON file defines the entire optimization study
- **Type Safety**: Pydantic validation at every modification point
- **Real-time Collaboration**: WebSocket-based sync between all clients
- **Responsive UI**: Optimistic updates with background synchronization
- **AI Integration**: Claude can modify configurations in Power Mode
### Architecture Quality Score: **8.5/10**
| Aspect | Score | Notes |
|--------|-------|-------|
| Data Integrity | 9/10 | Single source of truth, hash-based conflict detection |
| Type Safety | 9/10 | Pydantic models throughout backend |
| Extensibility | 8/10 | Custom extractors, algorithms supported |
| Performance | 8/10 | Optimistic updates, WebSocket streaming |
| Maintainability | 8/10 | Clear separation of concerns |
| Documentation | 7/10 | Good inline docs, needs more high-level guides |
---
## 1. Configuration Layer
### 1.1 AtomizerSpec v2.0 - The Single Source of Truth
**Location**: `studies/{study_name}/atomizer_spec.json`
The AtomizerSpec is the heart of Atomizer's configuration. Every component reads from and writes to this single file.
```
atomizer_spec.json
├── meta # Study metadata
│ ├── version: "2.0"
│ ├── study_name
│ ├── created_by # canvas | claude | api | migration
│ └── modified_at
├── model # NX model files
│ ├── sim: { path, solver }
│ ├── nx_part: { path }
│ └── fem: { path }
├── design_variables[] # Parameters to optimize
│ ├── id: "dv_001"
│ ├── name, expression_name
│ ├── type: continuous | discrete
│ ├── bounds: { min, max }
│ └── canvas_position
├── extractors[] # Physics result extractors
│ ├── id: "ext_001"
│ ├── type: mass | displacement | stress | zernike | custom
│ ├── config: {}
│ └── outputs: [{ name, metric }]
├── objectives[] # Optimization goals
│ ├── id: "obj_001"
│ ├── direction: minimize | maximize
│ ├── weight
│ └── source: { extractor_id, output_key }
├── constraints[] # Hard/soft constraints
│ ├── id: "con_001"
│ ├── operator: <= | >= | ==
│ ├── threshold
│ └── source: { extractor_id, output_key }
├── optimization # Algorithm settings
│ ├── algorithm: { type, config }
│ ├── budget: { max_trials }
│ └── surrogate: { enabled, type }
└── canvas # UI layout state
└── edges[]
```
### 1.2 Node ID Convention
All configurable elements use unique IDs with prefixes:
| Prefix | Element Type | Example |
|--------|--------------|---------|
| `dv_` | Design Variable | `dv_001`, `dv_002` |
| `ext_` | Extractor | `ext_001`, `ext_002` |
| `obj_` | Objective | `obj_001`, `obj_002` |
| `con_` | Constraint | `con_001`, `con_002` |
IDs are auto-generated: `{prefix}{max_existing + 1:03d}`
### 1.3 Legacy Configuration
**File**: `optimization_config.json` (deprecated)
Legacy studies may have this file. The `SpecMigrator` automatically converts them to AtomizerSpec v2.0 on load.
```python
from optimization_engine.config.migrator import SpecMigrator
migrator = SpecMigrator(study_dir)
spec = migrator.migrate_file(legacy_path, spec_path)
```
---
## 2. Frontend Architecture
### 2.1 Technology Stack
| Layer | Technology | Purpose |
|-------|------------|---------|
| Build | Vite + TypeScript | Fast bundling, type safety |
| UI | React 18 + TailwindCSS | Component framework |
| State | Zustand | Lightweight global state |
| Canvas | ReactFlow | Graph visualization |
| Communication | Fetch + WebSocket | API + real-time sync |
### 2.2 Directory Structure
```
atomizer-dashboard/frontend/src/
├── components/
│ ├── canvas/ # Canvas components
│ │ ├── AtomizerCanvas # Main wrapper
│ │ ├── SpecRenderer # Spec → ReactFlow
│ │ ├── nodes/ # Node type components
│ │ └── panels/ # Side panels
│ └── chat/ # Claude chat UI
├── hooks/
│ ├── useSpecStore.ts # Central state (Zustand)
│ ├── useChat.ts # Claude integration
│ ├── useCanvasStore.ts # Local canvas state
│ └── useSpecWebSocket.ts # Real-time sync
├── lib/
│ └── spec/converter.ts # Spec ↔ ReactFlow
├── types/
│ └── atomizer-spec.ts # TypeScript definitions
└── pages/
├── CanvasView.tsx # Main canvas page
├── Home.tsx # Study selection
└── Setup.tsx # Study wizard
```
### 2.3 State Management Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ useSpecStore (Zustand) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────────┐ │
│ │ spec │ │ hash │ │ isDirty │ │ selectedNode │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └───────┬───────┘ │
└───────┼────────────┼────────────┼───────────────┼──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐
│Canvas │ │Conflict │ │ Save │ │ NodeConfig │
│ Render │ │Detection│ │ Button │ │ Panel │
└─────────┘ └─────────┘ └─────────┘ └─────────────┘
```
**Key Actions**:
- `loadSpec(studyId)` - Fetch spec from backend
- `patchSpec(path, value)` - Update with conflict check
- `patchSpecOptimistic(path, value)` - Fire-and-forget update
- `addNode(type, data)` - Add design var/extractor/etc.
- `removeNode(nodeId)` - Delete with edge cleanup
### 2.4 Canvas Rendering Pipeline
```
AtomizerSpec JSON
specToNodes() [converter.ts]
├──► Model Node (synthetic)
├──► Solver Node (synthetic)
├──► DesignVar Nodes × N
├──► Extractor Nodes × N
├──► Objective Nodes × N
├──► Constraint Nodes × N
├──► Algorithm Node (synthetic)
└──► Surrogate Node (optional)
ReactFlow Component
Interactive Canvas
```
**Layout Constants**:
```
Design Variables: x = 50
Model: x = 280
Solver: x = 510
Extractors: x = 740
Objectives: x = 1020
Constraints: x = 1020 (offset y)
Algorithm: x = 1300
Surrogate: x = 1530
Row Height: 100px
```
---
## 3. Backend Architecture
### 3.1 Technology Stack
| Layer | Technology | Purpose |
|-------|------------|---------|
| Framework | FastAPI | Async REST + WebSocket |
| Validation | Pydantic | Schema enforcement |
| Database | SQLite | Trial storage (Optuna schema) |
| LLM | Anthropic Claude | AI assistance |
### 3.2 Directory Structure
```
atomizer-dashboard/backend/api/
├── main.py # FastAPI app
├── routes/
│ ├── spec.py # Spec CRUD + WebSocket
│ ├── optimization.py # Run management
│ ├── claude.py # Chat sessions
│ └── files.py # File operations
└── services/
├── spec_manager.py # Central spec management
├── claude_agent.py # Claude with tools
├── context_builder.py # System prompts
└── session_manager.py # WebSocket sessions
```
### 3.3 SpecManager Service
**The SpecManager is the gatekeeper for all spec modifications.**
```python
class SpecManager:
def __init__(self, study_path: Path):
self.study_path = study_path
self.spec_file = study_path / "atomizer_spec.json"
self.subscribers: List[WebSocket] = []
# Loading
def load_spec(self) -> AtomizerSpec
def load_raw(self) -> dict
# Validation
def validate(self, spec) -> ValidationReport
def validate_semantic(self, spec) -> ValidationReport
# Modifications
def patch_spec(self, path, value) -> dict
def add_node(self, type, data) -> str
def update_node(self, id, updates) -> None
def remove_node(self, id) -> None
# Persistence
def save_spec(self, spec) -> dict # Atomic write + hash
def compute_hash(self, spec) -> str
# Real-time sync
def subscribe(self, ws: WebSocket)
def broadcast(self, message: dict)
```
**Modification Flow**:
1. Load current spec
2. Apply modification
3. Validate with Pydantic
4. Atomic write to disk
5. Compute new hash
6. Broadcast to all WebSocket subscribers
7. Return hash + timestamp
### 3.4 REST API Endpoints
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/api/studies/{id}/spec` | Load full spec |
| GET | `/api/studies/{id}/spec/hash` | Get current hash |
| PUT | `/api/studies/{id}/spec` | Replace entire spec |
| PATCH | `/api/studies/{id}/spec` | JSONPath patch |
| POST | `/api/studies/{id}/spec/nodes` | Add node |
| PATCH | `/api/studies/{id}/spec/nodes/{nid}` | Update node |
| DELETE | `/api/studies/{id}/spec/nodes/{nid}` | Remove node |
| POST | `/api/studies/{id}/spec/validate` | Validate spec |
| WS | `/api/studies/{id}/spec/sync` | Real-time sync |
### 3.5 Conflict Detection
Uses SHA256 hash of spec content:
```
Client A loads spec (hash: abc123)
Client B loads spec (hash: abc123)
Client A modifies → sends with hash abc123
Server: hash matches → apply → new hash: def456
Server: broadcast to all clients
Client B modifies → sends with hash abc123
Server: hash mismatch (expected def456)
Server: return 409 Conflict
Client B: reload latest spec
```
---
## 4. Optimization Engine
### 4.1 Directory Structure
```
optimization_engine/
├── config/ # Configuration management
│ ├── spec_models.py # Pydantic models
│ ├── spec_validator.py # Semantic validation
│ └── migrator.py # Legacy migration
├── extractors/ # Physics extractors
│ ├── extract_displacement.py
│ ├── extract_stress.py
│ ├── extract_mass_*.py
│ ├── extract_zernike*.py
│ └── custom_extractor_loader.py
├── core/ # Optimization algorithms
│ ├── runner.py # Main loop
│ ├── method_selector.py # Algorithm selection
│ └── intelligent_optimizer.py # IMSO
├── nx/ # NX integration
│ ├── solver.py # Nastran execution
│ └── updater.py # Parameter updates
├── study/ # Study management
│ ├── creator.py
│ └── state.py
└── utils/
├── dashboard_db.py # Optuna schema
└── trial_manager.py # Trial CRUD
```
### 4.2 Extractor Library
| ID | Type | Function | Inputs |
|----|------|----------|--------|
| E1 | Displacement | Max/RMS displacement | OP2, subcase |
| E2 | Frequency | Eigenvalue | OP2, mode |
| E3 | Stress | Von Mises, principal | OP2, element set |
| E4 | Mass (BDF) | Total mass | BDF file |
| E5 | Mass (Expr) | NX expression | NX session |
| E8-10 | Zernike | OPD polynomial fit | OP2, grid config |
**Custom Extractor Pattern**:
```python
def extract_volume(op2_path: str) -> Dict[str, float]:
from pyNastran.op2.op2 import OP2
op2 = OP2()
op2.read_op2(op2_path)
# ... calculation
return {"volume_mm3": calculated_volume}
```
### 4.3 Trial Storage
**Folder Structure**:
```
studies/{study}/2_iterations/
├── trial_0001/
│ ├── params.json # Input parameters
│ ├── results.json # Objectives/constraints
│ ├── _meta.json # Metadata
│ └── *.op2, *.fem # FEA outputs
├── trial_0002/
└── ...
```
**Database Schema** (Optuna-compatible SQLite):
```sql
trials (trial_id, study_id, number, state, created_at)
trial_params (trial_id, param_name, param_value)
trial_values (trial_id, objective_id, value)
trial_user_attributes (trial_id, key, value_json)
```
---
## 5. Claude Integration
### 5.1 Two Operation Modes
| Mode | Endpoint | Capabilities | Use Case |
|------|----------|--------------|----------|
| **User** | `/ws` | Read-only, MCP tools | Safe exploration |
| **Power** | `/ws/power` | Full write access | Canvas modification |
### 5.2 Power Mode Tools
```python
# claude_agent.py - Direct API tools
add_design_variable(name, min, max, baseline, units)
add_extractor(name, type, config, custom_code)
add_objective(name, direction, weight, extractor_id)
add_constraint(name, operator, threshold, extractor_id)
update_spec_field(path, value) # JSONPath update
remove_node(node_id)
```
### 5.3 Context Building
The `ContextBuilder` assembles rich system prompts:
```
# Atomizer Assistant
## Current Mode: POWER (full write access)
## Current Study: bracket_optimization
- Design Variables: 3 (thickness, angle, radius)
- Extractors: 2 (Displacement, Mass)
- Objectives: 2 (Min mass, Max stiffness)
- Constraints: 1 (mass <= 0.2 kg)
- Status: 47/100 trials complete
## Canvas State
8 nodes, 11 edges
[Node list with IDs and types...]
## Available Tools
- add_design_variable: Add a new design variable
- add_extractor: Add physics extractor
- add_objective: Add optimization objective
- add_constraint: Add constraint
- update_spec_field: Update any field by JSONPath
- remove_node: Remove element by ID
**ACT IMMEDIATELY** when asked to modify things.
```
---
## 6. Data Flow Diagrams
### 6.1 Canvas Edit Flow
```
User edits node property
┌─────────────────────────────┐
│ useSpecStore.patchSpec() │
│ ┌─────────────────────────┐ │
│ │ 1. Optimistic UI update │ │
│ └───────────┬─────────────┘ │
│ │ │
│ ┌───────────▼─────────────┐ │
│ │ 2. Async PATCH request │ │
│ └───────────┬─────────────┘ │
└─────────────┼───────────────┘
┌─────────────────────────────┐
│ Backend: SpecManager │
│ ┌─────────────────────────┐ │
│ │ 3. JSONPath parse │ │
│ │ 4. Apply modification │ │
│ │ 5. Pydantic validate │ │
│ │ 6. Atomic file write │ │
│ │ 7. Compute new hash │ │
│ │ 8. Broadcast to clients │ │
│ └───────────┬─────────────┘ │
└─────────────┼───────────────┘
┌─────────────────────────────┐
│ All WebSocket Clients │
│ ┌─────────────────────────┐ │
│ │ 9. Receive spec_updated │ │
│ │ 10. Update local hash │ │
│ │ 11. Re-render if needed │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘
```
### 6.2 Optimization Run Flow
```
User clicks "Run"
┌─────────────────────────────┐
│ POST /api/optimization/start│
│ { study_id, trials, method }│
└─────────────┬───────────────┘
┌─────────────────────────────┐
│ Backend: Spawn runner │
│ ┌─────────────────────────┐ │
│ │ 1. Load spec │ │
│ │ 2. Initialize Optuna │ │
│ │ 3. Create trial folders │ │
│ └───────────┬─────────────┘ │
└─────────────┼───────────────┘
┌─────────┼─────────┐
│ │ │
▼ ▼ ▼
┌───────────────────────────────────────────────────────┐
│ For each trial (1 to N): │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 4. Optuna suggests parameters │ │
│ │ 5. Update NX expressions │ │
│ │ 6. Run Nastran simulation │ │
│ │ 7. Extract physics results │ │
│ │ 8. Compute objectives/constraints │ │
│ │ 9. Save to trial folder + database │ │
│ │ 10. Send WebSocket update │ │
│ └─────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘
┌─────────────────────────────┐
│ Frontend: Real-time updates │
│ ┌─────────────────────────┐ │
│ │ Update convergence plot │ │
│ │ Update trial table │ │
│ │ Show best design │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘
```
### 6.3 Claude Canvas Modification Flow
```
User: "Add volume extractor with constraint <= 1000"
┌─────────────────────────────┐
│ WebSocket: /ws/power │
│ { type: 'message', │
│ content: '...', │
│ canvas_state: {...} } │
└─────────────┬───────────────┘
┌─────────────────────────────┐
│ AtomizerClaudeAgent │
│ ┌─────────────────────────┐ │
│ │ 1. Build context prompt │ │
│ │ 2. Send to Claude API │ │
│ │ 3. Claude decides tools │ │
│ └───────────┬─────────────┘ │
└─────────────┼───────────────┘
┌─────────────────────────────┐
│ Claude Tool Calls: │
│ ┌─────────────────────────┐ │
│ │ add_extractor( │ │
│ │ name="Volume", │ │
│ │ type="custom", │ │
│ │ code="..." ) │ │
│ │ │ │
│ │ add_constraint( │ │
│ │ name="Max Volume", │ │
│ │ operator="<=", │ │
│ │ threshold=1000 ) │ │
│ └───────────┬─────────────┘ │
└─────────────┼───────────────┘
┌─────────────────────────────┐
│ Each tool modifies spec: │
│ ┌─────────────────────────┐ │
│ │ Load → Modify → Save │ │
│ │ Send spec_modified │ │
│ └───────────┬─────────────┘ │
└─────────────┼───────────────┘
┌─────────────────────────────┐
│ Frontend receives events: │
│ ┌─────────────────────────┐ │
│ │ spec_modified → reload │ │
│ │ Canvas shows new nodes │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘
```
---
## 7. Component Relationships
### 7.1 Frontend Component Hierarchy
```
<App>
├── <CanvasView>
│ ├── <AtomizerCanvas>
│ │ ├── <ReactFlow>
│ │ │ ├── <DesignVarNode> × N
│ │ │ ├── <ExtractorNode> × N
│ │ │ ├── <ObjectiveNode> × N
│ │ │ ├── <ConstraintNode> × N
│ │ │ ├── <ModelNode>
│ │ │ ├── <SolverNode>
│ │ │ └── <AlgorithmNode>
│ │ ├── <NodePalette>
│ │ ├── <NodeConfigPanel>
│ │ ├── <ValidationPanel>
│ │ └── <ExecuteDialog>
│ └── <ChatPanel>
│ ├── <ChatMessage> × N
│ └── <ToolCallCard> × M
├── <Home>
│ └── <StudyList>
└── <Setup>
└── <StudyWizard>
```
### 7.2 Backend Service Dependencies
```
FastAPI App
├── spec.py ─────────► SpecManager
│ ├── Pydantic Models
│ └── File I/O + Hash
├── claude.py ───────► AtomizerClaudeAgent
│ ├── ContextBuilder
│ ├── Anthropic Client
│ └── Write Tools
├── optimization.py ─► Runner Process
│ ├── TrialManager
│ ├── NX Solver
│ └── Extractors
└── WebSocket Hub ◄─── All routes broadcast
```
---
## 8. Critical Patterns
### 8.1 Modification Pattern
**Always use SpecManager for modifications:**
```python
# ❌ WRONG: Direct file write
with open("atomizer_spec.json", "w") as f:
json.dump(spec, f)
# ✅ CORRECT: Use SpecManager
manager = SpecManager(study_path)
spec = manager.load_spec()
spec.objectives[0].weight = 2.0
manager.save_spec(spec)
```
### 8.2 Optimistic Update Pattern
```typescript
// 1. Update UI immediately
setSpec(modifiedSpec);
// 2. Async sync to backend
patchSpec(path, value)
.then(({ hash }) => setHash(hash))
.catch(() => setSpec(originalSpec)); // Rollback on failure
```
### 8.3 WebSocket Sync Pattern
```
Client A ─────► Server ─────► Client B
│ │ │
│ PATCH │ │
├─────────────►│ │
│ │ broadcast │
│ ├─────────────►│
│ │ │
│◄─────────────┤◄─────────────┤
│ ack + hash │ │
```
---
## 9. Potential Improvements
### 9.1 Current Limitations
1. **No Undo/Redo**: Canvas modifications are immediate
2. **Single File Lock**: No distributed locking for multi-user
3. **Memory-only Sessions**: Session state lost on restart
4. **Limited Offline**: Requires backend connection
### 9.2 Recommended Enhancements
| Priority | Enhancement | Benefit |
|----------|-------------|---------|
| High | Operation history with undo | Better UX |
| High | Persistent sessions (Redis) | Scalability |
| Medium | Spec versioning/branching | Experimentation |
| Medium | Batch operations API | Performance |
| Low | Offline canvas editing | Flexibility |
---
## 10. Conclusion
Atomizer's architecture is **well-designed for its purpose**: enabling engineers to configure and run FEA optimizations through a visual interface with AI assistance.
**Strongest Points**:
- Single source of truth eliminates sync issues
- Pydantic ensures data integrity
- WebSocket enables real-time collaboration
- Optimistic updates provide responsive UX
**Areas for Attention**:
- Add undo/redo for canvas operations
- Consider persistent session storage for production
- Expand test coverage for spec migrations
The architecture is **production-ready** for single-user/small-team scenarios and can be enhanced for enterprise deployment.

View File

@@ -0,0 +1,519 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 1200" width="1400" height="1200">
<defs>
<!-- Gradients -->
<linearGradient id="headerGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#1a1a2e"/>
<stop offset="100%" style="stop-color:#16213e"/>
</linearGradient>
<linearGradient id="frontendGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#0f3460"/>
<stop offset="100%" style="stop-color:#0d2847"/>
</linearGradient>
<linearGradient id="backendGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#1a472a"/>
<stop offset="100%" style="stop-color:#143d20"/>
</linearGradient>
<linearGradient id="engineGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#4a1942"/>
<stop offset="100%" style="stop-color:#3d1536"/>
</linearGradient>
<linearGradient id="specGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#e94560"/>
<stop offset="100%" style="stop-color:#c73659"/>
</linearGradient>
<linearGradient id="claudeGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#d4a373"/>
<stop offset="100%" style="stop-color:#bc8f5f"/>
</linearGradient>
<!-- Arrow marker -->
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
</marker>
<marker id="arrowheadBlue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#3b82f6"/>
</marker>
<marker id="arrowheadGreen" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#22c55e"/>
</marker>
<marker id="arrowheadOrange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#f97316"/>
</marker>
<!-- Drop shadow -->
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="2" dy="2" stdDeviation="3" flood-opacity="0.3"/>
</filter>
</defs>
<!-- Background -->
<rect width="1400" height="1200" fill="#0f0f1a"/>
<!-- Title -->
<rect x="0" y="0" width="1400" height="60" fill="url(#headerGrad)"/>
<text x="700" y="40" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="#ffffff">
ATOMIZER ARCHITECTURE - Intelligence Layout
</text>
<text x="700" y="55" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">
AtomizerSpec v2.0 | Single Source of Truth Pattern | January 2026
</text>
<!-- ==================== FRONTEND SECTION ==================== -->
<g id="frontend">
<!-- Frontend Container -->
<rect x="30" y="80" width="420" height="350" rx="10" fill="url(#frontendGrad)" filter="url(#shadow)" stroke="#3b82f6" stroke-width="2"/>
<text x="240" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#3b82f6">FRONTEND (React + TypeScript)</text>
<!-- Canvas View -->
<rect x="50" y="120" width="180" height="80" rx="6" fill="#1e3a5f" stroke="#60a5fa" stroke-width="1"/>
<text x="140" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#60a5fa">CanvasView</text>
<text x="140" y="162" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#93c5fd">ReactFlow Canvas</text>
<text x="140" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#93c5fd">Node Rendering</text>
<text x="140" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#93c5fd">Edge Connections</text>
<!-- Chat Panel -->
<rect x="250" y="120" width="180" height="80" rx="6" fill="#1e3a5f" stroke="#60a5fa" stroke-width="1"/>
<text x="340" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#60a5fa">ChatPanel</text>
<text x="340" y="162" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#93c5fd">Claude Integration</text>
<text x="340" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#93c5fd">User/Power Mode</text>
<text x="340" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#93c5fd">Tool Call Display</text>
<!-- useSpecStore -->
<rect x="50" y="215" width="180" height="90" rx="6" fill="#0d47a1" stroke="#2196f3" stroke-width="2"/>
<text x="140" y="240" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#ffffff">useSpecStore</text>
<text x="140" y="257" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#bbdefb">Zustand Store</text>
<text x="140" y="272" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#90caf9">spec | hash | isDirty</text>
<text x="140" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#90caf9">selectedNode | validation</text>
<text x="140" y="298" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#64b5f6">patchSpec() | addNode()</text>
<!-- useChat -->
<rect x="250" y="215" width="180" height="90" rx="6" fill="#0d47a1" stroke="#2196f3" stroke-width="2"/>
<text x="340" y="240" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#ffffff">useChat</text>
<text x="340" y="257" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#bbdefb">WebSocket Hook</text>
<text x="340" y="272" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#90caf9">messages | isThinking</text>
<text x="340" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#90caf9">mode: user | power</text>
<text x="340" y="298" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#64b5f6">sendMessage() | switchMode()</text>
<!-- Converter -->
<rect x="50" y="320" width="380" height="45" rx="6" fill="#1565c0" stroke="#42a5f5" stroke-width="1"/>
<text x="240" y="342" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">spec/converter.ts</text>
<text x="240" y="358" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#bbdefb">specToNodes() | nodesToSpec() | Auto-layout: DVs→Model→Solver→Extractors→Objectives→Algorithm</text>
<!-- Types -->
<rect x="50" y="375" width="180" height="40" rx="4" fill="#1976d2" stroke="#64b5f6" stroke-width="1"/>
<text x="140" y="395" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#ffffff">types/atomizer-spec.ts</text>
<text x="140" y="408" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbdefb">TypeScript Definitions</text>
<!-- Panels -->
<rect x="250" y="375" width="180" height="40" rx="4" fill="#1976d2" stroke="#64b5f6" stroke-width="1"/>
<text x="340" y="395" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#ffffff">panels/*.tsx</text>
<text x="340" y="408" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbdefb">NodeConfig | Validation | Execute</text>
</g>
<!-- ==================== BACKEND SECTION ==================== -->
<g id="backend">
<!-- Backend Container -->
<rect x="490" y="80" width="420" height="350" rx="10" fill="url(#backendGrad)" filter="url(#shadow)" stroke="#22c55e" stroke-width="2"/>
<text x="700" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#22c55e">BACKEND (FastAPI + Python)</text>
<!-- Routes -->
<rect x="510" y="120" width="180" height="100" rx="6" fill="#14532d" stroke="#4ade80" stroke-width="1"/>
<text x="600" y="140" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4ade80">routes/</text>
<text x="600" y="158" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">spec.py - CRUD + WS sync</text>
<text x="600" y="173" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">claude.py - Chat sessions</text>
<text x="600" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">optimization.py - Run control</text>
<text x="600" y="203" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">files.py - Upload/download</text>
<!-- Services -->
<rect x="710" y="120" width="180" height="100" rx="6" fill="#14532d" stroke="#4ade80" stroke-width="1"/>
<text x="800" y="140" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4ade80">services/</text>
<text x="800" y="158" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">spec_manager.py</text>
<text x="800" y="173" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">claude_agent.py</text>
<text x="800" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">context_builder.py</text>
<text x="800" y="203" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">session_manager.py</text>
<!-- SpecManager Detail -->
<rect x="510" y="235" width="380" height="100" rx="6" fill="#166534" stroke="#22c55e" stroke-width="2"/>
<text x="700" y="258" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#ffffff">SpecManager (Central Gatekeeper)</text>
<text x="700" y="278" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#bbf7d0">load_spec() → validate() → patch_spec() → save_spec()</text>
<text x="700" y="295" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">Pydantic Validation | Atomic Writes | SHA256 Hashing</text>
<text x="700" y="312" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#86efac">WebSocket Broadcast | Conflict Detection</text>
<text x="700" y="328" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#4ade80">All modifications must go through SpecManager</text>
<!-- REST Endpoints -->
<rect x="510" y="350" width="180" height="65" rx="4" fill="#15803d" stroke="#4ade80" stroke-width="1"/>
<text x="600" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">REST API</text>
<text x="600" y="385" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">GET/PUT/PATCH /spec</text>
<text x="600" y="398" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">POST/PATCH/DELETE /nodes</text>
<text x="600" y="411" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">POST /validate</text>
<!-- WebSocket -->
<rect x="710" y="350" width="180" height="65" rx="4" fill="#15803d" stroke="#4ade80" stroke-width="1"/>
<text x="800" y="370" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">WebSocket</text>
<text x="800" y="385" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">/spec/sync - Real-time sync</text>
<text x="800" y="398" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">/ws - User mode chat</text>
<text x="800" y="411" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">/ws/power - Power mode</text>
</g>
<!-- ==================== ATOMIZER SPEC (CENTER) ==================== -->
<g id="spec">
<rect x="520" y="470" width="360" height="200" rx="12" fill="url(#specGrad)" filter="url(#shadow)" stroke="#ff6b6b" stroke-width="3"/>
<text x="700" y="500" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#ffffff">atomizer_spec.json</text>
<text x="700" y="520" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#fecaca">SINGLE SOURCE OF TRUTH</text>
<!-- Spec Structure -->
<rect x="540" y="535" width="150" height="120" rx="4" fill="#991b1b" stroke="#fca5a5" stroke-width="1"/>
<text x="615" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#fecaca">Structure</text>
<text x="615" y="572" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fecaca">meta: { version, name }</text>
<text x="615" y="586" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fecaca">model: { sim, fem }</text>
<text x="615" y="600" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fecaca">design_variables[]</text>
<text x="615" y="614" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fecaca">extractors[]</text>
<text x="615" y="628" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fecaca">objectives[]</text>
<text x="615" y="642" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fecaca">constraints[]</text>
<!-- Node IDs -->
<rect x="710" y="535" width="150" height="120" rx="4" fill="#991b1b" stroke="#fca5a5" stroke-width="1"/>
<text x="785" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#fecaca">Node IDs</text>
<text x="785" y="575" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#fecaca">dv_001, dv_002...</text>
<text x="785" y="592" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#fecaca">ext_001, ext_002...</text>
<text x="785" y="609" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#fecaca">obj_001, obj_002...</text>
<text x="785" y="626" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#fecaca">con_001, con_002...</text>
<text x="785" y="648" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#f87171">+ canvas_position</text>
</g>
<!-- ==================== OPTIMIZATION ENGINE ==================== -->
<g id="engine">
<rect x="950" y="80" width="420" height="350" rx="10" fill="url(#engineGrad)" filter="url(#shadow)" stroke="#c026d3" stroke-width="2"/>
<text x="1160" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#c026d3">OPTIMIZATION ENGINE</text>
<!-- Core -->
<rect x="970" y="120" width="180" height="95" rx="6" fill="#581c87" stroke="#d946ef" stroke-width="1"/>
<text x="1060" y="140" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#d946ef">core/</text>
<text x="1060" y="158" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">runner.py - Main loop</text>
<text x="1060" y="173" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">method_selector.py</text>
<text x="1060" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">intelligent_optimizer.py</text>
<text x="1060" y="203" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">gradient_optimizer.py</text>
<!-- Extractors -->
<rect x="1170" y="120" width="180" height="95" rx="6" fill="#581c87" stroke="#d946ef" stroke-width="1"/>
<text x="1260" y="140" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#d946ef">extractors/</text>
<text x="1260" y="158" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">displacement, stress</text>
<text x="1260" y="173" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">frequency, mass</text>
<text x="1260" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">zernike_opd</text>
<text x="1260" y="203" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">custom_extractor_loader</text>
<!-- Config -->
<rect x="970" y="230" width="180" height="80" rx="6" fill="#6b21a8" stroke="#a855f7" stroke-width="2"/>
<text x="1060" y="252" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">config/</text>
<text x="1060" y="270" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">spec_models.py (Pydantic)</text>
<text x="1060" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">spec_validator.py</text>
<text x="1060" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">migrator.py (legacy→v2)</text>
<!-- NX Integration -->
<rect x="1170" y="230" width="180" height="80" rx="6" fill="#581c87" stroke="#d946ef" stroke-width="1"/>
<text x="1260" y="252" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#d946ef">nx/</text>
<text x="1260" y="270" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">solver.py - Nastran exec</text>
<text x="1260" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">updater.py - Param update</text>
<text x="1260" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#e9d5ff">session_manager.py</text>
<!-- Utils -->
<rect x="970" y="325" width="180" height="55" rx="4" fill="#7c3aed" stroke="#a78bfa" stroke-width="1"/>
<text x="1060" y="345" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">utils/</text>
<text x="1060" y="362" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#ddd6fe">dashboard_db.py (Optuna)</text>
<text x="1060" y="375" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#ddd6fe">trial_manager.py</text>
<!-- Study -->
<rect x="1170" y="325" width="180" height="55" rx="4" fill="#7c3aed" stroke="#a78bfa" stroke-width="1"/>
<text x="1260" y="345" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">study/</text>
<text x="1260" y="362" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#ddd6fe">creator.py, state.py</text>
<text x="1260" y="375" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#ddd6fe">reset.py</text>
<!-- Algorithms Box -->
<rect x="970" y="390" width="380" height="35" rx="4" fill="#4c1d95" stroke="#8b5cf6" stroke-width="1"/>
<text x="1160" y="412" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#c4b5fd">Algorithms: TPE | CMA-ES | NSGA-II | IMSO | SAT_v3 | RandomSearch</text>
</g>
<!-- ==================== CLAUDE INTEGRATION ==================== -->
<g id="claude">
<rect x="950" y="470" width="420" height="200" rx="10" fill="url(#claudeGrad)" filter="url(#shadow)" stroke="#d97706" stroke-width="2"/>
<text x="1160" y="495" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#451a03">CLAUDE INTEGRATION</text>
<!-- AtomizerClaudeAgent -->
<rect x="970" y="510" width="180" height="145" rx="6" fill="#92400e" stroke="#fbbf24" stroke-width="1"/>
<text x="1060" y="532" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#fef3c7">AtomizerClaudeAgent</text>
<text x="1060" y="550" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#fde68a">Direct Anthropic API</text>
<text x="1060" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fef3c7">Tools (Power Mode):</text>
<text x="1060" y="585" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">add_design_variable()</text>
<text x="1060" y="598" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">add_extractor()</text>
<text x="1060" y="611" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">add_objective()</text>
<text x="1060" y="624" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">add_constraint()</text>
<text x="1060" y="637" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">update_spec_field()</text>
<text x="1060" y="650" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">remove_node()</text>
<!-- Context Builder -->
<rect x="1170" y="510" width="180" height="75" rx="6" fill="#92400e" stroke="#fbbf24" stroke-width="1"/>
<text x="1260" y="532" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#fef3c7">ContextBuilder</text>
<text x="1260" y="552" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#fde68a">System prompt assembly</text>
<text x="1260" y="568" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">Study context injection</text>
<text x="1260" y="581" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">Canvas state embedding</text>
<!-- Modes -->
<rect x="1170" y="595" width="85" height="55" rx="4" fill="#78350f" stroke="#f59e0b" stroke-width="1"/>
<text x="1212" y="615" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#fef3c7">User</text>
<text x="1212" y="630" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">Read-only</text>
<text x="1212" y="643" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fde68a">MCP tools</text>
<rect x="1265" y="595" width="85" height="55" rx="4" fill="#b45309" stroke="#fbbf24" stroke-width="2"/>
<text x="1307" y="615" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">Power</text>
<text x="1307" y="630" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fef3c7">Full write</text>
<text x="1307" y="643" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#fef3c7">Direct API</text>
</g>
<!-- ==================== FILE SYSTEM ==================== -->
<g id="filesystem">
<rect x="30" y="470" width="450" height="200" rx="10" fill="#1f2937" filter="url(#shadow)" stroke="#6b7280" stroke-width="2"/>
<text x="255" y="495" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#9ca3af">FILE SYSTEM</text>
<!-- Study Structure -->
<rect x="50" y="510" width="200" height="145" rx="6" fill="#374151" stroke="#6b7280" stroke-width="1"/>
<text x="150" y="530" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#d1d5db">studies/{name}/</text>
<text x="150" y="550" text-anchor="middle" font-family="monospace" font-size="9" fill="#9ca3af">atomizer_spec.json</text>
<text x="150" y="566" text-anchor="middle" font-family="monospace" font-size="9" fill="#6b7280">1_setup/model/</text>
<text x="150" y="582" text-anchor="middle" font-family="monospace" font-size="9" fill="#6b7280"> *.prt, *.sim, *.fem</text>
<text x="150" y="598" text-anchor="middle" font-family="monospace" font-size="9" fill="#6b7280">2_iterations/</text>
<text x="150" y="614" text-anchor="middle" font-family="monospace" font-size="9" fill="#6b7280"> trial_NNNN/</text>
<text x="150" y="630" text-anchor="middle" font-family="monospace" font-size="9" fill="#6b7280">3_results/</text>
<text x="150" y="646" text-anchor="middle" font-family="monospace" font-size="9" fill="#9ca3af"> study.db</text>
<!-- Database -->
<rect x="270" y="510" width="190" height="145" rx="6" fill="#374151" stroke="#6b7280" stroke-width="1"/>
<text x="365" y="530" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#d1d5db">study.db (Optuna)</text>
<text x="365" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#9ca3af">trials</text>
<text x="365" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#6b7280">trial_id | state | created</text>
<text x="365" y="590" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#9ca3af">trial_params</text>
<text x="365" y="605" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#6b7280">param_name | param_value</text>
<text x="365" y="625" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#9ca3af">trial_values</text>
<text x="365" y="640" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#6b7280">objective_id | value</text>
</g>
<!-- ==================== DATA FLOW ARROWS ==================== -->
<!-- Frontend to Backend (REST) -->
<path d="M 450 285 L 510 285" stroke="#3b82f6" stroke-width="2" marker-end="url(#arrowheadBlue)" fill="none"/>
<text x="480" y="278" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#60a5fa">REST</text>
<!-- Frontend to Backend (WebSocket) -->
<path d="M 450 315 L 510 315" stroke="#22c55e" stroke-width="2" marker-end="url(#arrowheadGreen)" fill="none" stroke-dasharray="5,3"/>
<text x="480" y="330" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#4ade80">WS</text>
<!-- Backend to Spec -->
<path d="M 700 420 L 700 470" stroke="#e94560" stroke-width="3" marker-end="url(#arrowhead)" fill="none"/>
<text x="715" y="445" font-family="Arial, sans-serif" font-size="8" fill="#f87171">Read/Write</text>
<!-- Spec to Engine -->
<path d="M 880 570 L 950 570" stroke="#c026d3" stroke-width="2" marker-end="url(#arrowhead)" fill="none"/>
<text x="915" y="563" font-family="Arial, sans-serif" font-size="8" fill="#d946ef">Load</text>
<!-- Claude to Spec -->
<path d="M 950 570 L 880 570" stroke="#d97706" stroke-width="2" marker-end="url(#arrowheadOrange)" fill="none" stroke-dasharray="5,3"/>
<text x="915" y="585" font-family="Arial, sans-serif" font-size="8" fill="#fbbf24">Modify</text>
<!-- Spec to FileSystem -->
<path d="M 520 570 L 480 570" stroke="#6b7280" stroke-width="2" marker-end="url(#arrowhead)" fill="none"/>
<text x="500" y="563" font-family="Arial, sans-serif" font-size="8" fill="#9ca3af">File</text>
<!-- Engine to FileSystem (DB) -->
<path d="M 950 400 L 480 590" stroke="#c026d3" stroke-width="1.5" marker-end="url(#arrowhead)" fill="none" stroke-dasharray="3,2"/>
<text x="720" y="485" font-family="Arial, sans-serif" font-size="8" fill="#d946ef">Trials</text>
<!-- Frontend to Claude (Chat) -->
<path d="M 430 155 L 1050 475" stroke="#d97706" stroke-width="1.5" marker-end="url(#arrowheadOrange)" fill="none" stroke-dasharray="5,3"/>
<text x="740" y="300" font-family="Arial, sans-serif" font-size="8" fill="#fbbf24">Chat WS</text>
<!-- ==================== LEGEND ==================== -->
<g id="legend">
<rect x="30" y="700" width="1340" height="80" rx="8" fill="#1a1a2e" stroke="#374151" stroke-width="1"/>
<text x="700" y="725" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#ffffff">DATA FLOW LEGEND</text>
<!-- REST -->
<line x1="80" y1="750" x2="130" y2="750" stroke="#3b82f6" stroke-width="2"/>
<text x="145" y="754" font-family="Arial, sans-serif" font-size="10" fill="#60a5fa">REST API</text>
<!-- WebSocket -->
<line x1="230" y1="750" x2="280" y2="750" stroke="#22c55e" stroke-width="2" stroke-dasharray="5,3"/>
<text x="295" y="754" font-family="Arial, sans-serif" font-size="10" fill="#4ade80">WebSocket</text>
<!-- Read/Write -->
<line x1="400" y1="750" x2="450" y2="750" stroke="#e94560" stroke-width="3"/>
<text x="465" y="754" font-family="Arial, sans-serif" font-size="10" fill="#f87171">Spec I/O</text>
<!-- Claude -->
<line x1="560" y1="750" x2="610" y2="750" stroke="#d97706" stroke-width="2" stroke-dasharray="5,3"/>
<text x="625" y="754" font-family="Arial, sans-serif" font-size="10" fill="#fbbf24">Claude</text>
<!-- Engine -->
<line x1="700" y1="750" x2="750" y2="750" stroke="#c026d3" stroke-width="2"/>
<text x="765" y="754" font-family="Arial, sans-serif" font-size="10" fill="#d946ef">Engine</text>
<!-- Key Principle -->
<rect x="900" y="740" width="430" height="25" rx="4" fill="#991b1b"/>
<text x="1115" y="757" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">
All modifications flow through atomizer_spec.json (Single Source of Truth)
</text>
</g>
<!-- ==================== CANVAS NODES DETAIL ==================== -->
<g id="canvas-nodes">
<rect x="30" y="800" width="650" height="180" rx="8" fill="#0d2847" stroke="#3b82f6" stroke-width="1"/>
<text x="355" y="825" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#3b82f6">CANVAS NODE TYPES (ReactFlow)</text>
<!-- Node types in columns -->
<g transform="translate(50, 845)">
<!-- Design Variables -->
<rect x="0" y="0" width="90" height="50" rx="4" fill="#1e40af" stroke="#60a5fa" stroke-width="1"/>
<text x="45" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">DesignVar</text>
<text x="45" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#93c5fd">dv_001</text>
<text x="45" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#93c5fd">bounds, baseline</text>
<!-- Model -->
<rect x="105" y="0" width="90" height="50" rx="4" fill="#065f46" stroke="#34d399" stroke-width="1"/>
<text x="150" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Model</text>
<text x="150" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#a7f3d0">synthetic</text>
<text x="150" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#a7f3d0">.sim path</text>
<!-- Solver -->
<rect x="210" y="0" width="90" height="50" rx="4" fill="#065f46" stroke="#34d399" stroke-width="1"/>
<text x="255" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Solver</text>
<text x="255" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#a7f3d0">synthetic</text>
<text x="255" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#a7f3d0">SOL 101/103</text>
<!-- Extractor -->
<rect x="315" y="0" width="90" height="50" rx="4" fill="#7c2d12" stroke="#fb923c" stroke-width="1"/>
<text x="360" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Extractor</text>
<text x="360" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#fed7aa">ext_001</text>
<text x="360" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#fed7aa">type, outputs</text>
<!-- Objective -->
<rect x="420" y="0" width="90" height="50" rx="4" fill="#7f1d1d" stroke="#f87171" stroke-width="1"/>
<text x="465" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Objective</text>
<text x="465" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#fecaca">obj_001</text>
<text x="465" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#fecaca">direction, weight</text>
<!-- Constraint -->
<rect x="525" y="0" width="90" height="50" rx="4" fill="#713f12" stroke="#facc15" stroke-width="1"/>
<text x="570" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Constraint</text>
<text x="570" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#fef08a">con_001</text>
<text x="570" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#fef08a">operator, threshold</text>
</g>
<!-- Second row -->
<g transform="translate(50, 910)">
<!-- Algorithm -->
<rect x="0" y="0" width="90" height="50" rx="4" fill="#581c87" stroke="#a855f7" stroke-width="1"/>
<text x="45" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Algorithm</text>
<text x="45" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#e9d5ff">synthetic</text>
<text x="45" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#e9d5ff">TPE/CMA-ES</text>
<!-- Surrogate -->
<rect x="105" y="0" width="90" height="50" rx="4" fill="#312e81" stroke="#818cf8" stroke-width="1"/>
<text x="150" y="20" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#ffffff">Surrogate</text>
<text x="150" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#c7d2fe">optional</text>
<text x="150" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="7" fill="#c7d2fe">MLP/GNN</text>
<!-- Flow -->
<text x="330" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">
Layout: DVs → Model → Solver → Extractors → Objectives/Constraints → Algorithm
</text>
</g>
</g>
<!-- ==================== KEY PATTERNS ==================== -->
<g id="patterns">
<rect x="700" y="800" width="670" height="180" rx="8" fill="#14532d" stroke="#22c55e" stroke-width="1"/>
<text x="1035" y="825" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#22c55e">KEY ARCHITECTURAL PATTERNS</text>
<!-- Optimistic Updates -->
<rect x="720" y="840" width="200" height="60" rx="4" fill="#166534" stroke="#4ade80" stroke-width="1"/>
<text x="820" y="860" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">Optimistic Updates</text>
<text x="820" y="877" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">1. Update UI immediately</text>
<text x="820" y="890" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">2. Async sync to backend</text>
<!-- Conflict Detection -->
<rect x="935" y="840" width="200" height="60" rx="4" fill="#166534" stroke="#4ade80" stroke-width="1"/>
<text x="1035" y="860" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">Conflict Detection</text>
<text x="1035" y="877" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">SHA256 hash comparison</text>
<text x="1035" y="890" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">409 Conflict → reload</text>
<!-- WebSocket Broadcast -->
<rect x="1150" y="840" width="200" height="60" rx="4" fill="#166534" stroke="#4ade80" stroke-width="1"/>
<text x="1250" y="860" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">WS Broadcast</text>
<text x="1250" y="877" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">All clients synced</text>
<text x="1250" y="890" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">Real-time updates</text>
<!-- Pydantic Validation -->
<rect x="720" y="910" width="200" height="60" rx="4" fill="#166534" stroke="#4ade80" stroke-width="1"/>
<text x="820" y="930" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">Pydantic Validation</text>
<text x="820" y="947" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">Every modification validated</text>
<text x="820" y="960" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">Schema + semantic checks</text>
<!-- Atomic Writes -->
<rect x="935" y="910" width="200" height="60" rx="4" fill="#166534" stroke="#4ade80" stroke-width="1"/>
<text x="1035" y="930" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">Atomic Writes</text>
<text x="1035" y="947" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">No partial updates</text>
<text x="1035" y="960" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">Write → hash → broadcast</text>
<!-- Node ID Convention -->
<rect x="1150" y="910" width="200" height="60" rx="4" fill="#166534" stroke="#4ade80" stroke-width="1"/>
<text x="1250" y="930" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="#ffffff">Node ID Convention</text>
<text x="1250" y="947" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">dv_, ext_, obj_, con_</text>
<text x="1250" y="960" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#bbf7d0">Auto-increment: {prefix}{N:03d}</text>
</g>
<!-- ==================== VERSION INFO ==================== -->
<g id="version">
<rect x="30" y="1000" width="1340" height="50" rx="8" fill="#1a1a2e" stroke="#374151" stroke-width="1"/>
<text x="700" y="1020" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#6b7280">
AtomizerSpec v2.0 | React 18 + Vite + Zustand | FastAPI + Pydantic | SQLite (Optuna) | Anthropic Claude API
</text>
<text x="700" y="1038" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#6b7280">
Optimization Algorithms: TPE | CMA-ES | NSGA-II | IMSO | SAT_v3 | GNN Surrogates | Extractors: displacement, stress, frequency, mass, zernike
</text>
</g>
<!-- ==================== INTELLIGENCE INDICATORS ==================== -->
<g id="intelligence">
<rect x="30" y="1060" width="1340" height="120" rx="8" fill="#1f1f3a" stroke="#8b5cf6" stroke-width="2"/>
<text x="700" y="1085" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#a78bfa">INTELLIGENCE LAYER</text>
<!-- Claude Power Mode -->
<rect x="60" y="1100" width="250" height="60" rx="4" fill="#3730a3" stroke="#818cf8" stroke-width="1"/>
<text x="185" y="1120" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">Claude Power Mode</text>
<text x="185" y="1138" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Direct spec modifications via tools</text>
<text x="185" y="1152" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">No permission prompts</text>
<!-- IMSO -->
<rect x="330" y="1100" width="250" height="60" rx="4" fill="#3730a3" stroke="#818cf8" stroke-width="1"/>
<text x="455" y="1120" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">IMSO (Intelligent Multi-Stage)</text>
<text x="455" y="1138" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Adaptive algorithm selection</text>
<text x="455" y="1152" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Surrogate-assisted optimization</text>
<!-- GNN Surrogates -->
<rect x="600" y="1100" width="250" height="60" rx="4" fill="#3730a3" stroke="#818cf8" stroke-width="1"/>
<text x="725" y="1120" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">GNN Surrogates</text>
<text x="725" y="1138" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Neural network FEA predictions</text>
<text x="725" y="1152" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">10x speedup via Zernike GNN</text>
<!-- SAT v3 -->
<rect x="870" y="1100" width="250" height="60" rx="4" fill="#3730a3" stroke="#818cf8" stroke-width="1"/>
<text x="995" y="1120" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">Self-Aware Turbo (SAT v3)</text>
<text x="995" y="1138" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Uncertainty-aware exploration</text>
<text x="995" y="1152" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Adaptive FEA validation</text>
<!-- Context Learning -->
<rect x="1140" y="1100" width="210" height="60" rx="4" fill="#3730a3" stroke="#818cf8" stroke-width="1"/>
<text x="1245" y="1120" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#ffffff">LAC (Learning Core)</text>
<text x="1245" y="1138" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Session insights persistence</text>
<text x="1245" y="1152" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#c7d2fe">Cross-study knowledge</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -0,0 +1,529 @@
# Atomizer Component Relationships
**Date**: January 2026
**Version**: 2.0
This document details how Atomizer's components interact with each other.
---
## 1. Configuration Files Hierarchy
```
┌─────────────────────────────────────────────────────────────────┐
│ atomizer_spec.json (v2.0) │
│ SINGLE SOURCE OF TRUTH │
└───────────────────────────┬─────────────────────────────────────┘
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Canvas UI │ │ Backend API │ │ Opt Engine │
│ (ReactFlow) │ │ (FastAPI) │ │ (Python) │
└───────────────┘ └───────────────┘ └───────────────┘
```
### Relationship Table
| Component | Reads From | Writes To | Frequency |
|-----------|------------|-----------|-----------|
| Canvas (React) | atomizer_spec.json | atomizer_spec.json | Real-time |
| Backend (FastAPI) | atomizer_spec.json | atomizer_spec.json | Per request |
| Engine (Python) | atomizer_spec.json | study.db | Per trial |
| Claude Agent | atomizer_spec.json | atomizer_spec.json | Per command |
---
## 2. Frontend Store Dependencies
```
┌─────────────────────┐
│ useSpecStore │
│ (Zustand) │
│ │
│ spec: AtomizerSpec │
│ hash: string │
│ isDirty: boolean │
│ selectedNodeId │
└──────────┬──────────┘
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ CanvasView │ │ NodeConfig │ │ Validation │
│ │ │ Panel │ │ Panel │
│ Subscribes: │ │ │ │ │
│ - spec │ │ Subscribes: │ │ Subscribes: │
│ - hash │ │ - spec │ │ - validation │
│ - isDirty │ │ - selected │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
┌─────────────────────┐
│ useChat │
│ (Hook) │
│ │
│ messages: Message[] │
│ isThinking: bool │
│ mode: user|power │
│ sessionId: string │
└──────────┬──────────┘
┌─────────────────────┐
│ ChatPanel │
│ │
│ Displays messages │
│ Tool call cards │
│ Mode toggle │
└─────────────────────┘
```
### State Flow
1. **User edits node**`useSpecStore.patchSpec(path, value)`
2. **Optimistic update** → UI immediately reflects change
3. **Async PATCH** → Backend validates and saves
4. **WebSocket broadcast** → All clients receive `spec_updated`
5. **Hash comparison** → Check for conflicts
---
## 3. Backend Service Dependencies
```
┌─────────────────────────────────────────────────────────────────┐
│ FastAPI App │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ spec.py │ │ claude.py │ │optimization.py│
│ (Routes) │ │ (Routes) │ │ (Routes) │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ SpecManager │◄─────│ClaudeAgent │ │ Runner │
│ │ │ │ │ (subprocess) │
│ - load_spec() │ │ - tools │ │ │
│ - save_spec() │ │ - chat() │ │ - run() │
│ - patch_spec()│ │ │ │ - stream() │
│ - broadcast() │ │ │ │ │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└──────────────────────┼──────────────────────┘
┌─────────────────────┐
│ atomizer_spec.json │
│ (Disk) │
└─────────────────────┘
```
### Service Responsibilities
| Service | Responsibility | Dependencies |
|---------|---------------|--------------|
| SpecManager | All spec CRUD, validation, broadcasting | Pydantic models |
| ClaudeAgent | AI chat with tools, context building | Anthropic API, SpecManager |
| ContextBuilder | System prompt assembly | Study data, canvas state |
| SessionManager | WebSocket lifecycle | In-memory store |
| Runner | Optimization loop | NX solver, extractors |
---
## 4. Optimization Engine Module Dependencies
```
┌─────────────────────────────────────────────────────────────────┐
│ optimization_engine/ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ config/ │ │ core/ │ │ nx/ │
│ │ │ │ │ │
│spec_models │◄─────│ runner │─────►│ solver │
│spec_validator│ │ IMSO │ │ updater │
│ migrator │ │ gradient │ │ session │
└──────┬──────┘ └──────┬──────┘ └─────────────┘
│ │
│ ┌──────┴──────┐
│ │ │
│ ▼ ▼
│ ┌─────────────┐ ┌─────────────┐
│ │ extractors/ │ │ surrogates │
│ │ │ │ │
│ │displacement │ │ MLP │
│ │ stress │ │ GNN │
│ │ zernike │ │ ensemble │
│ │ custom │ │ │
│ └─────────────┘ └─────────────┘
┌─────────────┐ ┌─────────────┐
│ study/ │ │ utils/ │
│ │ │ │
│ creator │ │dashboard_db │
│ state │ │trial_manager│
│ reset │ │ archiver │
└─────────────┘ └─────────────┘
```
### Module Interaction Flow
```
atomizer_spec.json
┌───────────────┐
│ spec_models │ ← Pydantic validation
└───────┬───────┘
┌───────────────┐
│ runner │ ← Main optimization loop
└───────┬───────┘
┌───────┴───────┐
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ nx/ │ │extract/ │
│ solver │ │ors │
└────┬────┘ └────┬────┘
│ │
└─────┬─────┘
┌─────────────┐
│ study.db │ ← Trial persistence
└─────────────┘
```
---
## 5. WebSocket Event Flow
### 5.1 Spec Sync WebSocket
```
Frontend Backend File System
│ │ │
│ ── PATCH /spec ──────► │ │
│ │ ── write ──────────────► │
│ │ ◄── confirm ──────────── │
│ │ │
│ ◄── spec_updated ───── │ ← broadcast to all │
│ │ │
│ (update local hash) │ │
```
### 5.2 Claude Chat WebSocket
```
Frontend Backend Claude API
│ │ │
│ ── message + state ──► │ │
│ │ ── prompt ─────────────► │
│ │ ◄── tool_call ────────── │
│ ◄── tool_call ──────── │ │
│ │ (execute tool) │
│ │ ── tool_result ────────► │
│ ◄── tool_result ────── │ ◄── response ─────────── │
│ ◄── text ───────────── │ │
│ ◄── done ───────────── │ │
│ │ │
│ (if spec_modified) │ │
│ ◄── spec_modified ──── │ │
│ (reload spec) │ │
```
### 5.3 Optimization Stream WebSocket
```
Frontend Backend Runner Process
│ │ │
│ ── start_opt ────────► │ ── spawn ──────────────► │
│ │ │
│ │ ◄── trial_start ──────── │
│ ◄── trial_start ────── │ │
│ │ │
│ │ ◄── trial_complete ───── │
│ ◄── trial_complete ─── │ │
│ │ │
│ (repeat for N trials) │ │
│ │ │
│ ◄── optimization_done ─ │ ◄── done ─────────────── │
```
---
## 6. Data Transformation Pipeline
### 6.1 Canvas → Spec → ReactFlow
```
User interacts with Canvas
┌─────────────────────────┐
│ ReactFlow onChange │
│ (nodes, edges) │
└───────────┬─────────────┘
┌─────────────────────────┐
│ useSpecStore.patch() │
│ JSONPath modification │
└───────────┬─────────────┘
┌─────────────────────────┐
│ Backend SpecManager │
│ Pydantic validation │
└───────────┬─────────────┘
┌─────────────────────────┐
│ atomizer_spec.json │
│ (persisted) │
└───────────┬─────────────┘
┌─────────────────────────┐
│ spec/converter.ts │
│ specToNodes() │
└───────────┬─────────────┘
┌─────────────────────────┐
│ ReactFlow renders │
│ updated canvas │
└─────────────────────────┘
```
### 6.2 Spec → Optimization → Results
```
atomizer_spec.json
┌─────────────────────────┐
│ Runner.load_config() │
│ Parse spec structure │
└───────────┬─────────────┘
┌─────────────────────────┐
│ Optuna.create_study() │
│ Initialize sampler │
└───────────┬─────────────┘
┌────────┴────────┐
│ Trial Loop │
│ (1 to N) │
└────────┬────────┘
┌─────────────────────────┐
│ NX solver.solve() │
│ Update expressions │
│ Run Nastran │
└───────────┬─────────────┘
┌─────────────────────────┐
│ extractors.extract() │
│ Read OP2 results │
└───────────┬─────────────┘
┌─────────────────────────┐
│ TrialManager.save() │
│ - params.json │
│ - results.json │
│ - study.db INSERT │
└─────────────────────────┘
```
---
## 7. Error Handling Chain
```
Frontend Error
├──► Validation Error (UI shows inline)
├──► Network Error (retry with exponential backoff)
└──► Conflict Error (409) → reload spec → show diff
Backend Error
├──► Pydantic ValidationError → 422 with field details
├──► SpecNotFoundError → 404
├──► SpecConflictError → 409 with current hash
└──► Internal Error → 500 with traceback (dev mode)
Engine Error
├──► NX Connection Error → retry with backoff
├──► Solver Error → mark trial FAILED, continue
├──► Extractor Error → log warning, use fallback
└──► Database Error → rollback transaction
```
---
## 8. Security Boundaries
```
┌─────────────────────────────────────────────────────────────────┐
│ User Browser │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ React Application │ │
│ │ - No direct file access │ │
│ │ - All data via REST/WebSocket │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
(HTTP/WS)
┌─────────────────────────────────────────────────────────────────┐
│ FastAPI Backend │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Input Validation │ │
│ │ - Pydantic schema validation │ │
│ │ - Path sanitization │ │
│ │ - Rate limiting (optional) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ File System Access │ │
│ │ - Restricted to studies/ directory │ │
│ │ - No path traversal (../) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Optimization Engine │
│ │
│ - Runs as separate process │
│ - Limited to study directory │
│ - NX subprocess sandboxed │
└─────────────────────────────────────────────────────────────────┘
```
---
## 9. Deployment Topology
```
┌─────────────────────────────────────────────────────────────────┐
│ Development Environment │
│ │
│ Frontend: npm run dev → localhost:5173 │
│ Backend: uvicorn --reload → localhost:8001 │
│ NX: Local NX installation │
│ Claude: Anthropic API (cloud) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Production Environment │
│ │
│ Frontend: Static files served by Nginx │
│ Backend: Gunicorn + Uvicorn workers │
│ NX: Licensed NX server │
│ Database: SQLite (can upgrade to PostgreSQL) │
│ Claude: Anthropic API (cloud) │
└─────────────────────────────────────────────────────────────────┘
```
---
## 10. Version Compatibility Matrix
| Component | Required Version | Notes |
|-----------|-----------------|-------|
| Python | 3.10+ | Type hints, async |
| Node.js | 18+ | ES modules |
| React | 18+ | Concurrent features |
| FastAPI | 0.100+ | Pydantic v2 |
| Pydantic | 2.0+ | New validation API |
| ReactFlow | 11+ | Custom nodes |
| Optuna | 3.0+ | Trial management |
| NX | 12+ (tested 2306) | NX Open API |
| Claude | claude-3-5-sonnet | Tool use |
---
## 11. Testing Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Test Pyramid │
└─────────────────────────────────────────────────────────────────┘
┌─────────────┐
│ E2E │ ← Playwright/Cypress
│ (few) │ Full user flows
└──────┬──────┘
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Integration │ │ Integration │
│ (frontend) │ │ (backend) │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Unit │ │ Unit │
│ (React) │ │ (Python) │
│ Jest/Vitest│ │ pytest │
└─────────────┘ └─────────────┘
```
### Test Coverage Goals
| Layer | Target | Current |
|-------|--------|---------|
| Unit (Python) | 80% | ~70% |
| Unit (React) | 70% | ~50% |
| Integration | 60% | ~40% |
| E2E | Key flows | Manual |
---
## Summary
Atomizer's architecture is built on these core principles:
1. **Single Source of Truth**: All configuration in `atomizer_spec.json`
2. **Type Safety**: Pydantic on backend, TypeScript on frontend
3. **Real-time Sync**: WebSocket broadcast for multi-client coordination
4. **Optimistic Updates**: Responsive UI with async persistence
5. **Modular Engine**: Pluggable extractors, algorithms, surrogates
The architecture is robust for its intended use case (engineering optimization) and can scale horizontally by:
- Adding more backend workers
- Sharding studies across directories
- Upgrading to distributed database (PostgreSQL)
- Adding Redis for session state