-
Notifications
You must be signed in to change notification settings - Fork 0
Updating for last run #2
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
base: agnosticAgent
Are you sure you want to change the base?
Changes from all commits
6244cfa
faa2d41
d4e034f
d6a1095
a3e3ee0
5842793
7e87190
b734d50
3c4d245
ed13a7e
c8cc083
387f01a
b8e6f50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # AlphaFold Pipeline Designs | ||
|
|
||
| This document explains the two pipeline designs for running AlphaFold tasks with support for multiple structures and GPU binding. | ||
|
|
||
| --- | ||
|
|
||
| ## 1. Single Pipeline with Parallel Structures (Current) | ||
|
|
||
| A single pipeline manages multiple structures. | ||
| AlphaFold tasks run concurrently within the same pipeline, each bound to a GPU. | ||
|
|
||
| ``` | ||
| Single Pipeline with Parallel Structures | ||
| ---------------------------------------- | ||
| [Pipeline] | ||
| | | ||
| +--> [AlphaFold A] --> [Output A] (GPU0) | ||
| +--> [AlphaFold B] --> [Output B] (GPU1) | ||
| +--> [AlphaFold C] --> [Output C] (GPU2) | ||
| +--> [AlphaFold D] --> [Output D] (GPU3) | ||
| ``` | ||
|
|
||
| - One pipeline orchestrates all structures. | ||
|
|
||
| ## 2. Separate Pipelines Design (Supported and can be enabled) | ||
|
|
||
| Each structure is processed by its own pipeline. | ||
|
|
||
| ``` | ||
| Separate Pipelines Design | ||
| ------------------------- | ||
| [Pipeline 1] --> [AlphaFold A] --> [Output A] | ||
| GPU0 | ||
|
|
||
| [Pipeline 2] --> [AlphaFold B] --> [Output B] | ||
| GPU1 | ||
|
|
||
| [Pipeline 3] --> [AlphaFold C] --> [Output C] | ||
| GPU2 | ||
| ``` | ||
|
|
||
| - Each pipeline launches independently. | ||
| - GPU allocation can be set per pipeline. | ||
| - Suitable if users prefer keeping pipelines isolated. | ||
|
|
||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,10 @@ def __init__(self, name, flow, configs=None, **kwargs): | |
| # Execution metadata | ||
| if configs is None: | ||
| configs = {} | ||
|
|
||
| self.is_child: bool = kwargs.get("is_child", False) | ||
| self.passes = kwargs.get("passes", 1) | ||
| self.start_pass: int = kwargs.get("start_pass", 1) | ||
| self.step_id = kwargs.get("step_id", 1) | ||
| self.seq_rank = kwargs.get("seq_rank", 0) | ||
| self.num_seqs = kwargs.get("num_seqs", 10) | ||
|
|
@@ -44,7 +47,7 @@ def __init__(self, name, flow, configs=None, **kwargs): | |
| ) | ||
| self.output_path_mpnn = os.path.join(self.output_path, "mpnn") | ||
| self.output_path_af = os.path.join( | ||
| self.output_path, "/af/prediction/best_models" | ||
| self.output_path, "af/prediction/best_models" | ||
| ) | ||
|
|
||
| # might have to do outside of initialization, so new pipelines | ||
|
|
@@ -64,6 +67,7 @@ def set_up_new_pipeline_dirs(self, new_pipeline_name): | |
| # all directories to create | ||
| subdirs = [ | ||
| "af/fasta", | ||
| "af/prediction", | ||
| "af/prediction/best_models", | ||
| "af/prediction/best_ptm", | ||
| "af/prediction/dimer_models", | ||
|
|
@@ -83,9 +87,7 @@ def register_pipeline_tasks(self): | |
| """Register all pipeline tasks""" | ||
|
|
||
| @self.auto_register_task() # MPNN | ||
| async def s1(task_description=None): | ||
| if task_description is None: | ||
| task_description = {"ranks": 1} | ||
| async def s1(task_description={"gpus_per_rank": 1}): # noqa: B006 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a mutable default argument like a dictionary is generally discouraged as it can lead to unexpected behavior if the object is modified. While it is not modified here, the previous pattern of using A safer implementation would be: async def s1(task_description=None):
if task_description is None:
task_description = {"gpus_per_rank": 1}
# ... rest of function |
||
| mpnn_script = os.path.join(self.base_path, "mpnn_wrapper.py") | ||
| output_dir = os.path.join(self.output_path_mpnn, f"job_{self.passes}") | ||
|
|
||
|
|
@@ -142,9 +144,7 @@ async def s3(): | |
|
|
||
| # alphafold, must be run separately for each structure one at a time! | ||
| @self.auto_register_task() | ||
| async def s4(target_fasta, task_description=None): | ||
| if task_description is None: | ||
| task_description = {"gpus_per_rank": 1} | ||
| async def s4(target_fasta, task_description={"gpus_per_rank": 1}): # noqa: B006 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the comment on A safer implementation would be: async def s4(target_fasta, task_description=None):
if task_description is None:
task_description = {"gpus_per_rank": 1}
# ... rest of function |
||
| cmd = ( | ||
| f"/bin/bash {self.base_path}/af2_multimer_reduced.sh " | ||
| f"{self.output_path}/af/fasta/ " | ||
|
|
@@ -154,10 +154,8 @@ async def s4(target_fasta, task_description=None): | |
|
|
||
| return cmd | ||
|
|
||
| @self.auto_register_task() # plddt_extract | ||
| async def s5(task_description=None): | ||
| if task_description is None: | ||
| task_description = {} | ||
| @self.auto_register_task() # pLDTT_extract | ||
| async def s5(task_description={}): # noqa: B006 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As with A safer implementation would be: async def s5(task_description=None):
if task_description is None:
task_description = {}
# ... rest of function |
||
| return ( | ||
| f"python3 {self.base_path}/plddt_extract_pipeline.py " | ||
| f"--path={self.base_path} " | ||
|
|
@@ -185,13 +183,22 @@ async def run(self): | |
| while self.passes <= self.max_passes: | ||
| self.logger.pipeline_log(f"Starting pass {self.passes}") | ||
|
|
||
| self.logger.pipeline_log("Submitting MPNN task") | ||
| await self.s1(task_description={"pre_exec": TASK_PRE_EXEC}) | ||
| self.logger.pipeline_log("MPNN task finished") | ||
| if self.is_child and self.passes == self.start_pass: | ||
| self.logger.pipeline_log( | ||
| "Skipping MPNN and Ranking steps for this child pipeline " | ||
| "in the current pass only." | ||
| ) | ||
|
|
||
| pass | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| self.logger.pipeline_log("Submitting sequence ranking task") | ||
| await self.s2() | ||
| self.logger.pipeline_log("Sequence ranking task finished") | ||
| else: | ||
| self.logger.pipeline_log("Submitting MPNN task") | ||
| await self.s1(task_description={"pre_exec": TASK_PRE_EXEC}) | ||
| self.logger.pipeline_log("MPNN task finished") | ||
|
|
||
| self.logger.pipeline_log("Submitting sequence ranking task") | ||
| await self.s2() | ||
| self.logger.pipeline_log("Sequence ranking task finished") | ||
|
|
||
| self.logger.pipeline_log("Submitting scoring task") | ||
| fasta_files = await self.s3() | ||
|
|
@@ -245,7 +252,7 @@ async def run(self): | |
| await asyncio.gather(*alphafold_tasks, return_exceptions=True) | ||
| self.logger.pipeline_log(f"{len(alphafold_tasks)} Alphafold tasks finished") | ||
|
|
||
| self.logger.pipeline_log("Submitting plddt extract") | ||
| self.logger.pipeline_log("Submitting pLDTT extraction task") | ||
|
|
||
| staged_file = f"af_stats_{self.name}_pass_{self.passes}.csv" | ||
|
|
||
|
|
@@ -260,8 +267,11 @@ async def run(self): | |
| ], | ||
| } | ||
| ) | ||
| self.logger.pipeline_log("Plddt extract finished") | ||
| self.logger.pipeline_log("pLDTT extract finished") | ||
|
|
||
| await self.run_adaptive_step(wait=True) | ||
|
|
||
| if self.kill_parent: | ||
| break | ||
|
|
||
| self.passes += 1 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a potential data loss bug here. If
sub_iter_seqsis not empty but a child pipeline cannot be created (e.g.,pipeline.sub_order >= MAX_SUB_PIPELINES), thiselseblock is executed. The proteins insub_iter_seqshave already been removed frompipeline.iter_seqson line 76. Since this block only updatesprevious_scores, those proteins are effectively dropped from any further processing. They should be added back to the current pipeline'siter_seqsif they are not moved to a child.Consider this implementation: