Drag-drop fixes: - Fix Objective default data: use nested 'source' object with extractor_id/output_name - Fix Constraint default data: use 'type' field (not constraint_type), 'threshold' (not limit) Undo/Redo fixes: - Remove dependency on isDirty flag (which is always false due to auto-save) - Record snapshots based on actual spec changes via deep comparison Code generation improvements: - Update system prompt to support multiple extractor types: * OP2-based extractors for FEA results (stress, displacement, frequency) * Expression-based extractors for NX model values (dimensions, volumes) * Computed extractors for derived values (no FEA needed) - Claude will now choose appropriate signature based on user's description
731 lines
26 KiB
Markdown
731 lines
26 KiB
Markdown
# 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.
|