## 📌 Overview

The MOS device characterization toolkit provides two complementary classes for designing analog IC circuits:

### MosDataManager

- Purpose: Access MOS device parameters from HDF5 database
- Features: 4D interpolation, batch queries, design-by-specification
- Use When: You need raw device parameters or design calculations

### MosDataPlotter

- Purpose: Create publication-quality device characteristic plots
- Features: Automatic scaling, unit symbols, customizable limits
- Use When: You need to visualize or present device behavior

## ⚡ Quick Start

### Installation

    from mos_data_manager import MosDataManager
    from mos_data_plotter import MosDataPlotter
    import numpy as np
    import matplotlib.pyplot as plt

### Initialize Database and Plotter

    db = MosDataManager('data/mos_data.h5', key='nmos')
    plotter = MosDataPlotter(db, apply_default_style=True, font_dir='fonts')

### 5-Minute Example: Plot I_D vs V_GS

    fig, ax = plotter.plot(
        x_param='vgs',
        y_params=['id'],
        fixed_inputs={'L': 1.8e-7, 'vds': 0.5, 'vbs': 0.0},
        y_scale=('A', 'uA'),
        figsize=(10, 6)
    )
    plt.savefig('id_vs_vgs.png', dpi=300)
    plt.show()

Output: Professional plot with labels, units, and gridlines

## 📊 MosDataManager - Data Access

### Core Concept: 4D Intrinsic Space

MOS devices are characterized by 4 intrinsic axes:

Parameter | Symbol | Unit | Grid Type | Notes
--- | --- | --- | --- | ---
Channel Length | L | meters (m) | Discrete | Must be exact match
Gate-Source Voltage | VGS | volts (V) | Continuous | Auto-interpolated if needed
Drain-Source Voltage | VDS | volts (V) | Continuous | Auto-interpolated if needed
Bulk-Source Voltage | VBS | volts (V) | Continuous | Auto-interpolated if needed

### Available Parameters

#### Base Parameters (from database)

    id, gm, gds, gmbs, cgg, cgs, cgd, cdb, csb, vth, vdsat, W

#### Derived Parameters (calculated on-the-fly)

- `id_w`: Current density (ID / W)
- `vov`: Overdrive voltage (VGS - Vth)
- `gm_id`: Transconductance efficiency (gm / ID)
- `rds`: Output resistance (1 / gds)
- `gain`: Intrinsic gain (gm / gds)
- `ft`: Transit frequency (gm / (2π × Cgg))

### Method 1: Basic Scalar Query

    result = db.lookup(
        inputs={
            'L': 1.8e-7,      # 180 nm (exact grid point)
            'vgs': 0.5,       # V (can be interpolated)
            'vds': 0.5,       # V (can be interpolated)
            'vbs': 0.0        # V (can be interpolated)
        },
        outputs=['id', 'gm']
    )

    print(f"I_D = {result['id']:.3e} A")
    print(f"g_m = {result['gm']:.3e} S")

📌 Key Point: L must exactly match a grid point. VGS, VDS, and VBS are automatically interpolated via 3D interpolation.

### Method 2: Batch Query (Fast!)

    result = db.lookup(
        inputs={
            'L': np.full(10, 1.8e-7),
            'vgs': np.linspace(0.2, 0.8, 10),
            'vds': np.full(10, 0.5),
            'vbs': np.full(10, 0.0)
        },
        outputs=['id', 'gm', 'gm_id']
    )

    # Results are numpy arrays
    for i in range(len(result['vgs'])):
        print(f"V_GS={result['vgs'][i]:.2f}V: I_D={result['id'][i]:.3e}A")

✓ 50x faster: Batch mode is ideal for parameter sweeps

### Method 3: Design-by-Specification

    result = db.design_lookup(
        inputs={
            'L': 1.8e-7,
            'vds': 0.5,
            'vbs': 0.0,
            'gm_id': 10.0     # Target efficiency
        },
        outputs=['id', 'gm']
    )

    print(f"Required V_GS: {result['vgs']:.4f} V")

💡 Use Case: Find VGS that achieves target gm/ID for efficient design

### Method 4: Explore Grid Points

    L_points = db.get_unique_values('L')
    vbs_points = db.get_unique_values('vbs')

    print(f"Available L: {L_points}")
    print(f"Available V_BS: {vbs_points}")

## 📈 MosDataPlotter - Visualization

### Universal Plotting: Plot Any Parameter vs Any Parameter

### Basic Single-Parameter Plot

    fig, ax = plotter.plot(
        x_param='vgs',
        y_params=['gm'],
        fixed_inputs={'L': 1.8e-7, 'vds': 0.5, 'vbs': 0.0},
        y_scale=('S', 'uS')  # Convert siemens to microsiemens
    )

### Multiple Parameters (Compare Behavior)

    fig, ax = plotter.plot(
        x_param='vgs',
        y_params=['id', 'gm'],
        fixed_inputs={'L': 1.8e-7, 'vds': 0.5, 'vbs': 0.0},
        y_scale=('A', 'uA')
    )

### Logarithmic Scales

    fig, ax = plotter.plot(
        x_param='vgs',
        y_params=['id'],
        fixed_inputs={'L': 1.8e-7, 'vds': 0.1, 'vbs': 0.0},
        log_y=True,  # Log scale for subthreshold region
        y_scale=('A', 'uA')
    )

### Custom Axis Limits

    fig, ax = plotter.plot(
        x_param='vgs',
        y_params=['gm'],
        fixed_inputs={'L': 1.8e-7, 'vds': 0.5, 'vbs': 0.0},
        xlim=(0.2, 0.8),  # V_GS from 0.2 to 0.8V
        ylim=(0, 500e-6)  # g_m from 0 to 500 µS
    )

📌 Auto-Limits: If not specified, limits are calculated with 5% padding for clean appearance

### Convenience Methods (Quick Shortcuts)

    plotter.plot_vs_vgs(L=1.8e-7, vds=0.5, vbs=0.0, y_params=['id'])
    plotter.plot_vs_vds(L=1.8e-7, vgs=0.6, vbs=0.0, y_params=['id'])
    plotter.plot_vs_L(vgs=0.6, vds=0.5, vbs=0.0, y_params=['gm'])

### Advanced Customization

    fig, ax = plotter.plot(
        x_param='vgs',
        y_params=['gm'],
        fixed_inputs={'L': 1.8e-7, 'vds': 0.5, 'vbs': 0.0},

        # Styling
        linewidth=3,
        marker='o',
        markersize=8,

        # Labels
        title='My Custom Title',
        xlabel_fontsize=14,
        ylabel_fontsize=14,

        # Grid
        show_grid=True,
        grid_linestyle='--',
        grid_alpha=0.4
    )

## 🔧 Design Workflows

### Workflow 1: High-Speed Amplifier Design

Objective: Design for high gain (50 V/V) and fast speed (5 GHz fT)
Steps:

1. Sweep VGS from 0.3V to 0.7V
2. Find VGS that gives target fT
3. Check if gain requirement is met
4. If not, use cascode for additional gain stage

### Workflow 2: Low-Power Efficient Design

Objective: Maximize energy efficiency (high gm/ID)
Steps:

1. Use design_lookup to find VGS for target gm/ID ≈ 15 S/A
2. This operates in weak inversion (very low current)
3. Verify gain and speed are acceptable
4. Optimize bias current for power budget

### Workflow 3: Robust Design (Process Variation)

Objective: Verify design works across L process corners
Steps:

1. Get all available L values from database
2. Run batch lookup across all L at fixed VGS, VDS
3. Check gain and current variation
4. Add compensation circuit if variation > 10%

## 💻 Practical Examples

### Example 1: Bandwidth Design

Size transistor for 1 GHz bandwidth with 10 fF load capacitance:

    C_load = 10e-15         # 10 fF
    target_BW = 1e9        # 1 GHz
    gm_required = 2 * np.pi * target_BW * C_load

    result = db.lookup(
        inputs={'L': 1.8e-7, 'vgs': 0.6, 'vds': 0.5, 'vbs': 0.0},
        outputs=['gm']
    )

    print(f"Required g_m: {gm_required:.3e} S")
    print(f"Achieved g_m: {result['gm']:.3e} S")

### Example 2: Efficiency Mapping

Visualize gm/ID across VGS-VDS space

    vgs_array = np.linspace(0.3, 0.7, 15)
    vds_array = np.linspace(0.1, 0.9, 10)

    # Use matplotlib contour plotting for 2D visualization

✓ Tip: Weak inversion (low VOV) gives best efficiency

## ⭐ Best Practices

### 1. Always Check Grid Points First

    L_available = db.get_unique_values('L')
    if 1.8e-7 in L_available:
        # Safe to use
        result = db.lookup({'L': 1.8e-7, ...}, ...)

### 2. Use Batch Mode for Sweeps (50x Faster!)

    # GOOD: Single batch query
    result = db.lookup({
        'L': np.full(100, 1.8e-7),
        'vgs': np.linspace(0.2, 0.8, 100),
        ...
    })

    # BAD: Loop of scalar queries (much slower)

### 3. Scale Units for Readability

    y_scale=('A', 'uA')  # Show as microamps
    y_scale=('S', 'uS')  # Show as microsiemens

### 4. Verify Design Solutions

    design_result = db.design_lookup(...)
    verify_result = db.lookup({...design_result...})
    assert abs(verify_result['target'] - target) < tolerance

### 5. Use get_unique_values() to Explore Grid

    print(db.get_unique_values('L'))
    print(db.get_unique_values('vbs'))

## 🛠️ Troubleshooting

### ❌ "Input L does not match grid point"

Solution: Check available L values

    L_available = db.get_unique_values('L')
    L_use = L_available[np.argmin(np.abs(L_available - L_desired))]

### ❌ NaN (Not a Number) Results

Cause: Division by zero in derived parameters

Solution: Filter invalid values

    valid_idx = np.isfinite(result['gm_id'])
    gm_id_valid = result['gm_id'][valid_idx]

### ❌ Plot Looks Stretched

Solution: Set explicit axis limits

    fig, ax = plotter.plot(
        ...,
        xlim=(xmin, xmax),
        ylim=(ymin, ymax)
    )

## 📚 API Reference

### MosDataManager.lookup()

    result = db.lookup(
        inputs={
            'L': 1.8e-7,           # Scalar or array (exact match required)
            'vgs': 0.5,            # Scalar or array (interpolated)
            'vds': 0.5,            # Scalar or array (interpolated)
            'vbs': 0.0             # Scalar or array (interpolated)
        },
        outputs=['id', 'gm', 'gm_id']  # List of parameters
    )
    # Returns: dict with parameter names as keys

### MosDataManager.design_lookup()

    result = db.design_lookup(
        inputs={
            'L': 1.8e-7,           # Fixed
            'vds': 0.5,            # Fixed
            'vbs': 0.0,            # Fixed
            'gm_id': 10.0          # Target derived parameter
        },
        outputs=['id', 'gm']       # Additional outputs
    )
    # Returns: dict including solved intrinsic axis

### MosDataPlotter.plot()

    fig, ax = plotter.plot(
        x_param='vgs',             # X-axis parameter
        y_params=['id'],           # List of Y parameters
        fixed_inputs={...},        # Other intrinsic axes
        x_scale=('V', 'V'),       # Unit scaling
        y_scale=('A', 'uA'),      # Unit scaling
        log_x=False,               # Log scales
        log_y=False,
        xlim=None,                 # Axis limits (auto if None)
        ylim=None,
        title='Title',             # Customization
        figsize=(10, 6),
        show_grid=True
    )
    # Returns: (fig, ax) matplotlib objects