/*
============================================================================
Cavitational Capacitive Drive (CCD) Process Handlers (mW/cm2)
Author: Mithun Padmakumar
Date: July 2026
Citation:
Padmakumar, M., Rajan, D., & Steephen, J. E. (2026). Cavitational capacitive
drive: A computationally efficient model for ultrasonic neuromodulation.
Journal of Neural Engineering.
Description:
Contains procedure definitions for inserting/updating the CCD mechanism,
managing simulation time steps, and pre-processing/saving simulation output
vectors (raw Vm, window-averaged Vm with metadata, full Cm waveform, AP times).
============================================================================
*/
// Procedures for CCD Mechanism Control
proc InsertCCD() {
if ($1) {
forall {
insert ccd
tbegin_ccd = tbegin
tdur_ccd = tdurn
usf_ccd = freq
usi_ccd = usi
for (x, 0) {
setpointer c_ccd(x), cm(x)
}
}
dt = 0.025 / freq
bFUSS = 1
printf("Activated CCD Focused Ultrasound Stimulation (FUSS).\n")
} else {
forall {
uninsert ccd
}
dt = 0.025
bFUSS = 0
printf("Deactivated CCD FUSS (Baseline RS Neuron).\n")
}
}
proc update_ccd() {
if (bFUSS) {
forall {
tbegin_ccd = tbegin
tdur_ccd = tdurn
usf_ccd = freq
usi_ccd = usi
}
dt = 0.025 / freq
printf("Updated CCD parameters: Freq=%d kHz, Intensity=%g mW/cm2, tbegin=%g ms, tdurn=%g ms.\n", freq, usi, tbegin, tdurn)
}
}
proc toggle_fuss() {
InsertCCD($1)
}
// Procedures for Output Management
objref outFile
objref processed_v, processed_t, g_proc
proc saveRawVm() {
strdef fname
if (bFUSS) {
sprint(fname, "Results/ccd_%g_%d.dat", usi, freq)
} else {
sprint(fname, "Results/baseline.dat")
}
outFile = new File()
outFile.wopen(fname)
for (i = 0; i < ts.size(); i += 1) {
outFile.printf("%g\t%g\n", ts.x(i), vsoma.x(i))
}
outFile.close()
print "Raw simulation result stored in ", fname
}
proc saveProcessedVm() {
strdef fname, metafile
processed_v = new Vector()
processed_t = new Vector()
if (bFUSS) {
w = 40
half_w = 20
step_size = freq
if (step_size < 1) step_size = 1
// Perform 40-point centered window averaging and downsampling by step_size (freq)
for (i = 0; i < ts.size(); i += step_size) {
sum_v = 0
count_v = 0
start_idx = i - half_w
if (start_idx < 0) start_idx = 0
end_idx = i + half_w - 1
if (end_idx >= vsoma.size()) end_idx = vsoma.size() - 1
for (k = start_idx; k <= end_idx; k += 1) {
sum_v += vsoma.x(k)
count_v += 1
}
if (count_v > 0) {
processed_v.append(sum_v / count_v)
processed_t.append(ts.x(i))
}
}
sprint(fname, "Results/ccd_processed_%g_%d.dat", usi, freq)
sprint(metafile, "Results/ccd_processed_%g_%d_metadata.dat", usi, freq)
} else {
// When CCD is deactivated, dt = 0.025 ms already; raw and processed waveforms are identical.
for (i = 0; i < ts.size(); i += 1) {
processed_v.append(vsoma.x(i))
processed_t.append(ts.x(i))
}
sprint(fname, "Results/baseline_processed.dat")
sprint(metafile, "Results/baseline_processed_metadata.dat")
}
// Display processed trace in a new graph window
g_proc = new Graph()
g_proc.size(0, tstop, -80, 40)
processed_v.plot(g_proc, processed_t, 2, 1)
if (bFUSS) {
g_proc.label(0.1, 0.9, "Processed Vm (Window Avg & Downsampled)")
} else {
g_proc.label(0.1, 0.9, "Processed Vm (Raw Vm at dt=0.025 ms)")
}
// Save processed waveform file
outFile = new File()
outFile.wopen(fname)
for (i = 0; i < processed_t.size(); i += 1) {
outFile.printf("%g\t%g\n", processed_t.x(i), processed_v.x(i))
}
outFile.close()
// Save metadata file (stored ONLY when processed waveform is saved)
outFile = new File()
outFile.wopen(metafile)
if (bFUSS) {
outFile.printf("FUSS=CCD\n")
outFile.printf("Frequency(kHz)=%d\n", freq)
outFile.printf("Intensity(mW/cm2)=%g\n", usi)
outFile.printf("Start(ms)=%g\n", tbegin)
outFile.printf("Duration(ms)=%g\n", tdurn)
outFile.printf("TSTOP(ms)=%g\n", tstop)
outFile.printf("WindowAveraging=40-point centered moving average\n")
outFile.printf("DownsamplingFactor=%d\n", freq)
outFile.printf("EffectiveDt(ms)=0.025\n")
} else {
outFile.printf("FUSS=none\n")
outFile.printf("TSTOP(ms)=%g\n", tstop)
outFile.printf("WindowAveraging=none (baseline simulation at dt=0.025 ms)\n")
outFile.printf("EffectiveDt(ms)=0.025\n")
}
outFile.close()
print "Processed Vm stored in ", fname
print "Metadata stored in ", metafile
}
proc saveCm() {
strdef fname
if (bFUSS) {
sprint(fname, "Results/ccd_Cm_%g_%d.dat", usi, freq)
} else {
sprint(fname, "Results/baseline_Cm.dat")
}
outFile = new File()
outFile.wopen(fname)
for (i = 0; i < ts.size(); i += 1) {
outFile.printf("%g\t%g\n", ts.x(i), cm_vec.x(i))
}
outFile.close()
print "Full Cm vector stored in ", fname
}
proc saveAPC() {
strdef fname
if (bFUSS) {
sprint(fname, "Results/APtimes_ccd_%g_%d.dat", usi, freq)
} else {
sprint(fname, "Results/APtimes_baseline.dat")
}
outFile = new File()
outFile.wopen(fname)
for (i = 0; i < ap_times.size(); i += 1) {
outFile.printf("%g\n", ap_times.x(i))
}
outFile.close()
print "AP times stored in ", fname
}