labdata 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,281 @@
1
+ from ..utils import *
2
+ from .utils import BaseCompute
3
+
4
+ class DeeplabcutCompute(BaseCompute):
5
+ container = 'labdata-deeplabcut'
6
+ cuda = True
7
+ name = 'deeplabcut'
8
+ url = 'http://github.com/DeepLabCut/DeepLabCut'
9
+ def __init__(self,job_id, allow_s3 = None, delete_results = True, **kwargs):
10
+ '''
11
+ Run deeplabcut on video or train a model
12
+ '''
13
+ super(DeeplabcutCompute,self).__init__(job_id, allow_s3 = allow_s3)
14
+ self.file_filters = ['.avi','.mov','.mp4','.zarr'] # allowed file extensions..
15
+ # default parameters
16
+ self.parameters = dict(algorithm = 'deeplabcut',
17
+ mode = None, # select 'train' or 'infer'
18
+ model_num = None,
19
+ label_set = None,
20
+ video_name = None,
21
+ net_type = 'resnet_50',
22
+ batch_size = 8,
23
+ iteractions = 100000)
24
+ self._init_job()
25
+ if not self.job_id is None:
26
+ self.add_parameter_key()
27
+
28
+ self.delete_results = delete_results
29
+
30
+ def add_parameter_key(self):
31
+ model_num, parameters = self._get_parameter_number()
32
+ from ..schema import PoseEstimationModel
33
+ if self.parameters['mode'] == 'train':
34
+ #if not model_num in parameters.model_num.values:
35
+ # PoseEstimationModel().insert1(dict(model_num = model_num,
36
+ # pose_label_set_num = self.parameters['label_set'],
37
+ # algorithm_name = self.name,
38
+ # parameters_dict = json.dumps(self.model_parameters),
39
+ # code_link = self.url),
40
+ # skip_duplicates=True) # these will be updated later
41
+ self.model_num = model_num
42
+ # check here if it was already infered with this model.
43
+
44
+ def _get_parameter_number(self):
45
+ self.model_parameters = dict(algorithm = self.parameters['algorithm'],
46
+ net_type = self.parameters['net_type'],
47
+ batch_size = self.parameters['batch_size'],
48
+ iteractions = self.parameters['iteractions'])
49
+ parameter_set_num = None
50
+ from ..schema import PoseEstimationModel
51
+ parameters = pd.DataFrame(PoseEstimationModel().fetch())
52
+ model_num = None
53
+ if self.parameters['mode'] == 'train':
54
+ for i,r in parameters.iterrows():
55
+ # go through every parameter and label_set
56
+ if (self.model_parameters == json.loads(r.parameters_dict) and
57
+ self.parameters['model_num'] is None and
58
+ self.parameters['pose_label_set_num'] == r['pose_label_set_num']):
59
+ model_num = r.model_num
60
+ if model_num is None:
61
+ if not self.parameters['model_num'] is None:
62
+ model_num = self.parameters['model_num']
63
+ elif len(parameters) == 0:
64
+ model_num = 1
65
+ else:
66
+ model_num = np.max(parameters.model_num.values)+1
67
+ self.parameters['model_num'] = model_num
68
+ return model_num,parameters
69
+ else:
70
+ return self.parameters['model_num'],parameters
71
+
72
+ def _secondary_parse(self,arguments,parameters = None):
73
+ '''
74
+ Handles parsing the command line interface
75
+ '''
76
+ if not parameters is None: # can just pass the parameters
77
+ self.parameters = parameters
78
+ else:
79
+ import argparse
80
+ parser = argparse.ArgumentParser(
81
+ description = 'Pose estimation analysis using DeepLabCut',
82
+ usage = '''
83
+ deeplabcut -a <SUBJECT> -s <SESSION> -- <TRAIN|INFER> <PARAMETERS>
84
+
85
+ Example for inference using a trained model (-m 1):
86
+
87
+ labdata2 run deeplabcut -a JC131 -s 20231025_194303 -- infer -m 1 -v side_cam
88
+
89
+ ''')
90
+
91
+ parser.add_argument('mode',action='store', type = str,
92
+ help = '[required] Specifies what to do (train or infer)')
93
+ parser.add_argument('-v','--video-name',
94
+ action='store', type = str, default = None,
95
+ help = "Select files to analyze (DatasetVideo.video_name)")
96
+ parser.add_argument('-l','--label-set',
97
+ action='store', default=None, type = int,
98
+ help = "Label set to run training.")
99
+ parser.add_argument('-m','--model-num',
100
+ action='store', default=None, type = int,
101
+ help = "Model number to run inference.")
102
+ parser.add_argument('--net-type',
103
+ action='store', type = str, default = 'resnet_50',
104
+ help = "Network to run (has to be in the container - resnet_50; resnet_101)")
105
+ parser.add_argument('-i','--iteractions',
106
+ action='store', default=300000, type = int,
107
+ help = "Number of iteractions for training")
108
+ args = parser.parse_args(arguments[1:])
109
+ self.parameters['mode'] = args.mode
110
+ self.parameters['video_name'] = args.video_name
111
+ self.parameters['label_set'] = args.label_set
112
+ self.parameters['model_num'] = args.model_num
113
+ self.parameters['net_type'] = args.net_type
114
+ self.parameters['iteractions'] = args.iteractions
115
+ if 'train' in self.parameters['mode']:
116
+ if self.parameters['label_set'] is None:
117
+ raise(ValueError('Need to define a label-set to train a model.'))
118
+ else:
119
+ if self.parameters['model_num'] is None:
120
+ raise(ValueError('Need to specify a model.'))
121
+ from ..schema import PoseEstimationModel
122
+ if not len(PoseEstimationModel & f'model_num = {self.parameters["model_num"]}'):
123
+ raise(ValueError(f'Could not find model {self.parameters["model_num"]}'))
124
+
125
+ def find_datasets(self, subject_name = None, session_name = None):
126
+ '''
127
+ Searches for subjects and sessions
128
+ '''
129
+ if self.parameters['mode'] == 'train':
130
+ # check that the label set exists...
131
+ from ..schema import PoseEstimationLabelSet
132
+ pose_label_set = (PoseEstimationLabelSet() & f'pose_label_set_num = {self.parameters["label_set"]}').fetch()
133
+ return
134
+ if subject_name is None and session_name is None and self.parameters['mode'] == 'infer':
135
+ raise(ValueError('Need to select a dataset to infer using a deeplabcut model.'))
136
+ keys = []
137
+ if not subject_name is None:
138
+ if len(subject_name) > 1:
139
+ raise ValueError(f'Please submit one subject at a time {subject_name}.')
140
+ if not subject_name[0] == '':
141
+ subject_name = subject_name[0]
142
+ if not session_name is None:
143
+ for s in session_name:
144
+ if not s == '':
145
+ keys.append(dict(subject_name = subject_name,
146
+ session_name = s))
147
+ else:
148
+ raise(NotImplementedError('Specifying no session is not yet implemented'))
149
+ from ..schema import DatasetVideo
150
+ datasets = []
151
+ for k in keys:
152
+ datasets += (DatasetVideo()& k).fetch(as_dict = True)
153
+ datasets = [dict(subject_name = d['subject_name'],
154
+ session_name = d['session_name'],
155
+ dataset_name = d['dataset_name']) for d in datasets]
156
+ datasets = list({v['session_name']:v for v in datasets}.values())
157
+ return datasets
158
+
159
+ def _compute(self):
160
+ import deeplabcut
161
+ from ..schema import PoseEstimationLabelSet, PoseEstimationModel, DatasetVideo, File, PoseEstimation
162
+ if self.parameters['mode'] == 'train':
163
+ # check that the label set exists...
164
+ print(self.parameters)
165
+ print(f'pose_label_set_num = {self.parameters["label_set"]}')
166
+ pose_label_set = PoseEstimationLabelSet() & f'pose_label_set_num = {self.parameters["label_set"]}'
167
+ cfgfile = create_project(self.parameters,self.parameters['model_num'])
168
+ #print("Checking the labels.")
169
+ #deeplabcut.check_labels(cfgfile)
170
+ print("Generating the training dataset")
171
+ deeplabcut.create_training_dataset(cfgfile)
172
+ print("Training network")
173
+ deeplabcut.train_network(cfgfile, maxiters = self.parameters['iteractions'])
174
+ # once training completes, create a zip with the model and upload.
175
+ PoseEstimationModel().insert_model(self.parameters['model_num'],
176
+ model_folder=Path(cfgfile).parent,
177
+ pose_label_set_num = self.parameters["label_set"],
178
+ algorithm_name = self.parameters['algorithm'],
179
+ parameters = self.model_parameters,
180
+ training_datetime=datetime.now(),
181
+ container_name = self.container,
182
+ code_link = self.url)
183
+ # Save to PoseEstimationModel()
184
+ elif self.parameters['mode'] == 'infer':
185
+ # download the model if needed
186
+ cfgfile = (PoseEstimationModel() & f'model_num = {self.parameters["model_num"]}').get_model()
187
+ datasets = (File() & (DatasetVideo.File() & self.dataset_key)).fetch(as_dict = True)
188
+ if not len(datasets):
189
+ raise(ValueError(f"Could not find {self.dataset_key}"))
190
+ if len(datasets) > 1:
191
+ # select the video to analyse
192
+ datasets = (File() & (DatasetVideo.File() & dict(
193
+ self.dataset_key,
194
+ video_name = self.parameters['video_name']))).fetch(as_dict = True)
195
+ localfiles = self.get_files(datasets)
196
+ resfile = deeplabcut.analyze_videos(cfgfile,[str(f) for f in localfiles], videotype='.avi')
197
+
198
+ # Save the results to PoseEstimation()
199
+ if len(localfiles)>1:
200
+ print(f'Not sure how to insert multiple files.. Check the inputs {localfiles}.')
201
+ resfile = Path(str(localfiles[0].with_suffix(''))+resfile).with_suffix('.h5') # assuming there is only one file
202
+ bodyparts,xyl = read_dlc_file(resfile)
203
+ toinsert = []
204
+ for i,b in enumerate(bodyparts):
205
+ toinsert.append(dict(self.dataset_key,
206
+ video_name = self.parameters['video_name'],
207
+ model_num = self.parameters["model_num"],
208
+ label_name = b,
209
+ x = xyl[:,i,0],
210
+ y = xyl[:,i,1],
211
+ likelihood = xyl[:,i,2]))
212
+ PoseEstimation().insert(toinsert)
213
+
214
+ def create_project(parameters,model_num):
215
+ import deeplabcut
216
+ from ..schema import PoseEstimationLabelSet
217
+ print(f'Creating model {model_num} from pose_label_set_num {parameters["label_set"]}')
218
+
219
+ parameter_set_key = (PoseEstimationLabelSet() & f'pose_label_set_num = {parameters["label_set"]}').proj().fetch1()
220
+ parameter_set = (PoseEstimationLabelSet & parameter_set_key).fetch1()
221
+ labels = (PoseEstimationLabelSet.Label & parameter_set_key).fetch()
222
+
223
+ project_path,frames,frames_labels = (PoseEstimationLabelSet & parameter_set_key).export_labeling(
224
+ model_num = model_num,
225
+ export_only_labeled = True)
226
+ print(f'Exporting to {project_path}.')
227
+ F = frames['frame'].iloc[0]
228
+ project_path = project_path.parent.parent
229
+ bodyparts = [a for a in np.unique(labels['label_name'])]
230
+
231
+ cfg_file, ruamelFile = deeplabcut.utils.auxiliaryfunctions.create_config_template(multianimal= False)
232
+
233
+ cfg_file["multianimalproject"] = False
234
+ cfg_file["bodyparts"] = bodyparts
235
+ cfg_file["skeleton"] = [bodyparts,bodyparts]
236
+ cfg_file["default_augmenter"] = "default"
237
+ cfg_file["default_net_type"] = parameters['net_type']
238
+
239
+ # common parameters:
240
+ cfg_file["Task"] = f'model_{model_num}'
241
+ cfg_file["scorer"] = parameter_set['labeler']
242
+ cfg_file["video_sets"] = {f'label_set_{parameters["label_set"]}':dict(crop=[0,F.shape[0],0,F.shape[1]])}
243
+ cfg_file["project_path"] = str(project_path)
244
+ cfg_file["date"] = datetime.now().strftime('%b%d')
245
+ cfg_file["cropping"] = False
246
+ cfg_file["batch_size"] = parameters["batch_size"]
247
+ cfg_file["start"] = 0
248
+ cfg_file["stop"] = 1
249
+ cfg_file["numframes2pick"] = 20
250
+ cfg_file["TrainingFraction"] = [0.95]
251
+ cfg_file["iteration"] = 0
252
+ cfg_file["snapshotindex"] = -1
253
+ cfg_file["x1"] = 0
254
+ cfg_file["x2"] = 640
255
+ cfg_file["y1"] = 277
256
+ cfg_file["y2"] = 624
257
+ cfg_file["corner2move2"] = (50, 50)
258
+ cfg_file["move2corner"] = True
259
+ cfg_file["skeleton_color"] = "black"
260
+ cfg_file["pcutoff"] = 0.6
261
+ cfg_file["dotsize"] = 12 # for plots size of dots
262
+ cfg_file["alphavalue"] = 0.7 # for plots transparency of markers
263
+ cfg_file["colormap"] = "rainbow" # for plots type of colormap
264
+ for p in ['videos','training-datasets','dlc-models']:
265
+ (project_path/p).mkdir(exist_ok = True)
266
+ projconfigfile = os.path.join(str(project_path), "config.yaml")
267
+ # Write dictionary to yaml config file
268
+ deeplabcut.utils.auxiliaryfunctions.write_config(projconfigfile, cfg_file)
269
+ return projconfigfile
270
+
271
+
272
+ def read_dlc_file(filepath):
273
+ posture = pd.read_hdf(filepath)
274
+
275
+ scorer = np.unique(posture.columns.get_level_values(0))[0]
276
+ bodyparts = np.unique(posture.columns.get_level_values(1))
277
+ xyl = []
278
+ for part in bodyparts:
279
+ xyl.append(np.vstack([posture[scorer][part]['x'].values,posture[scorer][part]['y'].values,posture[scorer][part]['likelihood'].values]))
280
+ xyl = np.stack(xyl).transpose(2,0,1) #frames,bodyparts,x-y-likelihood
281
+ return bodyparts, xyl
@@ -0,0 +1,194 @@
1
+ # this has functions to interact with different schedulers.
2
+
3
+ from ..utils import *
4
+ import subprocess as sub
5
+
6
+ LABDATA_LOG_FOLDER = Path(LABDATA_FILE).parent/'logs'
7
+
8
+ def slurm_exists():
9
+ proc = sub.Popen('sinfo', shell=True, stdout=sub.PIPE, stderr = sub.PIPE)
10
+ out,err = proc.communicate()
11
+ if len(err):
12
+ return False
13
+ return True
14
+
15
+ def slurm_submit(jobname,
16
+ command,
17
+ ntasks = None,
18
+ ncpuspertask = None,
19
+ gpus = None,
20
+ memory = None,
21
+ walltime = None,
22
+ partition = None,
23
+ begin = None,
24
+ conda_environment = None,
25
+ module_environment = None,
26
+ mail = None,
27
+ sbatch_append = '',
28
+ **kwargs):
29
+
30
+ if ncpuspertask is None and ntasks is None:
31
+ from multiprocessing import cpu_count
32
+ ncpuspertask = 4
33
+ ntasks = 1
34
+ if ntasks is None:
35
+ ntasks = 1
36
+ if ncpuspertask is None:
37
+ ncpuspertask = 1
38
+
39
+ sjobfile = '''#!/bin/bash -login
40
+ #SBATCH --job-name={jobname}
41
+ #SBATCH --output={logfolder}/{jobname}_%j.stdout
42
+ #SBATCH --error={logfolder}/{jobname}_%j.stdout
43
+
44
+ #SBATCH --ntasks={ntasks}
45
+ #SBATCH --cpus-per-task={ncpus}
46
+ '''.format(jobname = jobname,
47
+ logfolder = LABDATA_LOG_FOLDER,
48
+ ntasks = ntasks,
49
+ ncpus = ncpuspertask)
50
+ if not walltime is None:
51
+ sjobfile += '#SBATCH --time={0} \n'.format(walltime)
52
+ if not memory is None:
53
+ sjobfile += '#SBATCH --mem={0} \n'.format(memory)
54
+ if not gpus is None:
55
+ sjobfile += '#SBATCH --gpus={0} \n'.format(gpus)
56
+ if not partition is None:
57
+ sjobfile += '#SBATCH --partition={0} \n'.format(partition)
58
+ if not begin is None:
59
+ sjobfile += '#SBATCH --begin={0} \n'.format(begin)
60
+ if not mail is None:
61
+ sjobfile += '#SBATCH --mail-user={0} \n#SBATCH --mail-type=END,FAIL \n'.format(mail)
62
+ if not module_environment is None:
63
+ sjobfile += '\n module purge\n'
64
+ sjobfile += '\n module load {0} \n'.format(module_environment)
65
+ if not conda_environment is None:
66
+ sjobfile += 'conda activate {0} \n'.format(conda_environment)
67
+ sjobfile += '''echo JOB {jobname} STARTED `date`
68
+ {cmd}
69
+ echo JOB FINISHED `date`
70
+ '''.format(jobname = jobname, cmd = command)
71
+
72
+ if not LABDATA_LOG_FOLDER.exists():
73
+ LABDATA_LOG_FOLDER.makedirs()
74
+ nfiles = len(list(LABDATA_LOG_FOLDER.glob('*.sh')))
75
+ filename = LABDATA_LOG_FOLDER/'{jobname}_{nfiles}.sh'.format(jobname = jobname,
76
+ nfiles = nfiles+1)
77
+ with open(filename,'w') as f:
78
+ f.write(sjobfile)
79
+ submit_cmd = 'cd {0} && sbatch {2} {1}'.format(filename.parent,
80
+ filename.name,
81
+ sbatch_append)
82
+ proc = sub.Popen(submit_cmd, shell=True, stdout=sub.PIPE)
83
+ out,err = proc.communicate()
84
+
85
+ if b'Submitted batch job' in out:
86
+ jobid = int(re.findall("Submitted batch job ([0-9]+)", str(out))[0])
87
+ return jobid
88
+ else:
89
+ print(out)
90
+ return None
91
+
92
+
93
+ def ssh_connect(address,user,permission_key=None):
94
+ try:
95
+ import paramiko
96
+ except:
97
+ raise(OSError('You need paramiko installed: "pip install paramiko"'))
98
+
99
+
100
+ ssh = paramiko.SSHClient()
101
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
102
+ privkey = permission_key
103
+ if not permission_key is None:
104
+ keys_folder = Path(LABDATA_PATH)
105
+ key = list(keys_folder.rglob(permission_key+'*')) # doen't care where it is inside labdata
106
+ if len(key):
107
+ key = key[0]
108
+ with open(key,'r') as fd:
109
+ privkey = paramiko.RSAKey.from_private_key(fd)
110
+ ssh.connect(hostname = address,
111
+ username = user,
112
+ pkey = privkey)
113
+ return ssh
114
+
115
+ def slurm_schedule_remote(command,
116
+ address = None,
117
+ user = None,
118
+ permission_key=None,
119
+ jobname = 'unknown_1',
120
+ ncpus = None,
121
+ queue = None,
122
+ gpus = None,
123
+ memory = None,
124
+ walltime = None,
125
+ begin = None,
126
+ conda_environment = None,
127
+ module_environment = None,
128
+ mail = None,
129
+ exclusive = True,
130
+ pre_cmds = '',
131
+ remote_dir = '/shared/labdata/remote_jobs',
132
+ **kwargs):
133
+ sjobfile = f'''#!/bin/bash -login
134
+ #SBATCH --job-name={jobname}
135
+ #SBATCH --output={remote_dir}/{jobname}_%j.stdout
136
+ #SBATCH --error={remote_dir}/{jobname}_%j.stdout
137
+ #SBATCH --ntasks=1
138
+ '''
139
+ if not ncpus is None:
140
+ sjobfile += f'#SBATCH --cpus-per-task={ncpus}'
141
+ if not walltime is None:
142
+ sjobfile += '#SBATCH --time={0} \n'.format(walltime)
143
+ if not memory is None:
144
+ sjobfile += '#SBATCH --mem={0} \n'.format(memory)
145
+ if not gpus is None:
146
+ sjobfile += '#SBATCH --gpus={0} \n'.format(gpus)
147
+ if not queue is None:
148
+ sjobfile += '#SBATCH --partition={0} \n'.format(queue)
149
+ if not begin is None:
150
+ sjobfile += '#SBATCH --begin={0} \n'.format(begin)
151
+ if not mail is None:
152
+ sjobfile += '#SBATCH --mail-user={0} \n#SBATCH --mail-type=END,FAIL \n'.format(mail)
153
+ if exclusive:
154
+ sjobfile += '#SBATCH --exclusive \n'
155
+ if not module_environment is None:
156
+ sjobfile += '\n module purge\n'
157
+ sjobfile += '\n module load {0} \n'.format(module_environment)
158
+ if not conda_environment is None:
159
+ sjobfile += 'conda activate {0} \n'.format(conda_environment)
160
+ if not pre_cmds is None:
161
+ pre_cmds = '\n'.join(pre_cmds)
162
+ sjobfile += f'''echo JOB {jobname} STARTED \`date\`
163
+ tic=\`date +%s.%N\`
164
+ {pre_cmds}
165
+ {command}
166
+ echo JOB FINISHED \`date\`
167
+ toc=\`date +%s.%N\`
168
+ a=\`echo "(\$toc - \$tic)" | bc -l\`
169
+ b=\`printf %.3f \$a\`
170
+ echo JOB COMPLETED IN \$b min
171
+ '''
172
+ remote_command = f'''
173
+ mkdir -p {remote_dir}
174
+ cat > {remote_dir}/{jobname}.sh << EOL
175
+ {sjobfile}
176
+ EOL
177
+
178
+ sbatch {remote_dir}/{jobname}.sh
179
+ '''
180
+ # try to connect to ssh
181
+ #print(remote_command)
182
+ if not 'conn' in kwargs.keys():
183
+ conn = ssh_connect(address,user,permission_key)
184
+ else:
185
+ conn = kwargs['conn']
186
+ stdin,stdout,stderr = conn.exec_command(remote_command)
187
+ output = stdout.read().decode()
188
+ errors = stderr.read().decode()
189
+ if 'Submitted batch job' in output:
190
+ jobid = int(re.findall("Submitted batch job ([0-9]+)", str(output))[0])
191
+ return jobid
192
+ else:
193
+ print(output,errors)
194
+ return None
@@ -0,0 +1,95 @@
1
+ from ..utils import *
2
+
3
+ def build_singularity_container(definition_file,
4
+ force = False,
5
+ output_folder = None):
6
+ '''
7
+ Builds a singularity container from a definition file.
8
+
9
+ The default output folder is prefs['compute_containers']['local_path']
10
+
11
+ e.g.
12
+ image = build_singularity_container('/home/joao/lib/labdata/containers/labdata-base.sdef')
13
+
14
+ Joao Couto - labdata 2024
15
+ '''
16
+
17
+ try:
18
+ import spython
19
+ except:
20
+ print("You'll need to install singularity-python:")
21
+ print(" pip install spython")
22
+
23
+ if output_folder is None:
24
+ output_folder = prefs['compute_containers']['local_path']
25
+
26
+ output_folder = Path(output_folder)
27
+ if not output_folder.exists():
28
+ output_folder.mkdir(parents=True,exist_ok = True)
29
+
30
+ definition_file = Path(definition_file)
31
+ container_file = (output_folder/definition_file.stem).with_suffix('.sif')
32
+
33
+ from spython.main import Client
34
+
35
+ image = Client.build(recipe = f'{definition_file}',
36
+ image = f'{container_file}',
37
+ stream = False, sudo = False, force = force,
38
+ options=["--fakeroot"])
39
+
40
+ # alternatively this could also be done with the CLI
41
+ #from subprocess import check_output
42
+ #check_output(f'singularity build --fakeroot {container_file} {definition_file}',shell= True)
43
+
44
+ return image
45
+
46
+ def run_on_singularity(containerfile,
47
+ command,
48
+ cuda = False,
49
+ bind = [],
50
+ bind_from_prefs = False,
51
+ dry_run = False):
52
+ nv = ''
53
+ if cuda:
54
+ nv = '--nv'
55
+ bind_str = ''
56
+ for b in bind:
57
+ bind_str += ' --bind {b}'
58
+ if bind_from_prefs:
59
+ local_paths = prefs['local_paths']
60
+ for p in local_paths:
61
+ bind_str += f' --bind {p}:{p}'
62
+ p = prefs['scratch_path']
63
+ bind_str += f' --bind {p}:{p}'
64
+ cmd = f"singularity exec {nv} {bind_str} {containerfile} {command}"
65
+ if not dry_run:
66
+ import subprocess as sub # run this
67
+ sub.run(cmd,shell = True)
68
+ else:
69
+ return cmd
70
+
71
+ def run_on_apptainer(containerfile,
72
+ command,
73
+ launch_cmd = 'exec',
74
+ cuda = False,
75
+ bind = [],
76
+ bind_from_prefs = False,
77
+ dry_run = False):
78
+ nv = ''
79
+ if cuda:
80
+ nv = '--nv'
81
+ bind_str = ''
82
+ for b in bind:
83
+ bind_str += ' --bind {b}'
84
+ if bind_from_prefs:
85
+ local_paths = prefs['local_paths']
86
+ for p in local_paths:
87
+ bind_str += f' --bind {p}:{p}'
88
+ p = prefs['scratch_path']
89
+ bind_str += f' --bind {p}:{p}'
90
+ cmd = f"apptainer {launch_cmd} {nv} {bind_str} {containerfile} {command}"
91
+ if not dry_run:
92
+ import subprocess as sub # run this
93
+ sub.run(cmd,shell = True)
94
+ else:
95
+ return cmd