...
 
Commits (12)
......@@ -27,6 +27,7 @@ sudo apt-get install \
liblapack3gf \
ncurses-dev \
libreadline-dev \
libbz2-dev \
povray \
libfftw3-dev
......@@ -285,12 +286,13 @@ Download and build Intel OSPRay
cd $HOME/progs/
git clone https://github.com/ospray/ospray.git
cd ospray
git checkout v1.6.1
mkdir build
cd build
$HOME/progs/cmake-3.6.3-Linux-x86_64/bin/cmake \
-DCMAKE_INSTALL_PREFIX=$HOME/progs/ospray_install \
-DISPC_EXECUTABLE=$HOME/progs/ispc-v1.9.2-linux/ispc \
-Dembree_DIR=$HOME/progs/embree_install/lib/cmake/embree-2.17.0 \
-Dembree_DIR=$HOME/progs/embree_install/lib/cmake/embree-3.2.0 \
-DOSPRAY_APPS_BENCHMARK=OFF \
-DOSPRAY_APPS_EXAMPLEVIEWER=OFF \
-DOSPRAY_APPS_UTILITIES=OFF \
......@@ -345,8 +347,8 @@ $HOME/progs/cmake-3.6.3-Linux-x86_64/bin/cmake \
-DLIBAV_INCLUDE_DIR=$HOME/progs/libav/include \
-DLIBAV_LIBRARY_DIR=$HOME/progs/libav/lib \
-DOVITO_BUILD_PLUGIN_OSPRAY=ON \
-Dospray_DIR=$HOME/progs/ospray_install/lib/cmake/ospray-1.5.0 \
-Dembree_DIR=$HOME/progs/embree_install/lib/cmake/embree-2.17.0 \
-Dospray_DIR=$HOME/progs/ospray_install/lib/cmake/ospray-1.6.1 \
-Dembree_DIR=$HOME/progs/embree_install/lib/cmake/embree-3.2.0 \
-DISPC_EXECUTABLE=$HOME/progs/ispc-v1.9.2-linux/ispc \
-DTBB_INCLUDE_DIR=$HOME/progs/tbb2018_20170919oss/include/ \
-DTBB_LIBRARY=$HOME/progs/tbb2018_20170919oss/lib/intel64/gcc4.7/libtbb.so \
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -86,6 +86,10 @@
<entry><link linkend="particles.modifiers.identify_diamond_structure">Identify diamond structure</link></entry>
<entry>Identifies atoms that are arranged in a cubic or hexagonal diamond lattice.</entry>
</row>
<row>
<entry><link linkend="particles.modifiers.point_defect_analysis">Point defect analysis</link></entry>
<entry>Identifies vacancy and interstitial defect clusters in a crystal lattice.</entry>
</row>
<row>
<entry><link linkend="particles.modifiers.polyhedral_template_matching">Polyhedral Template Matching</link></entry>
<entry>Identifies common crystal structures using the PTM method.</entry>
......@@ -304,6 +308,8 @@
<xi:include href="particles/manual_selection.docbook"/>
<xi:include href="particles/point_defect_analysis.docbook"/>
<xi:include href="particles/polyhedral_template_matching.docbook"/>
<xi:include href="particles/python_script.docbook"/>
......
......@@ -52,21 +52,26 @@
<title>Role of the cutoff radius</title>
<para>
The <emphasis>cutoff radius</emphasis> is a parameter controlling the range of neighbors taken
into account when computing the atomic deformation gradient tensor of one particle. In other words in determines
the size of the spherical volume around the particle over which the relative motion of neighboring particles is averaged to obtain
the continuum deformation measure. As a rule of thumb: Use a small cutoff radius parameter to resolve all local details of the deformation field.
into account when computing the atomic deformation gradient tensor for a particle. In other words this parameter determines
the size of a spherical volume around the particle over which the relative motion of neighboring particles is averaged to obtain
the continuum deformation measure. As a rule of thumb: Use a smaller cutoff radius parameter to resolve all local details of the deformation field.
On the other hand, a large cutoff radius will yield slowly varying (smooth) deformation values at the atomic sites.
</para>
<para>
A typical choice for the cutoff radius parameter is to use the first minimum of the pair distribution function, i.e. placing
the cutoff halfway between the first and the second shell of neighbors in the system at hand. You can calculate the pair distribution
function for the current system using OVITO's <link linkend="particles.modifiers.coordination_analysis">Coordination Analysis</link> modifier.
</para>
<para>
Since the deformation gradient at each site is calculated from the relative motion of neighboring particles, it is important to ensure that sufficient
neighbors are within the given cutoff range (in the undeformed configuration). This puts a lower limit on the
cutoff radius that you can safely use. For three-dimensional systems, OVITO requires at a minimum three neighbor particles within
cutoff radius that you can safely use. For three-dimensional systems, OVITO requires at a minimum three neighbors within
the cutoff range of the central particle in order to calculate a deformation gradient tensor. Furthermore, these three neighbors must not be co-planar.
If the cutoff radius has been chosen too small and there are only less than three neighbors within range of the central particle,
the deformation gradient calculation and all subsequent calculations will fail for this particle. The modifier will notify you about this situation
in the status field and will, for diagnostic purposes, select all "undercoordinated" particles for which the calculation failed.
Depending on the situation, you may react to this kind of condition by bumping up the cutoff radius parameter such that more neighbors become within range
and the calculation succeeds.
If the cutoff radius has been chosen too small and there are less than three neighbors within range of the central particle,
the deformation gradient calculation and all subsequent calculations will fail for the particle. The modifier will notify you about this situation
in the status field and will, for diagnostic purposes, select all such "undercoordinated" particles for which the calculation failed.
Depending on the situation, you may react to this kind of condition by bumping up the cutoff radius parameter in order for more neighbors
to be included in the cutoff sphere.
</para>
</simplesect>
......@@ -90,6 +95,14 @@
is basically the <link xlink:href="http://li.mit.edu/A/Graphics/A/annotate_atomic_strain/Doc/main.pdf">residual of the least-squares fit</link> through which the deformation gradient is calculated.
It may be used as a diagnostic for identifying local irreversible shear transformations in amorphous solids, for example.
</para>
<para>
When calculating <inlineequation><mathphrase>D<superscript>2</superscript><subscript>min</subscript></mathphrase></inlineequation>, OVITO follows the original formulation given by
Falk and Langer, which consists of summing up the squared differences between the actual displacements of neighbors and
the computed affine approximation. In the current implementation, this sum does <emphasis>not</emphasis> get divided by the
number of neighbors within the cutoff range. Thus, the calculated <inlineequation><mathphrase>D<superscript>2</superscript><subscript>min</subscript></mathphrase></inlineequation>
value for a particle will depend on the number of neighbors that are included in the selected cutoff volume and will naturally rise
if you increase the cutoff radius.
</para>
<para>The atomic deformation gradient <inlineequation><mathphrase>F</mathphrase></inlineequation> can be decomposed into the product
<inlineequation><mathphrase>F=RU</mathphrase></inlineequation>
......
......@@ -17,32 +17,33 @@
</imageobject></mediaobject></screenshot></informalfigure>
This analysis modifier identifies point defects in crystalline structures using the so-called Wigner-Seitz cell method. It can be used to count vacancies and interstitials,
for example, or track their motion through the lattice.
</para>
for example, or track the motion of such point defects through the crystal lattice.
</para>
<simplesect>
<title>How the Wigner-Seitz cell method works</title>
<para>
The WS analysis method requires two configurations of the atomistic system as input: the <emphasis>reference
The WS cell method requires two explicit configurations of the atomistic system as input: the <emphasis>reference
configuration</emphasis> and the <emphasis>displaced configuration</emphasis>. The reference configuration defines where
atoms would be located in a defect-free state of the crystal. Thus, typically this is a perfect crystal lattice where
every site is occupied by exactly one atom. The displaced configuration is the one to be analyzed, which
typically contains some point defects, such as vacancies and interstitials, that we want to identify.
atoms would be (or have been) located in the defect-free state of the crystal. Thus, typically this is a perfect crystal lattice where
each site is occupied by exactly one atom. The displaced configuration, on the other hand, is the one to be analyzed, which
typically contains some point defects such as vacancies and interstitials that we want to identify.
</para>
<para>
To describe how this identification process works, we first have to introduce an important terminology:
To describe how this identification process works, we first have to introduce some important terminology:
In the following, atoms from the reference configuration will be denoted as <emphasis>sites</emphasis>,
while atoms from the displaced configuration will simply be referred to as <emphasis>atoms</emphasis>.
This distinction emphasizes that the displaced configuration is an arrangement of real atoms, while
the reference configuration only specifies locations in space where atoms are supposed to be located.
the reference configuration only specifies the <emphasis>locations</emphasis> in space where atoms are supposed to be located.
If an <emphasis>atom</emphasis> is located on or close to a <emphasis>site</emphasis>, we say that
the site is occupied by the atom.
this site is occupied by the atom.
</para>
<para>
The WS cell method determines which atoms occupy what sites. In the end, every atom will be assigned to exactly one site.
Some sites may be occupied by no atom at all. We call these sites <emphasis>vacancy sites</emphasis>. Other sites
may be occupied by more than one atom. We call such sites <emphasis>interstitial sites</emphasis> and the atoms
occupying them <emphasis>interstitial atoms</emphasis>. The assignment of atoms to a site is performed using
The WS cell method effectively determines which sites are occupied by what atoms. Ultimately, each atom will be assigned to exactly one site according to
a scheme that will be described below.
As a result, some sites may not be occupied by any atom at all. We call these sites <emphasis>vacancy sites</emphasis>. Other sites,
on the other hand, may be occupied by more than one atom. We call such sites <emphasis>interstitial sites</emphasis> and the atoms
occupying them <emphasis>interstitial atoms</emphasis>. The assignment of atoms to sites is performed using
a spatial partitioning scheme as depicted in these figures.
</para>
<para>
......@@ -54,23 +55,23 @@
of a site is said to occupy that site.
</para>
<para>
How is the Wigner-Seitz (i.e. Voronoi cell) of a site defined?
How is the Wigner-Seitz (or Voronoi) cell of a site defined?
Mathematically speaking, it is the locus of points in space that are closer to the site
than to any of the other sites. Note that points exactly on the border line between two Wigner-Seitz cells
have exactly the same distance to the two sites at their centers.
have exactly the same distance to the two sites in the cell centers.
</para>
<para>
It is worth pointing out that thanks to this property, the algorithm never has to construct the
geometric shapes of the WS cells explicitly. To determine in which cell a given atom is located, it is sufficient
to determine what is the closest site. The algorithm thus boils down to a closest point search. Taking an atom's location,
the algorithm finds out what site is closest to this location and increments that
It is worth pointing out that thanks to this property, the algorithm never has to actually construct the
geometric shapes of the WS cells explicitly. To determine which cell a given atom is located in, it is sufficient
to determine what is the closest site to the atom's location. Thus, the algorithm boils down to a closest point search.
Taking an atom's location, the algorithm finds out what site is closest to this location and increments that
site's counter, the so-called <emphasis>occupancy number</emphasis>, by one.
</para>
<para>
Note that the number of atoms in the displaced configuration and the number of sites in the reference configuration do not need to
be the same. But in cases where they are, the number of vacancies
and the number of interstitials found by the algorithm will exactly match. That is because, in this case, the sum over all occupancy
numbers is equal to the number of sites in the reference configuration.
numbers is equal to the number of sites in the reference configuration per construction.
</para>
</simplesect>
......
......@@ -16,16 +16,17 @@ def render(args):
xy = args.project_point(positions[pindex])
if xy is None: return
# Determine display radius of particle.
# Determine display radius of the particle.
radius = 0.0
if 'Radius' in data.particles:
radius = data.particles['Radius'][pindex]
elif 'Particle Type' in data.particles:
if radius <= 0 and 'Particle Type' in data.particles:
particle_type = data.particles['Particle Type'][pindex]
radius = data.particles['Particle Type'].type_by_id(particle_type).radius
else:
if radius <= 0:
radius = node.get_vis(ParticlesVis).radius
# Calculate screen-space size of particle in pixels.
# Calculate screen-space size of the particle in pixels.
screen_radius = args.project_size(positions[pindex], radius)
# Draw a dashed circle around the particle.
......
......@@ -249,7 +249,7 @@ The following script demonstrates how to use the `Matplotlib <http://matplotlib.
on top the three-dimensional visualization. The histogram data is dynamically computed by a :py:class:`~ovito.modifiers.HistogramModifier`
in the data pipeline in this example.
.. image:: /../manual/images/viewport_overlays/python_script_scale_bar_example.png
.. image:: /../manual/images/viewport_overlays/python_script_plot_example.png
.. literalinclude:: ../example_snippets/overlay_data_plot.py
:lines: 4-42
......
......@@ -30,6 +30,10 @@ aux_source_directories(SOURCES "Source Files\\api" api)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..)
ADD_LIBRARY(geogram STATIC ${SOURCES})
# Set visibility of symbols in this shared library to hidden by default, except those exported in the source code.
SET_TARGET_PROPERTIES(geogram PROPERTIES CXX_VISIBILITY_PRESET "hidden")
SET_TARGET_PROPERTIES(geogram PROPERTIES VISIBILITY_INLINES_HIDDEN ON)
# Make header files of this library available to dependent targets.
TARGET_INCLUDE_DIRECTORIES(geogram INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/..")
SET_PROPERTY(TARGET geogram PROPERTY POSITION_INDEPENDENT_CODE ON)
......
......@@ -50,7 +50,7 @@ ELSE()
# Since we will link this into the Particles plugin, we need to use the same setting for the fPIC flag.
SET_PROPERTY(TARGET muParser PROPERTY POSITION_INDEPENDENT_CODE ON)
# Export this target.
INSTALL(TARGETS muParser EXPORT OVITO
RUNTIME DESTINATION "${OVITO_RELATIVE_LIBRARY_DIRECTORY}" COMPONENT "runtime"
......
......@@ -159,6 +159,10 @@ public:
const string_type& GetToken() const;
EErrorCodes GetCode() const;
virtual const char* what() const noexcept override {
return GetMsg().c_str();
}
private:
string_type m_strMsg; ///< The message string
string_type m_strFormula; ///< Formula string
......
......@@ -105,7 +105,9 @@ void PromiseState::setFinishedNoSelfLock()
_state = State(_state | Finished);
// Make sure that a result has been set (if not in canceled or error state).
OVITO_ASSERT_MSG(_exceptionStore || isCanceled() || _resultSet.load() || !_resultsTuple, "PromiseState::setFinishedNoSelfLock()", "Result has not been set for this promise state.");
OVITO_ASSERT_MSG(_exceptionStore || isCanceled() || _resultSet.load() || !_resultsTuple,
"PromiseState::setFinishedNoSelfLock()",
qPrintable(QStringLiteral("Result has not been set for the promise state. Please check program code setting the promise state. Progress text: %1").arg(progressText())));
// Run the continuation functions.
for(auto& cont : _continuations)
......
......@@ -41,7 +41,7 @@ SET_OVITO_OBJECT_EDITOR(PointDefectAnalysisModifier, PointDefectAnalysisModifier
void PointDefectAnalysisModifierEditor::createUI(const RolloutInsertionParameters& rolloutParams)
{
// Create the rollout.
QWidget* rollout = createRollout(tr("Point defect analysis"), rolloutParams);
QWidget* rollout = createRollout(tr("Point defect analysis"), rolloutParams, "particles.modifiers.point_defect_analysis.html");
QVBoxLayout* layout = new QVBoxLayout(rollout);
layout->setContentsMargins(4,4,4,4);
......@@ -71,13 +71,13 @@ void PointDefectAnalysisModifierEditor::createUI(const RolloutInsertionParameter
// sublayout->addWidget(onlySelectedParticlesUI->checkBox(), 0, 0);
// Status label.
layout->addWidget(statusLabel());
layout->addWidget(statusLabel());
// Structure list.
StructureListParameterUI* structureTypesPUI = new StructureListParameterUI(this);
layout->addSpacing(10);
layout->addWidget(new QLabel(tr("Structure identification results:")));
layout->addWidget(structureTypesPUI->tableWidget());
// StructureListParameterUI* structureTypesPUI = new StructureListParameterUI(this);
// layout->addSpacing(10);
// layout->addWidget(new QLabel(tr("Structure identification results:")));
// layout->addWidget(structureTypesPUI->tableWidget());
// Open a sub-editor for the vis element.
new SubObjectParameterUI(this, PROPERTY_FIELD(PointDefectAnalysisModifier::pointDefectVis), rolloutParams.after(rollout));
......
......@@ -114,8 +114,10 @@ void ConstructSurfaceModifier::ConstructSurfaceEngine::perform()
size_t numInputParticles = positions()->size();
if(selection())
numInputParticles = positions()->size() - std::count(selection()->constDataInt(), selection()->constDataInt() + selection()->size(), 0);
if(numInputParticles <= 3)
if(numInputParticles <= 3) {
setResult(std::move(_results));
return;
}
// Algorithm is divided into several sub-steps.
// Assign weights to sub-steps according to estimated runtime.
......
......@@ -116,7 +116,6 @@ PipelineFlowState PointDefectResults::apply(TimePoint time, ModifierApplication*
if(MicrostructureObject* oldMicrostructure = output.findObject<MicrostructureObject>())
output.removeObject(oldMicrostructure);
// microstructureObj->addVisElement(modifier->dislocationVis());
// Create a display object for the slip surfaces.
microstructureObj->setDomain(input.findObject<SimulationCellObject>());
microstructureObj->setStorage(microstructure());
microstructureObj->setVisElement(modifier->pointDefectVis());
......@@ -144,8 +143,6 @@ PipelineFlowState PointDefectResults::apply(TimePoint time, ModifierApplication*
poh.outputProperty<ParticleProperty>(atomClusters());
if(latticePositions())
poh.outputProperty<ParticleProperty>(latticePositions());
// if(distanceTransform())
// poh.outputProperty<ParticleProperty>(distanceTransform());
if(defectId())
poh.outputProperty<ParticleProperty>(defectId());
......@@ -176,7 +173,7 @@ PipelineFlowState PointDefectResults::apply(TimePoint time, ModifierApplication*
poh.outputAttribute(QStringLiteral("PointDefectAnalysis.vacancy_site_count"), QVariant::fromValue(numVacancySites));
poh.outputAttribute(QStringLiteral("PointDefectAnalysis.interstitial_atom_count"), QVariant::fromValue(numInterstitialAtoms));
output.setStatus(PipelineStatus(PipelineStatus::Success, PointDefectAnalysisModifier::tr("%1 vacancy defects (%2 total vacant sites)\n%3 interstitial defects (%4 total excess atoms)")
output.setStatus(PipelineStatus(PipelineStatus::Success, PointDefectAnalysisModifier::tr("%1 vacancy-type defects (%2 total vacant sites)\n%3 interstitial-type defects (%4 total excess atoms)")
.arg(numVacancyDefects).arg(numVacancySites)
.arg(numInterstitialDefects).arg(numInterstitialAtoms)));
......
......@@ -354,14 +354,14 @@ bool PointDefectEngine::mapAtomsToPerfectLattice(Cluster* matrixCluster, size_t
const Point3& currentSpatialPos = spatialPositions[currentAtom];
int currentDistTransform = _results->distanceTransform()->getInt(currentAtom);
_elasticMapping.filterEdgesOfVertex(currentAtom, [&](auto vertex2, const Vector3& clusterVector, ClusterTransition* clusterTransition) {
_elasticMapping.filterEdgesOfVertex(currentAtom, [&,this](auto vertex2, const Vector3& clusterVector, ClusterTransition* clusterTransition) {
OVITO_ASSERT(!clusterTransition || clusterTransition->isSelfTransition());
if(clusterTransition && clusterTransition->isSelfTransition()) {
if(!visitedAtoms[vertex2]) {
visitedAtoms[vertex2] = true;
latticePositions->setPoint3(vertex2, currentLatticePos + clusterVector);
spatialPositions[vertex2] = currentSpatialPos +
cell().wrapVector(positions()->getPoint3(vertex2) - positions()->getPoint3(currentAtom));
this->cell().wrapVector(this->positions()->getPoint3(vertex2) - this->positions()->getPoint3(currentAtom));
if(_results->distanceTransform()->getInt(vertex2) < currentDistTransform)
toBeVisited.push_back(vertex2);
else
......@@ -370,10 +370,10 @@ bool PointDefectEngine::mapAtomsToPerfectLattice(Cluster* matrixCluster, size_t
else {
Vector3 lv = latticePositions->getPoint3(vertex2) - (currentLatticePos + clusterVector);
Point3 spatialPos = currentSpatialPos +
cell().wrapVector(positions()->getPoint3(vertex2) - positions()->getPoint3(currentAtom));
this->cell().wrapVector(this->positions()->getPoint3(vertex2) - this->positions()->getPoint3(currentAtom));
if(!spatialPositions[vertex2].equals(spatialPos)) {
Vector3 periodicCellVector = spatialPos - spatialPositions[vertex2];
Vector3 reducedPeriodicCellVector = cell().absoluteToReduced(periodicCellVector);
Vector3 reducedPeriodicCellVector = this->cell().absoluteToReduced(periodicCellVector);
OVITO_ASSERT(!reducedPeriodicCellVector.isZero());
bool found = false;
for(const auto& pbc : pbcShifts) {
......
......@@ -136,10 +136,12 @@ void PointDefectVis::render(TimePoint time, DataObject* dataObject, const Pipeli
int netCount = defect.atomCount - defect.siteCount;
if(netCount < 0) {
vacancySites.push_back(defect.vertex->pos());
if(!scaleSymbolSize()) netCount = -1;
vacancyRadii.push_back(symbolSize() / sqrt(3.0) * pow(-netCount, 1.0/2.0));
}
else if(netCount > 0) {
interstitialSites.push_back(defect.vertex->pos());
if(!scaleSymbolSize()) netCount = 1;
interstitialRadii.push_back(symbolSize() * pow(netCount, 1.0/2.0));
}
}
......
......@@ -11,10 +11,13 @@ ovito.modifiers.ConstructSurfaceModifier = ovito.plugins.CrystalAnalysis.Constru
ovito.modifiers.DislocationAnalysisModifier = ovito.plugins.CrystalAnalysis.DislocationAnalysisModifier
ovito.modifiers.ElasticStrainModifier = ovito.plugins.CrystalAnalysis.ElasticStrainModifier
#ovito.modifiers.GrainSegmentationModifier = ovito.plugins.CrystalAnalysis.GrainSegmentationModifier
ovito.modifiers.PointDefectAnalysisModifier = ovito.plugins.CrystalAnalysis.PointDefectAnalysisModifier
ovito.modifiers.__all__ += ['ConstructSurfaceModifier', 'DislocationAnalysisModifier',
'ElasticStrainModifier']
#ovito.modifiers.__all__ += ['GrainSegmentationModifier']
ovito.modifiers.__all__ += ['PointDefectAnalysisModifier']
# Copy enum list.
ovito.modifiers.ElasticStrainModifier.Lattice = ovito.modifiers.DislocationAnalysisModifier.Lattice
#ovito.modifiers.GrainSegmentationModifier.Lattice = ovito.modifiers.DislocationAnalysisModifier.Lattice
ovito.modifiers.PointDefectAnalysisModifier.Lattice = ovito.modifiers.PointDefectAnalysisModifier.Lattice
......@@ -10,7 +10,8 @@ import ovito.plugins.CrystalAnalysis
# Inject selected classes into parent module.
ovito.vis.DislocationVis = ovito.plugins.CrystalAnalysis.DislocationVis
ovito.vis.PartitionMeshVis = ovito.plugins.CrystalAnalysis.PartitionMeshVis
ovito.vis.__all__ += ['DislocationVis', 'PartitionMeshVis']
ovito.vis.PointDefectVis = ovito.plugins.CrystalAnalysis.PointDefectVis
ovito.vis.__all__ += ['DislocationVis', 'PartitionMeshVis', 'PointDefectVis']
# Inject enum types.
ovito.vis.DislocationVis.Shading = ovito.plugins.PyScript.ArrowShadingMode
......@@ -24,6 +24,8 @@
#include <plugins/crystalanalysis/modifier/dxa/DislocationAnalysisModifier.h>
#include <plugins/crystalanalysis/modifier/dxa/StructureAnalysis.h>
#include <plugins/crystalanalysis/modifier/elasticstrain/ElasticStrainModifier.h>
#include <plugins/crystalanalysis/modifier/latticedefects/PointDefectAnalysisModifier.h>
#include <plugins/crystalanalysis/modifier/latticedefects/PointDefectVis.h>
#include <plugins/crystalanalysis/objects/dislocations/DislocationVis.h>
#include <plugins/crystalanalysis/objects/dislocations/DislocationNetworkObject.h>
#include <plugins/crystalanalysis/objects/clusters/ClusterGraphObject.h>
......@@ -299,7 +301,7 @@ PYBIND11_MODULE(CrystalAnalysis, m)
":Default: ``False``\n")
;
#endif
ovito_class<CAImporter, FileSourceImporter>{m}
;
......@@ -466,6 +468,68 @@ PYBIND11_MODULE(CrystalAnalysis, m)
"\n\n"
":Default: ``True``\n")
;
ovito_class<PointDefectVis, DataVis>(m,
":Base class: :py:class:`ovito.vis.DataVis`\n\n"
"Controls the visual appearance of crystal point defects extracted by a :py:class:`~ovito.modifiers.PointDefectAnalysisModifier`. ")
.def_property("symbol_size", &PointDefectVis::symbolSize, &PointDefectVis::setSymbolSize,
"Specifies the radius of the symbols used for visualizing point defects (in length units)."
"\n\n"
":Default: 1.0\n")
.def_property("scale_symbols", &PointDefectVis::scaleSymbolSize, &PointDefectVis::setScaleSymbolSize,
"Controls whether symbols are scaled according to the number of excess atoms in the defect cluster."
"\n\n"
":Default: ``True``\n")
.def_property("transparency", &PointDefectVis::transparency, &PointDefectVis::setTransparency,
"Controls the transparency of the defect symbols in the range 0 to 1."
"\n\n"
":Default: 0.0\n")
.def_property("vacancy_color", &PointDefectVis::vacancyColor, &PointDefectVis::setVacancyBondColor,
"The display color used for vacancy-type defects (i.e. negative excess atom count)."
"\n\n"
":Default: (0.0, 0.6, 1.0)\n")
.def_property("interstitial_color", &PointDefectVis::interstitialColor, &PointDefectVis::setInterstitialColor,
"The display color used for interstitial-type defects (i.e. positive excess atom count)."
"\n\n"
":Default: (1.0, 0.4, 0.4)\n")
;
auto PointDefectAnalysisModifier_py = ovito_class<PointDefectAnalysisModifier, DislocationAnalysisModifier>(m,
":Base class: :py:class:`ovito.pipeline.Modifier`\n\n"
"This analysis modifier finds vacancy and interstitial defect clusters in a crystal lattice. "
"See also the corresponding `user manual page <../../particles.modifiers.point_defect_analysis.html>`__ for this modifier. "
"\n\n"
"**Modifier outputs:**"
"\n\n"
" * ``PointDefectAnalysis.vacancy_defect_count`` (:py:attr:`attribute <ovito.data.DataCollection.attributes>`):\n"
" The total number of vacancy-type defects (i.e. with a negative excess atom count).\n"
" * ``PointDefectAnalysis.interstitial_defect_count`` (:py:attr:`attribute <ovito.data.DataCollection.attributes>`):\n"
" The total number of interstitial-type defects (i.e. with a positive excess atom count).\n"
" * ``PointDefectAnalysis.vacancy_site_count`` (:py:attr:`attribute <ovito.data.DataCollection.attributes>`):\n"
" The sum of negative excess atom counts of all vacancy-type defects.\n"
" * ``PointDefectAnalysis.interstitial_atom_count`` (:py:attr:`attribute <ovito.data.DataCollection.attributes>`):\n"
" The sum of positive excess atom counts of all interstitial-type defects.\n"
" * ``Structure Type`` (:py:class:`~ovito.data.ParticleProperty`):\n"
" Particle property containing the local structural crystal type assigned to each atom by the modifier.\n"
" * ``Color`` (:py:class:`~ovito.data.ParticleProperty`):\n"
" Color assigned to each particle reflecting its identified structural type.\n"
" * ``Point Defect Identifier`` (:py:class:`~ovito.data.ParticleProperty`):\n"
" Particle property containing the zero-based index of the defect cluster the atom belongs to. Atoms in defect-free regions have a property value of -1.\n"
)
.def_property("input_crystal_structure", &PointDefectAnalysisModifier::inputCrystalStructure, &PointDefectAnalysisModifier::setInputCrystalStructure,
"The type of crystal to analyze. Must be one of: "
"\n\n"
" * ``PointDefectAnalysisModifier.Lattice.BCC``\n"
"\n\n"
":Default: ``PointDefectAnalysisModifier.Lattice.BCC``\n")
.def_property("surface_vis", &PointDefectAnalysisModifier::defectMeshVis, &PointDefectAnalysisModifier::setDefectMeshVis,
"The :py:class:`~ovito.vis.SurfaceMeshVis` element controlling the visual appearance of the defect surfaces.\n")
.def_property("point_vis", &PointDefectAnalysisModifier::pointDefectVis, &PointDefectAnalysisModifier::setPointDefectVis,
"The :py:class:`~ovito.vis.PointDefectVis` element controlling the visual appearance of the point defect symbols.\n")
;
ovito_class<PointDefectAnalysisModifierApplication, DislocationAnalysisModifierApplication>{m};
ovito_class<MicrostructureObject, PeriodicDomainDataObject>{m};
}
OVITO_REGISTER_PLUGIN_PYTHON_INTERFACE(CrystalAnalysis);
......
......@@ -90,7 +90,7 @@ void CoordinationNumberModifierEditor::createUI(const RolloutInsertionParameters
******************************************************************************/
bool CoordinationNumberModifierEditor::referenceEvent(RefTarget* source, const ReferenceEvent& event)
{
if(event.sender() == editObject() && event.type() == ReferenceEvent::ObjectStatusChanged) {
if(event.sender() == editObject() && (event.type() == ReferenceEvent::ObjectStatusChanged || event.type() == ReferenceEvent::TargetChanged)) {
plotRDFLater(this);
}
return ModifierPropertiesEditor::referenceEvent(source, event);
......
......@@ -98,7 +98,7 @@ Future<AsynchronousModifier::ComputeEnginePtr> AtomicStrainModifier::createEngin
return std::make_shared<AtomicStrainEngine>(validityInterval, posProperty->storage(), inputCell->data(), refPosProperty->storage(), refCell->data(),
identifierProperty ? identifierProperty->storage() : nullptr, refIdentifierProperty ? refIdentifierProperty->storage() : nullptr,
cutoff(), affineMapping(), useMinimumImageConvention(), calculateDeformationGradients(), calculateStrainTensors(),
calculateNonaffineSquaredDisplacements(), calculateRotations(), calculateStretchTensors());
calculateNonaffineSquaredDisplacements(), calculateRotations(), calculateStretchTensors(), selectInvalidParticles());
}
/******************************************************************************
......@@ -208,7 +208,8 @@ void AtomicStrainModifier::AtomicStrainEngine::computeStrain(size_t particleInde
Matrix_3<double> inverseV;
double detThreshold = (double)sumSquaredDistance * 1e-12;
if(numNeighbors < 2 || (!cell().is2D() && numNeighbors < 3) || !V.inverse(inverseV, detThreshold) || std::abs(W.determinant()) <= detThreshold) {
_results->invalidParticles()->setInt(particleIndex, 1);
if(_results->invalidParticles())
_results->invalidParticles()->setInt(particleIndex, 1);
if(_results->deformationGradients()) {
for(Matrix_3<double>::size_type col = 0; col < 3; col++) {
for(Matrix_3<double>::size_type row = 0; row < 3; row++) {
......@@ -299,7 +300,8 @@ void AtomicStrainModifier::AtomicStrainEngine::computeStrain(size_t particleInde
OVITO_ASSERT(std::isfinite(volumetricStrain));
_results->volumetricStrains()->setFloat(particleIndex, (FloatType)volumetricStrain);
_results->invalidParticles()->setInt(particleIndex, 0);
if(_results->invalidParticles())
_results->invalidParticles()->setInt(particleIndex, 0);
}
/******************************************************************************
......@@ -310,7 +312,8 @@ PipelineFlowState AtomicStrainModifier::AtomicStrainResults::apply(TimePoint tim
PipelineFlowState output = input;
ParticleOutputHelper poh(modApp->dataset(), output);
if(invalidParticles()->size() != poh.outputParticleCount())
OVITO_ASSERT(shearStrains());
if(shearStrains()->size() != poh.outputParticleCount())
modApp->throwException(tr("Cached modifier results are obsolete, because the number of input particles has changed."));
if(invalidParticles())
......
......@@ -63,14 +63,15 @@ private:
bool calculateStrainTensors,
bool calculateNonaffineSquaredDisplacements,
bool calculateRotations,
bool calculateStretchTensors) :
bool calculateStretchTensors,
bool selectInvalidParticles) :
ComputeEngineResults(validityInterval),
_shearStrains(std::make_shared<PropertyStorage>(particleCount, PropertyStorage::Float, 1, 0, tr("Shear Strain"), false)),
_volumetricStrains(std::make_shared<PropertyStorage>(particleCount, PropertyStorage::Float, 1, 0, tr("Volumetric Strain"), false)),
_strainTensors(calculateStrainTensors ? ParticleProperty::createStandardStorage(particleCount, ParticleProperty::StrainTensorProperty, false) : nullptr),
_deformationGradients(calculateDeformationGradients ? ParticleProperty::createStandardStorage(particleCount, ParticleProperty::DeformationGradientProperty, false) : nullptr),
_nonaffineSquaredDisplacements(calculateNonaffineSquaredDisplacements ? std::make_shared<PropertyStorage>(particleCount, PropertyStorage::Float, 1, 0, tr("Nonaffine Squared Displacement"), false) : nullptr),
_invalidParticles(ParticleProperty::createStandardStorage(particleCount, ParticleProperty::SelectionProperty, false)),
_invalidParticles(selectInvalidParticles ? ParticleProperty::createStandardStorage(particleCount, ParticleProperty::SelectionProperty, false) : nullptr),
_rotations(calculateRotations ? ParticleProperty::createStandardStorage(particleCount, ParticleProperty::RotationProperty, false) : nullptr),
_stretchTensors(calculateStretchTensors ? ParticleProperty::createStandardStorage(particleCount, ParticleProperty::StretchTensorProperty, false) : nullptr) {}
......@@ -131,7 +132,8 @@ private:
ConstPropertyPtr identifiers, ConstPropertyPtr refIdentifiers,
FloatType cutoff, AffineMappingType affineMapping, bool useMinimumImageConvention,
bool calculateDeformationGradients, bool calculateStrainTensors,
bool calculateNonaffineSquaredDisplacements, bool calculateRotations, bool calculateStretchTensors) :
bool calculateNonaffineSquaredDisplacements, bool calculateRotations, bool calculateStretchTensors,
bool selectInvalidParticles) :
RefConfigEngineBase(positions, simCell, refPositions, simCellRef,
std::move(identifiers), std::move(refIdentifiers), affineMapping, useMinimumImageConvention),
_cutoff(cutoff),
......@@ -141,7 +143,8 @@ private:
calculateStrainTensors,
calculateNonaffineSquaredDisplacements,
calculateRotations,
calculateStretchTensors)) {}
calculateStretchTensors,
selectInvalidParticles)) {}
/// Computes the modifier's results.
virtual void perform() override;
......
......@@ -215,10 +215,14 @@ Future<AsynchronousModifier::ComputeEnginePtr> ComputePropertyModifier::createEn
}
// Create engine object. Pass all relevant modifier parameters to the engine as well as the input data.
return std::make_shared<PropertyComputeEngine>(validityInterval, time, std::move(outp), posProperty->storage(),
std::shared_ptr<PropertyComputeEngine> engine = std::make_shared<PropertyComputeEngine>(validityInterval, time, std::move(outp), posProperty->storage(),
std::move(selProperty), inputCell->data(), neighborModeEnabled() ? cutoff() : 0,
expressions(), neighborExpressions(),
std::move(inputProperties), currentFrame, input.attributes());
setVariablesInfo(engine->inputVariableNames(), engine->inputVariableTable());
return engine;
}
/******************************************************************************
......
......@@ -122,6 +122,15 @@ public:
/// \brief Returns a human-readable text listing the input variables.
const QString& inputVariableTable() const { return _inputVariableTable; }
/// \brief Stores the given information about the available input variables in the modifier.
void setVariablesInfo(QStringList variableNames, QString variableTable) {
if(variableNames != _inputVariableNames || variableTable != _inputVariableTable) {
_inputVariableNames = std::move(variableNames);
_inputVariableTable = std::move(variableTable);
notifyDependents(ReferenceEvent::ObjectStatusChanged);
}
}
protected:
/// \brief Is called when the value of a property of this object has changed.
......
......@@ -50,6 +50,7 @@ Python class name User interface name
:py:class:`InvertSelectionModifier` :guilabel:`Invert selection`
:py:class:`LoadTrajectoryModifier` :guilabel:`Load trajectory`
:py:class:`ManualSelectionModifier` :guilabel:`Manual selection`
:py:class:`PointDefectAnalysisModifier` :guilabel:`Point defect analysis`
:py:class:`PolyhedralTemplateMatchingModifier` :guilabel:`Polyhedral template matching`
:py:class:`PythonScriptModifier` :guilabel:`Python script`
:py:class:`ReplicateModifier` :guilabel:`Replicate`
......
......@@ -297,6 +297,7 @@ PipelineFlowState HistogramModifier::evaluatePreliminary(TimePoint time, Modifie
// Store results in the ModifierApplication.
static_object_cast<HistogramModifierApplication>(modApp)->setBinCounts(ycoords);
static_object_cast<HistogramModifierApplication>(modApp)->setHistogramInterval({intervalStart, intervalEnd});
notifyDependents(ReferenceEvent::ObjectStatusChanged);
QString statusMessage;
if(outputSelection) {
......