lytools-HPC 0.0.1__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.
lytools_HPC/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# coding='utf-8'
|
|
2
|
+
__version__ = '0.0.1'
|
|
3
|
+
__package_name__ = 'lytools_HPC'
|
|
4
|
+
import warnings
|
|
5
|
+
import requests
|
|
6
|
+
from packaging import version
|
|
7
|
+
|
|
8
|
+
def check_latest_version(package_name, current_version, cache_hours=0.1):
|
|
9
|
+
cache_file = os.path.expanduser(f"~/.{package_name}_version_check")
|
|
10
|
+
latest_version = None
|
|
11
|
+
now = time.time()
|
|
12
|
+
|
|
13
|
+
if os.path.exists(cache_file):
|
|
14
|
+
try:
|
|
15
|
+
with open(cache_file, "r") as f:
|
|
16
|
+
data = json.load(f)
|
|
17
|
+
if now - data["timestamp"] < cache_hours * 3600:
|
|
18
|
+
latest_version = data["latest_version"]
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
if latest_version is None:
|
|
23
|
+
try:
|
|
24
|
+
url = f"https://pypi.org/pypi/{package_name}/json"
|
|
25
|
+
resp = requests.get(url, timeout=3)
|
|
26
|
+
if resp.status_code == 200:
|
|
27
|
+
data = resp.json()
|
|
28
|
+
latest_version = data["info"]["version"]
|
|
29
|
+
with open(cache_file, "w") as f:
|
|
30
|
+
json.dump({"latest_version": latest_version, "timestamp": now}, f)
|
|
31
|
+
except Exception:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
if version.parse(current_version) < version.parse(latest_version):
|
|
36
|
+
warnings.warn(
|
|
37
|
+
f"{package_name} {current_version} is outdated. "
|
|
38
|
+
f"Latest version is {latest_version}. "
|
|
39
|
+
f"Upgrade via: pip install -U {package_name}"
|
|
40
|
+
)
|
|
41
|
+
except Exception:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
current = __version__
|
|
46
|
+
check_latest_version(__package_name__, current)
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
from ._lytools_HPC import *
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import submitit
|
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
3
|
+
import redis
|
|
4
|
+
from threading import Lock
|
|
5
|
+
from pathos.multiprocessing import ProcessPool
|
|
6
|
+
import time
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import pickle
|
|
13
|
+
|
|
14
|
+
from rich.progress import (
|
|
15
|
+
Progress,
|
|
16
|
+
BarColumn,
|
|
17
|
+
TextColumn,
|
|
18
|
+
TimeRemainingColumn,
|
|
19
|
+
TimeElapsedColumn,
|
|
20
|
+
ProgressColumn
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def listdir(fdir):
|
|
25
|
+
'''
|
|
26
|
+
Mac OS
|
|
27
|
+
list the names of the files in the directory
|
|
28
|
+
return sorted files list without '.DS_store'
|
|
29
|
+
'''
|
|
30
|
+
list_dir = []
|
|
31
|
+
for f in sorted(os.listdir(fdir)):
|
|
32
|
+
if f.startswith('.'):
|
|
33
|
+
continue
|
|
34
|
+
list_dir.append(f)
|
|
35
|
+
return list_dir
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def mkdir(dir, force=False):
|
|
39
|
+
if not os.path.isdir(dir):
|
|
40
|
+
if force == True:
|
|
41
|
+
os.makedirs(dir)
|
|
42
|
+
else:
|
|
43
|
+
os.mkdir(dir)
|
|
44
|
+
|
|
45
|
+
def split_into_n_jobs(lst, n_jobs):
|
|
46
|
+
"""
|
|
47
|
+
Split the list into n_jobs parts.
|
|
48
|
+
"""
|
|
49
|
+
total = len(lst)
|
|
50
|
+
chunk_size = total // n_jobs
|
|
51
|
+
remainder = total % n_jobs
|
|
52
|
+
|
|
53
|
+
chunks = []
|
|
54
|
+
start = 0
|
|
55
|
+
|
|
56
|
+
for i in range(n_jobs):
|
|
57
|
+
extra = 1 if i < remainder else 0
|
|
58
|
+
end = start + chunk_size + extra
|
|
59
|
+
chunks.append(lst[start:end])
|
|
60
|
+
start = end
|
|
61
|
+
return chunks
|
|
62
|
+
|
|
63
|
+
_REDIS = None
|
|
64
|
+
|
|
65
|
+
def get_redis():
|
|
66
|
+
global _REDIS
|
|
67
|
+
if _REDIS is None:
|
|
68
|
+
_REDIS = HPC_redis().conn_redis()
|
|
69
|
+
return _REDIS
|
|
70
|
+
|
|
71
|
+
_LOCK = Lock()
|
|
72
|
+
_LOCAL_COUNTER = 0
|
|
73
|
+
|
|
74
|
+
def update_i(job_name, batch_size=100):
|
|
75
|
+
'''
|
|
76
|
+
:param job_name:
|
|
77
|
+
'''
|
|
78
|
+
global _LOCAL_COUNTER
|
|
79
|
+
|
|
80
|
+
with _LOCK:
|
|
81
|
+
_LOCAL_COUNTER += 1
|
|
82
|
+
|
|
83
|
+
if _LOCAL_COUNTER >= batch_size:
|
|
84
|
+
r = get_redis()
|
|
85
|
+
r.hincrby(job_name, "step", _LOCAL_COUNTER)
|
|
86
|
+
_LOCAL_COUNTER = 0
|
|
87
|
+
|
|
88
|
+
class IterSpeedColumn(ProgressColumn):
|
|
89
|
+
|
|
90
|
+
def render(self, task):
|
|
91
|
+
if task.finished:
|
|
92
|
+
return Text(f"{task.speed:.2f} it/s", style="green")
|
|
93
|
+
|
|
94
|
+
speed = task.speed
|
|
95
|
+
|
|
96
|
+
if speed is None:
|
|
97
|
+
return Text("-- it/s", style="dim")
|
|
98
|
+
|
|
99
|
+
return Text(f"{speed:.2f} it/s", style="cyan")
|
|
100
|
+
|
|
101
|
+
def sumbit_jobs_array(func,params_list,log_folder,job_name,
|
|
102
|
+
job_number_limit=500,
|
|
103
|
+
parallel_process_per_task=10,
|
|
104
|
+
slurm_array_parallelism=20,
|
|
105
|
+
parallel_process_p_or_t='p',
|
|
106
|
+
cpus_per_task=1,
|
|
107
|
+
mem_gb=1,
|
|
108
|
+
timeout_min=5,
|
|
109
|
+
slurm_partition="general",
|
|
110
|
+
exclude_nodes=None,
|
|
111
|
+
pbar_update_freq=1
|
|
112
|
+
):
|
|
113
|
+
'''
|
|
114
|
+
:param func: the kernel function to run, should take one argument, e.g. func(params)
|
|
115
|
+
:param params_list: list of tuples [params1, params2, ...]
|
|
116
|
+
:param log_folder: slurm log_folder
|
|
117
|
+
:param job_name: slurm job_name
|
|
118
|
+
:param job_number_limit: number of total jobs you want to submit to slurm
|
|
119
|
+
:param parallel_process_per_task: number of parallel processes per task, Recommend equal to :param cpus_per_task
|
|
120
|
+
:param slurm_array_parallelism: slurm array parallelism
|
|
121
|
+
:param parallel_process_p_or_t: 'p' for multiprocessing, 't' for multi-threading
|
|
122
|
+
:param cpus_per_task: number of cpus per task
|
|
123
|
+
:param mem_gb: memory per task
|
|
124
|
+
:param timeout_min: timeout in minutes
|
|
125
|
+
:param slurm_partition: slurm partition
|
|
126
|
+
:param exclude_nodes: list of nodes to exclude
|
|
127
|
+
:param pbar_update_freq: frequency of updating progress bar
|
|
128
|
+
'''
|
|
129
|
+
|
|
130
|
+
if len(params_list) == 0:
|
|
131
|
+
raise ValueError("params_list is empty")
|
|
132
|
+
if len(params_list) > job_number_limit:
|
|
133
|
+
super_params_list = split_into_n_jobs(params_list,job_number_limit)
|
|
134
|
+
if parallel_process_p_or_t == 't':
|
|
135
|
+
def super_func(chunk):
|
|
136
|
+
def wrapper(p):
|
|
137
|
+
func(p)
|
|
138
|
+
update_i(job_name,pbar_update_freq)
|
|
139
|
+
|
|
140
|
+
with ThreadPoolExecutor(max_workers=parallel_process_per_task) as Thread_:
|
|
141
|
+
list(Thread_.map(wrapper, chunk))
|
|
142
|
+
|
|
143
|
+
elif parallel_process_p_or_t == 'p':
|
|
144
|
+
def super_func(chunk):
|
|
145
|
+
# for p in chunk:
|
|
146
|
+
# func(p)
|
|
147
|
+
|
|
148
|
+
def wrapper(p):
|
|
149
|
+
func(p)
|
|
150
|
+
update_i(job_name,pbar_update_freq)
|
|
151
|
+
|
|
152
|
+
pool = ProcessPool(nodes=parallel_process_per_task)
|
|
153
|
+
pool.map(wrapper, chunk)
|
|
154
|
+
pass
|
|
155
|
+
else:
|
|
156
|
+
raise ValueError("parallel_process_p_or_t must be 'p' for multiprocessing or 't' for threading")
|
|
157
|
+
|
|
158
|
+
final_params_list = super_params_list
|
|
159
|
+
final_func = super_func
|
|
160
|
+
else:
|
|
161
|
+
final_params_list = params_list
|
|
162
|
+
final_func = func
|
|
163
|
+
|
|
164
|
+
if os.path.exists(log_folder):
|
|
165
|
+
for f in os.listdir(log_folder):
|
|
166
|
+
os.remove(os.path.join(log_folder, f))
|
|
167
|
+
mkdir(log_folder, force=True)
|
|
168
|
+
|
|
169
|
+
print('submiting...')
|
|
170
|
+
executor = submitit.AutoExecutor(folder=log_folder)
|
|
171
|
+
executor.update_parameters(
|
|
172
|
+
slurm_job_name=job_name,
|
|
173
|
+
cpus_per_task=cpus_per_task,
|
|
174
|
+
mem_gb=mem_gb,
|
|
175
|
+
timeout_min=timeout_min,
|
|
176
|
+
slurm_array_parallelism=slurm_array_parallelism,
|
|
177
|
+
slurm_partition=slurm_partition,
|
|
178
|
+
exclude=exclude_nodes,
|
|
179
|
+
)
|
|
180
|
+
jobs = executor.map_array(final_func, final_params_list)
|
|
181
|
+
|
|
182
|
+
info = {
|
|
183
|
+
"Total Cores Used": cpus_per_task * slurm_array_parallelism,
|
|
184
|
+
"Concurrent Processes": parallel_process_per_task * slurm_array_parallelism,
|
|
185
|
+
"Total Loop Length": len(params_list),
|
|
186
|
+
"Number of Jobs": len(final_params_list),
|
|
187
|
+
"First Job ID": jobs[0].job_id,
|
|
188
|
+
"Memory for Each Job(GB)":mem_gb,
|
|
189
|
+
"Time out Minutes For Each Job": timeout_min,
|
|
190
|
+
"Partition":slurm_partition,
|
|
191
|
+
}
|
|
192
|
+
pretty_table_print(info)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def pretty_table_print(info):
|
|
197
|
+
max_key_len = max(len(k) for k in info)
|
|
198
|
+
|
|
199
|
+
print("\n=== Job Summary ===")
|
|
200
|
+
for k, v in info.items():
|
|
201
|
+
print(f"{k + ':':<{max_key_len + 2}} {v}")
|
|
202
|
+
print("=" * (max_key_len + 15))
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
def monitoring_job(progress_dir_json):
|
|
206
|
+
refresh_interval = 1
|
|
207
|
+
# refresh_interval = 5
|
|
208
|
+
task_map = {}
|
|
209
|
+
with Progress(
|
|
210
|
+
TextColumn("[bold yellow]{task.description}"),
|
|
211
|
+
BarColumn(style="red", # Unfinished part
|
|
212
|
+
complete_style="green", # Finished part
|
|
213
|
+
finished_style="yellow"), # 100% finished),
|
|
214
|
+
TextColumn("{task.completed}/{task.total} {task.percentage:>3.0f}%"),
|
|
215
|
+
TimeElapsedColumn(),
|
|
216
|
+
TimeRemainingColumn(),
|
|
217
|
+
) as progress_total:
|
|
218
|
+
|
|
219
|
+
with Progress(
|
|
220
|
+
TextColumn("[bold yellow]{task.description}"),
|
|
221
|
+
BarColumn(),
|
|
222
|
+
TextColumn("{task.completed}/{task.total} {task.percentage:>3.0f}%"),
|
|
223
|
+
TimeElapsedColumn(),
|
|
224
|
+
TimeRemainingColumn(),
|
|
225
|
+
) as progress:
|
|
226
|
+
|
|
227
|
+
overall_task = None
|
|
228
|
+
total_jobs = None
|
|
229
|
+
|
|
230
|
+
while True:
|
|
231
|
+
|
|
232
|
+
files = list(progress_dir_json.glob("*.json"))
|
|
233
|
+
finished_jobs = 0
|
|
234
|
+
|
|
235
|
+
for f in files:
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
with open(f) as fp:
|
|
239
|
+
data = json.load(fp)
|
|
240
|
+
except:
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
jobid = data["jobid"]
|
|
244
|
+
step = data["step"]
|
|
245
|
+
total = data["total"]
|
|
246
|
+
total_job = data["total_job"]
|
|
247
|
+
sub_job_name = data["sub_job_name"]
|
|
248
|
+
|
|
249
|
+
# 初始化 total progress
|
|
250
|
+
|
|
251
|
+
if total_jobs is None:
|
|
252
|
+
total_jobs = total_job
|
|
253
|
+
overall_task = progress_total.add_task(
|
|
254
|
+
"[bold green]TOTAL", total=total_jobs
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# if completed
|
|
258
|
+
if step >= total:
|
|
259
|
+
|
|
260
|
+
finished_jobs += 1
|
|
261
|
+
|
|
262
|
+
if jobid in task_map:
|
|
263
|
+
progress.remove_task(task_map[jobid])
|
|
264
|
+
del task_map[jobid]
|
|
265
|
+
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
# create bar
|
|
269
|
+
if jobid not in task_map:
|
|
270
|
+
task_map[jobid] = progress.add_task(
|
|
271
|
+
f"({jobid}/{total_job}) {sub_job_name}", total=total
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
progress.update(task_map[jobid], completed=step)
|
|
275
|
+
|
|
276
|
+
# update total bar
|
|
277
|
+
if overall_task is not None:
|
|
278
|
+
progress_total.update(overall_task, completed=finished_jobs)
|
|
279
|
+
|
|
280
|
+
# All tasks completed
|
|
281
|
+
if total_jobs and finished_jobs == total_jobs:
|
|
282
|
+
break
|
|
283
|
+
|
|
284
|
+
time.sleep(refresh_interval)
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
class HPC_redis:
|
|
288
|
+
|
|
289
|
+
def __init__(self):
|
|
290
|
+
self.r = self.conn_redis()
|
|
291
|
+
pass
|
|
292
|
+
|
|
293
|
+
def conn_redis(self):
|
|
294
|
+
redis_conf = Path.home() / '.config' / 'redis' / 'redis.conf'
|
|
295
|
+
with open(redis_conf) as f:
|
|
296
|
+
redis_conf = f.readlines()
|
|
297
|
+
host = redis_conf[0].strip()
|
|
298
|
+
port = int(redis_conf[1].strip())
|
|
299
|
+
passwd = redis_conf[2].strip()
|
|
300
|
+
|
|
301
|
+
r = redis.Redis(
|
|
302
|
+
host=host,
|
|
303
|
+
port=port,
|
|
304
|
+
password=passwd,
|
|
305
|
+
)
|
|
306
|
+
# print(f"Connected to Redis at {host}:{port}")
|
|
307
|
+
return r
|
|
308
|
+
|
|
309
|
+
def hit_redis(self,job_name,amount=1):
|
|
310
|
+
self.r.hincrby(name=job_name,key=job_name,amount=amount)
|
|
311
|
+
|
|
312
|
+
def set_total_num(self,job_name,total_job:int):
|
|
313
|
+
# r.delete(job_name+'_total')
|
|
314
|
+
# r.hincrby(name=job_name, key=task_name+'_total', amount=total_job)
|
|
315
|
+
self.r.hset(job_name, 'total', str(total_job))
|
|
316
|
+
|
|
317
|
+
def delete_job(self,job_name):
|
|
318
|
+
self.r.delete(job_name)
|
|
319
|
+
pass
|
|
320
|
+
|
|
321
|
+
def query_redis(self,job_name):
|
|
322
|
+
while True:
|
|
323
|
+
print(self.r.hgetall(job_name))
|
|
324
|
+
time.sleep(1)
|
|
325
|
+
|
|
326
|
+
class Check_logs:
|
|
327
|
+
def __init__(self,log_folder):
|
|
328
|
+
self.log_folder = log_folder
|
|
329
|
+
pass
|
|
330
|
+
|
|
331
|
+
def read_err_files(self):
|
|
332
|
+
log_folder = self.log_folder
|
|
333
|
+
log_folder = Path(log_folder)
|
|
334
|
+
err_count = 0
|
|
335
|
+
for f in listdir(log_folder):
|
|
336
|
+
if not f.endswith(".err"):
|
|
337
|
+
continue
|
|
338
|
+
fpath = log_folder / f
|
|
339
|
+
# print(fpath)
|
|
340
|
+
with open(fpath) as fr:
|
|
341
|
+
err_content = fr.read()
|
|
342
|
+
if len(err_content) != 0:
|
|
343
|
+
print('==============')
|
|
344
|
+
print(f"Error in file: {f}")
|
|
345
|
+
print(err_content)
|
|
346
|
+
err_count += 1
|
|
347
|
+
print('#################')
|
|
348
|
+
print(f'Total error logs: {err_count}')
|
|
349
|
+
print('#################')
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
def read_out_files(self):
|
|
353
|
+
log_folder = self.log_folder
|
|
354
|
+
log_folder = Path(log_folder)
|
|
355
|
+
count = 0
|
|
356
|
+
for f in listdir(log_folder):
|
|
357
|
+
if not f.endswith(".out"):
|
|
358
|
+
continue
|
|
359
|
+
fpath = log_folder / f
|
|
360
|
+
# print(fpath)
|
|
361
|
+
with open(fpath) as fr:
|
|
362
|
+
log_content = fr.read()
|
|
363
|
+
print(log_content)
|
|
364
|
+
print('------------')
|
|
365
|
+
count += 1
|
|
366
|
+
print(f'Total files: {count}')
|
|
367
|
+
pass
|
|
368
|
+
|
|
369
|
+
def read_result_files(self):
|
|
370
|
+
log_folder = self.log_folder
|
|
371
|
+
log_folder = Path(log_folder)
|
|
372
|
+
count = 0
|
|
373
|
+
for f in listdir(log_folder):
|
|
374
|
+
if not f.endswith("_result.pkl"):
|
|
375
|
+
continue
|
|
376
|
+
fpath = log_folder / f
|
|
377
|
+
# print(fpath)
|
|
378
|
+
content = pickle.load(open(fpath, 'rb'))
|
|
379
|
+
print(content)
|
|
380
|
+
print('------------')
|
|
381
|
+
count += 1
|
|
382
|
+
print(f'Total files: {count}')
|
|
383
|
+
pass
|
|
384
|
+
|
|
385
|
+
def read_submit_files(self):
|
|
386
|
+
log_folder = self.log_folder
|
|
387
|
+
log_folder = Path(log_folder)
|
|
388
|
+
count = 0
|
|
389
|
+
for f in listdir(log_folder):
|
|
390
|
+
if not f.endswith("_submitted.pkl"):
|
|
391
|
+
continue
|
|
392
|
+
fpath = log_folder / f
|
|
393
|
+
# print(fpath)
|
|
394
|
+
content = pickle.load(open(fpath, 'rb'))
|
|
395
|
+
print(content)
|
|
396
|
+
print('------------')
|
|
397
|
+
count += 1
|
|
398
|
+
print(f'Total files: {count}')
|
|
399
|
+
pass
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def init_job(job_name,param_list):
|
|
403
|
+
total_job = len(param_list)
|
|
404
|
+
r = HPC_redis().conn_redis()
|
|
405
|
+
r.delete(job_name)
|
|
406
|
+
r.hset(job_name, 'total', str(total_job))
|
|
407
|
+
r.hset(job_name, 'step', str(0))
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def flush_progress(job_name):
|
|
411
|
+
global _LOCAL_COUNTER
|
|
412
|
+
|
|
413
|
+
if _LOCAL_COUNTER > 0:
|
|
414
|
+
r = get_redis()
|
|
415
|
+
r.hincrby(job_name, "step", _LOCAL_COUNTER)
|
|
416
|
+
_LOCAL_COUNTER = 0
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def update_progress(job_name, batch_size=100):
|
|
420
|
+
update_i(job_name, batch_size)
|
|
421
|
+
flush_progress(job_name)
|
|
422
|
+
|
|
423
|
+
def progress_bar_monitoring(job_name):
|
|
424
|
+
hpc_redis = HPC_redis()
|
|
425
|
+
info=hpc_redis.r.hgetall(job_name)
|
|
426
|
+
total = info.get(b'total')
|
|
427
|
+
step = info.get(b'step')
|
|
428
|
+
total_int = int(total)
|
|
429
|
+
step_int = int(step)
|
|
430
|
+
# exit()
|
|
431
|
+
with Progress(
|
|
432
|
+
TextColumn("[bold yellow]{task.description}"),
|
|
433
|
+
BarColumn(),
|
|
434
|
+
TextColumn("{task.completed}/{task.total} {task.percentage:>3.0f}%"),
|
|
435
|
+
TimeElapsedColumn(),
|
|
436
|
+
IterSpeedColumn(),
|
|
437
|
+
TimeRemainingColumn(),
|
|
438
|
+
# show_speed=True,
|
|
439
|
+
) as progress:
|
|
440
|
+
task = progress.add_task(f"[bold green]{job_name}", total=total_int)
|
|
441
|
+
while True:
|
|
442
|
+
info = hpc_redis.r.hgetall(job_name)
|
|
443
|
+
# print(info)
|
|
444
|
+
step = info.get(b'step')
|
|
445
|
+
step_int = int(step)
|
|
446
|
+
progress.update(task, completed=step_int)
|
|
447
|
+
if step_int >= total_int:
|
|
448
|
+
break
|
|
449
|
+
time.sleep(1)
|
|
450
|
+
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lytools_HPC
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Home-page: https://github.com/leeyang1991/lytools_HPC
|
|
5
|
+
Author: Yang Li
|
|
6
|
+
Author-email: leeyang1991@gmail.com
|
|
7
|
+
Requires-Python: >=3
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: submitit
|
|
10
|
+
Requires-Dist: redis
|
|
11
|
+
Requires-Dist: pathos
|
|
12
|
+
Requires-Dist: rich
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: packaging
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: author-email
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: requires-dist
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
|
|
23
|
+
# HPC Functions
|
|
24
|
+
|
|
25
|
+
## Monitoring you jobs with a progress bar.
|
|
26
|
+
|
|
27
|
+
This requires a Redis server to store the progress information. The following steps will guide you through setting up the Redis server and using submitit with a progress bar.
|
|
28
|
+
### Python packages required:
|
|
29
|
+
```bash
|
|
30
|
+
pip install submitit redis pathos rich
|
|
31
|
+
```
|
|
32
|
+
### Step 1: Deploy a Redis service
|
|
33
|
+
**DO NOT** deploy it on a HPC nodes. You can use your local machine or a server with a public IP address.
|
|
34
|
+
#### Deploy redis via Docker
|
|
35
|
+
Compose file for Redis server:
|
|
36
|
+
```yaml
|
|
37
|
+
services:
|
|
38
|
+
redis-progress:
|
|
39
|
+
image: redis:7
|
|
40
|
+
container_name: redis-progress
|
|
41
|
+
restart: unless-stopped
|
|
42
|
+
|
|
43
|
+
ports:
|
|
44
|
+
- "6379:6379"
|
|
45
|
+
|
|
46
|
+
volumes:
|
|
47
|
+
- ./redis_data:/data
|
|
48
|
+
|
|
49
|
+
command: >
|
|
50
|
+
redis-server
|
|
51
|
+
--appendonly yes
|
|
52
|
+
--requirepass yourpassword
|
|
53
|
+
--maxmemory 2gb
|
|
54
|
+
--maxmemory-policy allkeys-lru
|
|
55
|
+
|
|
56
|
+
deploy:
|
|
57
|
+
resources:
|
|
58
|
+
limits:
|
|
59
|
+
memory: 3g
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
```docker compose up -d``` # Start the Redis server
|
|
63
|
+
|
|
64
|
+
#### Test Redis connection
|
|
65
|
+
```python
|
|
66
|
+
import redis
|
|
67
|
+
r = redis.Redis(host='your_redis_host', port=6379, password='yourpassword')
|
|
68
|
+
r.set('test_key', 'test_value')
|
|
69
|
+
print(r.get('test_key').decode())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Create redis connection configuration file
|
|
73
|
+
Location: ```~/.config/redis/redis.conf```
|
|
74
|
+
```txt
|
|
75
|
+
your_redis_host
|
|
76
|
+
6379
|
|
77
|
+
yourpassword
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Step 2: Submit your jobs
|
|
81
|
+
```python
|
|
82
|
+
from HPC_func import *
|
|
83
|
+
|
|
84
|
+
def my_function(x):
|
|
85
|
+
return x * x
|
|
86
|
+
|
|
87
|
+
job_name = 'Your_job_name'
|
|
88
|
+
params_list = [1,2,3,4,5,6,7,8,9,10]
|
|
89
|
+
log_folder = 'Your_log_folder'
|
|
90
|
+
init_job(job_name, params_list)
|
|
91
|
+
sumbit_jobs_array(my_function, params_list, log_folder,
|
|
92
|
+
job_name=job_name,
|
|
93
|
+
job_number_limit=1,
|
|
94
|
+
parallel_process_per_task=2,
|
|
95
|
+
slurm_array_parallelism=1,
|
|
96
|
+
parallel_process_p_or_t='p',
|
|
97
|
+
cpus_per_task=10,
|
|
98
|
+
mem_gb=2,
|
|
99
|
+
timeout_min=10,
|
|
100
|
+
slurm_partition="general",
|
|
101
|
+
exclude_nodes=None,
|
|
102
|
+
pbar_update_freq=1,
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Step 3: Monitor the progress bar
|
|
107
|
+
```python
|
|
108
|
+
from HPC_func import *
|
|
109
|
+
job_name = 'Your_job_name'
|
|
110
|
+
progress_bar_monitoring(job_name)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Step 4: Check the log files
|
|
114
|
+
```python
|
|
115
|
+
from HPC_func import *
|
|
116
|
+
log_folder = 'Your_log_folder'
|
|
117
|
+
Check_logs(log_folder).read_err_files() # Read error files
|
|
118
|
+
Check_logs(log_folder).read_out_files() # Read output files
|
|
119
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
lytools_HPC/__init__.py,sha256=4EY45Ok2uAxesY3AQo-puVc7rpfv6-dh7mA74GOatfo,1573
|
|
2
|
+
lytools_HPC/_lytools_HPC.py,sha256=RirBkJmKsCoocgBnY68Ni5a5WtOqtmOG9LxrFOHe9Qg,13578
|
|
3
|
+
lytools_hpc-0.0.1.dist-info/METADATA,sha256=QrnBDjy4_uCMgCkuN6ZYv5Bqc_pxTkPqohndO23iP2g,2944
|
|
4
|
+
lytools_hpc-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
5
|
+
lytools_hpc-0.0.1.dist-info/top_level.txt,sha256=VIE9YTZbofWmUjTl3AFFJGfmWTOX7EV68IYJMMwsQuk,12
|
|
6
|
+
lytools_hpc-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lytools_HPC
|