Skip to content

Raster Scanning Code#55

Open
GlennZZY wants to merge 4 commits intomasterfrom
raster-scanning
Open

Raster Scanning Code#55
GlennZZY wants to merge 4 commits intomasterfrom
raster-scanning

Conversation

@GlennZZY
Copy link

Uploaded the code for raster scanning.

Since there's only one registry for simulation, the flag might not work at this moment

factory=list, repr=False, init=False
)
"""relative expression levels of neurons selected from each injection"""
Registry: DeviceInteractionRegistry = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure we need to store the registry when we can just do registry_for_sim(self.sim)?

self.focus_coords_per_injct.append(focus_coords)
self.rho_rel_per_injct.append(rho_rel)
self.Registry = registry_for_sim(self.sim)
self.Registry.update_fov(self.img_width)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this need to be updated on every injection? Also, it can't be right that there's a single update_fov for the whole registry, since this should vary for each light device

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the Scope should optionally have an imaging_light object, and the img_width and scan_freq would be updated specific to that light


raster_fov: int = field(default=500 * 1e-6 * meter, kw_only=True)

raster_enable: int = field(default=0, kw_only=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, these would be at the level of individual lights, not the whole registry

wavelength: Quantity = field(default=473 * nmeter, kw_only=True)
"""light wavelength with unit (usually nmeter)"""

scan_freq : int = field(default=30, kw_only=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd create a subclass RasterScanningLight to hide the scan_freq details from the typical Light object

cleo/registry.py Outdated
i_source = self.subgroup_idx_for_light[light]
light_prop_syn.epsilon[i_source, :] = epsilon
light_prop_syn.T[i_source, :] = light.transmittance(coords_from_ng(ng)).ravel()
light_prop_syn.scan_period = second / light.scan_freq
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd put these in a separate function that you call both here (on injection) and when you change the params on the scope/imaging light (setter functions). like update_raster_scan_params(scope) or something like that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like you've already done with update_fov but can include these other variables too

@kjohnsen kjohnsen assigned kjohnsen and GlennZZY and unassigned kjohnsen Nov 20, 2024
Copy link
Member

@kjohnsen kjohnsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good! Besides the comments below, as I was about to test it (with the script at the bottom that plots all the action spectra), it occurred to me it's getting to be too much to check just by looking at the numbers. I think we need a new tutorial notebook that goes over all the opsins that will serve two puposes: (1) quickly introduce the user to all the options and (2) let us visually inspect that all the opsins are behaving as expected. Can you create a docs/tutorials/opsin_selection.ipynb notebook where we plot 4 things for every opsin?

  1. The 1P action spectrum,
  2. the 2P action spectrum,
  3. the impulse response (the photocurrent after a quick (e.g., 5 ms), strong pulse),
  4. and the pulse response (what the photocurrent looks like with prolonged stimulation (should see peak, then back down to plateau)

Since there are now too many opsins to visualize all at once, can you break this into groups? For example, plot all the ChR2 variants together, all the Chrimson variants together, all the inhibitory pumps together, all the ChRmine variants together?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove these? we should add **/.DS_Store to .gitignore

"metadata": {
"kernelspec": {
"display_name": "python3",
"display_name": ".venv",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to commit this notebook if you didn't change it. Also, if you did change the notebook, use nbdev clean to get rid of stuff like this before committing

(490, 0.92),
(498, 1),
(516, 0.84),
(507, 0.90),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's another place the spectrum is out of order. For legibility and reproducibility, it should be in order, but I remembered I actually should have already accounted for this in light_dependence.py, line 125...don't know why I'm getting an error. Could you add a unit test for this? I guess it would be a new file tests/light/test_light_dependence.py

Copy link
Member

@kjohnsen kjohnsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall pattern of creating a light from the scope looks good! Just time to put it all together, make some tweaks, and most importantly, get it working in an end-to-end example and in unit tests

self.rho_rel_per_injct.append(rho_rel)
self.Registry = registry_for_sim(self.sim)
self.Registry.update_fov(self.img_width)
registry_for_sim(self.sim).update_fov(self.img_width)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this in 0.18 so the registry is stored on sim. so self.sim.registry from here on

def create_imaging_light(self):
self.imaging_light = Light(
name=f"{self.name}_imlight",
light_model=GaussianEllipsoid(radius=self.img_width/2),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would give the user the option to pass in things like wavelength. And radius would be the radius of a neuron (soma_radius). And don't inject for the user; require them to inject manually (since you inject on a per-neuron group basis)

registry_for_sim(self.sim).set_scan_on(self.imaging_light, flag)

# change frame rate
def set_scan_freq(self, hz: float):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use Python's built-in setter/getter syntax to make this nicer to use and more Pythonic: https://realpython.com/python-getter-setter/

def init_for_simulator(self, sim: CLSimulator) -> None:
registry = registry_for_sim(sim)
registry.init_register_light(self)
"""Test Code for setter method"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you'd move this to test_scope.py?

setattr(src, k, v)

def set_scan_on(self, light: "Light", enable: bool = True):
self._apply_to_source(light, is_scanning=int(bool(enable)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice idea with the _apply_to_source to reuse the logic of getting the imaging light subgroup. I'd use a more obvious name like _apply_to_img_light though

# fmt: on
src = self.source_for_light(light)
src.scan_period = (1 / light.scan_freq) * second
spot_radius = 1e-6 * meter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are already defined by the scope and shouldn't be redefined here

if self.raster_fov != new_fov:
self.raster_fov = new_fov
warnings.warn("Warning: cleo currently supports use of single scanning fov in simulation, the latest update parameter will be used", UserWarning)
def set_fov(self, light: "Light", fov):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think would should stick with img_width instead of fov for consistency with Scope and also because from what I understand, field of view is a technical term measured as an angle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants