From b9d40580cdfbc12b008e0635ce8a06e9d1183bde Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Sun, 4 May 2025 22:42:12 -0700 Subject: [PATCH 01/17] Add first version script --- cluster_hab/day1.py | 99 -------- cluster_hab/day2.py | 128 ---------- cluster_hab/day3.py | 153 ----------- cluster_hab/day4.py | 184 -------------- cluster_hab/day5.py | 210 --------------- .../stimulus_orderings/movie_order_groupA.txt | 230 ----------------- .../stimulus_orderings/movie_order_groupB.txt | 230 ----------------- .../movie_order_groupTEST.txt | 5 - .../rig_hab_day1_groupA.txt | 120 --------- .../rig_hab_day1_groupB.txt | 120 --------- .../rig_hab_day3_groupA.txt | 180 ------------- .../rig_hab_day3_groupB.txt | 180 ------------- .../rig_hab_day5_groupA.txt | 240 ------------------ .../rig_hab_day5_groupB.txt | 240 ------------------ stimulus_loop.py => stimulus_v2species.py | 0 15 files changed, 2319 deletions(-) delete mode 100755 cluster_hab/day1.py delete mode 100755 cluster_hab/day2.py delete mode 100755 cluster_hab/day3.py delete mode 100755 cluster_hab/day4.py delete mode 100755 cluster_hab/day5.py delete mode 100644 data/stimulus_orderings/movie_order_groupA.txt delete mode 100644 data/stimulus_orderings/movie_order_groupB.txt delete mode 100644 data/stimulus_orderings/movie_order_groupTEST.txt delete mode 100644 data/stimulus_orderings/rig_hab_day1_groupA.txt delete mode 100644 data/stimulus_orderings/rig_hab_day1_groupB.txt delete mode 100644 data/stimulus_orderings/rig_hab_day3_groupA.txt delete mode 100644 data/stimulus_orderings/rig_hab_day3_groupB.txt delete mode 100644 data/stimulus_orderings/rig_hab_day5_groupA.txt delete mode 100644 data/stimulus_orderings/rig_hab_day5_groupB.txt rename stimulus_loop.py => stimulus_v2species.py (100%) diff --git a/cluster_hab/day1.py b/cluster_hab/day1.py deleted file mode 100755 index 44e2ad9..0000000 --- a/cluster_hab/day1.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -Habituation Day 1. - -Total: 606 seconds = 10.1 min - -""" - -from psychopy import visual -from camstim import Stimulus, SweepStim, MovieStim -from camstim import Foraging -from camstim import Window, Warp - -# Create display window -window = visual.Window(fullscr=True, - monitor='Gamma1.Luminance50', - screen=0, - #warp=Warp.Spherical, - ) - -############ Stimulus definitions ############################################# -# Natual Movie 2 -nm2_path = r"//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Movie_TOE2.npy" - -nm2 = MovieStim(movie_path=nm2_path, - window=window, - frame_length=2.0/60.0, - size=(1920, 1080), - start_time=0.0, - stop_time=None, - flip_v=True, - runs=4,) - -# Drifting gratings -dg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([1.0, 2.0], 1), - 'SF': ([0.04], 2), - 'Ori': (range(0, 270, 45), 3), - }, - sweep_length=2.0, - start_time=0.0, - blank_length=1.0, - blank_sweeps=5, - runs=5, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn_path = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/sparse_noise_no_boundary_16x28_scaled.npy" - -lsn = MovieStim(movie_path=lsn_path, - window=window, - frame_length=0.5, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Set display sequences -nm2_ds = [(60, 180)] -dg_ds = [(225, 441)] -lsn_ds = [(486, 606)] - -nm2.set_display_sequence(nm2_ds) -dg.set_display_sequence(dg_ds) -lsn.set_display_sequence(lsn_ds) - -############ SweepStim Setup ################################################## -# kwargs -params = { -} - -# create SweepStim instance -ss = SweepStim(window, - stimuli=[nm2, dg, lsn], - pre_blank_sec=0, - post_blank_sec=0, - params=params, - ) - -# add in foraging so we can track wheel, potentially give rewards, etc -f = Foraging(window=window, - auto_update=False, - params=params, - nidaq_tasks={'digital_input': ss.di, - 'digital_output': ss.do,}) #share di and do with SS -ss.add_item(f, "foraging") - -# run it -ss.run() \ No newline at end of file diff --git a/cluster_hab/day2.py b/cluster_hab/day2.py deleted file mode 100755 index b66cf86..0000000 --- a/cluster_hab/day2.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -Habituation Day 2. - -Total: 1175 seconds = 19.6 min - -""" -import os -from psychopy import visual -from camstim import Stimulus, SweepStim, MovieStim, NaturalScenes -from camstim import Foraging -from camstim import Window, Warp - -# Create display window -window = visual.Window(fullscr=True, - monitor='Gamma1.Luminance50', - screen=0, - #warp=Warp.Spherical, - ) - -############ Stimulus definitions ############################################# - -# Locally Sparse Noise -lsn8_path = r"//allen/aibs/mat/michaelbu/Locally_Sparse_Noise_Trimmed/short/lsn_mat_8x14_short.npy" - -lsn8 = MovieStim(movie_path=lsn8_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Natural Scenes -scenes_folder = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Natural Images" -image_files = [os.path.join(scenes_folder, f) for f in os.listdir(scenes_folder) if \ - len(f) > 4 and f[-4:] in ['.jpg','.png','.tif','tiff']] - -ns = NaturalScenes(image_path_list=image_files, - window=window, - sweep_length=0.50, - start_time=0.0, - stop_time=None, - blank_length=0.0, - blank_sweeps=10, - runs=3, - shuffle=True,) - -# Drifting gratings -dg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([2.0, 4.0], 1), - 'SF': ([0.04], 2), - 'Ori': (range(0, 360, 45), 3), - }, - sweep_length=2.0, - start_time=0.0, - blank_length=1.0, - blank_sweeps=8, - runs=5, - shuffle=True, - save_sweep_table=True, - ) - -# Static Gratings -sg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'SF': ([0.02, 0.08, 0.32], 1), - 'Ori': (range(0, 180, 30), 2), - }, - sweep_length=0.5, - start_time=0.0, - blank_length=0.0, - blank_sweeps=10, - runs=5, - shuffle=True, - save_sweep_table=True, - ) - -# Set display sequences -lsn8_ds = [(90, 390)] -ns_ds = [(480, 675)] -dg_ds = [(765, 1035)] -sg_ds = [(1125, 1175)] - -lsn8.set_display_sequence(lsn8_ds) -ns.set_display_sequence(ns_ds) -dg.set_display_sequence(dg_ds) -sg.set_display_sequence(sg_ds) - -############ SweepStim Setup ################################################## -# kwargs -params = { -} - -# create SweepStim instance -ss = SweepStim(window, - stimuli=[lsn8, ns, dg, sg], - pre_blank_sec=0, - post_blank_sec=0, - params=params, - ) - -# add in foraging so we can track wheel, potentially give rewards, etc -f = Foraging(window=window, - auto_update=False, - params=params, - nidaq_tasks={'digital_input': ss.di, - 'digital_output': ss.do,}) #share di and do with SS -ss.add_item(f, "foraging") - -# run it -ss.run() diff --git a/cluster_hab/day3.py b/cluster_hab/day3.py deleted file mode 100755 index eaf0567..0000000 --- a/cluster_hab/day3.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -Habituation Day 3. - -Total: 1822 seconds = 30.4 min - -""" -import os -from psychopy import visual -from camstim import Stimulus, SweepStim, MovieStim, NaturalScenes -from camstim import Foraging -from camstim import Window, Warp - -# Create display window -window = visual.Window(fullscr=True, - monitor='Gamma1.Luminance50', - screen=0, - #warp=Warp.Spherical, - ) - -############ Stimulus definitions ############################################# -# Natual Movie 1 -nm2_path = r"//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Movie_TOE2.npy" - -nm2 = MovieStim(movie_path=nm2_path, - window=window, - frame_length=2.0/60.0, - size=(1920, 1080), - start_time=0.0, - stop_time=None, - flip_v=True, - runs=5,) - -# Natural Scenes -scenes_folder = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Natural Images" -image_files = [os.path.join(scenes_folder, f) for f in os.listdir(scenes_folder) if \ - len(f) > 4 and f[-4:] in ['.jpg','.png','.tif','tiff']] - -ns = NaturalScenes(image_path_list=image_files, - window=window, - sweep_length=0.50, - start_time=0.0, - stop_time=None, - blank_length=0.0, - blank_sweeps=20, - runs=5, - shuffle=True,) - -# Drifting gratings -dg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([1.0, 2.0, 4.0, 8.0], 1), - 'SF': ([0.04], 2), - 'Ori': (range(0, 360, 90), 3), - }, - sweep_length=2.0, - start_time=0.0, - blank_length=1.0, - blank_sweeps=15, - runs=7, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn8_path = r"//allen/aibs/mat/michaelbu/Locally_Sparse_Noise_Trimmed/short/lsn_mat_8x14_short.npy" - -lsn8 = MovieStim(movie_path=lsn8_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Static Gratings -sg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 1), - 'Ori': (range(0, 180, 30), 2), - }, - sweep_length=0.5, - start_time=0.0, - blank_length=0.0, - blank_sweeps=20, - runs=7, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn4_path = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/sparse_noise_no_boundary_16x28_scaled.npy" - -lsn4 = MovieStim(movie_path=lsn4_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Set display sequences -nm2_ds = [(90, 240)] -ns_ds = [(330, 640)] -dg_ds = [(730, 1087)] -lsn8_ds = [(1177, 1417)] -sg_ds = [(1407, 1512)] -lsn4_ds = [(1602, 1822)] - -nm2.set_display_sequence(nm2_ds) -ns.set_display_sequence(ns_ds) -dg.set_display_sequence(dg_ds) -lsn8.set_display_sequence(lsn8_ds) -sg.set_display_sequence(sg_ds) -lsn4.set_display_sequence(lsn4_ds) - -############ SweepStim Setup ################################################## -# kwargs -params = {} - -# create SweepStim instance -ss = SweepStim(window, - stimuli=[nm2, ns, dg, lsn8, sg, lsn4], - pre_blank_sec=0, - post_blank_sec=0, - params=params, - ) - -# add in foraging so we can track wheel, potentially give rewards, etc -f = Foraging(window=window, - auto_update=False, - params=params, - nidaq_tasks={'digital_input': ss.di, - 'digital_output': ss.do,}) #share di and do with SS -ss.add_item(f, "foraging") - -# run it -ss.run() diff --git a/cluster_hab/day4.py b/cluster_hab/day4.py deleted file mode 100755 index a41bfb4..0000000 --- a/cluster_hab/day4.py +++ /dev/null @@ -1,184 +0,0 @@ -""" -Habituation Day 4. -Short bursts of fast presentations - -Total: 2386 seconds = 39.8 minutes - -""" -import os -from psychopy import visual -from camstim import Stimulus, SweepStim, MovieStim, NaturalScenes -from camstim import Foraging -from camstim import Window, Warp - -# Create display window -window = visual.Window(fullscr=True, - monitor='Gamma1.Luminance50', - screen=0, - #warp=Warp.Spherical, - ) - -############ Stimulus definitions ############################################# -# 1 Hz flash for 10 sec -leak0 = Stimulus(visual.GratingStim(window, tex=None, color=0, - size=(1920,1200), units="pix"), - sweep_params={"Color":([-1,1], 0)}, - sweep_length=0.5, - start_time=0.0, - runs=10,) -# 0.5 Hz flash for 10 sec -leak1 = Stimulus(visual.GratingStim(window, tex=None, color=0, - size=(1920,1200), units="pix"), - sweep_params={"Color":([-1,1], 0)}, - sweep_length=0.25, - start_time=10.0, - runs=20,) - -# Natual Movie 1 -# nm1_path = r"//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Movie_TOE1.npy" -# -# nm1 = MovieStim(movie_path=nm1_path, -# window=window, -# frame_length=2.0/60.0, -# size=(1920, 1080), -# start_time=0.0, -# stop_time=None, -# flip_v=True, -# runs=4,) - -# Natual Movie 2 -nm2_path = r"//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Movie_TOE2.npy" - -nm2 = MovieStim(movie_path=nm2_path, - window=window, - frame_length=2.0/60.0, - size=(1920, 1080), - start_time=0.0, - stop_time=None, - flip_v=True, - runs=4,) - -# Natural Scenes -scenes_folder = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Natural Images" -image_files = [os.path.join(scenes_folder, f) for f in os.listdir(scenes_folder) if \ - len(f) > 4 and f[-4:] in ['.jpg','.png','.tif','tiff']] - -ns = NaturalScenes(image_path_list=image_files, - window=window, - sweep_length=0.25, - start_time=0.0, - stop_time=None, - blank_length=0.0, - blank_sweeps=50, - runs=5, - shuffle=True,) - -# Drifting gratings -dg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([1.0, 2.0, 8.0, 15.0], 1), - 'SF': ([0.04], 2), - 'Ori': (range(0, 360, 45), 3), - }, - sweep_length=2.0, - start_time=0.0, - blank_length=1.0, - blank_sweeps=15, - runs=2, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn8_path = r"//allen/aibs/mat/michaelbu/Locally_Sparse_Noise_Trimmed/short/lsn_mat_8x14_short.npy" - -lsn8 = MovieStim(movie_path=lsn8_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Static Gratings -sg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 1), - 'Ori': (range(0, 180, 30), 2), - 'Phase': ([0, 0.5], 3), - }, - sweep_length=0.25, - start_time=0.0, - blank_length=0.0, - blank_sweeps=25, - runs=7, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn4_path = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/sparse_noise_no_boundary_16x28_scaled.npy" - -lsn4 = MovieStim(movie_path=lsn4_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Set display sequences -# nm1_ds = [(2197, 2317)] -nm2_ds = [(499, 619),(2197, 2317)] -ns_ds = [(679, 829), (1453, 1603)] -dg_ds = [(1159, 1363), (1903, 2107)] -lsn8_ds = [(919, 1099)] -sg_ds = [(300, 409), (2377, 2486)] -lsn4_ds = [(90, 210), (1693, 1813)] - -# nm1.set_display_sequence(nm1_ds) -nm2.set_display_sequence(nm2_ds) -ns.set_display_sequence(ns_ds) -dg.set_display_sequence(dg_ds) -lsn8.set_display_sequence(lsn8_ds) -sg.set_display_sequence(sg_ds) -lsn4.set_display_sequence(lsn4_ds) - -############ SweepStim Setup ################################################## -# kwargs -params = {} - -# create SweepStim instance -ss = SweepStim(window, - stimuli=[leak0, leak1, nm2, ns, dg, lsn8, sg, lsn4], - pre_blank_sec=0, - post_blank_sec=0, - params=params, - ) - -# add in foraging so we can track wheel, potentially give rewards, etc -f = Foraging(window=window, - auto_update=False, - params=params, - nidaq_tasks={'digital_input': ss.di, - 'digital_output': ss.do,}) #share di and do with SS -ss.add_item(f, "foraging") - -# run it -ss.run() diff --git a/cluster_hab/day5.py b/cluster_hab/day5.py deleted file mode 100755 index ee16b28..0000000 --- a/cluster_hab/day5.py +++ /dev/null @@ -1,210 +0,0 @@ -""" -Habituation Day 5. -Short bursts of fast presentations - -Total: 3003 seconds = 50.0 minutes - -""" -import os -from psychopy import visual -from camstim import Stimulus, SweepStim, MovieStim, NaturalScenes -from camstim import Foraging -from camstim import Window, Warp - -# Create display window -window = visual.Window(fullscr=True, - monitor='Gamma1.Luminance50', - screen=0, - #warp=Warp.Spherical, - ) - -############ Stimulus definitions ############################################# -# 1 Hz flash for 10 sec -leak0 = Stimulus(visual.GratingStim(window, tex=None, color=0, - size=(1920,1200), units="pix"), - sweep_params={"Color":([-1,1], 0)}, - sweep_length=0.5, - start_time=0.0, - runs=10,) -# 0.5 Hz flash for 10 sec -leak1 = Stimulus(visual.GratingStim(window, tex=None, color=0, - size=(1920,1200), units="pix"), - sweep_params={"Color":([-1,1], 0)}, - sweep_length=0.25, - start_time=10.0, - runs=20,) - -# Natual Movie 1 -# nm1_path = r"//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Movie_TOE1.npy" -# -# nm1 = MovieStim(movie_path=nm1_path, -# window=window, -# frame_length=2.0/60.0, -# size=(1920, 1080), -# start_time=0.0, -# stop_time=None, -# flip_v=True, -# runs=5,) - -# Natual Movie 2 -nm2_path = r"//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Movie_TOE2.npy" - -nm2 = MovieStim(movie_path=nm2_path, - window=window, - frame_length=2.0/60.0, - size=(1920, 1080), - start_time=0.0, - stop_time=None, - flip_v=True, - runs=5,) - -# Natural Scenes -scenes_folder = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/Natural Images" -image_files = [os.path.join(scenes_folder, f) for f in os.listdir(scenes_folder) if \ - len(f) > 4 and f[-4:] in ['.jpg','.png','.tif','tiff']] - -ns = NaturalScenes(image_path_list=image_files, - window=window, - sweep_length=0.25, - start_time=0.0, - stop_time=None, - blank_length=0.0, - blank_sweeps=80, - runs=10, - shuffle=True,) - -# Drifting gratings type 1 -dg1 = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([1.0, 2.0, 4.0, 8.0, 15.0], 1), - 'SF': ([0.04], 2), - 'Ori': (range(0, 360, 45), 3), - }, - sweep_length=2.0, - start_time=0.0, - blank_length=1.0, - blank_sweeps=20, - runs=4, - shuffle=True, - save_sweep_table=True, - ) - -# Drifting gratings type 2 -dg2 = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([1.0, 4.0, 15.0], 1), - 'SF': ([0.04], 2), - 'Ori': (range(45, 360, 90), 3), - }, - sweep_length=2.0, - start_time=0.0, - blank_length=1.0, - blank_sweeps=20, - runs=5, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn8_path = r"//allen/aibs/mat/michaelbu/Locally_Sparse_Noise_Trimmed/short/lsn_mat_8x14_short.npy" - -lsn8 = MovieStim(movie_path=lsn8_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Static Gratings -sg = Stimulus(visual.GratingStim(window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 1), - 'Ori': (range(0, 180, 30), 2), - 'Phase': ([0, 0.25, 0.5], 3), - }, - sweep_length=0.25, - start_time=0.0, - blank_length=0.0, - blank_sweeps=25, - runs=19, - shuffle=True, - save_sweep_table=True, - ) - -# Locally Sparse Noise -lsn4_path = "//allen/programs/braintv/workgroups/neuralcoding/Saskia/Visual Stimuli 151207/sparse_noise_no_boundary_16x28_scaled.npy" - -lsn4 = MovieStim(movie_path=lsn4_path, - window=window, - frame_length=0.25, - size=(1260, 720), - start_time=0.0, - stop_time=None, - runs=1,) - -# Set display sequences -lsn8_ds = [(90,330)] -sg_ds = [(390,671), (2534,2697)] -lsn4_ds = [(731,1131)] -nm2_ds = [(1191,1341), (2324,2474)] -dg1_ds = [(1401, 1905)] -ns_ds = [(1965,2264)] -nm1_ds = [(2324, 2474)] -dg2_ds = [(2757,2946)] - -# nm1.set_display_sequence(nm1_ds) -nm2.set_display_sequence(nm2_ds) -ns.set_display_sequence(ns_ds) -dg1.set_display_sequence(dg1_ds) -dg2.set_display_sequence(dg2_ds) -lsn8.set_display_sequence(lsn8_ds) -sg.set_display_sequence(sg_ds) -lsn4.set_display_sequence(lsn4_ds) - -############ SweepStim Setup ################################################## -# kwargs -params = {} - -# create SweepStim instance -ss = SweepStim(window, - stimuli=[leak0, leak1, nm2, ns, dg1, dg2, lsn8, sg, lsn4], - pre_blank_sec=0, - post_blank_sec=0, - params=params, - ) - -# add in foraging so we can track wheel, potentially give rewards, etc -f = Foraging(window=window, - auto_update=False, - params=params, - nidaq_tasks={'digital_input': ss.di, - 'digital_output': ss.do,}) #share di and do with SS -ss.add_item(f, "foraging") - -# run it -ss.run() diff --git a/data/stimulus_orderings/movie_order_groupA.txt b/data/stimulus_orderings/movie_order_groupA.txt deleted file mode 100644 index c9ab687..0000000 --- a/data/stimulus_orderings/movie_order_groupA.txt +++ /dev/null @@ -1,230 +0,0 @@ -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 diff --git a/data/stimulus_orderings/movie_order_groupB.txt b/data/stimulus_orderings/movie_order_groupB.txt deleted file mode 100644 index b9eb447..0000000 --- a/data/stimulus_orderings/movie_order_groupB.txt +++ /dev/null @@ -1,230 +0,0 @@ -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -52 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 -52 -3 diff --git a/data/stimulus_orderings/movie_order_groupTEST.txt b/data/stimulus_orderings/movie_order_groupTEST.txt deleted file mode 100644 index dd76fe4..0000000 --- a/data/stimulus_orderings/movie_order_groupTEST.txt +++ /dev/null @@ -1,5 +0,0 @@ -10 -20 -30 -40 -50 diff --git a/data/stimulus_orderings/rig_hab_day1_groupA.txt b/data/stimulus_orderings/rig_hab_day1_groupA.txt deleted file mode 100644 index 6e09974..0000000 --- a/data/stimulus_orderings/rig_hab_day1_groupA.txt +++ /dev/null @@ -1,120 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 diff --git a/data/stimulus_orderings/rig_hab_day1_groupB.txt b/data/stimulus_orderings/rig_hab_day1_groupB.txt deleted file mode 100644 index ce58c30..0000000 --- a/data/stimulus_orderings/rig_hab_day1_groupB.txt +++ /dev/null @@ -1,120 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 diff --git a/data/stimulus_orderings/rig_hab_day3_groupA.txt b/data/stimulus_orderings/rig_hab_day3_groupA.txt deleted file mode 100644 index 7ee9a6e..0000000 --- a/data/stimulus_orderings/rig_hab_day3_groupA.txt +++ /dev/null @@ -1,180 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 diff --git a/data/stimulus_orderings/rig_hab_day3_groupB.txt b/data/stimulus_orderings/rig_hab_day3_groupB.txt deleted file mode 100644 index b86e11c..0000000 --- a/data/stimulus_orderings/rig_hab_day3_groupB.txt +++ /dev/null @@ -1,180 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 diff --git a/data/stimulus_orderings/rig_hab_day5_groupA.txt b/data/stimulus_orderings/rig_hab_day5_groupA.txt deleted file mode 100644 index 8afb3d6..0000000 --- a/data/stimulus_orderings/rig_hab_day5_groupA.txt +++ /dev/null @@ -1,240 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 diff --git a/data/stimulus_orderings/rig_hab_day5_groupB.txt b/data/stimulus_orderings/rig_hab_day5_groupB.txt deleted file mode 100644 index 13b2adc..0000000 --- a/data/stimulus_orderings/rig_hab_day5_groupB.txt +++ /dev/null @@ -1,240 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -1 -2 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 diff --git a/stimulus_loop.py b/stimulus_v2species.py similarity index 100% rename from stimulus_loop.py rename to stimulus_v2species.py From c94b72265a1485c427a950f97bc25f8af7913fc8 Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Sun, 4 May 2025 22:49:14 -0700 Subject: [PATCH 02/17] Remove old code --- README.md | 45 ++-------- stimulus_v2species.py | 204 +++++++++++++++++++----------------------- 2 files changed, 98 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index 1bf6e3a..da91b15 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# OpenScope visual loop: -A temporary repository for the OpenScope visual loop project +# OpenScope V2 species: +A temporary repository for the OpenScope V2 species project # Installation @@ -37,44 +37,9 @@ For debugging purposes please download shortened versions of the movie clips fro 1. Activate the environment:
`conda activate allen_stimulus` - 3. Run the stimulus_loop.py script (for group A input 0 and for group B input 1): -
`python stimulus_loop.py 0` + 3. Run the stimulus_v2species.py script: +
`python stimulus_v2species.py` -# Debugging mode -To run the script `stimulus_loop.py` in debugging mode, comment out line 50 and uncomment line 57. -
This will change the path of the movie clips from their full length version (i.e., 30 sec) to their shortened version (5 sec) with helpful labels. # Stimulus design -The experiment consists of two phases in which different sets of 30 sec long natural movies will be presented: - - Phase 1 consists of two natural movies (i.e., movie01 and movie02) and a constant gray screen (i.e., movie00). - - Phase 2 consists of 50 natural movies (i.e., movie03-movie52) and a constant gray screen (i.e., movie00). - -Animals are assigned into two groups (A and B), each presented with the movies in different order. -
In both groups, a total of 230 movies will be presented for a total time of 115 min: - 1. For group A: - - Phase 1: - - 50 presentations of movie01 (25 min) - - 10 presentations of movie00 (5 min) - - 50 alternations between movie01 and movie02 (25 min) - - 10 presentations of movie00 (5 min) - - - Phase 2: - - 20 presentations of movie03 (10 min) - - 10 presentations of movie00 (5 min) - - 50 sequential presentations of movie03 to movie52 (25 min) - - 10 presentations of movie00 (5 min) - - 20 alternations between movie03 and movie52 (10 min) - - 2. For group B: - - Phase 1: - - 50 alternations between movie01 and movie02 (25 min) - - 10 presentations of movie00 (5 min) - - 50 presentations of movie01 (25 min) - - 10 presentations of movie00 (5 min) - - - Phase 2: - - 20 presentations of movie03 (10 min) - - 10 presentations of movie00 (5 min) - - 50 presentations of movie52 (25 min) - - 10 presentations of movie00 (5 min) - - 20 alternations between movie03 and movie52 (10 min) +The experiment consists of ... \ No newline at end of file diff --git a/stimulus_v2species.py b/stimulus_v2species.py index 8e9997c..85b7487 100644 --- a/stimulus_v2species.py +++ b/stimulus_v2species.py @@ -1,95 +1,22 @@ # -*- coding: utf-8 -*- # Stimulus design # -# The experiment consists of two phases in which different sets of 30 sec long natural movies will be presented: -# - Phase 1 consists of two natural movies (i.e., movie01 and movie02) and a constant gray screen (i.e., movie00). -# - Phase 2 consists of 50 natural movies (i.e., movie03-movie52) and a constant gray screen (i.e., movie00). -# -# Animals are assigned into two groups (A and B), each presented with the movies in different order. -# In both groups, a total of 230 movies will be presented for a total time of 115 min: -# 1. For group A: -# - Phase 1: -# - 50 presentations of movie01 (25 min) -# - 10 presentations of movie00 (5 min) -# - 50 alternations between movie01 and movie02 (25 min) -# - 10 presentations of movie00 (5 min) -# -# - Phase 2: -# - 20 presentations of movie03 (10 min) -# - 10 presentations of movie00 (5 min) -# - 50 sequential presentations of movie03 to movie52 (25 min) -# - 10 presentations of movie00 (5 min) -# - 20 alternations between movie03 and movie52 (10 min) -# -# 2. For group B: -# - Phase 1: -# - 50 alternations between movie01 and movie02 (25 min) -# - 10 presentations of movie00 (5 min) -# - 50 presentations of movie01 (25 min) -# - 10 presentations of movie00 (5 min) -# -# - Phase 2: -# - 20 presentations of movie03 (10 min) -# - 10 presentations of movie00 (5 min) -# - 50 presentations of movie52 (25 min) -# - 10 presentations of movie00 (5 min) -# - 20 alternations between movie03 and movie52 (10 min) +# Please write here any comments or notes about the stimulus design +# that are relevant for the experiment. import psychopy.visual import camstim import argparse import numpy as np -from psychopy import monitors -from camstim import SweepStim, Foraging, Window, Warp, MovieStim +from psychopy import monitors, visual +from camstim import SweepStim, Foraging, Window, Warp import glob import logging import yaml import os +from camstim.sweepstim import Stimulus -def make_movie_stimulus(movie_paths, order, window): - """Generate a Stimulus that plays a series of movie clips in a specified order.""" - - # Convert the order into a list of display sequence tuples. There should be one display sequence list per movie - # clip. Each display sequence list contains a set of tuples of the form (start_second, stop_second). Theses tuples - # define the start and stop time for when to play the clip and are determined by the order vector - movie_length = 30 # in seconds - frame_rate = 30.0 - - all_starts = np.arange(0, movie_length * len(order), movie_length).astype(float) - display_sequences = [] - for i in np.unique(order): - display_sequences.append(list(zip(all_starts[order == i], all_starts[order == i] + movie_length))) - - # Load each movie clip into its own MovieStim object. The display sequence will be set later so the clips play - # in the correct order. - stims = [] - unique_movies = np.unique(order) - num_movies = len(unique_movies) - for i in range(num_movies): - current_movie_id = unique_movies[i] - # If the order index is less than the number of movie clips, load the movie clip. - if i < len(movie_paths): - - # The movie clips should be 30 seconds long and should be played at 30 fps. - s = MovieStim(movie_path=movie_paths[current_movie_id], - window=window, - frame_length=1.0 / frame_rate, - size=(1920, 1080), - start_time=0.0, - stop_time=None, - flip_v=True, runs=len(display_sequences[i])) - s.set_display_sequence(display_sequences[i]) - - else: - raise ValueError("Order index is greater than the number of movie clips.") - - stims.append(s) - - last_time = all_starts[-1] + movie_length - - return stims, last_time - -def create_receptive_field_mapping(number_runs = 15): +def create_receptive_field_mapping(window, number_runs = 15): x = np.arange(-40,45,10) y = np.arange(-40,45,10) position = [] @@ -109,7 +36,7 @@ def create_receptive_field_mapping(number_runs = 15): 'Contrast': ([0.8], 4), 'TF': ([4.0], 1), 'SF': ([0.08], 2), - 'Ori': ([0,45,90], 3), + 'Ori': ([0,45,90, ], 3), }, sweep_length=0.25, start_time=0.0, @@ -119,10 +46,38 @@ def create_receptive_field_mapping(number_runs = 15): shuffle=True, save_sweep_table=True, ) - stimulus.stim_path = r"C:\\not_a_stim_script\\create_receptive_field_mapping.stim" + stimulus.stim_path = r"C:\\not_a_stim_script\\receptive_field_block.stim" return stimulus +def create_gratingStim(window, number_runs = 15): + stimulus_grating = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([4.0], 1), + 'SF': ([0.08], 2), + 'Ori': (range(0, 360, 45), 3), + }, + sweep_length=1, + start_time=0.0, + blank_length=0.5, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_grating.stim_path = r"C:\\not_a_stim_script\\drifting_gratings_field_block.stim" + + return stimulus_grating + if __name__ == "__main__": parser = argparse.ArgumentParser("mtrain") @@ -140,49 +95,76 @@ def create_receptive_field_mapping(number_runs = 15): json_params = yaml.load(f) logging.info("Loaded json parameters from mtrain") # end of mtrain part - - data_folder = json_params.get('data_folder', os.path.abspath("data")) # An integer representing the experiment group. Defaults to test group (value of 2). - group = json_params.get('stimulus_orderings', 'movie_order_groupTEST') - - opto_disabled = json_params.get('disable_opto', True) - dev_mode = json_params.get("dev_mode", True) - - # Paths to the movie clip files to load. - movie_clip_files = glob.glob(os.path.join(data_folder, 'full_movies','*.npy')) - - # Load the movie clip order for each experimental group as provided by the Ziv lab: - order = (np.loadtxt(os.path.join(data_folder, 'stimulus_orderings', group+".txt")).astype(int)) + dev_mode = json_params.get('dev_mode', True) + num_reps = json_params.get('num_reps', 1) + inter_block_interval = json_params.get('inter_block_interval', 10) dist = 15.0 wid = 52.0 - # create a monitor if dev_mode: - monitor = monitors.Monitor('testMonitor', distance=dist, width=wid) + my_monitor = monitors.Monitor(name='Test') + my_monitor.setSizePix((800,600)) + my_monitor.setWidth(wid) + my_monitor.setDistance(dist) + my_monitor.saveMon() + win = Window(size=[800,600], # [1024,768], + fullscr=True, + screen=0, + monitor= my_monitor, + warp=Warp.Spherical, + color= "gray", + units = 'deg' + ) + else: + win = Window( + fullscr=True, + screen=0, + monitor='Gamma1.Luminance50', + warp=Warp.Spherical, + ) + + + nb_runs_ephys_rf = 12 + nb_run_gratings = 22 + ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) + drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) + + All_stim = [] + + # Add RF code from ephys + current_time = 0 + if num_reps == 1: + length_rf_seconds = 10 + else: + length_rf_seconds = 60*nb_runs_ephys_rf + + ephys_rf_stim.set_display_sequence([(current_time, current_time+length_rf_seconds)]) + + All_stim.append(ephys_rf_stim) + print("length_rf_seconds: ",length_rf_seconds) + + # drifting_grating_stim + if num_reps == 1: + length_drifting_grating_seconds = 10 else: - monitor = "Gamma1.Luminance50" + length_drifting_grating_seconds = 8*1.5*nb_run_gratings - # Create display window - window = Window(fullscr=True, # Will return an error due to default size. Ignore. - monitor=monitor, # Will be set to a gamma calibrated profile by MPE - screen=0, - warp=Warp.Spherical - ) + current_time = current_time+length_rf_seconds+inter_block_interval - stims, last_time = make_movie_stimulus(movie_clip_files, order, window) + drifting_grating_stim.set_display_sequence([(current_time, current_time+length_drifting_grating_seconds)]) - # We create the receptive field mapping stimulus - gabors_rf_20 = create_receptive_field_mapping(8) - gabors_rf_20_ds = [(last_time, last_time+640)] - gabors_rf_20.set_display_sequence(gabors_rf_20_ds) - stims.append(gabors_rf_20) + All_stim.append(drifting_grating_stim) + print("length_drifting_grating_seconds: ",length_drifting_grating_seconds) + pre_blank = 0 + post_blank = 0 ss = SweepStim(window, - stimuli=stims, - # pre_blank_sec=1, - # post_blank_sec=1, + stimuli=All_stim, + pre_blank_sec= pre_blank, + post_blank_sec= post_blank, params={}, ) From 7dda7c9476b2a0509b4c419ed1973ce51696031d Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Sun, 4 May 2025 22:52:30 -0700 Subject: [PATCH 03/17] Fix code --- stimulus_v2species.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stimulus_v2species.py b/stimulus_v2species.py index 85b7487..92a7bf5 100644 --- a/stimulus_v2species.py +++ b/stimulus_v2species.py @@ -111,7 +111,7 @@ def create_gratingStim(window, number_runs = 15): my_monitor.setDistance(dist) my_monitor.saveMon() win = Window(size=[800,600], # [1024,768], - fullscr=True, + fullscr=False, screen=0, monitor= my_monitor, warp=Warp.Spherical, @@ -161,7 +161,7 @@ def create_gratingStim(window, number_runs = 15): pre_blank = 0 post_blank = 0 - ss = SweepStim(window, + ss = SweepStim(win, stimuli=All_stim, pre_blank_sec= pre_blank, post_blank_sec= post_blank, @@ -169,7 +169,7 @@ def create_gratingStim(window, number_runs = 15): ) # add in foraging so we can track wheel, potentially give rewards, etc - f = Foraging(window = window, + f = Foraging(window = win, auto_update = False, params= {} ) From 21e5d1dd622df2258d97116d3ea8a4dfabbd5712 Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Mon, 5 May 2025 15:42:37 -0700 Subject: [PATCH 04/17] Update README.md --- README.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/README.md b/README.md index da91b15..0819b20 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,6 @@ A temporary repository for the OpenScope V2 species project 5. Download required video clips from movie_clips.zip and extract into the data directory. -# Input files -The software requires two sets of input files. There should be a set of text files present under `data/stimulus_orderings` that indicate the display order of video clips for different phases of the experiment. - -In addition, there should be a set of video clips (stored as raw .npy files). -
These clips must be downloaded and extracted into the data folder from [full_movies.zip](https://weizmannacil-my.sharepoint.com/:u:/g/personal/daniel_deitch_weizmann_ac_il/EbetUfh76FtBtDkpqd-7gAEB43WxjSCKutxW8sJtvIfCiA?e=LPY5ND) and stored in the path `data/full_movies`. - -For debugging purposes please download shortened versions of the movie clips from [short_movies.zip](https://weizmannacil-my.sharepoint.com/:u:/g/personal/daniel_deitch_weizmann_ac_il/EZzpjTqcXG9Bn4Xe-u9pgXIBHzLbIWfmtd8xKI4lvwIwvQ?e=uLVxT0) and extracted into the data folder and store them in the path `data/short_movies`. - # Running the scripts 1. Activate the environment:
`conda activate allen_stimulus` @@ -42,4 +34,4 @@ For debugging purposes please download shortened versions of the movie clips fro # Stimulus design -The experiment consists of ... \ No newline at end of file +The experiment consists of ... From a49e835a2a0666a519f353afb28f4de30fb9bf75 Mon Sep 17 00:00:00 2001 From: Madineh Sedigh-Sarvestani Date: Mon, 19 May 2025 10:14:48 -0400 Subject: [PATCH 05/17] work in progress: --- stimulus_v2species.py | 54 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/stimulus_v2species.py b/stimulus_v2species.py index 92a7bf5..666fb9a 100644 --- a/stimulus_v2species.py +++ b/stimulus_v2species.py @@ -15,8 +15,8 @@ import os from camstim.sweepstim import Stimulus - -def create_receptive_field_mapping(window, number_runs = 15): +## This is the RF mapping stim +def create_receptive_field_mapping(window, number_runs = 5): x = np.arange(-40,45,10) y = np.arange(-40,45,10) position = [] @@ -50,7 +50,8 @@ def create_receptive_field_mapping(window, number_runs = 15): return stimulus -def create_gratingStim(window, number_runs = 15): +## This is drifting gratings +def create_gratingStim(window, number_runs = 5): stimulus_grating = Stimulus(visual.GratingStim( window, pos=(0, 0), @@ -78,6 +79,35 @@ def create_gratingStim(window, number_runs = 15): return stimulus_grating +## This is drifting graints +def create_full_field_flash(window, number_runs = 5): + stimulus_flash = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.0, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([4.0], 1), + 'SF': ([0.00], 2), + 'Ori': (range(0, 360, 180), 3), + }, + sweep_length=1, + start_time=0.0, + blank_length=0.5, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_flash.stim_path = r"C:\\not_a_stim_script\\flash_field_block.stim" + + return stimulus_flash + if __name__ == "__main__": parser = argparse.ArgumentParser("mtrain") @@ -129,8 +159,10 @@ def create_gratingStim(window, number_runs = 15): nb_runs_ephys_rf = 12 nb_run_gratings = 22 + nb_run_flash = 10 ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) + flash_stim = create_full_field_flash(win, number_runs=nb_run_flash) All_stim = [] @@ -159,6 +191,22 @@ def create_gratingStim(window, number_runs = 15): All_stim.append(drifting_grating_stim) print("length_drifting_grating_seconds: ",length_drifting_grating_seconds) + + # flash_stim + if num_reps == 1: + length_flash_seconds = 10 + else: + length_flash_seconds = 8*1.5*nb_run_flash + + current_time = current_time+length_flash_seconds+inter_block_interval + + flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) + + All_stim.append(flash_stim) + print("length_flash_seconds: ",length_flash_seconds) + + + pre_blank = 0 post_blank = 0 ss = SweepStim(win, From 665a243a6189974bced4ff39956f5f01544e0143 Mon Sep 17 00:00:00 2001 From: Madineh Sedigh-Sarvestani Date: Mon, 19 May 2025 12:18:34 -0400 Subject: [PATCH 06/17] created separate scripts for ephys and 2p. Number of runs is currently hard-coded and not externalized. num_reps is being read from .json file and defaults to 1 if file doesn't exist --- README.md | 14 +- images/stim_overview.png | Bin 0 -> 128476 bytes stimulus_v2species_2p.py | 232 +++++++++++++++++++++++++++++ stimulus_v2species_ephys.py | 285 ++++++++++++++++++++++++++++++++++++ 4 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 images/stim_overview.png create mode 100644 stimulus_v2species_2p.py create mode 100644 stimulus_v2species_ephys.py diff --git a/README.md b/README.md index 0819b20..c2895d7 100644 --- a/README.md +++ b/README.md @@ -34,4 +34,16 @@ A temporary repository for the OpenScope V2 species project # Stimulus design -The experiment consists of ... +The experiment consists of both ephys (Neuropixels) and calcium imaging (2P imaging with Gcamp viral vectors) recordings, in separate mice. Neuropixels recordings consist of 4 simultaneous probes across V1, covering the extent of the visual field map. 2P imaging recordings consist of 12 FOVs, imaged 4 at a time, also across the span of the visual field of V1. + +The stimuli used for Neuropixels and 2P are similar, with the exception of full-field flashes, which rquire temporally precise responses and are therefor only used with Neuropixels recordings. Warping is applied to the monitor. + +Gabor Patches: Small drifting grating patches tiling the monitor, used to map receptive fields. Each patch is 20 degrees, with 10 degree overlap. TF and SF are fixed but orientation varies (0, 45,90). + +Drifting Gratings: Typical drifting gratings, with 5 TF (1.0, 2.0, 4.0, 8.0, 15.0) and 5 SF (0.02, 0.04, 0.08, 0.16, 0.32), and 4 orientations (0,45,90,135). + +Full-field flash: Full-field bright or dark flashes. + +Due to technical constraints, the duration of 2P recordings is more limited than Neuropixels recordings, so the number of repeated trials varies by stimulus modality. + +![Overview of 2p and ephys stimuli](images/stim_overview.png) \ No newline at end of file diff --git a/images/stim_overview.png b/images/stim_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..997a5f5353a9f17d2ed987f438a2860b80e1785c GIT binary patch literal 128476 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�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 JF6*2UngDbHE8hSB literal 0 HcmV?d00001 diff --git a/stimulus_v2species_2p.py b/stimulus_v2species_2p.py new file mode 100644 index 0000000..00eb259 --- /dev/null +++ b/stimulus_v2species_2p.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +# Stimulus design +# +# Please write here any comments or notes about the stimulus design +# that are relevant for the experiment. +import psychopy.visual +import camstim +import argparse +import numpy as np +from psychopy import monitors, visual +from camstim import SweepStim, Foraging, Window, Warp +import glob +import logging +import yaml +import os +from camstim.sweepstim import Stimulus +from itertools import product + +## This is the RF mapping stim +def create_receptive_field_mapping(window, number_runs = 5): + x = np.arange(-40,45,10) + y = np.arange(-40,45,10) + position = [] + for i in x: + for j in y: + position.append([i,j]) + + stimulus = Stimulus(visual.GratingStim(window, + units='deg', + size=20, + mask="circle", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Pos':(position, 0), + 'Contrast': ([0.8], 4), + 'TF': ([4.0], 1), + 'SF': ([0.08], 2), + 'Ori': ([0,45,90, ], 3), + }, + sweep_length=0.25, + start_time=0.0, + blank_length=0.0, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus.stim_path = r"C:\\not_a_stim_script\\receptive_field_block.stim" + + return stimulus + +## This is drifting gratings +def create_gratingStim(window, number_runs = 5): + stimulus_grating = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([1.0, 2.0, 4.0, 8.0, 15.0], 1), + 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 2), + 'Ori': (range(0, 360, 90), 3), + }, + sweep_length=1, + start_time=0.0, + blank_length=1.25, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_grating.stim_path = r"C:\\not_a_stim_script\\drifting_gratings_field_block.stim" + + return stimulus_grating + +#this gets total conditions for stimuli defined above +def count_total_conditions(stimulus): + """ + Given a camstim Stimulus object, return the number of unique parameter combinations. + """ + sweep_params = stimulus.sweep_params + param_values = [] + + for key, (values, _) in sweep_params.items(): + # Ensure range is converted to list + if type(values).__name__ == 'range': + values = list(values) + param_values.append(values) + + total_conditions = len(list(product(*param_values))) + return total_conditions + + +#this gets trial duration for stimuli defined above +def get_trial_duration_seconds(stimulus): + """ + Returns the duration of one full trial (sweep + blank) in seconds. + """ + sweep_length = getattr(stimulus, 'sweep_length', 0) + blank_length = getattr(stimulus, 'blank_length', 0) + return sweep_length + blank_length + +############################################################################## main code block + +if __name__ == "__main__": + parser = argparse.ArgumentParser("mtrain") + parser.add_argument("json_path", nargs="?", type=str, default="") + + args, _ = parser.parse_known_args() # <- this ensures that we ignore other arguments that might be needed by camstim + + # print args + if args.json_path == "": + logging.warning("No json path provided, using default parameters. THIS IS NOT THE EXPECTED BEHAVIOR FOR PRODUCTION RUNS") + json_params = {} + else: + with open(args.json_path, 'r') as f: + # we use the yaml package here because the json package loads as unicode, which prevents using the keys as parameters later + json_params = yaml.load(f) + logging.info("Loaded json parameters from mtrain") + # end of mtrain part + + # An integer representing the experiment group. Defaults to test group (value of 2). + dev_mode = json_params.get('dev_mode', True) + num_reps = json_params.get('num_reps', 3) + print("num_reps is: ",num_reps) + inter_block_interval = json_params.get('inter_block_interval', 10) + + dist = 15.0 + wid = 52.0 + + if dev_mode: + my_monitor = monitors.Monitor(name='Test') + my_monitor.setSizePix((800,600)) + my_monitor.setWidth(wid) + my_monitor.setDistance(dist) + my_monitor.saveMon() + win = Window(size=[800,600], # [1024,768], + fullscr=False, + screen=0, + monitor= my_monitor, + warp=Warp.Spherical, + color= "gray", + units = 'deg' + ) + else: + win = Window( + fullscr=True, + screen=0, + monitor='Gamma1.Luminance50', + warp=Warp.Spherical, + ) + + + #define number of trials for each stim type + nb_runs_ephys_rf = 10 + nb_run_gratings = 15 + + + ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) + drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) + + print( + "ephys_rf, total conditions: ", count_total_conditions(ephys_rf_stim), + ", and trial duration: ", get_trial_duration_seconds(ephys_rf_stim)) + + print( + "gratings, total conditions: ", count_total_conditions(drifting_grating_stim), + ", and trial duration: ", get_trial_duration_seconds(drifting_grating_stim)) + + All_stim = [] + + # Add RF code from ephys + current_time = 0 + if num_reps == 1: + length_rf_seconds = 10 #for testing purposes + else: + length_rf_seconds = ( + count_total_conditions(ephys_rf_stim) + *get_trial_duration_seconds(ephys_rf_stim) + *nb_runs_ephys_rf + ) + + ephys_rf_stim.set_display_sequence([(current_time, current_time+length_rf_seconds)]) + + All_stim.append(ephys_rf_stim) + print("length_rf_minutes: ",length_rf_seconds/60) + + # drifting_grating_stim + if num_reps == 1: + length_drifting_grating_seconds = 10 + else: + length_drifting_grating_seconds = ( + count_total_conditions(drifting_grating_stim) + *get_trial_duration_seconds(drifting_grating_stim) + *nb_run_gratings + ) + + current_time = current_time+length_rf_seconds+inter_block_interval + + drifting_grating_stim.set_display_sequence([(current_time, current_time+length_drifting_grating_seconds)]) + + All_stim.append(drifting_grating_stim) + print("length_drifting_grating_minutes: ",length_drifting_grating_seconds/60) + + + + + pre_blank = 0 + post_blank = 0 + ss = SweepStim(win, + stimuli=All_stim, + pre_blank_sec= pre_blank, + post_blank_sec= post_blank, + params={}, + ) + + # add in foraging so we can track wheel, potentially give rewards, etc + f = Foraging(window = win, + auto_update = False, + params= {} + ) + + ss.add_item(f, "foraging") + + ss.run() diff --git a/stimulus_v2species_ephys.py b/stimulus_v2species_ephys.py new file mode 100644 index 0000000..b6f542d --- /dev/null +++ b/stimulus_v2species_ephys.py @@ -0,0 +1,285 @@ +# -*- coding: utf-8 -*- +# Stimulus design +# +# Please write here any comments or notes about the stimulus design +# that are relevant for the experiment. +import psychopy.visual +import camstim +import argparse +import numpy as np +from psychopy import monitors, visual +from camstim import SweepStim, Foraging, Window, Warp +import glob +import logging +import yaml +import os +from camstim.sweepstim import Stimulus +from itertools import product + +## This is the RF mapping stim +def create_receptive_field_mapping(window, number_runs = 5): + x = np.arange(-40,45,10) + y = np.arange(-40,45,10) + position = [] + for i in x: + for j in y: + position.append([i,j]) + + stimulus = Stimulus(visual.GratingStim(window, + units='deg', + size=20, + mask="circle", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Pos':(position, 0), + 'Contrast': ([0.8], 4), + 'TF': ([4.0], 1), + 'SF': ([0.08], 2), + 'Ori': ([0,45,90,], 3), + }, + sweep_length=0.25, + start_time=0.0, + blank_length=0.0, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus.stim_path = r"C:\\not_a_stim_script\\receptive_field_block.stim" + + return stimulus + +## This is drifting gratings +def create_gratingStim(window, number_runs = 5): + stimulus_grating = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([1.0, 2.0, 4.0, 8.0, 15.0], 1), + 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 2), + 'Ori': (range(0, 360, 90), 3), + }, + sweep_length=1, + start_time=0.0, + blank_length=1.25, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_grating.stim_path = r"C:\\not_a_stim_script\\drifting_gratings_field_block.stim" + + return stimulus_grating + +## This is drifting gratings +def create_full_field_flash(window, number_runs = 5): + stimulus_flash = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.0, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([4.0], 1), + 'SF': ([0.00], 2), + 'Ori': (range(0, 180, 45), 3), + }, + sweep_length=0.25, + start_time=0.0, + blank_length=2, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_flash.stim_path = r"C:\\not_a_stim_script\\flash_field_block.stim" + + return stimulus_flash + + +#this gets total conditions for stimuli defined above +def count_total_conditions(stimulus): + """ + Given a camstim Stimulus object, return the number of unique parameter combinations. + """ + sweep_params = stimulus.sweep_params + param_values = [] + + for key, (values, _) in sweep_params.items(): + # Ensure range is converted to list + if type(values).__name__ == 'range': + values = list(values) + param_values.append(values) + + total_conditions = len(list(product(*param_values))) + return total_conditions + + +#this gets trial duration for stimuli defined above +def get_trial_duration_seconds(stimulus): + """ + Returns the duration of one full trial (sweep + blank) in seconds. + """ + sweep_length = getattr(stimulus, 'sweep_length', 0) + blank_length = getattr(stimulus, 'blank_length', 0) + return sweep_length + blank_length + + ############################################################################## main code block + + +if __name__ == "__main__": + parser = argparse.ArgumentParser("mtrain") + parser.add_argument("json_path", nargs="?", type=str, default="") + + args, _ = parser.parse_known_args() # <- this ensures that we ignore other arguments that might be needed by camstim + + # print args + if args.json_path == "": + logging.warning("No json path provided, using default parameters. THIS IS NOT THE EXPECTED BEHAVIOR FOR PRODUCTION RUNS") + json_params = {} + else: + with open(args.json_path, 'r') as f: + # we use the yaml package here because the json package loads as unicode, which prevents using the keys as parameters later + json_params = yaml.load(f) + logging.info("Loaded json parameters from mtrain") + # end of mtrain part + + # An integer representing the experiment group. Defaults to test group (value of 2). + dev_mode = json_params.get('dev_mode', True) + num_reps = json_params.get('num_reps', 1) + print("num_reps is: ",num_reps) + + inter_block_interval = json_params.get('inter_block_interval', 10) + + dist = 15.0 + wid = 52.0 + + if dev_mode: + my_monitor = monitors.Monitor(name='Test') + my_monitor.setSizePix((800,600)) + my_monitor.setWidth(wid) + my_monitor.setDistance(dist) + my_monitor.saveMon() + win = Window(size=[800,600], # [1024,768], + fullscr=False, + screen=0, + monitor= my_monitor, + warp=Warp.Spherical, + color= "gray", + units = 'deg' + ) + else: + win = Window( + fullscr=True, + screen=0, + monitor='Gamma1.Luminance50', + warp=Warp.Spherical, + ) + + + nb_runs_ephys_rf = 20 + nb_run_gratings = 15 + nb_run_flash = 150 + + ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) + drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) + flash_stim = create_full_field_flash(win, number_runs=nb_run_flash) + + print( + "ephys_rf, total conditions: ", count_total_conditions(ephys_rf_stim), + ", and trial duration: ", get_trial_duration_seconds(ephys_rf_stim)) + + print( + "gratings, total conditions: ", count_total_conditions(drifting_grating_stim), + ", and trial duration: ", get_trial_duration_seconds(drifting_grating_stim)) + + print( + "FLASH, total conditions: ", count_total_conditions(flash_stim), + ", and trial duration: ", get_trial_duration_seconds(flash_stim)) + + All_stim = [] + + # Add RF code from ephys + current_time = 0 + if num_reps == 1: + length_rf_seconds = 10 #for testing purposes + else: + length_rf_seconds = ( + count_total_conditions(ephys_rf_stim) + *get_trial_duration_seconds(ephys_rf_stim) + *nb_runs_ephys_rf + ) + + ephys_rf_stim.set_display_sequence([(current_time, current_time+length_rf_seconds)]) + + All_stim.append(ephys_rf_stim) + print("length_rf_minutes: ",length_rf_seconds/60) + + # drifting_grating_stim + if num_reps == 1: + length_drifting_grating_seconds = 10 + else: + length_drifting_grating_seconds = ( + count_total_conditions(drifting_grating_stim) + *get_trial_duration_seconds(drifting_grating_stim) + *nb_run_gratings + ) + + current_time = current_time+length_rf_seconds+inter_block_interval + + drifting_grating_stim.set_display_sequence([(current_time, current_time+length_drifting_grating_seconds)]) + + All_stim.append(drifting_grating_stim) + print("length_drifting_grating_minutes: ",length_drifting_grating_seconds/60) + + + # flash_stim + if num_reps == 1: + length_flash_seconds = 10 + else: + length_flash_seconds = ( + count_total_conditions(flash_stim) + *get_trial_duration_seconds(flash_stim) + *nb_run_flash + ) + + current_time = current_time+length_flash_seconds+inter_block_interval + + flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) + + All_stim.append(flash_stim) + print("length_flash_minutes: ",length_flash_seconds/60) + + + + pre_blank = 0 + post_blank = 0 + ss = SweepStim(win, + stimuli=All_stim, + pre_blank_sec= pre_blank, + post_blank_sec= post_blank, + params={}, + ) + + # add in foraging so we can track wheel, potentially give rewards, etc + f = Foraging(window = win, + auto_update = False, + params= {} + ) + + ss.add_item(f, "foraging") + + ss.run() From 49bdbf611bb3baa579714432a28d63e595873fd6 Mon Sep 17 00:00:00 2001 From: Madineh Sedigh-Sarvestani Date: Mon, 19 May 2025 12:23:11 -0400 Subject: [PATCH 07/17] changing num_reps in 2p back to default --- stimulus_v2species_2p.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stimulus_v2species_2p.py b/stimulus_v2species_2p.py index 00eb259..4fc19ad 100644 --- a/stimulus_v2species_2p.py +++ b/stimulus_v2species_2p.py @@ -66,7 +66,7 @@ def create_gratingStim(window, number_runs = 5): 'Contrast': ([0.8], 0), 'TF': ([1.0, 2.0, 4.0, 8.0, 15.0], 1), 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 2), - 'Ori': (range(0, 360, 90), 3), + 'Ori': (range(0, 180, 45), 3), }, sweep_length=1, start_time=0.0, @@ -128,7 +128,7 @@ def get_trial_duration_seconds(stimulus): # An integer representing the experiment group. Defaults to test group (value of 2). dev_mode = json_params.get('dev_mode', True) - num_reps = json_params.get('num_reps', 3) + num_reps = json_params.get('num_reps', 1) print("num_reps is: ",num_reps) inter_block_interval = json_params.get('inter_block_interval', 10) From a2033bc8e417e691f5b32292db78cb86b8b1a388 Mon Sep 17 00:00:00 2001 From: Madineh Sedigh-Sarvestani Date: Mon, 19 May 2025 12:23:57 -0400 Subject: [PATCH 08/17] removing extra files --- stimulus_v2species.py | 227 ------------------------------------------ 1 file changed, 227 deletions(-) delete mode 100644 stimulus_v2species.py diff --git a/stimulus_v2species.py b/stimulus_v2species.py deleted file mode 100644 index 666fb9a..0000000 --- a/stimulus_v2species.py +++ /dev/null @@ -1,227 +0,0 @@ -# -*- coding: utf-8 -*- -# Stimulus design -# -# Please write here any comments or notes about the stimulus design -# that are relevant for the experiment. -import psychopy.visual -import camstim -import argparse -import numpy as np -from psychopy import monitors, visual -from camstim import SweepStim, Foraging, Window, Warp -import glob -import logging -import yaml -import os -from camstim.sweepstim import Stimulus - -## This is the RF mapping stim -def create_receptive_field_mapping(window, number_runs = 5): - x = np.arange(-40,45,10) - y = np.arange(-40,45,10) - position = [] - for i in x: - for j in y: - position.append([i,j]) - - stimulus = Stimulus(visual.GratingStim(window, - units='deg', - size=20, - mask="circle", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Pos':(position, 0), - 'Contrast': ([0.8], 4), - 'TF': ([4.0], 1), - 'SF': ([0.08], 2), - 'Ori': ([0,45,90, ], 3), - }, - sweep_length=0.25, - start_time=0.0, - blank_length=0.0, - blank_sweeps=0, - runs=number_runs, - shuffle=True, - save_sweep_table=True, - ) - stimulus.stim_path = r"C:\\not_a_stim_script\\receptive_field_block.stim" - - return stimulus - -## This is drifting gratings -def create_gratingStim(window, number_runs = 5): - stimulus_grating = Stimulus(visual.GratingStim( - window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.1, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([4.0], 1), - 'SF': ([0.08], 2), - 'Ori': (range(0, 360, 45), 3), - }, - sweep_length=1, - start_time=0.0, - blank_length=0.5, - blank_sweeps=0, - runs=number_runs, - shuffle=True, - save_sweep_table=True, - ) - stimulus_grating.stim_path = r"C:\\not_a_stim_script\\drifting_gratings_field_block.stim" - - return stimulus_grating - -## This is drifting graints -def create_full_field_flash(window, number_runs = 5): - stimulus_flash = Stimulus(visual.GratingStim( - window, - pos=(0, 0), - units='deg', - size=(250, 250), - mask="None", - texRes=256, - sf=0.0, - ), - sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([4.0], 1), - 'SF': ([0.00], 2), - 'Ori': (range(0, 360, 180), 3), - }, - sweep_length=1, - start_time=0.0, - blank_length=0.5, - blank_sweeps=0, - runs=number_runs, - shuffle=True, - save_sweep_table=True, - ) - stimulus_flash.stim_path = r"C:\\not_a_stim_script\\flash_field_block.stim" - - return stimulus_flash - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("mtrain") - parser.add_argument("json_path", nargs="?", type=str, default="") - - args, _ = parser.parse_known_args() # <- this ensures that we ignore other arguments that might be needed by camstim - - # print args - if args.json_path == "": - logging.warning("No json path provided, using default parameters. THIS IS NOT THE EXPECTED BEHAVIOR FOR PRODUCTION RUNS") - json_params = {} - else: - with open(args.json_path, 'r') as f: - # we use the yaml package here because the json package loads as unicode, which prevents using the keys as parameters later - json_params = yaml.load(f) - logging.info("Loaded json parameters from mtrain") - # end of mtrain part - - # An integer representing the experiment group. Defaults to test group (value of 2). - dev_mode = json_params.get('dev_mode', True) - num_reps = json_params.get('num_reps', 1) - inter_block_interval = json_params.get('inter_block_interval', 10) - - dist = 15.0 - wid = 52.0 - - if dev_mode: - my_monitor = monitors.Monitor(name='Test') - my_monitor.setSizePix((800,600)) - my_monitor.setWidth(wid) - my_monitor.setDistance(dist) - my_monitor.saveMon() - win = Window(size=[800,600], # [1024,768], - fullscr=False, - screen=0, - monitor= my_monitor, - warp=Warp.Spherical, - color= "gray", - units = 'deg' - ) - else: - win = Window( - fullscr=True, - screen=0, - monitor='Gamma1.Luminance50', - warp=Warp.Spherical, - ) - - - nb_runs_ephys_rf = 12 - nb_run_gratings = 22 - nb_run_flash = 10 - ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) - drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) - flash_stim = create_full_field_flash(win, number_runs=nb_run_flash) - - All_stim = [] - - # Add RF code from ephys - current_time = 0 - if num_reps == 1: - length_rf_seconds = 10 - else: - length_rf_seconds = 60*nb_runs_ephys_rf - - ephys_rf_stim.set_display_sequence([(current_time, current_time+length_rf_seconds)]) - - All_stim.append(ephys_rf_stim) - print("length_rf_seconds: ",length_rf_seconds) - - # drifting_grating_stim - if num_reps == 1: - length_drifting_grating_seconds = 10 - else: - length_drifting_grating_seconds = 8*1.5*nb_run_gratings - - current_time = current_time+length_rf_seconds+inter_block_interval - - drifting_grating_stim.set_display_sequence([(current_time, current_time+length_drifting_grating_seconds)]) - - All_stim.append(drifting_grating_stim) - print("length_drifting_grating_seconds: ",length_drifting_grating_seconds) - - - # flash_stim - if num_reps == 1: - length_flash_seconds = 10 - else: - length_flash_seconds = 8*1.5*nb_run_flash - - current_time = current_time+length_flash_seconds+inter_block_interval - - flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) - - All_stim.append(flash_stim) - print("length_flash_seconds: ",length_flash_seconds) - - - - pre_blank = 0 - post_blank = 0 - ss = SweepStim(win, - stimuli=All_stim, - pre_blank_sec= pre_blank, - post_blank_sec= post_blank, - params={}, - ) - - # add in foraging so we can track wheel, potentially give rewards, etc - f = Foraging(window = win, - auto_update = False, - params= {} - ) - - ss.add_item(f, "foraging") - - ss.run() From 507c391a7ea7845a627751f8e81b46f0b9b13d0e Mon Sep 17 00:00:00 2001 From: Madineh Sedigh-Sarvestani Date: Mon, 19 May 2025 12:43:03 -0400 Subject: [PATCH 09/17] updating readme with bullet-list of parameters controlling stim duration --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c2895d7..a8bd928 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,11 @@ A temporary repository for the OpenScope V2 species project 1. Activate the environment:
`conda activate allen_stimulus` - 3. Run the stimulus_v2species.py script: -
`python stimulus_v2species.py` + 3. Run the stimulus_v2species_2p.py script for 2P stimuli: +
`python stimulus_v2species_2p.py` + + 4. Run the stimulus_v2species_2p.py script for ephys stimuli: +
`python stimulus_v2species_ephys.py` # Stimulus design @@ -46,4 +49,13 @@ Full-field flash: Full-field bright or dark flashes. Due to technical constraints, the duration of 2P recordings is more limited than Neuropixels recordings, so the number of repeated trials varies by stimulus modality. -![Overview of 2p and ephys stimuli](images/stim_overview.png) \ No newline at end of file +![Overview of 2p and ephys stimuli](images/stim_overview.png) + +For stimulus_v2species_2p.py, these parameters are currently hard-coded: + nb_runs_ephys_rf = 10 + nb_run_gratings = 15 + +For stimulus_v2species_ephys.py, these parameters are currently hard-coded: + nb_runs_ephys_rf = 20 + nb_run_gratings = 15 + nb_run_flash = 150 \ No newline at end of file From 087fab728958fc9fed377e0dc065e604b4d5fe9c Mon Sep 17 00:00:00 2001 From: Madineh Sarvestani Date: Mon, 19 May 2025 12:47:16 -0400 Subject: [PATCH 10/17] Update README.md editing readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a8bd928..2ebb046 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,10 @@ Due to technical constraints, the duration of 2P recordings is more limited than ![Overview of 2p and ephys stimuli](images/stim_overview.png) For stimulus_v2species_2p.py, these parameters are currently hard-coded: - nb_runs_ephys_rf = 10 - nb_run_gratings = 15 +* nb_runs_ephys_rf = 10 +* nb_run_gratings = 15 For stimulus_v2species_ephys.py, these parameters are currently hard-coded: - nb_runs_ephys_rf = 20 - nb_run_gratings = 15 - nb_run_flash = 150 \ No newline at end of file +* nb_runs_ephys_rf = 20 +* nb_run_gratings = 15 +* nb_run_flash = 150 From e11218dc363c7b41c0c6cdd23184e711d4d39b86 Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Wed, 4 Jun 2025 12:47:45 -0700 Subject: [PATCH 11/17] Add parameters passing --- stimulus_v2species_2p.py | 15 ++++----------- stimulus_v2species_ephys.py | 16 ++++++---------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/stimulus_v2species_2p.py b/stimulus_v2species_2p.py index 4fc19ad..10af5e1 100644 --- a/stimulus_v2species_2p.py +++ b/stimulus_v2species_2p.py @@ -131,6 +131,8 @@ def get_trial_duration_seconds(stimulus): num_reps = json_params.get('num_reps', 1) print("num_reps is: ",num_reps) inter_block_interval = json_params.get('inter_block_interval', 10) + nb_runs_ephys_rf = json_params.get('nb_runs_ephys_rf', 10) + nb_run_gratings = json_params.get('nb_run_gratings', 15) dist = 15.0 wid = 52.0 @@ -157,12 +159,6 @@ def get_trial_duration_seconds(stimulus): warp=Warp.Spherical, ) - - #define number of trials for each stim type - nb_runs_ephys_rf = 10 - nb_run_gratings = 15 - - ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) @@ -178,7 +174,7 @@ def get_trial_duration_seconds(stimulus): # Add RF code from ephys current_time = 0 - if num_reps == 1: + if dev_mode: length_rf_seconds = 10 #for testing purposes else: length_rf_seconds = ( @@ -193,7 +189,7 @@ def get_trial_duration_seconds(stimulus): print("length_rf_minutes: ",length_rf_seconds/60) # drifting_grating_stim - if num_reps == 1: + if dev_mode: length_drifting_grating_seconds = 10 else: length_drifting_grating_seconds = ( @@ -209,9 +205,6 @@ def get_trial_duration_seconds(stimulus): All_stim.append(drifting_grating_stim) print("length_drifting_grating_minutes: ",length_drifting_grating_seconds/60) - - - pre_blank = 0 post_blank = 0 ss = SweepStim(win, diff --git a/stimulus_v2species_ephys.py b/stimulus_v2species_ephys.py index b6f542d..ba3c5c2 100644 --- a/stimulus_v2species_ephys.py +++ b/stimulus_v2species_ephys.py @@ -159,10 +159,11 @@ def get_trial_duration_seconds(stimulus): # An integer representing the experiment group. Defaults to test group (value of 2). dev_mode = json_params.get('dev_mode', True) - num_reps = json_params.get('num_reps', 1) - print("num_reps is: ",num_reps) inter_block_interval = json_params.get('inter_block_interval', 10) + nb_runs_ephys_rf = json_params.get('nb_runs_ephys_rf', 20) + nb_run_gratings = json_params.get('nb_run_gratings', 15) + nb_run_flash = json_params.get('nb_run_flash', 150) dist = 15.0 wid = 52.0 @@ -189,11 +190,6 @@ def get_trial_duration_seconds(stimulus): warp=Warp.Spherical, ) - - nb_runs_ephys_rf = 20 - nb_run_gratings = 15 - nb_run_flash = 150 - ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) flash_stim = create_full_field_flash(win, number_runs=nb_run_flash) @@ -214,7 +210,7 @@ def get_trial_duration_seconds(stimulus): # Add RF code from ephys current_time = 0 - if num_reps == 1: + if dev_mode: length_rf_seconds = 10 #for testing purposes else: length_rf_seconds = ( @@ -229,7 +225,7 @@ def get_trial_duration_seconds(stimulus): print("length_rf_minutes: ",length_rf_seconds/60) # drifting_grating_stim - if num_reps == 1: + if dev_mode: length_drifting_grating_seconds = 10 else: length_drifting_grating_seconds = ( @@ -247,7 +243,7 @@ def get_trial_duration_seconds(stimulus): # flash_stim - if num_reps == 1: + if dev_mode: length_flash_seconds = 10 else: length_flash_seconds = ( From 47971ff802c270d9e90f7c4070a3a5acf380fcb8 Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Wed, 4 Jun 2025 12:53:00 -0700 Subject: [PATCH 12/17] Add post blank --- stimulus_v2species_ephys.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stimulus_v2species_ephys.py b/stimulus_v2species_ephys.py index ba3c5c2..0982d6a 100644 --- a/stimulus_v2species_ephys.py +++ b/stimulus_v2species_ephys.py @@ -164,6 +164,7 @@ def get_trial_duration_seconds(stimulus): nb_runs_ephys_rf = json_params.get('nb_runs_ephys_rf', 20) nb_run_gratings = json_params.get('nb_run_gratings', 15) nb_run_flash = json_params.get('nb_run_flash', 150) + post_blank = json_params.get('post_blank', 0) dist = 15.0 wid = 52.0 @@ -262,7 +263,7 @@ def get_trial_duration_seconds(stimulus): pre_blank = 0 - post_blank = 0 + post_blank = post_blank ss = SweepStim(win, stimuli=All_stim, pre_blank_sec= pre_blank, From 66f59cd5d6303b5748127a46f6d059447160ecbf Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Wed, 4 Jun 2025 14:49:16 -0700 Subject: [PATCH 13/17] Shared script --- stimulus_v2species_ephys.py | 36 ++-- stimulus_v2species_shared_oephys.py | 280 ++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+), 19 deletions(-) create mode 100644 stimulus_v2species_shared_oephys.py diff --git a/stimulus_v2species_ephys.py b/stimulus_v2species_ephys.py index 0982d6a..9950d2a 100644 --- a/stimulus_v2species_ephys.py +++ b/stimulus_v2species_ephys.py @@ -242,25 +242,23 @@ def get_trial_duration_seconds(stimulus): All_stim.append(drifting_grating_stim) print("length_drifting_grating_minutes: ",length_drifting_grating_seconds/60) - - # flash_stim - if dev_mode: - length_flash_seconds = 10 - else: - length_flash_seconds = ( - count_total_conditions(flash_stim) - *get_trial_duration_seconds(flash_stim) - *nb_run_flash - ) - - current_time = current_time+length_flash_seconds+inter_block_interval - - flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) - - All_stim.append(flash_stim) - print("length_flash_minutes: ",length_flash_seconds/60) - - + if nb_run_flash> 0: + # flash_stim + if dev_mode: + length_flash_seconds = 10 + else: + length_flash_seconds = ( + count_total_conditions(flash_stim) + *get_trial_duration_seconds(flash_stim) + *nb_run_flash + ) + + current_time = current_time+length_flash_seconds+inter_block_interval + + flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) + + All_stim.append(flash_stim) + print("length_flash_minutes: ",length_flash_seconds/60) pre_blank = 0 post_blank = post_blank diff --git a/stimulus_v2species_shared_oephys.py b/stimulus_v2species_shared_oephys.py new file mode 100644 index 0000000..9950d2a --- /dev/null +++ b/stimulus_v2species_shared_oephys.py @@ -0,0 +1,280 @@ +# -*- coding: utf-8 -*- +# Stimulus design +# +# Please write here any comments or notes about the stimulus design +# that are relevant for the experiment. +import psychopy.visual +import camstim +import argparse +import numpy as np +from psychopy import monitors, visual +from camstim import SweepStim, Foraging, Window, Warp +import glob +import logging +import yaml +import os +from camstim.sweepstim import Stimulus +from itertools import product + +## This is the RF mapping stim +def create_receptive_field_mapping(window, number_runs = 5): + x = np.arange(-40,45,10) + y = np.arange(-40,45,10) + position = [] + for i in x: + for j in y: + position.append([i,j]) + + stimulus = Stimulus(visual.GratingStim(window, + units='deg', + size=20, + mask="circle", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Pos':(position, 0), + 'Contrast': ([0.8], 4), + 'TF': ([4.0], 1), + 'SF': ([0.08], 2), + 'Ori': ([0,45,90,], 3), + }, + sweep_length=0.25, + start_time=0.0, + blank_length=0.0, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus.stim_path = r"C:\\not_a_stim_script\\receptive_field_block.stim" + + return stimulus + +## This is drifting gratings +def create_gratingStim(window, number_runs = 5): + stimulus_grating = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.1, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([1.0, 2.0, 4.0, 8.0, 15.0], 1), + 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 2), + 'Ori': (range(0, 360, 90), 3), + }, + sweep_length=1, + start_time=0.0, + blank_length=1.25, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_grating.stim_path = r"C:\\not_a_stim_script\\drifting_gratings_field_block.stim" + + return stimulus_grating + +## This is drifting gratings +def create_full_field_flash(window, number_runs = 5): + stimulus_flash = Stimulus(visual.GratingStim( + window, + pos=(0, 0), + units='deg', + size=(250, 250), + mask="None", + texRes=256, + sf=0.0, + ), + sweep_params={ + 'Contrast': ([0.8], 0), + 'TF': ([4.0], 1), + 'SF': ([0.00], 2), + 'Ori': (range(0, 180, 45), 3), + }, + sweep_length=0.25, + start_time=0.0, + blank_length=2, + blank_sweeps=0, + runs=number_runs, + shuffle=True, + save_sweep_table=True, + ) + stimulus_flash.stim_path = r"C:\\not_a_stim_script\\flash_field_block.stim" + + return stimulus_flash + + +#this gets total conditions for stimuli defined above +def count_total_conditions(stimulus): + """ + Given a camstim Stimulus object, return the number of unique parameter combinations. + """ + sweep_params = stimulus.sweep_params + param_values = [] + + for key, (values, _) in sweep_params.items(): + # Ensure range is converted to list + if type(values).__name__ == 'range': + values = list(values) + param_values.append(values) + + total_conditions = len(list(product(*param_values))) + return total_conditions + + +#this gets trial duration for stimuli defined above +def get_trial_duration_seconds(stimulus): + """ + Returns the duration of one full trial (sweep + blank) in seconds. + """ + sweep_length = getattr(stimulus, 'sweep_length', 0) + blank_length = getattr(stimulus, 'blank_length', 0) + return sweep_length + blank_length + + ############################################################################## main code block + + +if __name__ == "__main__": + parser = argparse.ArgumentParser("mtrain") + parser.add_argument("json_path", nargs="?", type=str, default="") + + args, _ = parser.parse_known_args() # <- this ensures that we ignore other arguments that might be needed by camstim + + # print args + if args.json_path == "": + logging.warning("No json path provided, using default parameters. THIS IS NOT THE EXPECTED BEHAVIOR FOR PRODUCTION RUNS") + json_params = {} + else: + with open(args.json_path, 'r') as f: + # we use the yaml package here because the json package loads as unicode, which prevents using the keys as parameters later + json_params = yaml.load(f) + logging.info("Loaded json parameters from mtrain") + # end of mtrain part + + # An integer representing the experiment group. Defaults to test group (value of 2). + dev_mode = json_params.get('dev_mode', True) + + inter_block_interval = json_params.get('inter_block_interval', 10) + nb_runs_ephys_rf = json_params.get('nb_runs_ephys_rf', 20) + nb_run_gratings = json_params.get('nb_run_gratings', 15) + nb_run_flash = json_params.get('nb_run_flash', 150) + post_blank = json_params.get('post_blank', 0) + + dist = 15.0 + wid = 52.0 + + if dev_mode: + my_monitor = monitors.Monitor(name='Test') + my_monitor.setSizePix((800,600)) + my_monitor.setWidth(wid) + my_monitor.setDistance(dist) + my_monitor.saveMon() + win = Window(size=[800,600], # [1024,768], + fullscr=False, + screen=0, + monitor= my_monitor, + warp=Warp.Spherical, + color= "gray", + units = 'deg' + ) + else: + win = Window( + fullscr=True, + screen=0, + monitor='Gamma1.Luminance50', + warp=Warp.Spherical, + ) + + ephys_rf_stim = create_receptive_field_mapping(win, number_runs=nb_runs_ephys_rf) + drifting_grating_stim = create_gratingStim(win, number_runs=nb_run_gratings) + flash_stim = create_full_field_flash(win, number_runs=nb_run_flash) + + print( + "ephys_rf, total conditions: ", count_total_conditions(ephys_rf_stim), + ", and trial duration: ", get_trial_duration_seconds(ephys_rf_stim)) + + print( + "gratings, total conditions: ", count_total_conditions(drifting_grating_stim), + ", and trial duration: ", get_trial_duration_seconds(drifting_grating_stim)) + + print( + "FLASH, total conditions: ", count_total_conditions(flash_stim), + ", and trial duration: ", get_trial_duration_seconds(flash_stim)) + + All_stim = [] + + # Add RF code from ephys + current_time = 0 + if dev_mode: + length_rf_seconds = 10 #for testing purposes + else: + length_rf_seconds = ( + count_total_conditions(ephys_rf_stim) + *get_trial_duration_seconds(ephys_rf_stim) + *nb_runs_ephys_rf + ) + + ephys_rf_stim.set_display_sequence([(current_time, current_time+length_rf_seconds)]) + + All_stim.append(ephys_rf_stim) + print("length_rf_minutes: ",length_rf_seconds/60) + + # drifting_grating_stim + if dev_mode: + length_drifting_grating_seconds = 10 + else: + length_drifting_grating_seconds = ( + count_total_conditions(drifting_grating_stim) + *get_trial_duration_seconds(drifting_grating_stim) + *nb_run_gratings + ) + + current_time = current_time+length_rf_seconds+inter_block_interval + + drifting_grating_stim.set_display_sequence([(current_time, current_time+length_drifting_grating_seconds)]) + + All_stim.append(drifting_grating_stim) + print("length_drifting_grating_minutes: ",length_drifting_grating_seconds/60) + + if nb_run_flash> 0: + # flash_stim + if dev_mode: + length_flash_seconds = 10 + else: + length_flash_seconds = ( + count_total_conditions(flash_stim) + *get_trial_duration_seconds(flash_stim) + *nb_run_flash + ) + + current_time = current_time+length_flash_seconds+inter_block_interval + + flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) + + All_stim.append(flash_stim) + print("length_flash_minutes: ",length_flash_seconds/60) + + pre_blank = 0 + post_blank = post_blank + ss = SweepStim(win, + stimuli=All_stim, + pre_blank_sec= pre_blank, + post_blank_sec= post_blank, + params={}, + ) + + # add in foraging so we can track wheel, potentially give rewards, etc + f = Foraging(window = win, + auto_update = False, + params= {} + ) + + ss.add_item(f, "foraging") + + ss.run() From a140e19c04638b0a49e8eff0904f4481e7fadc0b Mon Sep 17 00:00:00 2001 From: decrow3 Date: Sat, 7 Jun 2025 14:29:56 -0700 Subject: [PATCH 14/17] Updating full_field_flash stimuli --- stimulus_v2species_ephys.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stimulus_v2species_ephys.py b/stimulus_v2species_ephys.py index 9950d2a..8b080cf 100644 --- a/stimulus_v2species_ephys.py +++ b/stimulus_v2species_ephys.py @@ -88,14 +88,14 @@ def create_full_field_flash(window, number_runs = 5): units='deg', size=(250, 250), mask="None", - texRes=256, + tex='sin', sf=0.0, ), sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([4.0], 1), + 'Contrast': ([1.0, -1.0], 0), # +1 = white, -1 = black on gray bg + 'TF': ([0.0], 1), 'SF': ([0.00], 2), - 'Ori': (range(0, 180, 45), 3), + 'Ori': ([0], 3), }, sweep_length=0.25, start_time=0.0, From e4a010ca43d0cb398cf9cb662f769795afc5b37b Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Sun, 8 Jun 2025 23:05:33 -0700 Subject: [PATCH 15/17] Propagate to share script --- stimulus_v2species_shared_oephys.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stimulus_v2species_shared_oephys.py b/stimulus_v2species_shared_oephys.py index 9950d2a..8b080cf 100644 --- a/stimulus_v2species_shared_oephys.py +++ b/stimulus_v2species_shared_oephys.py @@ -88,14 +88,14 @@ def create_full_field_flash(window, number_runs = 5): units='deg', size=(250, 250), mask="None", - texRes=256, + tex='sin', sf=0.0, ), sweep_params={ - 'Contrast': ([0.8], 0), - 'TF': ([4.0], 1), + 'Contrast': ([1.0, -1.0], 0), # +1 = white, -1 = black on gray bg + 'TF': ([0.0], 1), 'SF': ([0.00], 2), - 'Ori': (range(0, 180, 45), 3), + 'Ori': ([0], 3), }, sweep_length=0.25, start_time=0.0, From 914a53766d480b1f3e8f8618f093572bf6e6a965 Mon Sep 17 00:00:00 2001 From: Jerome Lecoq Date: Mon, 9 Jun 2025 16:02:19 -0700 Subject: [PATCH 16/17] Fix duration drifting block --- stimulus_v2species_shared_oephys.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/stimulus_v2species_shared_oephys.py b/stimulus_v2species_shared_oephys.py index 8b080cf..43831aa 100644 --- a/stimulus_v2species_shared_oephys.py +++ b/stimulus_v2species_shared_oephys.py @@ -221,7 +221,6 @@ def get_trial_duration_seconds(stimulus): ) ephys_rf_stim.set_display_sequence([(current_time, current_time+length_rf_seconds)]) - All_stim.append(ephys_rf_stim) print("length_rf_minutes: ",length_rf_seconds/60) @@ -236,9 +235,7 @@ def get_trial_duration_seconds(stimulus): ) current_time = current_time+length_rf_seconds+inter_block_interval - drifting_grating_stim.set_display_sequence([(current_time, current_time+length_drifting_grating_seconds)]) - All_stim.append(drifting_grating_stim) print("length_drifting_grating_minutes: ",length_drifting_grating_seconds/60) @@ -253,10 +250,8 @@ def get_trial_duration_seconds(stimulus): *nb_run_flash ) - current_time = current_time+length_flash_seconds+inter_block_interval - + current_time = current_time+length_drifting_grating_seconds+inter_block_interval flash_stim.set_display_sequence([(current_time, current_time+length_flash_seconds)]) - All_stim.append(flash_stim) print("length_flash_minutes: ",length_flash_seconds/60) From e9c5862e4d62aa6414d3e5b3a5c1a01b679376b5 Mon Sep 17 00:00:00 2001 From: Madineh Sarvestani Date: Wed, 18 Jun 2025 12:16:09 -0400 Subject: [PATCH 17/17] Update stimulus_v2species_ephys.py fixing drifting grating angles --- stimulus_v2species_ephys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stimulus_v2species_ephys.py b/stimulus_v2species_ephys.py index 8b080cf..93dd230 100644 --- a/stimulus_v2species_ephys.py +++ b/stimulus_v2species_ephys.py @@ -66,7 +66,7 @@ def create_gratingStim(window, number_runs = 5): 'Contrast': ([0.8], 0), 'TF': ([1.0, 2.0, 4.0, 8.0, 15.0], 1), 'SF': ([0.02, 0.04, 0.08, 0.16, 0.32], 2), - 'Ori': (range(0, 360, 90), 3), + 'Ori': (range(0, 180, 45), 3), }, sweep_length=1, start_time=0.0,