3434import datetime
3535import functools
3636import os
37- import pathlib
3837import shutil
3938import string
4039import textwrap
5655from alphafold3 .model import params
5756from alphafold3 .model import post_processing
5857from alphafold3 .model .components import utils
58+ from etils import epath
5959import haiku as hk
6060import jax
6161from jax import numpy as jnp
6262import numpy as np
6363import tokamax
6464
65- _HOME_DIR = pathlib .Path . home ()
65+ _HOME_DIR = epath .Path ( '~' ). expanduser ()
6666_DEFAULT_MODEL_DIR = _HOME_DIR / 'models'
6767_DEFAULT_DB_DIR = _HOME_DIR / 'public_databases'
6868
69-
7069# Input and output paths.
71- _JSON_PATH = flags . DEFINE_string (
70+ _JSON_PATH = epath . DEFINE_path (
7271 'json_path' ,
7372 None ,
7473 'Path to the input JSON file.' ,
7574)
76- _INPUT_DIR = flags . DEFINE_string (
75+ _INPUT_DIR = epath . DEFINE_path (
7776 'input_dir' ,
7877 None ,
7978 'Path to the directory containing input JSON files.' ,
8079)
81- _OUTPUT_DIR = flags . DEFINE_string (
80+ _OUTPUT_DIR = epath . DEFINE_path (
8281 'output_dir' ,
8382 None ,
8483 'Path to a directory where the results will be saved.' ,
8584)
86- MODEL_DIR = flags . DEFINE_string (
85+ MODEL_DIR = epath . DEFINE_path (
8786 'model_dir' ,
8887 _DEFAULT_MODEL_DIR .as_posix (),
8988 'Path to the model to use for inference.' ,
220219 ' calculation. Must be set for sharded databases.' ,
221220 lower_bound = 0.0 ,
222221)
223- _PDB_DATABASE_PATH = flags . DEFINE_string (
222+ _PDB_DATABASE_PATH = epath . DEFINE_path (
224223 'pdb_database_path' ,
225224 '${DB_DIR}/mmcif_files' ,
226225 'PDB database directory with mmCIF files path, used for template search.' ,
@@ -420,11 +419,11 @@ def __init__(
420419 self ,
421420 config : model .Model .Config ,
422421 device : jax .Device ,
423- model_dir : pathlib . Path ,
422+ model_dir : epath . PathLike ,
424423 ):
425424 self ._model_config = config
426425 self ._device = device
427- self ._model_dir = model_dir
426+ self ._model_dir = epath . Path ( model_dir )
428427
429428 @functools .cached_property
430429 def model_params (self ) -> hk .Params :
@@ -605,19 +604,19 @@ def predict_structure(
605604
606605def write_fold_input_json (
607606 fold_input : folding_input .Input ,
608- output_dir : os .PathLike [ str ] | str ,
607+ output_dir : epath .PathLike ,
609608) -> None :
610609 """Writes the input JSON to the output directory."""
611- os .makedirs (output_dir , exist_ok = True )
612- path = os .path .join (output_dir , f'{ fold_input .sanitised_name ()} _data.json' )
610+ output_dir = epath .Path (output_dir )
611+ output_dir .mkdir (parents = True , exist_ok = True )
612+ path = output_dir / f'{ fold_input .sanitised_name ()} _data.json'
613613 print (f'Writing model input JSON to { path } ' )
614- with open (path , 'wt' ) as f :
615- f .write (fold_input .to_json ())
614+ path .write_text (fold_input .to_json ())
616615
617616
618617def write_outputs (
619618 all_inference_results : Sequence [ResultsForSeed ],
620- output_dir : os .PathLike [ str ] | str ,
619+ output_dir : epath .PathLike ,
621620 job_name : str ,
622621 compress_large_output_files : bool = False ,
623622) -> None :
@@ -627,15 +626,16 @@ def write_outputs(
627626 max_ranking_result = None
628627
629628 output_terms = (
630- pathlib .Path (alphafold3 .cpp .__file__ ).parent / 'OUTPUT_TERMS_OF_USE.md'
629+ epath .Path (alphafold3 .cpp .__file__ ).parent / 'OUTPUT_TERMS_OF_USE.md'
631630 ).read_text ()
632631
633- os .makedirs (output_dir , exist_ok = True )
632+ output_dir = epath .Path (output_dir )
633+ output_dir .mkdir (parents = True , exist_ok = True )
634634 for results_for_seed in all_inference_results :
635635 seed = results_for_seed .seed
636636 for sample_idx , result in enumerate (results_for_seed .inference_results ):
637- sample_dir = os . path . join ( output_dir , f'seed-{ seed } _sample-{ sample_idx } ' )
638- os . makedirs ( sample_dir , exist_ok = True )
637+ sample_dir = output_dir / f'seed-{ seed } _sample-{ sample_idx } '
638+ sample_dir . mkdir ( parents = True , exist_ok = True )
639639 post_processing .write_output (
640640 inference_result = result ,
641641 output_dir = sample_dir ,
@@ -649,21 +649,19 @@ def write_outputs(
649649 max_ranking_result = result
650650
651651 if embeddings := results_for_seed .embeddings :
652- embeddings_dir = os . path . join ( output_dir , f'seed-{ seed } _embeddings' )
653- os . makedirs ( embeddings_dir , exist_ok = True )
652+ embeddings_dir = output_dir / f'seed-{ seed } _embeddings'
653+ embeddings_dir . mkdir ( parents = True , exist_ok = True )
654654 post_processing .write_embeddings (
655655 embeddings = embeddings ,
656656 output_dir = embeddings_dir ,
657657 name = f'{ job_name } _seed-{ seed } ' ,
658658 )
659659
660660 if (distogram := results_for_seed .distogram ) is not None :
661- distogram_dir = os .path .join (output_dir , f'seed-{ seed } _distogram' )
662- os .makedirs (distogram_dir , exist_ok = True )
663- distogram_path = os .path .join (
664- distogram_dir , f'{ job_name } _seed-{ seed } _distogram.npz'
665- )
666- with open (distogram_path , 'wb' ) as f :
661+ distogram_dir = output_dir / f'seed-{ seed } _distogram'
662+ distogram_dir .mkdir (parents = True , exist_ok = True )
663+ distogram_path = distogram_dir / f'{ job_name } _seed-{ seed } _distogram.npz'
664+ with distogram_path .open ('wb' ) as f :
667665 np .savez_compressed (f , distogram = distogram .astype (np .float16 ))
668666
669667 if max_ranking_result is not None : # True iff ranking_scores non-empty.
@@ -677,29 +675,31 @@ def write_outputs(
677675 )
678676 # Save csv of ranking scores with seeds and sample indices, to allow easier
679677 # comparison of ranking scores across different runs.
680- with open (
681- os .path .join (output_dir , f'{ job_name } _ranking_scores.csv' ), 'wt'
682- ) as f :
678+ ranking_scores_csv_path = output_dir / f'{ job_name } _ranking_scores.csv'
679+ with ranking_scores_csv_path .open ('w' ) as f :
683680 writer = csv .writer (f )
684681 writer .writerow (['seed' , 'sample' , 'ranking_score' ])
685682 writer .writerows (ranking_scores )
686683
687684
688- def replace_db_dir (path_with_db_dir : str , db_dirs : Sequence [str ]) -> str :
685+ def replace_db_dir (
686+ path_with_db_dir : epath .PathLike , db_dirs : Sequence [str ]
687+ ) -> str :
689688 """Replaces the DB_DIR placeholder in a path with the given DB_DIR."""
689+ path_with_db_dir = os .fspath (path_with_db_dir )
690690 template = string .Template (path_with_db_dir )
691691 if 'DB_DIR' in template .get_identifiers ():
692692 for db_dir in db_dirs :
693693 path = template .substitute (DB_DIR = db_dir )
694- if os . path .exists (path ):
694+ if epath . Path ( path ) .exists ():
695695 return path
696696 raise FileNotFoundError (
697697 f'{ path_with_db_dir } with ${{DB_DIR}} not found in any of { db_dirs } .'
698698 )
699699 if (sharded_paths := shards .get_sharded_paths (path_with_db_dir )) is not None :
700- db_exists = all (os . path .exists (p ) for p in sharded_paths )
700+ db_exists = all (epath . Path ( p ) .exists () for p in sharded_paths )
701701 else :
702- db_exists = os . path .exists (path_with_db_dir )
702+ db_exists = epath . Path ( path_with_db_dir ) .exists ()
703703 if not db_exists :
704704 raise FileNotFoundError (f'{ path_with_db_dir } does not exist.' )
705705 return path_with_db_dir
@@ -711,7 +711,7 @@ def process_fold_input(
711711 data_pipeline_config : pipeline .DataPipelineConfig | None ,
712712 * ,
713713 model_runner : None ,
714- output_dir : os .PathLike [ str ] | str ,
714+ output_dir : epath .PathLike ,
715715 buckets : Sequence [int ] | None = None ,
716716 ref_max_modified_date : datetime .date | None = None ,
717717 conformer_max_iterations : int | None = None ,
@@ -729,7 +729,7 @@ def process_fold_input(
729729 data_pipeline_config : pipeline .DataPipelineConfig | None ,
730730 * ,
731731 model_runner : ModelRunner ,
732- output_dir : os .PathLike [ str ] | str ,
732+ output_dir : epath .PathLike ,
733733 buckets : Sequence [int ] | None = None ,
734734 ref_max_modified_date : datetime .date | None = None ,
735735 conformer_max_iterations : int | None = None ,
@@ -746,7 +746,7 @@ def process_fold_input(
746746 data_pipeline_config : pipeline .DataPipelineConfig | None ,
747747 * ,
748748 model_runner : ModelRunner | None ,
749- output_dir : os .PathLike [ str ] | str ,
749+ output_dir : epath .PathLike ,
750750 buckets : Sequence [int ] | None = None ,
751751 ref_max_modified_date : datetime .date | None = None ,
752752 conformer_max_iterations : int | None = None ,
@@ -803,13 +803,11 @@ def process_fold_input(
803803 if not fold_input .chains :
804804 raise ValueError ('Fold input has no chains.' )
805805
806- if (
807- not force_output_dir
808- and os .path .exists (output_dir )
809- and os .listdir (output_dir )
810- ):
806+ output_dir = epath .Path (output_dir )
807+ if not force_output_dir and output_dir .exists () and any (output_dir .iterdir ()):
811808 new_output_dir = (
812- f'{ output_dir } _{ datetime .datetime .now ().strftime ("%Y%m%d_%H%M%S" )} '
809+ output_dir .parent
810+ / f'{ output_dir .name } _{ datetime .datetime .now ().strftime ("%Y%m%d_%H%M%S" )} '
813811 )
814812 print (
815813 f'Output will be written in { new_output_dir } since { output_dir } is'
@@ -874,13 +872,9 @@ def main(_):
874872 )
875873
876874 if _INPUT_DIR .value is not None :
877- fold_inputs = folding_input .load_fold_inputs_from_dir (
878- pathlib .Path (_INPUT_DIR .value )
879- )
875+ fold_inputs = folding_input .load_fold_inputs_from_dir (_INPUT_DIR .value )
880876 elif _JSON_PATH .value is not None :
881- fold_inputs = folding_input .load_fold_inputs_from_path (
882- pathlib .Path (_JSON_PATH .value )
883- )
877+ fold_inputs = folding_input .load_fold_inputs_from_path (_JSON_PATH .value )
884878 else :
885879 raise AssertionError (
886880 'Exactly one of --json_path or --input_dir must be specified.'
@@ -891,7 +885,7 @@ def main(_):
891885
892886 # Make sure we can create the output directory before running anything.
893887 try :
894- os . makedirs ( _OUTPUT_DIR .value , exist_ok = True )
888+ _OUTPUT_DIR .value . mkdir ( parents = True , exist_ok = True )
895889 except OSError as e :
896890 print (f'Failed to create output directory { _OUTPUT_DIR .value } : { e } ' )
897891 raise
@@ -993,7 +987,7 @@ def main(_):
993987 return_distogram = _SAVE_DISTOGRAM .value ,
994988 ),
995989 device = devices [_GPU_DEVICE .value ],
996- model_dir = pathlib . Path ( MODEL_DIR .value ) ,
990+ model_dir = MODEL_DIR .value ,
997991 )
998992 # Check we can load the model parameters before launching anything.
999993 print ('Checking that model parameters can be loaded...' )
@@ -1010,7 +1004,7 @@ def main(_):
10101004 fold_input = fold_input ,
10111005 data_pipeline_config = data_pipeline_config ,
10121006 model_runner = model_runner ,
1013- output_dir = os . path . join ( _OUTPUT_DIR .value , fold_input .sanitised_name () ),
1007+ output_dir = _OUTPUT_DIR .value / fold_input .sanitised_name (),
10141008 buckets = tuple (int (bucket ) for bucket in _BUCKETS .value ),
10151009 ref_max_modified_date = max_template_date ,
10161010 conformer_max_iterations = _CONFORMER_MAX_ITERATIONS .value ,
0 commit comments