# Simulation of tripartite synapse model # Tiina Manninen and Ausra Saudargiene # Reference: Tiina Manninen, Ausra Saudargiene, and Marja-Leena Linne. Astrocyte-mediated spike-timing-dependent # long-term depression modulates synaptic properties in the developing cortex. PLoS Comput Biol, 2020. # ----------------------------------------------------------------------- from datetime import datetime import os from preneuron import Pre from postneuron import Post from astrocyte import Astro from tqdm import tqdm from scipy import io from collections import defaultdict def state_var_to_be_saved(pre, post, astro): return{ "Ca_CaNHVA_pre": pre["Ca_CaNHVA_pre"], "Ca_NMDAR_pre": pre["Ca_NMDAR_pre"], "CaN_pre": pre["CaN_pre"], "Glu_syncleft": post["Glu_syncleft"], "Prel_pre": pre["Prel_pre"], "RA2_O_pre": pre["RA2_O_pre"], "Rrel_pre": pre["Rrel_pre"], "V_pre": pre["V_pre"], "AG_post": post["AG_post"], "Ca_post": post["Ca_post"], "Ca_ER_post": post["Ca_ER_post"], "Ca_DAG_GaGTP_PLC_post": post["Ca_DAG_GaGTP_PLC_post"], "Ca_DAG_PLC_post": post["Ca_DAG_PLC_post"], "Ca_GaGTP_PLC_post": post["Ca_GaGTP_PLC_post"], "Ca_PLC_post": post["Ca_PLC_post"], "DAG_post": post["DAG_post"], "GaGTP_PLC_post": post["GaGTP_PLC_post"], "h_IP3R_post": post["h_IP3R_post"], "IP3_post": post["IP3_post"], "PLC_post": post["PLC_post"], "V_dend_post": post["V_dend_post"], "V_soma_post": post["V_soma_post"], "Ca_astro": astro["Ca_astro"], "Glu_extsyn": astro["Glu_extsyn"], "h_astro": astro["h_astro"], "IP3_astro": astro["IP3_astro"], "Rrel_astro": astro["Rrel_astro"] } def main(path, f_pre): # Start to count the time spent in simulation time_start = datetime.now() print("Simulation started at", time_start) try: os.makedirs(path) except OSError: print("Creation of the directory %s failed" % path) else: print("Successfully created the directory %s " % path) # -------------------- # External stimulation # -------------------- stim_start = 20000 # ms; Stimulation start time trainlengthtime = 25000 # ms; Stimulation lasting time restlengthtime = 20000 # ms; Resting time after stimulation ends no_trains = 1 # 1; Number of trains pulserate = 0.2 # Hz, 1/s; Frequency of stimulus pulselengthtime = 10 # ms; Length of the pulse A_stim_post = 0 # uA/cm^2; External current amplitude to postsynaptic neuron per unit area A_stim_pre = 10 # uA/cm^2; External current amplitude to presynaptic neuron per unit area dt = 0.05 # ms; Simulation time step T_end = stim_start + no_trains * (trainlengthtime + restlengthtime) # ms; Simulation end time Nsteps = round(T_end / dt) # Number of simulation steps t = [i * dt for i in range(Nsteps + 1)] # ms; Time vector pulselength = round(pulselengthtime / dt) no_pulses = round(pulserate * trainlengthtime * 1e-3) pauselengthtime = (trainlengthtime - no_pulses * pulselengthtime) / no_pulses # ms pauselength = round(pauselengthtime / dt) restlength = round(restlengthtime / dt) # No need for T_shift in the simulations because no pairing protocol, only presynaptic stimulus steps_T_shift = 0 # Only presynaptic stimulus, postsynaptic stimulus is zero stim_pause_post = ([A_stim_post] * pulselength + [0] * pauselength) * no_pulses stim_pause_pre = ([A_stim_pre] * pulselength + [0] * pauselength) * no_pulses # Postsynaptic stimulus is zero I_ext_post = [0] * (round(stim_start / dt) + 1) + (stim_pause_post + [0] * restlength) * no_trains # Presynaptic stimulus I_ext_pre = [0] * (round(stim_start / dt) + steps_T_shift + 1) + (stim_pause_pre + [0] * restlength) * no_trains # --------------------------------- # Presynaptic neuron initialization # --------------------------------- pre_params = Pre.get_parameters() pre_init = Pre.get_initial_values(pre_params) pre = Pre(pre_params, pre_init) # ---------------------------------- # Postsynaptic neuron initialization # ---------------------------------- post_params = Post.get_parameters() post_init = Post.get_initial_values(post_params) post = Post(post_params, post_init) # ------------------------ # Astrocyte initialization # ------------------------ astro_params = Astro.get_parameters() astro_init = Astro.get_initial_values(astro_params) astro = Astro(astro_params, astro_init) # ----------------------------------------- # Initialization of Ca leak flux parameters # ----------------------------------------- Ca_flux_post = post.calcium_other_fluxes() Ca_par_post = post.calcium_leak_parameters(Ca_flux_post["J_CaL_post"], Ca_flux_post["J_IP3R_post"], Ca_flux_post["J_NMDAR_post"], Ca_flux_post["J_PMCA_post"], Ca_flux_post["J_SERCA_post"]) Ca_flux_astro = astro.calcium_other_fluxes() Ca_par_astro = astro.calcium_leak_parameters(Ca_flux_astro["J_IP3R_astro"], Ca_flux_astro["J_SERCA_astro"]) # ---------------------------------------------------------- # Variables to be saved in a dictionary and then into a file # ---------------------------------------------------------- state_var = state_var_to_be_saved(pre.x, post.x, astro.x) saved_state_var = {key: [values] for key, values in state_var.items()} saved_other_var = defaultdict(list) t_spike = 10 # ms for i in tqdm(range(Nsteps)): if i == stim_start * 1 / 2 / dt or i == stim_start * 3 / 4 / dt: # --------------------------------- # Adjusting Ca leak flux parameters # --------------------------------- Ca_flux_post = post.calcium_other_fluxes() Ca_par_post = post.calcium_leak_parameters(Ca_flux_post["J_CaL_post"], Ca_flux_post["J_IP3R_post"], Ca_flux_post["J_NMDAR_post"], Ca_flux_post["J_PMCA_post"], Ca_flux_post["J_SERCA_post"]) Ca_flux_astro = astro.calcium_other_fluxes() Ca_par_astro = astro.calcium_leak_parameters(Ca_flux_astro["J_IP3R_astro"], Ca_flux_astro["J_SERCA_astro"]) # --------------------------------------------- # Saving old values for certain state variables # --------------------------------------------- Ca_pre_old = pre.x["Ca_CaNHVA_pre"] Prel_pre_old = pre.x["Prel_pre"] Rrel_pre_old = pre.x["Rrel_pre"] V_pre_old = pre.x["V_pre"] Ca_astro_old = astro.x["Ca_astro"] Rrel_astro_old = astro.x["Rrel_astro"] # ---------------------- # Differential equations # ---------------------- deriv_pre, other_var_pre = pre.derivative( astro.x["Glu_extsyn"], post.x["Glu_syncleft"], I_ext_pre[i+1]) deriv_post, other_var_post = post.derivative( pre.params["f_Glu_pre"], I_ext_post[i+1], Ca_par_post["r_leakCell_post"], Ca_par_post["r_leakER_post"]) deriv_ast, other_var_ast = astro.derivative(post.x["AG_post"], Ca_par_astro["r_leakER_astro"]) # ---------------------------------- # Solving the differential equations # ---------------------------------- pre.solve_deriv(deriv_pre, dt) post.solve_deriv(deriv_post, dt) astro.solve_deriv(deriv_ast, dt) # ----------------------------------------------------- # Updating those variables that include delta functions # ----------------------------------------------------- # Counting the time from previous presynaptic spike if (pre.x["V_pre"] >= 0) and (V_pre_old < 0): t_spike = 0 else: t_spike = t_spike + dt # Glu release from presynaptic neuron if (pre.x["Ca_CaNHVA_pre"] >= pre.params["C_thr_pre"]) and (t_spike < 10): pre.solve_deltaf(Ca_pre_old, f_pre, Prel_pre_old, Rrel_pre_old) post.solve_deltaf(pre, Rrel_pre_old) t_spike = 10 # Glu release from astrocyte if (astro.x["Ca_astro"] >= astro.params["C_thr_astro"]) and (Ca_astro_old < astro.params["C_thr_astro"]): astro.solve_deltaf(Rrel_astro_old) # ----------- # Saving data # ----------- state_var = state_var_to_be_saved(pre.x, post.x, astro.x) for key, values in state_var.items(): saved_state_var[key].append(values) other_var = {"f_pre": f_pre, "Glu_NMDAR_pre": other_var_pre["Glu_NMDAR_pre"], "ICaNHVA_pre": other_var_pre["ICaNHVA_pre"], "ICa_NMDAR_pre": other_var_pre["ICa_NMDAR_pre"], "I_AMPAR_post": other_var_post["I_AMPAR_post"], "ICaLHVA_dend_post": other_var_post["ICaLHVA_dend_post"], "ICaLLVA_dend_post": other_var_post["ICaLLVA_dend_post"], "ICa_NMDAR_post": other_var_post["ICa_NMDAR_post"], "J_CaL_post": other_var_post["J_CaL_post"], "J_IP3R_post": other_var_post["J_IP3R_post"], "J_leakCell_post": other_var_post["J_leakCell_post"], "J_leakER_post": other_var_post["J_leakER_post"], "J_NMDAR_post": other_var_post["J_NMDAR_post"], "J_PMCA_post": other_var_post["J_PMCA_post"], "J_SERCA_post": other_var_post["J_SERCA_post"]} for key, values in other_var.items(): saved_other_var[key].append(values) # Saving dictionaries to mat files io.savemat(os.path.join(path, "state_var_results.mat"), saved_state_var) io.savemat(os.path.join(path, "other_var_results.mat"), saved_other_var) io.savemat(os.path.join(path, "time_stimuli.mat"), {**{"time": [tp / 1000 for tp in t]}, **{"I_ext_pre": [I_ext_pre]}, **{"I_ext_post": [I_ext_post]}}) io.savemat(os.path.join(path, "stimulation_parameters.mat"), {**{"dt": dt}, **{"pulserate": pulserate}}) # Simulation time time_end = datetime.now() total_time = (time_end - time_start).seconds / 60. # min print("\n") print("Simulation finished at", time_end) print("Total time = {0:.2f} minutes".format(total_time)) if __name__ == "__main__": # ------------------------- # Before pairing simulation # ------------------------- # Fraction of presynaptic Glu release inhibition f_pre_base = 0 # Define the name of the result directory to be created path = "./results_before_pairing/" # Calling the main function for simulation main(path, f_pre_base) # ------------------------- # After pairing simulations # ------------------------- # Fraction of presynaptic Glu release inhibition # End values from post-pre pairing simulations f_pre = {"10": 0.4968, "20": 0.4872, "30": 0.4690, "40": 0.4592, "50": 0.4483, "60": 0.4269, "70": 0.4066, "80": 0.3846, "90": 0.3613, "100": 0.3380, "110": 0.3131, "120": 0.2878, "130": 0.2601, "140": 0.2327, "150": 0.1893, "160": 0.1740, "170": 0.1283, "180": 0.1126, "190": 0.0630, "200": 0.0279 } for key in f_pre: # ms; Temporal difference between post and pre activation in pairing protocol, \ # needed here just to save the data to correct folders # Define the name of the result directory to be created path_template = "./results_after_pairing/%sms/" path = path_template % key # Calling the main function for simulation main(path, f_pre[key])