ComputationalCryoEM / ASPIRE-Python

Algorithms for Single Particle Reconstruction
http://spr.math.princeton.edu
GNU General Public License v3.0
49 stars 21 forks source link

Separate Relion-specific defaults from `ImageSource` #519

Closed chris-langfield closed 2 years ago

chris-langfield commented 3 years ago

WIP

The ImageSource class of aspire.source.image contains a number of Relion-specific parameters and defaults. This causes some issues when attempting to subclass for other types of data sources.

This issue can start as a description of parts of this class that currently rely on or set these defaults.

One of the main issues is the assumption baked into the ImageSource code that the data source will contain metadata related to CTF.

The __init__ of RelionSource depends on this behavior of ImageSource.filter_indices. In this code:

  filter_params, filter_indices = np.unique(                                                                     
            metadata[                                                                                                  
                [                                                                                                      
                    "_rlnVoltage",                                                                                     
                    "_rlnDefocusU",                                                                                    
                    "_rlnDefocusV",                                                                                    
                    "_rlnDefocusAngle",                                                                                
                    "_rlnSphericalAberration",                                                                         
                    "_rlnAmplitudeContrast",                                                                           
                ]                                                                                                      
            ].values,                                                                                                  
            return_inverse=True,                                                                                       
            axis=0,                                                                                                    
        )                 

filter_indices is set to a partition of the indices of metadata according to unique CTF parameters. But hidden in this assignment is the code in the setter above, which populates Relion-specific metadata. The sequence of events is, more or less:


CTF_params = {
            "_rlnVoltage",                                                                                     
            "_rlnDefocusU",                                                                                    
            "_rlnDefocusV",                                                                                    
            "_rlnDefocusAngle",                                                                        
            "_rlnAmplitudeContrast",   
         }
if CTF_params.issubset(metadata.columns):
    filter_params, filter_indices = np.unique(metadata[list(CTF_params)])
    filters = []                                                                                                   
        for row in filter_params:                                                                                      
            filters.append(                                                                                            
                CTFFilter(                                                                                             
                    pixel_size=self.pixel_size,                                                                        
                    voltage=row[0],                                                                                    
                    defocus_u=row[1],                                                                                  
                    defocus_v=row[2],                                                                                  
                    defocus_ang=row[3] * np.pi / 180,  # degrees to radians                                            
                    Cs=row[4],                                                                                         
                    alpha=row[5],                                                                                      
                    B=B,                                                                                               
                )                                                                                                      
            )                                                                                                          

        filter_values = np.zeros((len(indices), len(attribute_list)))                                              
        for i, filt in enumerate(self.unique_filters):                                                             
            filter_values[indices == i] = [                                                                        
                getattr(filt, attribute, np.nan) for attribute in attribute_list                                   
            ]
        self.set_metadata(list(CTF_params), filter_values)

This could probably be turned into its own method, and the extra code removed from the setter of filter_indices.

garrettwrong commented 2 years ago

@chris-langfield , this one is done too right?

chris-langfield commented 2 years ago

Yes, ty