22
33import argparse
44import logging
5+ import math
56import os
7+ import subprocess
68import time
79from datetime import datetime
810from functools import partial , singledispatch , wraps
3941import murfey .server .prometheus as prom
4042import murfey .server .websocket
4143from murfey .client .contexts .tomo import _midpoint
42- from murfey .server .config import get_hostname , get_machine_config , get_microscope
44+ from murfey .server .config import (
45+ MachineConfig ,
46+ get_hostname ,
47+ get_machine_config ,
48+ get_microscope ,
49+ )
4350from murfey .server .murfey_db import url # murfey_db
4451
4552try :
@@ -1547,6 +1554,95 @@ def _register_class_selection(message: dict, _db=murfey_db, demo: bool = False):
15471554 _db .close ()
15481555
15491556
1557+ def _find_initial_model (visit : str , machine_config : MachineConfig ) -> Path | None :
1558+ if machine_config .initial_model_search_directory :
1559+ visit_directory = (
1560+ machine_config .rsync_basepath
1561+ / (machine_config .rsync_module or "data" )
1562+ / str (datetime .now ().year )
1563+ / visit
1564+ )
1565+ possible_models = list (
1566+ (visit_directory / machine_config .initial_model_search_directory ).glob (
1567+ "*.mrc"
1568+ )
1569+ )
1570+ if possible_models :
1571+ return sorted (possible_models , key = lambda x : x .stat ().st_ctime )[- 1 ]
1572+ return None
1573+
1574+
1575+ def _downscaled_box_size (
1576+ particle_diameter : int , pixel_size : float
1577+ ) -> Tuple [int , float ]:
1578+ box_size = int (math .ceil (1.2 * particle_diameter ))
1579+ box_size = box_size + box_size % 2
1580+ for small_box_pix in (
1581+ 48 ,
1582+ 64 ,
1583+ 96 ,
1584+ 128 ,
1585+ 160 ,
1586+ 192 ,
1587+ 256 ,
1588+ 288 ,
1589+ 300 ,
1590+ 320 ,
1591+ 360 ,
1592+ 384 ,
1593+ 400 ,
1594+ 420 ,
1595+ 450 ,
1596+ 480 ,
1597+ 512 ,
1598+ 640 ,
1599+ 768 ,
1600+ 896 ,
1601+ 1024 ,
1602+ ):
1603+ # Don't go larger than the original box
1604+ if small_box_pix > box_size :
1605+ return box_size , pixel_size
1606+ # If Nyquist freq. is better than 8.5 A, use this downscaled box, else step size
1607+ small_box_angpix = pixel_size * box_size / small_box_pix
1608+ if small_box_angpix < 4.25 :
1609+ return small_box_pix , small_box_angpix
1610+ raise ValueError (f"Box size is too large: { box_size } " )
1611+
1612+
1613+ def _resize_intial_model (
1614+ downscaled_box_size : int ,
1615+ downscaled_pixel_size : float ,
1616+ input_path : Path ,
1617+ output_path : Path ,
1618+ executables : Dict [str , str ],
1619+ env : Dict [str , str ],
1620+ ) -> None :
1621+ if executables .get ("relion_image_handler" ):
1622+ comp_proc = subprocess .run (
1623+ [
1624+ f"{ executables ['relion_image_handler' ]} " ,
1625+ "--i" ,
1626+ str (input_path ),
1627+ "--new_box" ,
1628+ str (downscaled_box_size ),
1629+ "--rescale_angpix" ,
1630+ str (downscaled_pixel_size ),
1631+ "--force_header_angpix" ,
1632+ "--o" ,
1633+ str (output_path ),
1634+ ],
1635+ capture_output = True ,
1636+ text = True ,
1637+ env = env ,
1638+ )
1639+ if comp_proc .returncode :
1640+ logger .error (
1641+ f"Resizing initial model { input_path } failed \n { comp_proc .stdout } "
1642+ )
1643+ return None
1644+
1645+
15501646def _register_3d_batch (message : dict , _db = murfey_db , demo : bool = False ):
15511647 """Received 3d batch from class selection service"""
15521648 class3d_message = message .get ("class3d_message" )
@@ -1567,6 +1663,36 @@ def _register_3d_batch(message: dict, _db=murfey_db, demo: bool = False):
15671663 ).one ()
15681664 other_options = dict (feedback_params )
15691665
1666+ visit_name = (
1667+ _db .exec (
1668+ select (db .ClientEnvironment ).where (
1669+ db .ClientEnvironment .session_id == message ["session_id" ]
1670+ )
1671+ )
1672+ .one ()
1673+ .visit
1674+ )
1675+
1676+ provided_initial_model = _find_initial_model (visit_name , machine_config )
1677+ if provided_initial_model :
1678+ rescaled_initial_model_path = (
1679+ provided_initial_model .parent
1680+ / f"{ provided_initial_model .stem } _rescaled{ provided_initial_model .suffix } "
1681+ )
1682+ _resize_intial_model (
1683+ * _downscaled_box_size (
1684+ message ["particle_diameter" ],
1685+ relion_options ["angpix" ],
1686+ ),
1687+ provided_initial_model ,
1688+ rescaled_initial_model_path ,
1689+ machine_config .external_executables ,
1690+ machine_config .external_environment ,
1691+ )
1692+ feedback_params .initial_model = str (rescaled_initial_model_path )
1693+ _db .add (feedback_params )
1694+ _db .commit ()
1695+
15701696 if feedback_params .hold_class3d :
15711697 # If waiting then save the message
15721698 class3d_params = _db .exec (
0 commit comments