VGSOT/SOT MTJ switching simulation (electronic + anisotropy + LLG step solver)
Project description
vgsot-sim
A physics-based simulation toolkit for VGSOT-MTJ (Voltage-Gated Spin-Orbit Torque Magnetic Tunnel Junction) and SOT-MTJ switching dynamics, calibrated to the 80 nm β-W / CoFeB devices.
The simulator couples the following physical effects into a single time-domain switching model:
- Three-terminal electronic transport (
V1, V2, V3→I_SOT,V_MTJ) - Voltage-controlled magnetic anisotropy (VCMA)
- Spin-transfer torque (STT) and damping-/field-like spin-orbit torque (SOT)
- Stochastic thermal-fluctuation field obeying the FDT (Brown 1963)
- PMA + demagnetisation (thin-disk and exact oblate-ellipsoid tensor)
- Coupled self-heating RC dynamics with
M_s(T),K_i(T)feedback - Bias-dependent TMR(V) (Lorentzian or PDK 3-parameter form) with optional series-resistance toggle
- Two LLG integrators: legacy spherical-Euler (
(θ, φ)closed form) and a norm-preserving Cayley vector stepper with explicitσ̂_SH
This combination reproduces the chapter §2.3.3 Sigmoid-shaped P_sw(V) curves at the 0.75 ns operating point, the Néel–Brown V_th(t_w) trend, and the process-variability budget of §2.3.5.
The package can be used either:
- as a command-line simulator (
vgsot-sim <case>) - as a Python simulation library (high-level case API + low-level kernels)
- or invoked from the chapter-figure pipeline under
scripts/
For implementation status and release history, see:
- Implementation status — physics-feature coverage map
- Version notes — release-by-release physics changes
- Project structure — package layout
- Technical details — full physics derivations & calibration notes
Installation
Clone the repository and install in editable mode:
pip install -e .
Requirements (automatically installed):
- numpy
- scipy
- matplotlib
- tqdm
Tested on Python 3.10+. The chapter scripts under scripts/09_simulation_figures/
additionally mirror each rendered PNG into article/00_chapter_drafts/figs/
(the canonical chapter folder), so the manuscript and the code stay in
lock-step. Run them from inside a checkout that has those directories, or
delete the trailing shutil.copy line if you only want the local figure.
Information flow
flowchart LR
classDef io fill:#f5f3ff,stroke:#7c3aed,stroke-width:1.2px,color:#111;
classDef case fill:#f3e8ff,stroke:#9333ea,stroke-width:1.2px,color:#111;
classDef kernel fill:#ede9fe,stroke:#7c3aed,stroke-width:1.2px,color:#111;
classDef out fill:#faf5ff,stroke:#a855f7,stroke-width:1.2px,color:#111;
CLI["CLI / Config"]:::io
CASE["Selected case<br/>(time-series / SER)"]:::case
INIT["initialize.py<br/>initial state"]:::kernel
ELEC["electronic.py<br/>current / voltage mapping"]:::kernel
DYN["dynamic_switching.py<br/>magnetization update"]:::kernel
ANI["anisotropy.py<br/>effective field"]:::kernel
STO["stochastic.py<br/>thermal noise"]:::kernel
TMR["tmr.py<br/>resistance feedback"]:::kernel
RES["SimResult / SweepResult /<br/>SerResult"]:::out
SAVE["result_io.py<br/>CSV / plot export"]:::out
CLI --> CASE
CASE --> INIT
CASE --> ELEC
CASE --> DYN
DYN --> ANI
ANI --> STO
DYN --> TMR
INIT --> CASE
ELEC --> CASE
DYN --> CASE
TMR --> CASE
CASE --> RES
RES --> SAVE
This structure separates physics kernels, experiment orchestration, and output utilities, making the project usable both as a CLI simulator and as a reusable Python library.
For more details, please refer to: Project structure
Running Simulation Cases
All experiments are exposed through a unified CLI:
vgsot-sim <case_name>
By default, outputs are written to:
./result/Chapter02_local_24.png...Chapter02_local_27.png(figures)./result/*.csv(time series / sweep results)
You can change output directory via:
vgsot-sim <case_name> --out_dir my_results
Disable progress bars:
vgsot-sim <case_name> --no_progress
Available cases
The CLI now exposes only the four cases that map to the chapter §2.3.3 Device A
P→AP experimental protocol (same-batch detailed-P_sw measurement, t_w = 0.75 ns
write pulse, V_MTJ = 0):
# 1) Three-terminal voltage control — device demo (§2.1.1 T-circuit)
vgsot-sim terminal_voltage_control
# 2) Baseline: SOT-only, constant current pulse (V_MTJ=0)
vgsot-sim sot_only_constant_current
# 3) No-VCMA SOT switching: sweep I_SOT and overlay mz(t)
vgsot-sim sot_switching_no_vcma
# 4) Monte-Carlo P_sw vs |I_SOT| at 0.75 ns (chapter §2.3.3 main case)
vgsot-sim ser_sot_no_vcma_thermal
2026-05 pruning. The earlier VCMA-assisted and two-pulse VGSOT cases (
vcma_assisted_switching_{isot,vmtj}_sweep,optimized_vgsot_switching,ser_optimized_vgsot) have been removed: they came from the upstream Verilog-A port and have no counterpart in the same-batch Device A measurements that the package is now calibrated against. The process-variability sweep remains available as a Python-only API viavgsot_sim.ser_cases.variability_sweep.
Calibration note (2026-05). Default
theta_SH = 0.04(chapter §2.3.4 calibration to Device A P→AP, V_th @ 0.75 ns ≈ 894 mV). With this default the deterministic switching threshold sits around|I_SOT| ≈ 1.1 mA, so the defaulti_sot_listbrackets0.7 – 1.5 mA— not the µA range used by older versions. Passtheta_SH=0.25(literature β-W) explicitly when you need the bare-material regime.
For more information, please refer to: Simulation Cases and Their Physical Meaning
Default parameters are listed in: Default parameters by case
Chapter figure scripts (scripts/)
The scripts/ folder is organised by thesis chapter section. Each subdirectory
mixes a small Python driver with the figures it produces, and the calibrated
defaults of vgsot_sim are the contract between them:
| Folder | Section | Purpose |
|---|---|---|
02_pdk/ |
§2.2 | Hikstor 40 nm PDK behavioural model (.scs + .va) reference |
04_thermal_nonidealities/ |
§2.2.2 | Thermal transients, M_s(T)/K_i(T) sweep, self-heating trajectory |
05_experimental_raw_data/ |
§2.3 | Raw wafer-measurement data files |
06_psw_t_fitting/ |
§2.3.3 | Hysteresis → V_th → Néel-Brown (Δ, V_c0) extraction |
07_process_variability/ |
§2.3.5 | Brinkman CV(Δ) budget + D2D Monte-Carlo |
08_sampling_effect/ |
§2.3.6 | Wilson coverage + MC sampling-size sensitivity |
09_simulation_figures/ |
§2.3.3–2.3.4 | New (2026-05): vgsot-sim chapter figures |
The §2.3 analysis math previously inlined in scripts 06/07/08 is also exposed
as an importable API under vgsot_sim.analysis (nb_fit, sigmoid_fit,
variability, sampling). See src/vgsot_sim/analysis/__init__.py.
scripts/09_simulation_figures/ — vgsot-sim chapter figures
This is the latest research output: a small driver bundle that feeds the
calibrated vgsot_sim library through the §2.3.3 detailed-P_sw protocol
(t_w = 0.75 ns write pulse, V_MTJ = 0, P→AP) and renders three figures used
in the chapter.
| Script | Output figure | Demonstrates |
|---|---|---|
plot_single_trajectory.py |
Chapter02_local_08.png |
Single-trajectory (m_z, R_MTJ, I_SOT) for sub-/just-above/super-threshold I_SOT, self-heating ON, with M_s(T) / K_i(T) feedback |
plot_3d_trajectory.py |
Chapter02_local_09.png |
Magnetisation vector m(t) traced on the unit sphere + time-encoded colour, plus m_x, m_y, m_z time series |
plot_ser_mc.py |
Chapter02_local_10.png |
Monte-Carlo P_sw vs ` |
calibrate_to_experiment.py |
(stdout) | θ_SH calibration scan against Device A V_th(0.75 ns) = 894 mV — produces the table that selected the theta_SH = 0.04 default |
plot_ser_mc.py accepts CLI toggles that exercise the new physics options:
python plot_ser_mc.py --metric=psw # default: report P_sw = 1 − SER (chapter convention)
python plot_ser_mc.py --metric=ser # legacy: report SER directly
python plot_ser_mc.py --rng-mode=generator # multiprocessing-friendly RNG (default: "legacy")
python plot_ser_mc.py --integrator=cayley # norm-preserving Cartesian stepper (default: "euler_spherical")
Each driver writes to its own folder and mirrors the figure into
article/00_chapter_drafts/figs/, so the manuscript and the code stay in
lock-step. For the physics defaults these figures rely on (theta_SH=0.04,
TMR=1.0, RA=16.6e-12 Ω·m², etc.) see docs/technical_details.md §2.3.
Preview of the latest 09 outputs (full size + ON/OFF + Wilson CI inset are in the script folder):
| Single trajectory | 3D magnetisation | Monte-Carlo P_sw |
|---|---|---|
Chapter02_local_08.png |
Chapter02_local_09.png |
Chapter02_local_10.png |
For the upstream §2.2 device-physics figures see also: T-circuit · behavioural layers · vgsot-sim architecture · self-heating trajectory.
Using as a Python library (recommended)
Besides running from command line, you can import and run each case directly in your own Python scripts, and override parameters as needed. The recommended workflow is:
- Create a configuration dataclass
- Run a simulation case
- Optionally save results using
result_io
1. Basic Python API usage
Quick start (minimal example)
from vgsot_sim import sot_only_constant_current
res = sot_only_constant_current()
print(res.mz[-1])
Example: run a deterministic SOT sweep at the chapter §2.3.3 operating point.
from vgsot_sim import (
sot_switching_no_vcma,
SotSwitchingNoVcmaConfig,
)
cfg = SotSwitchingNoVcmaConfig(
# Defaults already use the §2.3.3 protocol; override I_SOT if you like.
i_sot_list=[-1500e-6, -1200e-6, -1100e-6, -1000e-6, -800e-6],
)
result = sot_switching_no_vcma(cfg)
print(result.time_s.shape)
print(result.mz_curves.keys())
print(result.r_mtj_curves.keys())
print(result.pulse_curves.keys())
Returned object:
| field | type | description |
|---|---|---|
time_s |
np.ndarray |
simulation time axis |
mz_curves |
dict[str, np.ndarray] |
magnetization trajectories |
r_mtj_curves |
dict[str, np.ndarray] |
MTJ resistance vs time |
pulse_curves |
dict[str, np.ndarray] |
applied pulse waveform |
switch_energy_j |
dict[str, float] |
switching energy |
pulse_ylabel |
str |
label for pulse plot |
Each entry in curves corresponds to one sweep parameter.
2. plotting results (optional)
import matplotlib.pyplot as plt
for label, mz in result.mz_curves.items():
plt.plot(result.time_s, mz, label=label)
plt.xlabel("time (s)")
plt.ylabel("mz")
plt.legend()
plt.show()
3. Running Monte-Carlo SER simulations
Example:
from vgsot_sim import (
ser_sot_no_vcma_thermal,
SerSotNoVcmaThermalConfig,
)
cfg = SerSotNoVcmaThermalConfig(
trials=500,
# Default theta_SH=0.04 puts the threshold near ~1.1 mA; pick the sweep range accordingly.
i_sot_list=[-1500e-6, -1200e-6, -1100e-6, -1000e-6, -800e-6],
)
res = ser_sot_no_vcma_thermal(
cfg,
seed=2026, # reproducible per-trial seeding
enable_self_heating=True, # couple T(t) into M_s, K_i each step
T_ambient_K=300.0,
)
print(res.x) # I_SOT values (A)
print(res.ser) # switching error rate
print(res.psw) # equivalent switching success probability (1 − SER)
Returned object:
SerResult
├── x : ndarray # I_SOT values
├── ser : ndarray # switching error rate
├── psw : property # 1 − ser, the chapter-§2.3.3 convention
└── x_label : str
Reproducibility & integrator toggles
For Monte-Carlo runs that need to be byte-reproducible (regression tests,
chapter figures), pass seed= plus either rng_mode="legacy" (default, calls
np.random.seed(...) once per trial — matches the chapter figures bit-for-bit)
or rng_mode="generator" (independent np.random.default_rng per trial,
multiprocessing-friendly). Both modes derive the per-trial seeds from the same
_trial_seed(seed, i_sot, trial_idx) formula, so the sequence of seeds is
identical.
You can also force the LLG step type on the SER case:
res = ser_sot_no_vcma_thermal(
cfg, seed=2026,
rng_mode="generator", # "legacy" | "generator"
integrator="cayley", # None (stepper default) | "euler_spherical" | "cayley"
enable_self_heating=True,
)
The Cayley vector stepper preserves |m|=1 to machine precision and takes
σ̂_SH as an explicit 3-vector argument (default [-1, 0, 0]). It reports a
threshold ~10 % higher than the spherical-Euler default at 0.75 ns; the
chapter calibration was performed against the latter, so "euler_spherical"
remains the default.
4. Low-level simulation kernels (advanced users)
If you want full control over excitation waveforms, you can call the internal kernels directly:
run_piecewise_terminal_voltage(...)
run_piecewise_direct_excitation(...)
Example:
from vgsot_sim import run_piecewise_direct_excitation
res = run_piecewise_direct_excitation(
sim_start_step=1,
sim_mid1_step=750, # 0.75 ns write pulse (at t_step = 1 ps)
sim_mid2_step=4000, # 3.25 ns relaxation tail
sim_end_step=4000,
pap=1,
v_mtj_stage1=0.0, v_mtj_stage2=0.0, v_mtj_stage3=0.0,
i_sot_stage1=-2000e-6, # super-threshold pulse for the calibrated theta_SH=0.04
i_sot_stage2=0.0,
i_sot_stage3=0.0,
estt_stage1=0, esot_stage1=1,
estt_stage2=0, esot_stage2=1,
estt_stage3=0, esot_stage3=1,
vnv=0, non=1, r_sot_fl_dl=0.83,
# ── New opt-in toggles (all default to the chapter behaviour) ──────────
enable_self_heating=True, # couple T(t) -> M_s(T), K_i(T) per step
T_ambient_K=300.0,
integrator="euler_spherical", # or "cayley" for explicit sigma_SH
sigma_SH=None, # default [-1, 0, 0]; Cayley only
rng=None, # pass np.random.default_rng(seed) for byte-repro
)
print(res.time_s)
print(res.mz)
print(res.T_K) # populated when enable_self_heating=True
print(res.Ms_T, res.Ki_T) # T-corrected material params per step
These kernels return a SimResult dataclass containing the full time evolution
of the system. With enable_self_heating=True the result additionally carries
T_K, Ms_T, Ki_T arrays for post-hoc inspection of the thermal coupling.
For full API documentation, please refer to: API document
For more detailed API usage, please refer to: CASES GUIDE
5. Process-variability sweep (built-in analysis API)
Since 2026-05 the §2.3.5 Brinkman / MC variability budget is also exposed as a
first-class case so it can be re-derived in one call without the external
07_process_variability script:
from vgsot_sim.ser_cases import variability_sweep
res = variability_sweep(
Delta=5.15, # thermal stability factor (chapter §2.3.3 NB fit)
Vc0=0.884, # zero-temperature critical voltage (V)
tw_ns=0.75, # pulse width
beta_meas=44.6, # Device A P→AP measured Sigmoid slope (V⁻¹)
)
print(res.cv_sweep) # CV(Δ) values scanned
print(res.beta_combined) # η_c · F(CV) · β_NB^fit, the joint D2D+C2C prediction
The lower-level math (nb_fit, sigmoid_fit, variability, sampling) is
also importable from vgsot_sim.analysis.
Notes
I_SOTunits: AmpereV_MTJunits: Volt- time units: seconds
pap=1initialises in the anti-parallel state (mz ≈ −1); the resistance flat at the start of the simulation isR_AP, notR_P. This matches the chapter convention but surprises some readers — see docs/IMPLEMENTATION_STATUS.md "Known caveats".
For reproducible Monte-Carlo runs, prefer the explicit-rng= plumbing over the
legacy global seed:
import numpy as np
from vgsot_sim import run_piecewise_direct_excitation
rng = np.random.default_rng(seed=42)
res = run_piecewise_direct_excitation(..., rng=rng)
The legacy np.random.seed(100) path still works for older scripts when
rng=None. See docs/technical_details.md §2.7 for
the full reproducibility model.
Citation
If you use this simulator in academic research, please cite:
Zhang Jincheng. (2026). VGSOT-SIM: A VGSOT switching simulation toolkit [Computer software]. GitHub.
For more technical details, please refer to: Technical Details
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vgsot_sim-0.1.1.tar.gz.
File metadata
- Download URL: vgsot_sim-0.1.1.tar.gz
- Upload date:
- Size: 58.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e1df384c91d46605738877d09a6ef48bba9d97ebe662b8594fdaaf5bdd0f464
|
|
| MD5 |
2bd3fd938f02b85c90e2fc9149d22cad
|
|
| BLAKE2b-256 |
9fe203a144791d077e414df33c361ae907b7989866b4343b7ab3c75e6a85c37d
|
File details
Details for the file vgsot_sim-0.1.1-py3-none-any.whl.
File metadata
- Download URL: vgsot_sim-0.1.1-py3-none-any.whl
- Upload date:
- Size: 57.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bfffe3ddd926215df20483aef007007068bd7272fb2dbb15ba37d9db387d4a5
|
|
| MD5 |
f6e33ed24ccb906e3d0d0a4331f1d5e9
|
|
| BLAKE2b-256 |
bd6f5a6e4ecfbe178c10ed69d4de01a0e2cf0536a3648bd60b1528f91af3e12a
|