/*================================================================================== Program to perform Monte Carlo simulations through placebo zone. See Kettlewell & Siminski 'Optimal Model Selection for RDD and Related Designs' Authors: Nathan Kettlewell & Peter Siminski Updated: 1 December 2025 Version: 1.2 ==================================================================================*/ capture program drop pzms_sim program define pzms_sim, eclass version 12.1 #delimit ; syntax varlist(min=2 max=2) [if] [in], MAXBW(numlist min=1) [Sims(integer 50) C(real 0) pzms_opts(string) cct_opts(string)] ; #delimit cr tokenize "`varlist'" tempvar d dXrv rv rv2 rv3 rv4 rv5 res rv0_1 res newrv newy capture ssc install pzms capture which rdrobust if _rc == 111 { noi di as err "pzms_sim requires the user command {bf:rdobust} to be installed." exit } capture which betafit if _rc == 111 { noi di as err "pzms_sim requires the user command {bf:betafit} to be installed." exit } *Preserve dataset preserve capture keep `if' capture keep `in' qui { sum `2', detail local rvmin = r(min) local rvmax = r(max) * fit a beta distribution to the data g `rv0_1' = (`2' - `rvmin')/(`rvmax'-`rvmin') // this is the rv rescaled to range from zero to 1 betafit `rv0_1' local alpha = e(alpha) local beta = e(beta) * first fit a 5th order polynomial with a discontinuity and kink at the threshold g `rv' = `2' - `c' g `d' = `rv' > 0 g `rv2' = `rv'^2 g `rv3' = `rv'^3 g `rv4' = `rv'^4 g `rv5' = `rv'^5 g `dXrv' = `d'*`rv' reg `1' `d' `rv' `rv2' `rv3' `rv4' `rv5' `dXrv' foreach coef in `d' `rv' `rv2' `rv3' `rv4' `rv5' `dXrv' { local b`coef' = _b[`coef'] } local target = _b[`d'] predict `res', resid sum `res' local res_mean = r(mean) local res_sd = r(sd) g double `newrv' = . g double `newy' = . local z = 0 foreach x in `maxbw' { local z = `z'+1 local mean_ssq_pzms`z' = 0 local mean_bw_pzms`z' = 0 } local mean_ssq_cct = 0 local mean_bw_cct = 0 *Loop through each simulated dataset forvalues i = 1/`sims' { replace `newrv' = rbeta(`alpha', `beta')*(`rvmax'-`rvmin')+`rvmin' replace `rv' = `newrv' - `c' replace `d' = `rv' > 0 replace `dXrv' = `d'*`rv' replace `rv2' = `rv'^2 replace `rv3' = `rv'^3 replace `rv4' = `rv'^4 replace `rv5' = `rv'^5 replace `newy' = `b_cons' +`b`d''*`d' +`b`rv''*`rv' + `b`dXrv''*`dXrv' + `b`rv2''*`rv2' + /// `b`rv3''*`rv3' + `b`rv4''*`rv4' + `b`rv5''*`rv5' + rnormal(0,`res_sd') //simulated dep var local z = 0 foreach x in `maxbw' { if `i' == 1 { noi di as error _newline "Be mindful of any warning messages (checking maxbw = `x' case...)" noi pzms `newy' `newrv', maxbw(`x') c(`c') `pzms_opts' nolog nodisplay(1) //for first iteration, display pzms output so user sees any warnings } else { pzms `newy' `newrv', maxbw(`x') c(`c') `pzms_opts' } local z = `z'+1 local mean_ssq_pzms`z' = `mean_ssq_pzms'`z'+(1/`sims')*(e(pz_est)-`target')^2 local mean_bw_pzms`z' = `mean_bw_pzms'`z' + (1/`sims')*e(pz_winbw) } rdrobust `newy' `newrv', c(`c') `cct_opts' local mean_ssq_cct = `mean_ssq_cct'+(1/`sims')*(e(tau_cl)-`target')^2 local mean_bw_cct = `mean_bw_cct' + (1/`sims')*e(h_l) if `i' == 1 { noi di as text _newline "pzms_sim iterations count below. pzms_sim will perform `sims' simulations." } noi di "." _cont if `i'/50 == round(`i'/50) { noi di as text " `i'/`sims' reps" _cont noi di "" } } } local j = wordcount("`maxbw'") matrix J = J(`j',3,0) //matrix for storing KS results matrix colnames J = "Max BW" "RMSE" "Mean BW" local z = 0 foreach x in `maxbw' { local z = `z'+1 local rmse_pzms = `mean_ssq_pzms'`z'^0.5 matrix J[`z',1] = `x' matrix J[`z',2] = `rmse_pzms' matrix J[`z',3] = `mean_bw_pzms'`x' noi di _newline noi di as result "RMSE KS = `rmse_pzms' with maxbw = `x'" noi di as result "Mean bandwidth KS = `mean_bw_pzms'`x' with maxbw = `x'" } local rmse_cct = `mean_ssq_cct'^0.5 noi di _newline noi di as result "RMSE CCT = `rmse_cct'" noi di as result "Mean bandwidth CCT = `mean_bw_cct'" capture ereturn clear capture ereturn matrix pzms_rmse = J capture ereturn scalar cct_rmse = `rmse_cct' capture ereturn scalar cct_bw = `mean_bw_cct' restore end