diff --git a/.claude/skills/generate-report.md b/.claude/skills/generate-report.md index 56b6d5a6..69d2a12f 100644 --- a/.claude/skills/generate-report.md +++ b/.claude/skills/generate-report.md @@ -394,7 +394,7 @@ Would you like me to: When a STUDY_REPORT.md file is generated, it can be viewed directly in the Atomizer Dashboard: -1. **Save report to**: `studies/{study_name}/2_results/STUDY_REPORT.md` +1. **Save report to**: `studies/{study_name}/STUDY_REPORT.md` (study root folder) 2. **View in dashboard**: Click "Study Report" button on the dashboard 3. **Features**: - Full markdown rendering with proper typography diff --git a/CLAUDE.md b/CLAUDE.md index 8e8cd15a..6fac8734 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,6 +51,26 @@ When optimization needs >50 trials: - Suggest fixes - Recover from failures +## Python Environment + +**CRITICAL: Always use the `atomizer` conda environment.** All dependencies are pre-installed. + +```bash +# Activate before ANY Python command +conda activate atomizer + +# Then run scripts +python run_optimization.py --start +python -m optimization_engine.runner ... +``` + +**DO NOT:** +- Install packages with pip/conda (everything is already installed) +- Create new virtual environments +- Use system Python + +**Pre-installed packages include:** optuna, numpy, scipy, pandas, matplotlib, pyNastran, torch, plotly, and all Atomizer dependencies. + ## Key Files & Locations ``` diff --git a/atomizer-dashboard/frontend/src/components/ClaudeTerminal.tsx b/atomizer-dashboard/frontend/src/components/ClaudeTerminal.tsx index 09219a4d..ef0c027d 100644 --- a/atomizer-dashboard/frontend/src/components/ClaudeTerminal.tsx +++ b/atomizer-dashboard/frontend/src/components/ClaudeTerminal.tsx @@ -9,7 +9,8 @@ import { Minimize2, X, RefreshCw, - AlertCircle + AlertCircle, + FolderOpen } from 'lucide-react'; import { useStudy } from '../context/StudyContext'; @@ -33,6 +34,8 @@ export const ClaudeTerminal: React.FC = ({ const [isConnecting, setIsConnecting] = useState(false); const [_error, setError] = useState(null); const [cliAvailable, setCliAvailable] = useState(null); + const [contextSet, setContextSet] = useState(false); + const [settingContext, setSettingContext] = useState(false); // Check CLI availability useEffect(() => { @@ -165,8 +168,7 @@ export const ClaudeTerminal: React.FC = ({ xtermRef.current?.clear(); xtermRef.current?.writeln('\x1b[1;32mConnected to Claude Code\x1b[0m'); if (selectedStudy?.id) { - xtermRef.current?.writeln(`\x1b[90mStudy context: \x1b[1;33m${selectedStudy.id}\x1b[0m`); - xtermRef.current?.writeln('\x1b[90mTip: Tell Claude about your study, e.g. "Help me with study ' + selectedStudy.id + '"\x1b[0m'); + xtermRef.current?.writeln(`\x1b[90mStudy: \x1b[1;33m${selectedStudy.id}\x1b[0m \x1b[90m- Click "Set Context" to initialize\x1b[0m`); } xtermRef.current?.writeln(''); @@ -241,8 +243,26 @@ export const ClaudeTerminal: React.FC = ({ wsRef.current = null; } setIsConnected(false); + setContextSet(false); }, []); + // Set study context - sends context message to Claude silently + const setStudyContext = useCallback(() => { + if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN || !selectedStudy?.id) return; + + setSettingContext(true); + // Send context message - Claude should use CLAUDE.md and .claude/skills/ for guidance + const contextMessage = `Context: Working on study "${selectedStudy.id}" at studies/${selectedStudy.id}/. ` + + `Read .claude/skills/ for task protocols. Use atomizer conda env. Acknowledge briefly.`; + wsRef.current.send(JSON.stringify({ type: 'input', data: contextMessage + '\n' })); + + // Mark as done after Claude has had time to process + setTimeout(() => { + setSettingContext(false); + setContextSet(true); + }, 500); + }, [selectedStudy]); + // Cleanup on unmount useEffect(() => { return () => { @@ -293,6 +313,35 @@ export const ClaudeTerminal: React.FC = ({ {isConnected ? 'Disconnect' : 'Connect'} + {/* Set Context button - always show, with different states */} + + {onToggleExpand && ( + ) : ( + + )} + + + + + + + {/* Settings Toggle */} + + + {/* Error */} + {error && ( +
+ + {error} +
+ )} + + + {/* Collapsible Settings */} + {showSettings && ( +
+
+
+ + setSettings({ ...settings, maxIterations: parseInt(e.target.value) || 100 })} + className="w-16 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" + /> +
+
+ + setSettings({ ...settings, feaBatchSize: parseInt(e.target.value) || 5 })} + className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" + /> +
+
+ + setSettings({ ...settings, patience: parseInt(e.target.value) || 5 })} + className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" + /> +
+
+ + setSettings({ ...settings, tuneTrials: parseInt(e.target.value) || 30 })} + className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" + /> +
+
+ + setSettings({ ...settings, ensembleSize: parseInt(e.target.value) || 3 })} + className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" + /> +
+
+ + setValidateTopN(parseInt(e.target.value) || 5)} + className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" + /> +
+ +
+
+ )} + + ); + } + + // Vertical layout (original sidebar layout) return (
{/* Header */} diff --git a/atomizer-dashboard/frontend/src/pages/Dashboard.tsx b/atomizer-dashboard/frontend/src/pages/Dashboard.tsx index d386e457..4090b5e3 100644 --- a/atomizer-dashboard/frontend/src/pages/Dashboard.tsx +++ b/atomizer-dashboard/frontend/src/pages/Dashboard.tsx @@ -1,15 +1,10 @@ import { useState, useEffect, lazy, Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; -import { - LineChart, Line, ScatterChart, Scatter, - XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, Cell -} from 'recharts'; import { Terminal } from 'lucide-react'; import { useOptimizationWebSocket } from '../hooks/useWebSocket'; import { apiClient } from '../api/client'; import { useStudy } from '../context/StudyContext'; import { Card } from '../components/common/Card'; -import { MetricCard } from '../components/dashboard/MetricCard'; import { ControlPanel } from '../components/dashboard/ControlPanel'; import { ClaudeTerminal } from '../components/ClaudeTerminal'; import { ParetoPlot } from '../components/ParetoPlot'; @@ -19,7 +14,7 @@ import { ConvergencePlot } from '../components/ConvergencePlot'; import { StudyReportViewer } from '../components/StudyReportViewer'; import { ConsoleOutput } from '../components/ConsoleOutput'; import { ExpandableChart } from '../components/ExpandableChart'; -import type { Trial, ConvergenceDataPoint, ParameterSpaceDataPoint } from '../types'; +import type { Trial } from '../types'; // Lazy load Plotly components for better initial load performance const PlotlyParallelCoordinates = lazy(() => import('../components/plotly/PlotlyParallelCoordinates').then(m => ({ default: m.PlotlyParallelCoordinates }))); @@ -417,68 +412,62 @@ export default function Dashboard() {
-
- {/* Control Panel - Left Sidebar (smaller) */} - + {/* Control Panel - Full Width on Top */} +
+ +
- {/* Main Content - takes most of the space */} -
- {/* Study Name Header */} - {selectedStudyId && ( -
-

- {selectedStudyId} -

- {studyMetadata?.description && ( -

{studyMetadata.description}

+ {/* Main Layout: Charts + Claude Terminal */} +
+ {/* Main Content - Charts stacked vertically */} +
+ {/* Study Name Header + Metrics in one row */} +
+
+ {selectedStudyId && ( + <> +

+ {selectedStudyId} +

+ {studyMetadata?.description && ( +

{studyMetadata.description}

+ )} + )}
- )} - - {/* Metrics Grid */} -
- - - 0 ? avgObjective.toFixed(4) : '-'} - valueColor="text-blue-400" - /> - 0 ? 'text-red-400' : 'text-green-400'} - /> + {/* Compact Metrics */} +
+
+
{allTrials.length}
+
Trials
+
+
+
+ {bestValue === Infinity ? '-' : bestValue.toFixed(4)} +
+
Best
+
+
+
+ {avgObjective > 0 ? avgObjective.toFixed(4) : '-'} +
+
Avg
+
+
+
0 ? 'text-red-400' : 'text-green-400'}`}> + {prunedCount} +
+
Pruned
+
+
- {/* Protocol 13: Intelligent Optimizer & Pareto Front */} + {/* Pareto Front - Full Width */} {selectedStudyId && paretoFront.length > 0 && studyMetadata && studyMetadata.objectives && ( -
- -
-
- Algorithm: {studyMetadata.sampler || 'NSGA-II'} -
-
- Type: Multi-objective -
-
- Objectives: {studyMetadata.objectives?.length || 2} -
-
- Design Variables: {studyMetadata.design_variables?.length || 0} -
-
-
+
{chartLibrary === 'plotly' ? ( }> @@ -486,7 +475,7 @@ export default function Dashboard() { trials={allTrialsRaw} paretoFront={paretoFront} objectives={studyMetadata.objectives} - height={350} + height={300} /> ) : ( @@ -500,11 +489,11 @@ export default function Dashboard() {
)} - {/* Parallel Coordinates (full width for multi-objective) */} + {/* Parallel Coordinates - Full Width */} {allTrialsRaw.length > 0 && studyMetadata && studyMetadata.objectives && studyMetadata.design_variables && ( -
+
{chartLibrary === 'plotly' ? ( @@ -514,7 +503,7 @@ export default function Dashboard() { objectives={studyMetadata.objectives} designVariables={studyMetadata.design_variables} paretoFront={paretoFront} - height={450} + height={350} /> ) : ( @@ -531,9 +520,9 @@ export default function Dashboard() { {/* Convergence Plot - Full Width */} {allTrialsRaw.length > 0 && ( -
+
{chartLibrary === 'plotly' ? ( @@ -543,7 +532,7 @@ export default function Dashboard() { objectiveIndex={0} objectiveName={studyMetadata?.objectives?.[0]?.name || 'Objective'} direction="minimize" - height={350} + height={280} /> ) : ( @@ -560,7 +549,7 @@ export default function Dashboard() { {/* Parameter Importance - Full Width */} {allTrialsRaw.length > 0 && (studyMetadata?.design_variables?.length > 0 || (allTrialsRaw[0]?.params && Object.keys(allTrialsRaw[0].params).length > 0)) && ( -
+
) : ( @@ -595,138 +584,6 @@ export default function Dashboard() {
)} - {/* Charts */} -
- {/* Convergence Chart */} - - - {convergenceData.length > 0 ? ( - - - - - - - - - - - - ) : ( -
- No trial data yet -
- )} -
-
- - {/* Parameter Space Chart with Selectable Axes */} - - - Parameter Space - {paramNames.length > 2 && ( -
- X: - - Y: - -
- )} -
- }> - {parameterSpaceData.length > 0 ? ( - - - - - - { - if (name === 'objective') return [value.toFixed(4), 'Objective']; - return [value.toFixed(3), name]; - }} - /> - - {parameterSpaceData.map((entry, index) => ( - - ))} - - - - ) : ( -
- No trial data yet -
- )} - - -
- {/* Trial History with Sort Controls and Pagination */} {/* Console Output - at the bottom */} -
+
- {/* Claude Code Terminal - Right Sidebar (taller for better visibility) */} + {/* Claude Code Terminal - Right Sidebar (wider for better visibility) */} {chatOpen && ( -