feat: Add comprehensive study creation Claude skill
- New create-study.md skill for complete study scaffolding - Interactive discovery process for problem understanding - Automated generation of all study infrastructure: - optimization_config.json with protocol selection - workflow_config.json for future intelligent workflows - run_optimization.py with proper multi-objective/multi-solution support - reset_study.py for database management - README.md with comprehensive documentation - NX_FILE_MODIFICATIONS_REQUIRED.md when needed - Protocol selection guidance (Protocol 10 vs 11) - Extractor mapping to centralized library - Multi-solution workflow detection - Dashboard integration instructions - User interaction best practices with confirmation steps - Common patterns and critical reminders - Reference to existing studies as templates Enables users to create complete, working optimization studies from natural language descriptions with proper Claude-guided workflow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
514
.claude/skills/create-study.md
Normal file
514
.claude/skills/create-study.md
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
# Create Optimization Study Skill
|
||||||
|
|
||||||
|
**Last Updated**: November 24, 2025
|
||||||
|
**Version**: 1.0 - Complete Study Scaffolding
|
||||||
|
|
||||||
|
You are helping the user create a complete Atomizer optimization study from a natural language description.
|
||||||
|
|
||||||
|
## Your Role
|
||||||
|
|
||||||
|
Guide the user through an interactive conversation to:
|
||||||
|
1. Understand their optimization problem
|
||||||
|
2. Classify objectives, constraints, and design variables
|
||||||
|
3. Create the complete study infrastructure
|
||||||
|
4. Generate all required files with proper configuration
|
||||||
|
5. Provide clear next steps for running the optimization
|
||||||
|
|
||||||
|
## Study Structure
|
||||||
|
|
||||||
|
A complete Atomizer study has this structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
studies/{study_name}/
|
||||||
|
├── 1_setup/
|
||||||
|
│ ├── model/
|
||||||
|
│ │ ├── {Model}.prt # NX Part file (user provides)
|
||||||
|
│ │ ├── {Model}_sim1.sim # NX Simulation file (user provides)
|
||||||
|
│ │ └── {Model}_fem1.fem # FEM mesh file (auto-generated by NX)
|
||||||
|
│ ├── optimization_config.json # YOU GENERATE THIS
|
||||||
|
│ └── workflow_config.json # YOU GENERATE THIS
|
||||||
|
├── 2_results/ # Created automatically during optimization
|
||||||
|
│ ├── study.db # Optuna SQLite database
|
||||||
|
│ ├── optimization_history_incremental.json
|
||||||
|
│ └── [various analysis files]
|
||||||
|
├── run_optimization.py # YOU GENERATE THIS
|
||||||
|
├── reset_study.py # YOU GENERATE THIS
|
||||||
|
├── README.md # YOU GENERATE THIS
|
||||||
|
└── NX_FILE_MODIFICATIONS_REQUIRED.md # YOU GENERATE THIS (if needed)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interactive Discovery Process
|
||||||
|
|
||||||
|
### Step 1: Problem Understanding
|
||||||
|
|
||||||
|
Ask clarifying questions to understand:
|
||||||
|
|
||||||
|
**Engineering Context**:
|
||||||
|
- "What component are you optimizing?"
|
||||||
|
- "What is the engineering application or scenario?"
|
||||||
|
- "What are the real-world requirements or constraints?"
|
||||||
|
|
||||||
|
**Objectives**:
|
||||||
|
- "What do you want to optimize?" (minimize/maximize)
|
||||||
|
- "Is this single-objective or multi-objective?"
|
||||||
|
- "What are the target values or acceptable ranges?"
|
||||||
|
|
||||||
|
**Constraints**:
|
||||||
|
- "What limits must be satisfied?"
|
||||||
|
- "What are the threshold values?"
|
||||||
|
- "Are these hard constraints (must satisfy) or soft constraints (prefer to satisfy)?"
|
||||||
|
|
||||||
|
**Design Variables**:
|
||||||
|
- "What parameters can be changed?"
|
||||||
|
- "What are the min/max bounds for each parameter?"
|
||||||
|
- "Are these NX expressions, geometry features, or material properties?"
|
||||||
|
|
||||||
|
**Simulation Setup**:
|
||||||
|
- "What NX model files do you have?"
|
||||||
|
- "What analysis types are needed?" (static, modal, thermal, etc.)
|
||||||
|
- "What results need to be extracted?" (stress, displacement, frequency, mass, etc.)
|
||||||
|
|
||||||
|
### Step 2: Classification & Analysis
|
||||||
|
|
||||||
|
Use the `analyze-workflow` skill to classify the problem:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Invoke the analyze-workflow skill with user's description
|
||||||
|
# This returns JSON with classified engineering features, extractors, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
Review the classification with the user and confirm:
|
||||||
|
- Are the objectives correctly identified?
|
||||||
|
- Are constraints properly classified?
|
||||||
|
- Are extractors mapped to the right result types?
|
||||||
|
- Is the protocol selection appropriate?
|
||||||
|
|
||||||
|
### Step 3: Protocol Selection
|
||||||
|
|
||||||
|
Based on analysis, recommend protocol:
|
||||||
|
|
||||||
|
**Protocol 11 (Multi-Objective NSGA-II)**:
|
||||||
|
- Use when: 2-3 conflicting objectives
|
||||||
|
- Algorithm: NSGAIISampler
|
||||||
|
- Output: Pareto front of optimal trade-offs
|
||||||
|
- Example: Minimize mass + Maximize frequency
|
||||||
|
|
||||||
|
**Protocol 10 (Single-Objective with Intelligent Strategies)**:
|
||||||
|
- Use when: 1 objective with constraints
|
||||||
|
- Algorithm: TPE, CMA-ES, or adaptive
|
||||||
|
- Output: Single optimal solution
|
||||||
|
- Example: Minimize stress subject to displacement < 1.5mm
|
||||||
|
|
||||||
|
**Legacy (Basic TPE)**:
|
||||||
|
- Use when: Simple single-objective problem
|
||||||
|
- Algorithm: TPE
|
||||||
|
- Output: Single optimal solution
|
||||||
|
- Example: Quick exploration or testing
|
||||||
|
|
||||||
|
### Step 4: Extractor Mapping
|
||||||
|
|
||||||
|
Map each result extraction to centralized extractors:
|
||||||
|
|
||||||
|
| User Need | Extractor | Parameters |
|
||||||
|
|-----------|-----------|------------|
|
||||||
|
| Displacement | `extract_displacement` | `op2_file`, `subcase` |
|
||||||
|
| Von Mises Stress | `extract_solid_stress` | `op2_file`, `subcase`, `element_type` |
|
||||||
|
| Natural Frequency | `extract_frequency` | `op2_file`, `subcase`, `mode_number` |
|
||||||
|
| FEM Mass | `extract_mass_from_bdf` | `bdf_file` |
|
||||||
|
| CAD Mass | `extract_mass_from_expression` | `prt_file`, `expression_name` |
|
||||||
|
|
||||||
|
### Step 5: Multi-Solution Detection
|
||||||
|
|
||||||
|
Check if multi-solution workflow is needed:
|
||||||
|
|
||||||
|
**Indicators**:
|
||||||
|
- Extracting both static results (stress, displacement) AND modal results (frequency)
|
||||||
|
- User mentions "static + modal analysis"
|
||||||
|
- Objectives/constraints require different solution types
|
||||||
|
|
||||||
|
**Action**:
|
||||||
|
- Set `solution_name=None` in `run_optimization.py` to solve all solutions
|
||||||
|
- Document requirement in `NX_FILE_MODIFICATIONS_REQUIRED.md`
|
||||||
|
- Use `SolveAllSolutions()` protocol (see [NX_MULTI_SOLUTION_PROTOCOL.md](../docs/NX_MULTI_SOLUTION_PROTOCOL.md))
|
||||||
|
|
||||||
|
## File Generation
|
||||||
|
|
||||||
|
### 1. optimization_config.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"study_name": "{study_name}",
|
||||||
|
"description": "{concise description}",
|
||||||
|
"engineering_context": "{detailed real-world context}",
|
||||||
|
|
||||||
|
"optimization_settings": {
|
||||||
|
"protocol": "protocol_11_multi_objective", // or protocol_10, etc.
|
||||||
|
"n_trials": 30,
|
||||||
|
"sampler": "NSGAIISampler", // or "TPESampler"
|
||||||
|
"pruner": null,
|
||||||
|
"timeout_per_trial": 600
|
||||||
|
},
|
||||||
|
|
||||||
|
"design_variables": [
|
||||||
|
{
|
||||||
|
"parameter": "{nx_expression_name}",
|
||||||
|
"bounds": [min, max],
|
||||||
|
"description": "{what this controls}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"objectives": [
|
||||||
|
{
|
||||||
|
"name": "{objective_name}",
|
||||||
|
"goal": "minimize", // or "maximize"
|
||||||
|
"weight": 1.0,
|
||||||
|
"description": "{what this measures}",
|
||||||
|
"target": {target_value},
|
||||||
|
"extraction": {
|
||||||
|
"action": "extract_{type}",
|
||||||
|
"domain": "result_extraction",
|
||||||
|
"params": {
|
||||||
|
"result_type": "{type}",
|
||||||
|
"metric": "{specific_metric}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"constraints": [
|
||||||
|
{
|
||||||
|
"name": "{constraint_name}",
|
||||||
|
"type": "less_than", // or "greater_than"
|
||||||
|
"threshold": {value},
|
||||||
|
"description": "{engineering justification}",
|
||||||
|
"extraction": {
|
||||||
|
"action": "extract_{type}",
|
||||||
|
"domain": "result_extraction",
|
||||||
|
"params": {
|
||||||
|
"result_type": "{type}",
|
||||||
|
"metric": "{specific_metric}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"simulation": {
|
||||||
|
"model_file": "{Model}.prt",
|
||||||
|
"sim_file": "{Model}_sim1.sim",
|
||||||
|
"fem_file": "{Model}_fem1.fem",
|
||||||
|
"solver": "nastran",
|
||||||
|
"analysis_types": ["static", "modal"] // or just ["static"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"reporting": {
|
||||||
|
"generate_plots": true,
|
||||||
|
"save_incremental": true,
|
||||||
|
"llm_summary": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. workflow_config.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"workflow_id": "{study_name}_workflow",
|
||||||
|
"description": "{workflow description}",
|
||||||
|
"steps": [] // Can be empty for now, used by future intelligent workflow system
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. run_optimization.py
|
||||||
|
|
||||||
|
Generate a complete Python script based on protocol:
|
||||||
|
|
||||||
|
**Key sections**:
|
||||||
|
- Import statements (centralized extractors, NXSolver, Optuna)
|
||||||
|
- Configuration loading
|
||||||
|
- Objective function with proper:
|
||||||
|
- Design variable sampling
|
||||||
|
- Simulation execution with multi-solution support
|
||||||
|
- Result extraction using centralized extractors
|
||||||
|
- Constraint checking
|
||||||
|
- Return format (tuple for multi-objective, float for single-objective)
|
||||||
|
- Study creation with proper:
|
||||||
|
- Directions for multi-objective (`['minimize', 'maximize']`)
|
||||||
|
- Sampler selection (NSGAIISampler or TPESampler)
|
||||||
|
- Storage location
|
||||||
|
- Results display and dashboard instructions
|
||||||
|
|
||||||
|
**Template**: Use [studies/drone_gimbal_arm_optimization/run_optimization.py](../studies/drone_gimbal_arm_optimization/run_optimization.py:1) as reference
|
||||||
|
|
||||||
|
### 4. reset_study.py
|
||||||
|
|
||||||
|
Simple script to delete Optuna database:
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""Reset {study_name} optimization study by deleting database."""
|
||||||
|
import optuna
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
study_dir = Path(__file__).parent
|
||||||
|
storage = f"sqlite:///{study_dir / '2_results' / 'study.db'}"
|
||||||
|
study_name = "{study_name}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
optuna.delete_study(study_name=study_name, storage=storage)
|
||||||
|
print(f"[OK] Deleted study: {study_name}")
|
||||||
|
except KeyError:
|
||||||
|
print(f"[WARNING] Study '{study_name}' not found (database may not exist)")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Error: {e}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. README.md
|
||||||
|
|
||||||
|
Comprehensive documentation including:
|
||||||
|
- Engineering scenario and context
|
||||||
|
- Problem statement with real-world constraints
|
||||||
|
- Multi-objective trade-offs (if applicable)
|
||||||
|
- Design variables and their effects
|
||||||
|
- Expected outcomes
|
||||||
|
- Study configuration details
|
||||||
|
- File structure explanation
|
||||||
|
- Running instructions
|
||||||
|
- Dashboard monitoring guide
|
||||||
|
- Results interpretation guide
|
||||||
|
- Comparison with other studies
|
||||||
|
- Technical notes
|
||||||
|
|
||||||
|
**Template**: Use [studies/drone_gimbal_arm_optimization/README.md](../studies/drone_gimbal_arm_optimization/README.md:1) as reference
|
||||||
|
|
||||||
|
### 6. NX_FILE_MODIFICATIONS_REQUIRED.md (if needed)
|
||||||
|
|
||||||
|
If multi-solution workflow or specific NX setup is required:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# NX File Modifications Required
|
||||||
|
|
||||||
|
Before running this optimization, you must modify the NX simulation files.
|
||||||
|
|
||||||
|
## Required Changes
|
||||||
|
|
||||||
|
### 1. Add Modal Analysis Solution (if needed)
|
||||||
|
|
||||||
|
Current: Only static analysis (SOL 101)
|
||||||
|
Required: Static + Modal (SOL 101 + SOL 103)
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Open `{Model}_sim1.sim` in NX
|
||||||
|
2. Solution → Create → Modal Analysis
|
||||||
|
3. Set frequency extraction parameters
|
||||||
|
4. Save simulation
|
||||||
|
|
||||||
|
### 2. Update Load Cases (if needed)
|
||||||
|
|
||||||
|
Current: [describe current loads]
|
||||||
|
Required: [describe required loads]
|
||||||
|
|
||||||
|
Steps: [specific instructions]
|
||||||
|
|
||||||
|
### 3. Verify Material Properties
|
||||||
|
|
||||||
|
Required: [material name and properties]
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After modifications:
|
||||||
|
1. Run simulation manually in NX
|
||||||
|
2. Verify OP2 files are generated
|
||||||
|
3. Check solution_1.op2 and solution_2.op2 exist (if multi-solution)
|
||||||
|
```
|
||||||
|
|
||||||
|
## User Interaction Best Practices
|
||||||
|
|
||||||
|
### Ask Before Generating
|
||||||
|
|
||||||
|
Always confirm with user:
|
||||||
|
1. "Here's what I understand about your optimization problem: [summary]. Is this correct?"
|
||||||
|
2. "I'll use Protocol {X} because [reasoning]. Does this sound right?"
|
||||||
|
3. "I'll create extractors for: [list]. Are these the results you need?"
|
||||||
|
4. "Should I generate the complete study structure now?"
|
||||||
|
|
||||||
|
### Provide Clear Next Steps
|
||||||
|
|
||||||
|
After generating files:
|
||||||
|
```
|
||||||
|
✓ Created study: studies/{study_name}/
|
||||||
|
✓ Generated optimization config
|
||||||
|
✓ Generated run_optimization.py with {protocol}
|
||||||
|
✓ Generated README.md with full documentation
|
||||||
|
|
||||||
|
Next Steps:
|
||||||
|
1. Place your NX files in studies/{study_name}/1_setup/model/
|
||||||
|
- {Model}.prt
|
||||||
|
- {Model}_sim1.sim
|
||||||
|
2. [If NX modifications needed] Read NX_FILE_MODIFICATIONS_REQUIRED.md
|
||||||
|
3. Test with 3 trials: cd studies/{study_name} && python run_optimization.py --trials 3
|
||||||
|
4. Monitor in dashboard: http://localhost:3003
|
||||||
|
5. Full run: python run_optimization.py --trials {n_trials}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handle Edge Cases
|
||||||
|
|
||||||
|
**User has incomplete information**:
|
||||||
|
- Suggest reasonable defaults based on similar studies
|
||||||
|
- Document assumptions clearly in README
|
||||||
|
- Mark as "REQUIRES USER INPUT" in generated files
|
||||||
|
|
||||||
|
**User wants custom extractors**:
|
||||||
|
- Explain centralized extractor library
|
||||||
|
- If truly custom, guide them to create in `optimization_engine/extractors/`
|
||||||
|
- Inherit from `OP2Extractor` base class
|
||||||
|
|
||||||
|
**User unsure about bounds**:
|
||||||
|
- Recommend conservative bounds based on engineering judgment
|
||||||
|
- Suggest iterative approach: "Start with [bounds], then refine based on initial results"
|
||||||
|
|
||||||
|
**User doesn't have NX files yet**:
|
||||||
|
- Generate all Python/JSON files anyway
|
||||||
|
- Create placeholder model directory
|
||||||
|
- Provide clear instructions for adding NX files later
|
||||||
|
|
||||||
|
## Integration with Dashboard
|
||||||
|
|
||||||
|
Always mention dashboard capabilities:
|
||||||
|
|
||||||
|
**For Multi-Objective Studies**:
|
||||||
|
- "You'll see the Pareto front in real-time on the dashboard"
|
||||||
|
- "Use parallel coordinates plot to explore trade-offs"
|
||||||
|
- "Green lines = feasible, red lines = constraint violations"
|
||||||
|
|
||||||
|
**For Single-Objective Studies**:
|
||||||
|
- "Monitor convergence in real-time"
|
||||||
|
- "See best value improving over trials"
|
||||||
|
- "Check parameter space exploration"
|
||||||
|
|
||||||
|
**Dashboard Access**:
|
||||||
|
```bash
|
||||||
|
# Terminal 1: Backend
|
||||||
|
cd atomizer-dashboard/backend && python -m uvicorn api.main:app --reload
|
||||||
|
|
||||||
|
# Terminal 2: Frontend
|
||||||
|
cd atomizer-dashboard/frontend && npm run dev
|
||||||
|
|
||||||
|
# Browser: http://localhost:3003
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Mass Minimization with Constraints
|
||||||
|
|
||||||
|
```
|
||||||
|
Objective: Minimize mass
|
||||||
|
Constraints: Stress < limit, Displacement < limit, Frequency > limit
|
||||||
|
Protocol: Protocol 10 (single-objective TPE)
|
||||||
|
Extractors: extract_mass_from_expression, extract_solid_stress,
|
||||||
|
extract_displacement, extract_frequency
|
||||||
|
Multi-Solution: Yes (static + modal)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: Mass vs Frequency Trade-off
|
||||||
|
|
||||||
|
```
|
||||||
|
Objectives: Minimize mass, Maximize frequency
|
||||||
|
Constraints: Stress < limit, Displacement < limit
|
||||||
|
Protocol: Protocol 11 (multi-objective NSGA-II)
|
||||||
|
Extractors: extract_mass_from_expression, extract_frequency,
|
||||||
|
extract_solid_stress, extract_displacement
|
||||||
|
Multi-Solution: Yes (static + modal)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 3: Stress Minimization
|
||||||
|
|
||||||
|
```
|
||||||
|
Objective: Minimize stress
|
||||||
|
Constraints: Displacement < limit
|
||||||
|
Protocol: Protocol 10 (single-objective TPE)
|
||||||
|
Extractors: extract_solid_stress, extract_displacement
|
||||||
|
Multi-Solution: No (static only)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Critical Reminders
|
||||||
|
|
||||||
|
### Multi-Objective Return Format
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ✅ CORRECT: Return tuple with proper semantic directions
|
||||||
|
study = optuna.create_study(
|
||||||
|
directions=['minimize', 'maximize'], # Semantic directions
|
||||||
|
sampler=NSGAIISampler()
|
||||||
|
)
|
||||||
|
|
||||||
|
def objective(trial):
|
||||||
|
return (mass, frequency) # Return positive values
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ❌ WRONG: Using negative values
|
||||||
|
return (mass, -frequency) # Creates degenerate Pareto front
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Solution NX Protocol
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ✅ CORRECT: Solve all solutions
|
||||||
|
result = nx_solver.run_simulation(
|
||||||
|
sim_file=sim_file,
|
||||||
|
working_dir=model_dir,
|
||||||
|
expression_updates=design_vars,
|
||||||
|
solution_name=None # None = solve ALL solutions
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ❌ WRONG: Only solves first solution
|
||||||
|
solution_name="Solution 1" # Multi-solution workflows will fail
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extractor Selection
|
||||||
|
|
||||||
|
Always use centralized extractors from `optimization_engine/extractors/`:
|
||||||
|
- Standardized error handling
|
||||||
|
- Consistent return formats
|
||||||
|
- Well-tested and documented
|
||||||
|
- No code duplication
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
After completing study creation, provide:
|
||||||
|
|
||||||
|
1. **Summary Table**:
|
||||||
|
```
|
||||||
|
Study Created: {study_name}
|
||||||
|
Protocol: {protocol}
|
||||||
|
Objectives: {list}
|
||||||
|
Constraints: {list}
|
||||||
|
Design Variables: {list}
|
||||||
|
Multi-Solution: {Yes/No}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **File Checklist**:
|
||||||
|
```
|
||||||
|
✓ studies/{study_name}/1_setup/optimization_config.json
|
||||||
|
✓ studies/{study_name}/1_setup/workflow_config.json
|
||||||
|
✓ studies/{study_name}/run_optimization.py
|
||||||
|
✓ studies/{study_name}/reset_study.py
|
||||||
|
✓ studies/{study_name}/README.md
|
||||||
|
[✓] studies/{study_name}/NX_FILE_MODIFICATIONS_REQUIRED.md (if needed)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Next Steps** (as shown earlier)
|
||||||
|
|
||||||
|
## Remember
|
||||||
|
|
||||||
|
- Be conversational and helpful
|
||||||
|
- Ask clarifying questions early
|
||||||
|
- Confirm understanding before generating
|
||||||
|
- Provide context for technical decisions
|
||||||
|
- Make next steps crystal clear
|
||||||
|
- Anticipate common mistakes
|
||||||
|
- Reference existing studies as examples
|
||||||
|
- Always test-run your generated code mentally
|
||||||
|
|
||||||
|
The goal is for the user to have a COMPLETE, WORKING study that they can run immediately after placing their NX files.
|
||||||
Reference in New Issue
Block a user