How to remove the modifier attributes computed from data.apply() method

Hi,

I wrote a user-defined modifier to compute the porosity under different virtual probe sphere radius, just like

def radius_porosity(frame, data):
    table = data.tables.create(identifier='r-porosity', plot_mode=DataTable.PlotMode.Scatter, title='Porosity vs Radius')
    x_coords = np.arange(1.0, 3.1, 0.1)
    table.x = table.create_property('Radius (Angstrom)', data=x_coords)
    porosity = np.zeros(len(x_coords))

    cellvolume = data.cell.volume
    for i, r in enumerate(x_coords):
        data.apply(ConstructSurfaceModifier(   
            method = ConstructSurfaceModifier.Method.AlphaShape,    
            radius = r,    
            identify_regions = True))  
        # print(data.attributes)
        
        # method1
        porosity[i] = data.attributes['ConstructSurfaceMesh.void_volume']/cellvolume*100 
        del data.attributes['ConstructSurfaceMesh.void_volume'] 

        # method2
        # if i == 0:
        #     porosity[i] = data.attributes['ConstructSurfaceMesh.void_volume']/cellvolume*100
        # else:
        #     porosity[i] = data.attributes[f'ConstructSurfaceMesh.void_volume.{i+1}']/cellvolume*100  
        
    table.y = table.create_property('Porosity (%)', data=porosity)

The question is every time when the

data.apply(ConstructSurfaceModifier())

was used, a bunch of related global attributes will be produced and will be numbered automatically. Here in the example above, yes I can realize what I want by using the two methods above. However, I just wonder is there any other method let me can directly remove all of the effects produced by previous data.apply()?

Thanks a lot!

Best regards,
Fan

If you do not need the respective Surface Meshes and Global attributes to be part of the pipeline, you could clone the Data Collection and do the following:

from ovito.data import *
import numpy as np 
from ovito.modifiers import *

def calculate_porosity(data: DataCollection, r: float):
    data = data.clone()
    data.apply(ConstructSurfaceModifier(   
            method = ConstructSurfaceModifier.Method.AlphaShape,    
            radius = r,    
            identify_regions = True))  
    return data.attributes['ConstructSurfaceMesh.void_volume']/data.cell.volume*100 

def modify(frame: int, data: DataCollection):
    table = data.tables.create(identifier='r-porosity', plot_mode=DataTable.PlotMode.Scatter, title='Porosity vs Radius')
    x_coords = np.arange(1.0, 3.1, 0.1)
    table.x = table.create_property('Radius (Angstrom)', data=x_coords)
    porosity = np.zeros(len(x_coords))

    for i, r in enumerate(x_coords):
        porosity[i] = calculate_porosity(data, r)
    
    table.y = table.create_property('Porosity (%)', data=porosity)

Hi kalcher,

Thanks a lot. I really appreciate the detailed instructions. That’s really helpful. I never thought we can do like this. Thanks again!

Cheers,
Fan

Great! Here are some additional tips for you and others reading this: If you’d like to experiment with a more interactive version of this parameter study, you can use OVITO Pro’s advanced Python API. Once you’ve loaded a static frame into OVITO’s interactive viewport, the following Python modifier code will generate individual animation frames for each value within the specified probe sphere radius range (highlighted in red in the screenshot). By pressing the play button, you’ll be able to see the resulting Surface Meshes.

from ovito.data import DataCollection
from ovito.modifiers import ConstructSurfaceModifier
from ovito.pipeline import ModifierInterface
import numpy as np
from ovito.traits import Vector3, OvitoObject
from traits.api import observe
from ovito.vis import SurfaceMeshVis

class ParameterStudy(ModifierInterface):
    r = Vector3(default=(1.0, 3.1, 0.1), label = "Value range (start, stop, step)", ovito_group = "Probe sphere radius")
    mesh_vis = OvitoObject(SurfaceMeshVis, title="Surface Mesh")
    
    def compute_trajectory_length(self, **kwargs):
        return len(np.arange(*self.r))

    def modify(self, data: DataCollection, *, frame: int, **kwargs):
        data.apply(ConstructSurfaceModifier(   
            method = ConstructSurfaceModifier.Method.AlphaShape,    
            radius = np.arange(*self.r)[frame], 
            smoothing_level = 0,
            identify_regions=True,
            vis = self.mesh_vis))  
        data.attributes["Porosity"] = data.attributes['ConstructSurfaceMesh.void_volume']/data.cell.volume*100 
        data.attributes["Probe sphere radius"] = np.arange(*self.r)[frame]
        
    # This is needed to notify the pipeline system whenever the animation length is changed by the user:
    @observe("r")
    def anim_duration_changed(self, event):
        self.notify_trajectory_length_changed()
      

In a next step, you can then use the Time Series Modifier to generate the data table containing the evolution of calculated porosity values and add a Data Plot Viewport Layer to easily generate an animation like the following:

This is all based on this code-example given in the OVITO python reference:
https://www.ovito.org/docs/current/python/modules/ovito_pipeline.html#ovito.pipeline.ModifierInterface.compute_trajectory_length

1 Like

That’s pretty cool! Thanks a lot! Hope it can help more researchers.

Cheers!

1 Like