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()?
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)
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: