Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/source/_static/recipes/cleaning_cluster_population.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Code from the Spatial Denoising tutorial."""

from pathlib import Path

import numpy as np

import dynsight
from dynsight.data_processing import cleaning_cluster_population
from dynsight.trajectory import Trj


def main() -> None:
"""Code from the Spatial Denoising tutorial."""
# Loading an example trajectory
files_path = Path("INPUT")
trj = Trj.init_from_xtc(
traj_file=files_path / "ice_water_ox.xtc",
topo_file=files_path / "ice_water_ox.gro",
)

# Computing TimeSOAP descriptor
_, tsoap = trj.get_timesoap(
r_cut=10,
n_max=8,
l_max=8,
n_jobs=4, # Adjust n_jobs according to your computer capabilities
)

# Applying Spatial Denoising
sliced_trj = trj.with_slice(slice(0, -1, 1))
sp_denoised_tsoap = tsoap.spatial_average(
trj=sliced_trj,
r_cut=10,
n_jobs=4, # Adjust n_jobs according to your computer capabilities
)

# Performing Onion Clustering on the descriptor computed
delta_t_list, n_clust, unclass_frac, labels = (
sp_denoised_tsoap.get_onion_analysis(
delta_t_min=2,
delta_t_num=20,
fig1_path=files_path / "denoised_onion_analysis.png",
fig2_path=files_path / "cluster_population.png",
)
)

# Saving Onion output in an array
onion_output = np.array([delta_t_list, n_clust, unclass_frac]).T

# Assigning clusters with population <5% to the unclassified environment
# (label=-1)
cleaned_labels = cleaning_cluster_population(
labels, threshold=0.05, assigned_env=-1
)

# Updating the data and plotting the cleaned number of clusters and
# unclassified fraction
delta_t_list = onion_output[
:, 0
] # since unchanged, windows can be copied from above

n_clust = np.zeros(delta_t_list.shape[0], dtype=np.int64)
unclass_frac = np.zeros(delta_t_list.shape[0])
for i in range(delta_t_list.shape[0]):
n_clust[i] = np.unique(cleaned_labels[:, :, i]).size - 1
unclass_frac[i] = np.sum(cleaned_labels[:, :, i] == -1) / np.size(
cleaned_labels[:, :, i]
)

cleaned_onion_output = np.array([delta_t_list, n_clust, unclass_frac]).T

dynsight.onion.plot_smooth.plot_time_res_analysis(
files_path / "cleaned_onion_analysis.png", cleaned_onion_output
)


if __name__ == "__main__":
main()
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions docs/source/data_processing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,11 @@ Classification
savereferences <_autosummary/dynsight.data_processing.savereferences>
getreferencesfromdataset <_autosummary/dynsight.data_processing.getreferencesfromdataset>
applyclassification <_autosummary/dynsight.data_processing.applyclassification>

Clustering
----------

.. toctree::
:maxdepth: 1

cleaning_cluster_population <_autosummary/dynsight.data_processing.cleaning_cluster_population>
12 changes: 6 additions & 6 deletions docs/source/logs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ Logs

dynsight logging system.

.. warning::
.. note::

A default instance of :class:`Logger` is **automatically created** when importing the ``dynsight`` package.
This instance is available as ``dynsight.logs.logger``.

You can configure it, for example to disable the automatic recording of datasets, using:
A default :class:`Logger` is istantiated to keep the user updated on the ongoing computational steps.

An option of :class:`Logger` that automatically saves and records the dataset
can be activated after importing the ``dynsight`` package by using:

.. code-block:: python

import dynsight
dynsight.logs.logger.configure(auto_recording=False)
dynsight.logs.logger.configure(auto_recording=True)

You can also access all its attributes and methods described in the Logs page below.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorials/spatial_denoising.rst
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ Full scripts and input files
assert soap_test.meta["l_max"]==8
assert np.allclose(soap_test.dataset, reference_soap, atol=1e-6)

_, tsoap_test = trj.get_timesoap(
_, tsoap_test = trj_test.get_timesoap(
soap_insight=soap_test,
)

Expand Down
219 changes: 219 additions & 0 deletions docs/source/tutorials/tips_and_tricks.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
Tips and Tricks
===============

Welcome to the Tips and Trick section of the dynsight platform.
Here we will show you additional hints that can help you when analyzing your data.


At the end of every section, you will find links to download the full ``python`` scripts
and its relevant input files.

Cleaning cluster population
---------------------------

Sometimes, clusters obtained with Onion Clustering analysis can be very small.
To better interpret the results, it can be useful to remove those ones by assigning them to
the cluster of the unclassified particles.
This is achieved through the class, :class:`.data_processing.cleaning_cluster_population()`, which
assign the cluster under a certain population threshold to a specific cluster selected by the user.

As an example, we consider the ouput of the analysis computed in the `spatial denoising tutorial <./spatial_denoising.html>`_.
Briefly, we consider the denoised ``TimeSOAP`` descriptor that can be ontained from:

.. code-block:: python

from pathlib import Path
from dynsight.trajectory import Trj

files_path = Path("source/_static/simulations")
trj = Trj.init_from_xtc(
traj_file=files_path / "ice_water_ox.xtc",
topo_file=files_path / "ice_water_ox.gro",
)

_, tsoap = trj.get_timesoap(
r_cut=10,
n_max=8,
l_max=8,
n_jobs=4, # Adjust n_jobs according to your computer capabilities
)

sliced_trj = trj.with_slice(slice(0, -1, 1))
sp_denoised_tsoap = tsoap.spatial_average(
trj=sliced_trj,
r_cut=10,
n_jobs=4, # Adjust n_jobs according to your computer capabilities
)

delta_t_list, n_clust, unclass_frac, labels = sp_denoised_tsoap.get_onion_analysis(
delta_t_min=2,
delta_t_num=20,
fig1_path=files_path / "denoised_onion_analysis.png",
fig2_path=files_path / "cluster_population.png",
)

.. testcode:: tips_and_tricks_test
:hide:

from pathlib import Path
from dynsight.trajectory import Trj

files_path = Path("source/_static/simulations")
trj = Trj.init_from_xtc(
traj_file=files_path / "ice_water_ox.xtc",
topo_file=files_path / "ice_water_ox.gro",
)

assert trj.n_atoms == 2048
assert trj.n_frames == 1001

.. testcode:: tips_and_tricks_test
:hide:

import numpy as np

trj_test = trj.with_slice(slice(0, 2, 1))

expected_tests = Path("source/_static/tutorials/spatial_denoising/doctests")

soap_test = trj_test.get_soap(
r_cut=10,
n_max=8,
l_max=8,
n_jobs=1, # Adjust n_jobs according to your computer capabilities
)

_, tsoap_test = trj.get_timesoap(
soap_insight=soap_test,
)

assert tsoap_test.meta["r_cut"]==10
assert tsoap_test.meta["n_max"]==8
assert tsoap_test.meta["l_max"]==8

reference_tsoap = np.load(expected_tests / "test_tsoap.npy")
assert np.allclose(tsoap_test.dataset, reference_tsoap, atol=1e-6)

sliced_trj_test = trj.with_slice(slice(0, 1, 1))
sp_denoised_tsoap_test = tsoap_test.spatial_average(
trj=sliced_trj_test,
r_cut=10,
n_jobs=1,
)

reference_denoised_tsoap = np.load(expected_tests / "test_denoised_tsoap.npy")
assert np.allclose(sp_denoised_tsoap_test.dataset, reference_denoised_tsoap, atol=1e-6)

For further details users should refers to `spatial denoising tutorial <./spatial_denoising.html>`_.

Figure `cluster_population.png` shows the population of every cluster, where blue refers to the unclassified fraction:

.. image:: ../_static/tutorials/tips_and_tricks/cluster_population.png
:scale: 15%
:align: center

Before cleaning the cluster we have to save the output from the Onion analysis in an array:

.. code-block:: python

import numpy as np

onion_output = np.array([delta_t_list, n_clust, unclass_frac]).T

The small clusters can be removed and assigned to the unclassified fraction using the
class :class:`.data_processing.cleaning_cluster_population()`:

.. code-block:: python

from dynsight.data_processing import cleaning_cluster_population

cleaned_labels = cleaning_cluster_population(labels, threshold=0.05, assigned_env=-1)

where `leaned_labels` has the same dimensions as `labels`. Now we can reproduce the plot with the number
of clusters and the unclassified fraction after re-organizing the data:

.. code-block:: python

import dynsight

delta_t_list = onion_output[:, 0] #since unchanged, windows can be copied from above

n_clust = np.zeros(delta_t_list.shape[0],dtype=np.int64)
unclass_frac = np.zeros(delta_t_list.shape[0])
for i in range(delta_t_list.shape[0]):
n_clust[i] = np.unique(cleaned_labels[:, :, i]).size - 1
unclass_frac[i] = np.sum(cleaned_labels[:, :, i] == -1) / np.size(cleaned_labels[:, :, i])

cleaned_onion_output = np.array([delta_t_list, n_clust, unclass_frac]).T

dynsight.onion.plot_smooth.plot_time_res_analysis("cleaned_onion_analysis.png", cleaned_onion_output)

.. testcode:: tips_and_tricks_test
:hide:

from dynsight.data_processing import cleaning_cluster_population

expected_tests = Path("../tests/data_processing/cluster/test_cluster")

labels = np.zeros((4, 10, 3), dtype=int)

labels[:, :, 0] = np.array(
[
[0, 0, 0, 1, 1, 1, 2, 2, 2, 2],
[0, 0, 0, 1, 1, 1, 2, 2, 2, 2],
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3],
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3],
]
)

labels[:, :, 1] = np.array(
[
[0, 0, 0, 0, 0, 1, 1, 1, 1, 4],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 4],
[0, 0, 0, 0, 0, 1, 1, 1, 4, 4],
[0, 0, 0, 0, 0, 1, 1, 1, 4, 4],
]
)

labels[:, :, 2] = np.array(
[
[0, 0, 9, 9, 0, 1, 1, 1, 1, 9],
[0, 0, 0, 9, 0, 1, 1, 1, 1, 9],
[0, 0, 0, 9, 0, 1, 1, 1, 1, 9],
[0, 9, 0, 0, 0, 1, 1, 1, 1, 9],
]
)

test_clean_pop = cleaning_cluster_population(
labels,
threshold=0.05,
assigned_env=99,
)

exp_clean_pop = np.load(expected_tests / "c0_clean_pop_th5_ass99_exNone.npy")
assert np.array_equal(exp_clean_pop, test_clean_pop)

On the left are reported the results from Onion clustering on the denoised time-series (`denoised_onion_analysis.png`
from `spatial denoising tutorial <./spatial_denoising.html>`_), while on the rigth is reported the figure
`cleaned_onion_analysis.png`

.. image:: ../_static/tutorials/spatial_denoising/denoised_onion_analysis.png
:scale: 8%
:align: left

.. image:: ../_static/tutorials/tips_and_tricks/cleaned_onion_analysis.png
:scale: 8%
:align: right

.. raw:: html

<div style="clear: both;"></div>

Full scripts and input files
----------------------------

.. raw:: html

<a class="btn-download" href="../_static/simulations/ice_water_ox.gro" download>⬇️ Download the .gro file</a> <br>
<a class="btn-download" href="../_static/simulations/ice_water_ox.xtc" download>⬇️ Download the .xtc file</a> <br>
<a class="btn-download" href="../_static/recipes/cleaning_cluster_population.py" download>⬇️ Download Python Script</a>
13 changes: 13 additions & 0 deletions docs/source/tutorials_menu.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ so stay tuned for future updates!
.. rubric:: Spatial Denoising
:class: tutorial-card-title

.. grid-item-card::
:link: tutorials/tips_and_tricks
:link-type: doc
:class-card: tutorial-card

.. image:: _static/tutorials/spatial_denoising/denoised_onion_analysis.png
:alt: Tips and Tricks
:class: tutorial-card-img

.. rubric:: Tips and Tricks
:class: tutorial-card-title

.. grid-item-card::
:class-card: tutorial-card

Expand All @@ -51,6 +63,7 @@ so stay tuned for future updates!

Getting Started <tutorials/getting_started>
Spatial Denoising <tutorials/spatial_denoising>
Tips and Tricks <tutorials/tips_and_tricks>

Other example files
-------------------
Expand Down
4 changes: 2 additions & 2 deletions examples/onion_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def main() -> None:
coord_1d = Insight(coord_2d.dataset[:, :, 0])

# Test onion clustering on a wide range of time resolutions
delta_t_list, n_clust, unclass_frac = coord_1d.get_onion_analysis(
delta_t_list, n_clust, unclass_frac, envs = coord_1d.get_onion_analysis(
fig1_path=data_path / "time-res_1d.png",
fig2_path=data_path / "pop_fracs_1d.png",
)
Expand All @@ -110,7 +110,7 @@ def main() -> None:
onion_results.plot_state_populations(data_path / "state_pops_1d.png")

# Test onion clustering on a wide range of time resolutions
delta_t_list, n_clust, unclass_frac = coord_2d.get_onion_analysis(
delta_t_list, n_clust, unclass_frac, envs = coord_2d.get_onion_analysis(
fig1_path=data_path / "time-res_2d.png",
fig2_path=data_path / "pop_fracs_2d.png",
)
Expand Down
Loading