- 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
26 KiB
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.
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 backendpatchSpec(path, value)- Update with conflict checkpatchSpecOptimistic(path, value)- Fire-and-forget updateaddNode(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.
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:
- Load current spec
- Apply modification
- Validate with Pydantic
- Atomic write to disk
- Compute new hash
- Broadcast to all WebSocket subscribers
- 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:
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):
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
# 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:
# ❌ 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
// 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
- No Undo/Redo: Canvas modifications are immediate
- Single File Lock: No distributed locking for multi-user
- Memory-only Sessions: Session state lost on restart
- 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.