Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CryoSPARC Live Connection #234

Merged
merged 30 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Smartscope/core/pipelines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
from .smartscope_preprocessing_cmd_kwargs import SmartScopePreprocessingCmdKwargs
from .smartscope_preprocessing_pipeline import SmartscopePreprocessingPipeline
from .smartscope_preprocessing_pipeline_form import SmartScopePreprocessingPipelineForm
from .cryosparc_live import CryoSPARC
from .cryosparc_live import CryoSPARCPipeline
101 changes: 86 additions & 15 deletions Smartscope/core/pipelines/cryosparc_live.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,51 @@
from .smartscope_preprocessing_pipeline_form import SmartScopePreprocessingPipelineForm
from .smartscope_preprocessing_cmd_kwargs import SmartScopePreprocessingCmdKwargs

from typing import Union
from pathlib import Path
from pydantic import BaseModel, Field, validator

logger = logging.getLogger(__name__)

from django import forms
from cryosparc.tools import CryoSPARC

class CryoSPARCPipelineForm(forms.Form):
cs_address = forms.URLField(label='CryoSPARC URL:',help_text='Web address of CryoSPARC installation (must be accessible from the SmartScope computer).')
cs_address = forms.CharField(label='CryoSPARC URL:',help_text='Web address of CryoSPARC installation (must be accessible from the SmartScope computer).')
cs_port = forms.IntegerField(label='CryoSPARC Port:',help_text='Port of CryoSPARC installation. Defaults to 39000 if not set')
cs_license = forms.CharField(label='CryoSPARC License Key',help_text='CryoSPARC License Key')
cs_project = forms.IntegerField(label='CryoSPARC Project # P',
help_text='Enter the project number of the CryoSPARC project you would like to spawn the Live sessions in. Omit the P at the beginning'
)
cs_email = forms.CharField(label='CryoSPARC User Email',help_text='CryoSPARC User Email Address')
cs_password = forms.CharField(label='CryoSPARC User Password',help_text='CryoSPARC User Password')
cs_project = forms.IntegerField(label='CryoSPARC Project # P',help_text='Enter the project number of the CryoSPARC project you would like to spawn the Live sessions in. Omit the P at the beginning')
cs_worker_processes=forms.IntegerField(label='# of pre-processing workers:',help_text='Number of worker processes to spawn')
cs_preprocessing_lane = forms.CharField(label='Name of pre-processing lane:',help_text='Name of lane to use for CryoSPARC Live preprocessing lane')
frames_directory = forms.CharField(help_text='Locations to look for the frames file other. '+ \
'Will look in the default smartscope/movies location by default.')
frames_directory = forms.CharField(help_text='Absolute path for frame directory relative to CryoSPARC Master instance')
cs_dose=forms.FloatField(label='Dose',help_text='Total dose in e/A2')
cs_apix=forms.FloatField(label='Pixel Size',help_text='Angstroms per pixel')
cs_lanes=forms.CharField(label='Worker Lane',help_text='Name of CryoSPARC Live Worker Lane')

def __init__(self, *args,**kwargs):
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
visible.field.required = False


class CryoSPARC(PreprocessingPipeline):
class CryoSPARCCmdKwargs(BaseModel):
cs_address:str = ""
cs_port:int = 39000
cs_license:str = ""
cs_email:str = ""
cs_password:str = ""
cs_project:int = 9999
cs_worker_processes:int = 1
cs_preprocessing_lane:str = ""
frames_directory:str = ""
cs_dose:float = 50.0
cs_apix:float = 1.0
cs_lanes:str = ""


class CryoSPARCPipeline(PreprocessingPipeline):
verbose_name = 'CryoSPARC Live Pre-Processing Pipeline'
name = 'cryoSPARC'
description = 'Spawn CryoSPARC Live sessions at each grid. Requires a functional CryoSPARC installation.'
Expand All @@ -51,7 +72,7 @@ class CryoSPARC(PreprocessingPipeline):
child_process = []
to_update = []
incomplete_processes = []
cmdkwargs_handler = SmartScopePreprocessingCmdKwargs
cmdkwargs_handler = CryoSPARCCmdKwargs
pipeline_form= CryoSPARCPipelineForm

def __init__(self, grid: AutoloaderGrid, cmd_data:Dict):
Expand All @@ -60,16 +81,66 @@ def __init__(self, grid: AutoloaderGrid, cmd_data:Dict):
self.detector = self.grid.session_id.detector_id
self.cmd_data = self.cmdkwargs_handler.parse_obj(cmd_data)
logger.debug(self.cmd_data)
self.frames_directory = [Path(self.detector.frames_directory)]
if self.cmd_data.frames_directory is not None:
self.frames_directory.append(self.cmd_data.frames_directory)

self.license = self.cmd_data.cs_license
self.host = self.cmd_data.cs_address
self.base_port = self.cmd_data.cs_port
self.email = self.cmd_data.cs_email
self.password = self.cmd_data.cs_password
self.project = 'P' + str(self.cmd_data.cs_project)
self.frames_directory = self.cmd_data.frames_directory
self.dose = self.cmd_data.cs_dose
self.apix = self.cmd_data.cs_apix
self.lane = self.cmd_data.cs_lanes

self.cs_session = ""

def start(self): #Abstract Class Function - Required
pass

#Setup connection to CryoSPARC Instance
cs_instance = CryoSPARC(license=self.license,host=self.host,base_port=self.base_port,email=self.email,password=self.password)
csparc_debug = str(cs_instance.test_connection())
logger.debug(f'CryoSPARC Connection Test: {csparc_debug}')

#Need to check here if session already exists. If so, skip creation, if not, create.

#Create new CryoSPARC Live session
cs_session = cs_instance.rtp.create_new_live_workspace(project_uid=str(self.project), created_by_user_id=str(cs_instance.cli.get_id_by_email(self.email)), title=str(self.grid.session_id))
return cs_session

#Setup lanes
cs_instance.rtp.update_compute_configuration(project_uid=str(self.project), session_uid=cs_session, key='phase_one_lane', value=str(self.lane))
cs_instance.rtp.update_compute_configuration(project_uid=str(self.project), session_uid=cs_session, key='phase_one_gpus', value=2)
cs_instance.rtp.update_compute_configuration(project_uid=str(self.project), session_uid=cs_session, key='phase_two_lane', value=str(self.lane))
cs_instance.rtp.update_compute_configuration(project_uid=str(self.project), session_uid=cs_session, key='auxiliary_lane', value=str(self.lane))

#Setup exposure group
cs_instance.rtp.exposure_group_update_value(project_uid=str(self.project), session_uid=cs_session, exp_group_id=1, name='file_engine_watch_path_abs', value=str(self.frames_directory))
cs_instance.rtp.exposure_group_update_value(project_uid=str(self.project), session_uid=cs_session, exp_group_id=1, name='file_engine_filter', value='.tif')
cs_instance.rtp.exposure_group_finalize_and_enable(project_uid=str(self.project), session_uid=cs_session, exp_group_id=1)

#Motion Correction Settings
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='mscope_params', param_name='accel_kv', value=float(self.microscope.voltage))
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='mscope_params', param_name='cs_mm', value=float(self.microscope.spherical_abberation))

##Need to check for if files have been written here, and get values from .mdoc file.
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='mscope_params', param_name='total_dose_e_per_A2', value=float(self.dose))
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='mscope_params', param_name='psize_A', value=float(self.apix))
##Also need gain controls here (Flip/rotate)

#Extraction Settings
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='blob_pick', param_name='diameter', value=100)
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='blob_pick', param_name='diameter_max', value=200)
cs_instance.rtp.set_param(project_uid=str(self.project), session_uid=cs_session, param_sec='extraction', param_name='box_size_pix', value=440)

#Start the session
cs_instance.rtp.start_session(project_uid=str(self.project), session_uid=cs_session, user_id=cs_instance.cli.get_id_by_email(self.email))

def stop(self): #Abstract Class Function - Required
pass
#Turn off live session
cs_instance = CryoSPARC(license=self.license,host=self.host,base_port=self.base_port,email=self.email,password=self.password)
cs_instance.rtp.pause_session(project_uid=str(self.project), session_uid=self.cs_session)

def check_for_update(self, instance): #Abstract Class Function - Required
#Here should probably go some logic that will get the hole and image, check CryoSPARC for existing thumbnail and data, and update the object
pass

4 changes: 2 additions & 2 deletions Smartscope/core/preprocessing_pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

logger = logging.getLogger(__name__)

from .pipelines import PreprocessingPipelineCmd, SmartscopePreprocessingPipeline, CryoSPARC
from .pipelines import PreprocessingPipelineCmd, SmartscopePreprocessingPipeline, CryoSPARCPipeline


PREPROCESSING_PIPELINE_FACTORY = dict(smartscopePipeline=SmartscopePreprocessingPipeline, cryoSPARC=CryoSPARC)
PREPROCESSING_PIPELINE_FACTORY = dict(smartscopePipeline=SmartscopePreprocessingPipeline, cryoSPARC=CryoSPARCPipeline)

def load_preprocessing_pipeline(file:Path):
if file.exists():
Expand Down