// Study Configuration Management Functions // Global state for configuration let currentConfigStudy = null; let extractedExpressions = null; let studyConfiguration = { design_variables: [], objectives: [], constraints: [], optimization_settings: {} }; // ==================== // Create Study Modal // ==================== function showCreateStudyModal() { document.getElementById('createStudyModal').style.display = 'flex'; } function closeCreateStudyModal() { document.getElementById('createStudyModal').style.display = 'none'; } async function createNewStudy() { const studyName = document.getElementById('createStudyName').value; const description = document.getElementById('createStudyDescription').value; if (!studyName) { showError('Please enter a study name'); return; } try { const response = await fetch(`${API_BASE}/study/create`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ study_name: studyName, description: description }) }); const data = await response.json(); if (data.success) { showSuccess(`Study "${studyName}" created successfully!`); closeCreateStudyModal(); // Open the study for configuration loadStudyConfig(studyName); refreshStudies(); } else { showError('Failed to create study: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } // ==================== // Study Configuration // ==================== async function loadStudyConfig(studyName) { currentConfigStudy = studyName; // Hide other views, show config view document.getElementById('welcomeScreen').style.display = 'none'; document.getElementById('studyDetails').style.display = 'none'; document.getElementById('studyConfig').style.display = 'block'; // Update header document.getElementById('configStudyTitle').textContent = `Configure: ${studyName}`; document.getElementById('configStudyMeta').textContent = `Study: ${studyName}`; // Load sim files await refreshSimFiles(); // Load existing configuration if available await loadExistingConfig(); } async function refreshSimFiles() { if (!currentConfigStudy) return; try { const response = await fetch(`${API_BASE}/study/${currentConfigStudy}/sim/files`); const data = await response.json(); if (data.success) { // Update sim folder path document.getElementById('simFolderPath').textContent = data.sim_folder; // Render files list const filesList = document.getElementById('simFilesList'); if (data.files.length === 0) { filesList.innerHTML = '

No files yet. Drop your .sim and .prt files in the folder.

'; } else { const html = data.files.map(file => `
${file.name}
${(file.size / 1024).toFixed(1)} KB
`).join(''); filesList.innerHTML = html; } // Enable/disable explore button document.getElementById('exploreBtn').disabled = !data.has_sim_file; } } catch (error) { showError('Failed to load sim files: ' + error.message); } } async function exploreSimFile() { if (!currentConfigStudy) return; try { showSuccess('Exploring .sim file with NX... This may take a minute.'); const response = await fetch(`${API_BASE}/study/${currentConfigStudy}/explore`, { method: 'POST' }); const data = await response.json(); if (data.success) { extractedExpressions = data.expressions; displayExpressions(data.expressions); showSuccess('Expression extraction complete!'); } else { showError('Failed to explore .sim file: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } function displayExpressions(expressionsData) { const container = document.getElementById('expressionsList'); container.style.display = 'block'; const expressions = expressionsData.expressions_by_part; const metadata = expressionsData.metadata; let html = `

Expressions Found: ${metadata.total_expressions} (${metadata.variable_candidates} potential design variables)

`; // Display expressions by part for (const [partName, exprs] of Object.entries(expressions)) { if (exprs.length === 0) continue; html += `
${partName}
`; exprs.forEach(expr => { const isCandidate = expr.is_variable_candidate ? '✓' : ''; html += `
${isCandidate} ${expr.name}
Value: ${expr.value} ${expr.units} | Formula: ${expr.formula}
`; }); } container.innerHTML = html; } function selectExpressionForVariable(partName, exprName) { // Find the expression const expressions = extractedExpressions.expressions_by_part[partName]; const expr = expressions.find(e => e.name === exprName); if (!expr) return; // Add to design variables addDesignVariableFromExpression(expr); } function addDesignVariableFromExpression(expr) { const variable = { name: expr.name, min: expr.value * 0.8, // 20% below current max: expr.value * 1.2, // 20% above current units: expr.units }; studyConfiguration.design_variables.push(variable); renderDesignVariablesConfig(); } function addDesignVariable() { const variable = { name: `variable_${studyConfiguration.design_variables.length + 1}`, min: 0, max: 100, units: 'mm' }; studyConfiguration.design_variables.push(variable); renderDesignVariablesConfig(); } function renderDesignVariablesConfig() { const container = document.getElementById('designVariablesConfig'); if (studyConfiguration.design_variables.length === 0) { container.innerHTML = '

No design variables yet

'; return; } const html = studyConfiguration.design_variables.map((variable, index) => `
${variable.name}
`).join(''); container.innerHTML = html; } function updateDesignVariable(index, field, value) { studyConfiguration.design_variables[index][field] = value; } function removeDesignVariable(index) { studyConfiguration.design_variables.splice(index, 1); renderDesignVariablesConfig(); } // ==================== // Objectives Configuration // ==================== function addObjective() { const objective = { name: `objective_${studyConfiguration.objectives.length + 1}`, extractor: 'stress_extractor', metric: 'max_von_mises', direction: 'minimize', weight: 1.0, units: 'MPa' }; studyConfiguration.objectives.push(objective); renderObjectivesConfig(); } function renderObjectivesConfig() { const container = document.getElementById('objectivesConfig'); if (studyConfiguration.objectives.length === 0) { container.innerHTML = '

No objectives yet

'; return; } const html = studyConfiguration.objectives.map((objective, index) => `
${objective.name}
`).join(''); container.innerHTML = html; } function updateObjective(index, field, value) { studyConfiguration.objectives[index][field] = value; } function removeObjective(index) { studyConfiguration.objectives.splice(index, 1); renderObjectivesConfig(); } // ==================== // Constraints Configuration // ==================== function addConstraint() { const constraint = { name: `constraint_${studyConfiguration.constraints.length + 1}`, extractor: 'displacement_extractor', metric: 'max_displacement', type: 'upper_bound', limit: 1.0, units: 'mm' }; studyConfiguration.constraints.push(constraint); renderConstraintsConfig(); } function renderConstraintsConfig() { const container = document.getElementById('constraintsConfig'); if (studyConfiguration.constraints.length === 0) { container.innerHTML = '

No constraints yet

'; return; } const html = studyConfiguration.constraints.map((constraint, index) => `
${constraint.name}
`).join(''); container.innerHTML = html; } function updateConstraint(index, field, value) { studyConfiguration.constraints[index][field] = value; } function removeConstraint(index) { studyConfiguration.constraints.splice(index, 1); renderConstraintsConfig(); } // ==================== // Save Configuration // ==================== async function saveStudyConfiguration() { if (!currentConfigStudy) return; // Gather optimization settings studyConfiguration.optimization_settings = { n_trials: parseInt(document.getElementById('nTrials').value) || 50, sampler: document.getElementById('samplerType').value, n_startup_trials: parseInt(document.getElementById('startupTrials').value) || 20 }; try { const response = await fetch(`${API_BASE}/study/${currentConfigStudy}/config`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(studyConfiguration) }); const data = await response.json(); if (data.success) { showSuccess('Configuration saved successfully!'); } else { showError('Failed to save configuration: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } async function loadExistingConfig() { if (!currentConfigStudy) return; try { const response = await fetch(`${API_BASE}/study/${currentConfigStudy}/config`); const data = await response.json(); if (data.success && data.config) { studyConfiguration = data.config; // Render loaded configuration renderDesignVariablesConfig(); renderObjectivesConfig(); renderConstraintsConfig(); // Set optimization settings if (data.config.optimization_settings) { document.getElementById('nTrials').value = data.config.optimization_settings.n_trials || 50; document.getElementById('samplerType').value = data.config.optimization_settings.sampler || 'TPE'; document.getElementById('startupTrials').value = data.config.optimization_settings.n_startup_trials || 20; } } } catch (error) { console.error('Failed to load existing config:', error); } } // ==================== // Utility Functions // ==================== function openSimFolder() { if (!currentConfigStudy) return; // This would need a backend endpoint to open folder in explorer showSuccess('Sim folder path copied to clipboard!'); } function backToStudyList() { document.getElementById('studyConfig').style.display = 'none'; document.getElementById('welcomeScreen').style.display = 'block'; currentConfigStudy = null; extractedExpressions = null; }