diff --git a/AUTHORS.md b/AUTHORS.md index 32aeff8..8f79b3b 100755 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -7,6 +7,9 @@ All contributing authors are listed in this file below. The repository history at https://github.com/ljwoods2/zarrtraj and the CHANGELOG show individual code contributions. +New contributors should add themselves to the end of this file AND to +the file CITATION.cff at the end of the top-level authors list. + ## Chronological list of authors +## [0.3.0] 2024-10-24 + +## Authors +- ljwoods2 + +## Added +- added CITATION.cff file (issue #69, PR #68) + ## [0.2.1] 2024-07-28 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..40ae659 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,65 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: 'Zarrtraj: A Python package for streaming molecular dynamics trajectories from cloud services' +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Lawson + email: lawsonw84@gmail.com + family-names: Woods + orcid: 'https://orcid.org/0009-0003-0713-4167' + affiliation: >- + School of Computing and Augmented Intelligence, + Arizona State University, Tempe, Arizona, United + States of America + - given-names: Hugo + family-names: MacDermott-Opeskin + orcid: 'https://orcid.org/0000-0002-7393-7457' + affiliation: >- + Open Molecular Software Foundation, Davis, CA, United + States of America + email: hugomacdermott-opeskin@mdanalysis.org + - given-names: Edis + family-names: Jakupovic + orcid: 'https://orcid.org/0000-0001-8813-6356' + affiliation: >- + Center for Biological Physics, Arizona State + University, Tempe, AZ, United States of America + - given-names: Yuxuan + orcid: 'https://orcid.org/0000-0003-4390-8556' + family-names: Zhuang + affiliation: >- + Department of Computer Science, Stanford University, + Stanford, CA 94305, USA. + - given-names: Richard + orcid: 'https://orcid.org/0000-0002-3241-1846' + family-names: Gowers + name-particle: J + affiliation: Charm Therapeutics, London, United Kingdom + - given-names: Oliver + family-names: Beckstein + affiliation: >- + Center for Biological Physics, Arizona State + University, Tempe, AZ, United States of America + orcid: 'https://orcid.org/0000-0003-1340-0831' +identifiers: + - type: doi + value: 10.5281/zenodo.13887976 +repository-code: 'https://github.com/Becksteinlab/zarrtraj' +url: 'https://zarrtraj.readthedocs.io/en/latest/index.html' +abstract: >- + Zarrtraj is an MDAnalysis MDAKit for streaming H5MD and + ZarrMD trajectory files from cloud storage like AWS S3, + Google Cloud Buckets, and Azure Data lakes and Blob + Storage +keywords: + - streaming + - molecular-dynamics + - file-format + - mdanalysis + - zarr +license: GPL-3.0-or-later diff --git a/docs/source/index.rst b/docs/source/index.rst index 919eb21..8651a10 100755 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,6 +16,7 @@ This means users can interact with massive trajectory files without ever storing :caption: Contents: installation + yiip_example walkthrough api performance_considerations diff --git a/docs/source/yiip_example.rst b/docs/source/yiip_example.rst new file mode 100644 index 0000000..30a6a32 --- /dev/null +++ b/docs/source/yiip_example.rst @@ -0,0 +1,31 @@ +YiiP Protein Example +==================== + +To get started immediately with *Zarrtraj*, we have made the topology and trajectory of the +`YiiP protein in a POPC membrane `_ +publicly available for streaming. The trajectory is stored in in the `zarrmd` format +for optimal streaming performance. + +To access the trajectory, follow this example: + +.. code-block:: python + + import zarrtraj + import MDAnalysis as mda + import fsspec + + + with fsspec.open("gcs://zarrtraj-test-data/YiiP_system.pdb", "r") as top: + + u = mda.Universe( + top, "gcs://zarrtraj-test-data/yiip.zarrmd", topology_format="PDB" + ) + + for ts in u.trajectory: + # Do something + + +While there is not yet an officially recommended way to access cloud-stored topologies, this +method of opening a Python `File`-like object from the topology URL in PDB format using +`FSSpec `_ +works with MDAnalysis 2.7.0. Check back later for further development! \ No newline at end of file diff --git a/joss_paper/RMSD.png b/joss_paper/RMSD.png new file mode 100644 index 0000000..7d5ec9c Binary files /dev/null and b/joss_paper/RMSD.png differ diff --git a/joss_paper/benchmark.png b/joss_paper/benchmark.png new file mode 100644 index 0000000..edafbd0 Binary files /dev/null and b/joss_paper/benchmark.png differ diff --git a/joss_paper/figure_1.ipynb b/joss_paper/figure_1.ipynb new file mode 100644 index 0000000..3bb22ff --- /dev/null +++ b/joss_paper/figure_1.ipynb @@ -0,0 +1,78 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generate iteration speed figure for JOSS paper. Iteration times come from https://becksteinlab.github.io/zarrtraj/#/" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAJOCAYAAAAqFJGJAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABykElEQVR4nO3dd3QU1f/G8WdTSSMESKiBANJ7r0rvIFioSlWUpjRRQRFQ6YiioiBfaVJEEFDpSEdQQBApYqGD9JKEACHl/v7gl5UloWVCNuX9Oodz2JnZ3c/u3szOs3PnXpsxxggAAAAALHBxdgEAAAAAUj+CBQAAAADLCBYAAAAALCNYAAAAALCMYAEAAADAMoIFAAAAAMsIFgAAAAAsI1gAAAAAsIxgAQAAAMAyggVSpN9//11dunRRvnz5lCFDBvn6+qpcuXIaO3asLl265OzyHrnOnTsrJCTE2WVYtnv3btWsWVP+/v6y2Wz66KOP4m3TuXNn2Wy2+/7r3LmzpVqOHj0qm82mGTNmWHqcu7l27ZqGDRumDRs2PJLHt2LDhg2y2WxauHChfdnWrVs1bNgwXblyxXmF3aeOWrVqqVatWslekySdOHFCPXv2VKFCheTl5aXMmTOrZMmS6tatm06cOOGUmh7UsGHDZLPZ7rtdrVq1VKJEiUdeT0REhMaMGaPSpUsrY8aM8vPzU4ECBdS6dWtt3LjRvt2BAwc0bNgwHT169JHXlBLMmDHDvn9LaL9hjNFjjz0mm82W5H8HISEhDvvUuH1EStx/IXVxc3YBwJ2mTp2qnj17qnDhwho4cKCKFSumqKgo7dy5U5MnT9a2bdu0ePFiZ5f5SA0ZMkR9+vRxdhmWde3aVREREfr6668VEBCQYFgaMmSIunfvbr+9a9cu9erVSyNHjlTt2rXtywMDAy3VkiNHDm3btk0FChSw9Dh3c+3aNQ0fPlySnHYw/DC2bt2q4cOHq3PnzsqUKVOKrOOzzz5zSk0nT55UuXLllClTJg0YMECFCxdWaGioDhw4oG+++UaHDx9WcHCwU2pLbWJiYtSgQQPt3btXAwcOVKVKlSRJf//9t3744Qdt3rxZNWvWlHQrWAwfPly1atVKEz+sPCg/Pz99+eWX8fYbGzdu1KFDh+Tn5/fIayhXrpy2bdumYsWKPfLnQtpGsECKsm3bNvXo0UP169fXkiVL5OnpaV9Xv359DRgwQCtXrnRihY/WtWvX5O3t/cgOfpPbvn371K1bNzVu3Piu2xQoUMDh9d64cUOSVLBgQVWpUuWu97t+/boyZMjwQL/MSpKnp+c9Hy+letjX6WxxbTgpOOsgZ+rUqbpw4YK2b9+ufPny2Ze3bNlSgwcPVmxsrFPqSo02bdqkrVu3atq0aerSpYt9ecOGDdW7d+9keS+Tsk0+Cm3atNGcOXM0adIkZcyY0b78yy+/VNWqVRUWFvbIa8iYMWOq3D8i5aErFFKUkSNHymaz6YsvvnAIFXE8PDz05JNP2m/HxsZq7NixKlKkiDw9PRUUFKSOHTvq5MmTDveLO+W/bds2VatWTV5eXgoJCdH06dMlScuWLVO5cuXk7e2tkiVLxgsvcV0Ldu/eraeffloZM2aUv7+/nn/+eZ0/f95h2/nz56tBgwbKkSOHvLy8VLRoUb355puKiIhw2K5z587y9fXV3r171aBBA/n5+alu3br2dXf+YrdgwQJVrlxZ/v7+8vb2Vv78+dW1a1eHbY4fP67nn39eQUFB8vT0VNGiRfXBBx84fHnHdQkaP368JkyYoHz58snX11dVq1bVzz//fK+Px27fvn1q0aKFAgIClCFDBpUpU0YzZ860r487xR8dHa3PP//cfro/seIeb/Xq1eratasCAwPl7e2tyMhI/fPPP+rSpYsKFiwob29v5cqVS82bN9fevXsdHuNuXaH+/vtvtW/f3uE9mzRpUrwarly5ogEDBih//vz2ttakSRMdPHhQR48etZ9RGT58eILdt7Zs2aK6devKz89P3t7eqlatmpYtW/ZAr3PLli2y2WyaN29evLpmzZolm82mHTt2PPD7OWzYMA0cOFCSlC9fvgS7Y8yfP19Vq1aVj4+PfH191bBhQ+3evdvhce7VhtesWaMWLVood+7cypAhgx577DG9/PLLunDhwgPXkVBXqEuXLqlnz57KlSuXPDw8lD9/fr311luKjIx02M5ms6l379766quvVLRoUXl7e6t06dJaunTpfd+fixcvysXFRUFBQQmud3H576sz7j3Yv3+/6tatKx8fHwUGBqp37966du2aw/2MMfrss89UpkwZeXl5KSAgQM8++6wOHz4c7zl+/PFH1a1bVxkzZpS3t7eqV6+utWvXxttu2bJlKlOmjDw9PZUvXz6NHz/+vq/vTps3b1aVKlXk5eWlXLlyaciQIYqJibHXXLBgQTVs2DDe/a5evSp/f3/16tXrro998eJFSbfOGCYk7r2cMWOGWrVqJUmqXbu2vS3c/vc6bdo0lS5dWhkyZFDmzJn11FNP6Y8//nB4PKttMs53332nUqVKydPTU/nz59fEiRMT7GL2MJ/p3bRr106SHP6+Q0ND9e2338bbx8e5efOm3n//fft3X2BgoLp06RLv+ygqKkqvv/66smfPLm9vb9WoUUPbt2+P93gJdYW6W1fEO7+f4vat48aN05gxYxQSEiIvLy/VqlVLf/31l6KiovTmm28qZ86c8vf311NPPaVz58498PuDVMYAKUR0dLTx9vY2lStXfuD7vPTSS0aS6d27t1m5cqWZPHmyCQwMNMHBweb8+fP27WrWrGmyZMliChcubL788kuzatUq06xZMyPJDB8+3JQsWdLMmzfPLF++3FSpUsV4enqaU6dO2e8/dOhQI8nkzZvXDBw40KxatcpMmDDB+Pj4mLJly5qbN2/at33vvffMhx9+aJYtW2Y2bNhgJk+ebPLly2dq167tUHunTp2Mu7u7CQkJMaNGjTJr1641q1atsq/LmzevfdutW7cam81m2rZta5YvX27WrVtnpk+fbjp06GDf5ty5cyZXrlwmMDDQTJ482axcudL07t3bSDI9evSwb3fkyBEjyYSEhJhGjRqZJUuWmCVLlpiSJUuagIAAc+XKlXu+5wcPHjR+fn6mQIECZtasWWbZsmWmXbt2RpIZM2aMvZZt27YZSebZZ58127ZtM9u2bXugz3T9+vVGklmwYIF92fTp040kkytXLvPSSy+ZFStWmIULF5ro6GizceNGM2DAALNw4UKzceNGs3jxYtOyZUvj5eVlDh48GO91T58+3b5s//79xt/f35QsWdLMmjXLrF692gwYMMC4uLiYYcOG2bcLCwszxYsXNz4+Pubdd981q1atMt9++63p06ePWbdunblx44ZZuXKlkWReeOEF++v9559/jDHGbNiwwbi7u5vy5cub+fPnmyVLlpgGDRoYm81mvv766wd6nWXLljXVq1eP935VrFjRVKxY8aHe0xMnTphXXnnFSDKLFi2y1xsaGmqMMWbEiBHGZrOZrl27mqVLl5pFixaZqlWrGh8fH7N//377496rDX/++edm1KhR5vvvvzcbN240M2fONKVLlzaF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nTp1ctguNjZWjRo10o4dOxQREaGIiAjt2LFDTz/9tDJkyGB/Xj8/PzVv3vyer+92fn5+Dt1L42qPjY3Vpk2b7Nt06dJFM2bMsHfpXLdunQ4cOKDevXvf9zm6du2qkydPau7cuXr11VcVHBys2bNnq2bNmho3btx9779t2zZdv3493r4+ODhYderUSbCLWGLbZEREhHbu3KmWLVvKw8PDfl9fX9947+uDfqYPombNmipQoICmTZumvXv3aseOHXftBrV06VJlypRJzZs3d3jeMmXKKHv27PbnjWt7d/veSmpNmjRx6CYY913atGlTh+3ilh8/fjzJa4DzcfE2UoysWbPK29tbR44ceaDt79V3N2fOnPEOHjJnzhxvOw8Pj3jL475M4i4ivl327Nkdbru5uSlLliz2Wq5evarHH39cGTJk0Pvvv69ChQrJ29tbJ06c0NNPP63r16873N/b29vhYr27eeKJJ7RkyRJ9/PHH6tixoyIjI1W8eHG99dZb9v65Fy9eTHAklbjQFFdjnCxZsjjcjrum5c4a73Tx4sW7vucJPU9SSuh5+/fvr0mTJumNN95QzZo1FRAQIBcXF7344ov3fC0XL15UdHS0PvnkE33yyScJbhPX9/r8+fPKkydPomq+fPmyjDEP9Z4ltK2np6defvllffDBBxo3bpyioqL0zTffqH///glej5RYZ8+elfRfmL3T7QcOUsJtODY2Vg0aNNC///6rIUOGqGTJkvLx8VFsbKyqVKly3zZ2NxcvXlT27Nnj9XMPCgqSm5vbfdu4dOt9fNDnz5s3r3r06GG//c0336hdu3YaOHCgQz/1uP3A7eL2FXE1nT17VsYYZcuWLcHnyp8/v307SfHC0+0uXbokm82m2NjYePuk25/7QSRUz521S9Irr7yiTz/9VHPmzNFLL72kTz/9VLlz51aLFi0e6Hn8/f3Vrl07+/5q//79qlevnt566y1169btniOT3W9fv2bNGodlVtpk3N9rQu/Lncse9DN9EDabTV26dNHHH3+sGzduqFChQnr88ccT3Pbs2bO6cuWKQ/C5Xdx+K+59u9v3VlK723fpw3zHIvUjWCDFcHV1Vd26dbVixQqdPHlSuXPnvuf2cTvG06dPx9v233//VdasWZO8xjNnzihXrlz229HR0bp48aK9lnXr1unff//Vhg0b7GcpJN11roCHuaC5RYsWatGihSIjI/Xzzz9r1KhRat++vUJCQlS1alVlyZJFp0+fjne/f//9V5KS7P1IrudJSELv1+zZs9WxY0eNHDnSYfmFCxfuebASEBBgP2twt4tP40YECgwMjDcgwIOKCzoP857drV306NFDo0eP1rRp03Tjxg1FR0c7DNWbFOJqWbhwof3X3HtJqNZ9+/Zpz549mjFjhjp16mRf/s8//1iqLUuWLPrll19kjHF43nPnzik6OvqRtj3p1i+9o0aN0r59+xyW37kfkG7tK+Jqlm69rzabTZs3b04wCMYti3sNn3zyyV1H6cmWLZuioqJks9nsz3O7hJbdTVyQSej+t7+exx57TI0bN9akSZPUuHFjff/99xo+fLjDWc+HUbx4cbVt21YfffSR/vrrr3hnWm93+77+Tgnt6620yYCAANlstnu+L3Ee9DN9UJ07d9Y777yjyZMna8SIEXfdLmvWrMqSJctdR0iMO0sX977d7XvrfjJkyKDQ0NB4yxO62B2IQ1copCiDBg2SMUbdunXTzZs3462PiorSDz/8IEmqU6eOpFsHlrfbsWOH/vjjD/tIIElpzpw5Dre/+eYbRUdH20fOiPtCu/MLZcqUKUlWg6enp2rWrKkxY8ZIkn2knrp16+rAgQPatWuXw/ZxowbdPieEFXXr1rUHqDufx9vbO9mHLLTZbPHe72XLliXYXex23t7eql27tnbv3q1SpUqpQoUK8f7FfTE3btxYf/31V7zuZLe72xkfHx8fVa5cWYsWLXJYFxsbq9mzZyt37twqVKjQA73WHDlyqFWrVvrss880efJkNW/ePNFnUu5Wb8OGDeXm5qZDhw4l+J5UqFDhvo/9MH8HD3qmTLrV9q5evaolS5Y4LJ81a5Z9fVJI6ABWunVG8sSJEw5dJ+PcuW+YO3eupP/mNGnWrJmMMTp16lSC72nJkiUlSdWrV1emTJl04MCBu77/Hh4e8vHxUaVKlbRo0SKHX37Dw8Pt+8gHER4eru+//z5e7S4uLnriiScclvfp00e///67OnXqJFdXV3Xr1u2+j3/x4sUE9+WSdPDgQUn/nbm7W1uoWrWqvLy84u3rT548ae+aeT8P2iZ9fHxUoUIFLVmyxKHuq1evxhtR7EE/0weVK1cuDRw4UM2bN3cIP3dq1qyZLl68qJiYmASft3DhwpL+a3t3+966n5CQEP31118OI65dvHhRW7dufajXhfSFMxZIUapWrarPP/9cPXv2VPny5dWjRw8VL15cUVFR2r17t7744guVKFFCzZs3V+HChfXSSy/pk08+kYuLixo3bqyjR49qyJAhCg4OVr9+/ZK8vkWLFsnNzU3169fX/v37NWTIEJUuXVqtW7eWJFWrVk0BAQHq3r27hg4dKnd3d82ZM0d79uyx9LzvvPOOTp48qbp16yp37ty6cuWKJk6c6HD9Rr9+/TRr1iw1bdpU7777rvLmzatly5bps88+U48ePR744PV+hg4dqqVLl6p27dp65513lDlzZs2ZM0fLli3T2LFj5e/vnyTP86CaNWumGTNmqEiRIipVqpR+/fVXjRs37r5nvCRp4sSJqlGjhh5//HH16NFDISEhCg8P1z///KMffvjBHiT69u2r+fPnq0WLFnrzzTdVqVIlXb9+XRs3blSzZs3sffnz5s2r7777TnXr1lXmzJmVNWtWhYSEaNSoUapfv75q166t1157TR4eHvrss8+0b98+zZs376HOXPXp00eVK1eWJPtwyYkRd9AzceJEderUSe7u7ipcuLBCQkL07rvv6q233tLhw4fVqFEjBQQE6OzZs9q+fbt8fHzsEwHeTZEiRVSgQAG9+eabMsYoc+bM+uGHH+J1WblXHQlNCtaxY0dNmjRJnTp10tGjR1WyZElt2bJFI0eOVJMmTRJ1DUxCRowYoZ9++klt2rSxDyN65MgRffrpp7p48WK86wI8PDz0wQcf6OrVq6pYsaK2bt2q999/X40bN1aNGjUk3QoML730krp06aKdO3fqiSeekI+Pj06fPq0tW7aoZMmS6tGjh3x9ffXJJ5+oU6dOunTpkp599lkFBQXp/Pnz2rNnj86fP6/PP/9ckvTee++pUaNG9jl+YmJiNGbMGPn4+OjSpUsP9FqzZMmiHj166Pjx4ypUqJCWL1+uqVOnqkePHvFCa/369VWsWDGtX7/ePqz1/axfv159+vTRc889p2rVqilLliw6d+6c5s2bp5UrV6pjx472v9W4WcC/+OIL+fn5KUOGDMqXL5+yZMmiIUOGaPDgwerYsaPatWunixcvavjw4cqQIYOGDh163zoepk2+++67atq0qRo2bKg+ffooJiZG48aNk6+vr8P7+qCf6cMYPXr0fbdp27at5syZoyZNmqhPnz6qVKmS3N3ddfLkSa1fv14tWrTQU089paJFi+r555/XRx99JHd3d9WrV0/79u3T+PHjH6gLbocOHTRlyhQ9//zz6tatmy5evKixY8c+0H2RjjnponHgnn777TfTqVMnkydPHuPh4WEf1vWdd96xDz9ozK2RUsaMGWMKFSpk3N3dTdasWc3zzz9vTpw44fB4NWvWNMWLF4/3PHnz5jVNmzaNt1x3jGYUNyrUr7/+apo3b258fX2Nn5+fadeunTl79qzDfbdu3WqqVq1qvL29TWBgoHnxxRfNrl274o1I1KlTJ+Pj45Pg679z1I2lS5eaxo0bm1y5chkPDw8TFBRkmjRpYjZv3uxwv2PHjpn27dubLFmyGHd3d1O4cGEzbtw4h5E64kbwGDduXIKvO6FRje60d+9e07x5c+Pv7288PDxM6dKlHV7b7Y+XlKNC7dixI972ly9fNi+88IIJCgoy3t7epkaNGmbz5s3xRjSJe90zZsxwuP+RI0dM165dTa5cuYy7u7sJDAw01apVM++//3685+nTp4/JkyePcXd3N0FBQaZp06YOQ9r++OOPpmzZssbT0zPeqCubN282derUMT4+PsbLy8tUqVLF/PDDDw7Pca/XebuQkBBTtGjRe25zu4TeU2OMGTRokMmZM6d99KPbR4RZsmSJqV27tsmYMaPx9PQ0efPmNc8++6zDcKf3asMHDhww9evXN35+fiYgIMC0atXKHD9+PME2drc6EhqV5uLFi6Z79+4mR44cxs3NzeTNm9cMGjTI3Lhxw2G7u7W9vHnzOnwuCfn5559Nr169TOnSpU3mzJmNq6urCQwMNI0aNTLLly932DbuPfj9999NrVq1jJeXl8mcObPp0aOHuXr1arzHnjZtmqlcubK9HRQoUMB07NjRYcQ6Y4zZuHGjadq0qcmcObNxd3c3uXLlMk2bNo33GX7//femVKlS9iFvR48ebd9f3U/cfnHDhg2mQoUKxtPT0+TIkcMMHjzYREVFJXifYcOGGUnm559/vu/jG3NrhKS3337bVK9e3WTPnt24ubkZPz8/U7lyZfPJJ584jExnzK2RhPLly2dcXV3j7TP/97//2V+rv7+/adGiRbyhbpOqTS5evNiULFnS4X199dVXTUBAQLzHfdDP9E4P+vd+56hQxhgTFRVlxo8fb0qXLm0yZMhgfH19TZEiRczLL79s/v77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wwYVLVpUPXr0iDfnDdKmuw1NferUKa1atUo1atRQkSJFEhzxiVCB5MQZCwB4hHbv3q2hQ4eqWrVqWrRokbJnz65GjRrpxRdf1CeffKIRI0Zox44d9r70MTExio6OZnjZdOjgwYMqX768ateurcDAQH3zzTdq27atWrRooSeffFJLlixRjx49NGvWLNWvX59fotORCRMmaMyYMTpy5Ii8vb3jDU0dFhbG0NRIERhuFgAeobJly6pQoULatGmTNm/erMaNG2vt2rWqU6eOWrRooTx58qhPnz727V1dXTkwSKfi5ryJjIy0z3kzbtw4Pfnkk5KY8ya9YWhqpEYECwBIQtevX9fQoUN19OhR+7Lx48fr9OnTGjRokHr06KEpU6aoaNGiatSokTJmzKjly5friy++cF7RSDGY8wZx4oamXrVqlcqWLas2bdrowoULWrRokT744AONHTtW7du3l3RraOrAwECGpobTESwAIAnNmzdPs2fPVsWKFTV37lydOHFCkjRx4kQdPHhQq1atUtasWTV16lSNGTNGOXLkkJubm4oUKeLkypFSeHh4OJy1unLlij777DP169dP77zzDnPepBO3D03t7u6ufv36ac2aNfrpp5/0wgsv2Oe7kRiaGikH11gAQBI7ePCgJk+erKlTp6pp06Zq166dnnrqKb344ovKmDGjJkyYYN/29OnTioyMVEhICH3mEc/tc96MHj1azzzzDO0kDbvzs12+fLmaNWumOXPm2Iemvn1Ewbihqfv27WsfIAJwJoIFAFgUFRWl6Ohoh7koJGnRokWaOXOmNm/erJ49e6pixYpq27atvvrqKz377LPxHocDRtxp+/btWr58uRo3buww5w3tJO1haGqkBQQLALDg2rVrqly5sjw8PDRmzBgFBwercOHC9vWHDx/WTz/9pL59+6pGjRo6cuSIoqOjtXjxYoftgLthzpv04YMPPtDAgQOVK1cuh6Gpd+3apW7duqlZs2YOQ1N/8cUXunjxourXr+8wNDXBAs5EsAAAC/bt26dOnTopT548unbtmsLDw9WkSRM9//zzCgkJsW935swZDRw4UJs2bdKJEye0d+9eFS9e3HmFA0hRGJoaaQHBAgAsOH/+vF588UU9//zzatiwoZYvX64hQ4aoZMmSCggI0HvvvacMGTIoc+bMunbtmrZu3So/Pz9VrlzZ2aUDSGFee+01HThwQIsXL9a0adP0448/6uzZs5oxY4aeffZZ5c6dW0uXLnV2mcBdESwAwKLZs2dr0KBB+vHHH+3dm+rVq6d169apaNGiKl++vJo1a6bWrVs73I/+0ED6df36dY0ePVpdunRxOLtZtmxZ1a5dWxMmTNCFCxc0aNAgrV+/Xjlz5rRPjPfSSy85r3DgHhhuFgAsatmypSpXrqxNmzZJksaOHatffvlF69ev12uvvaaYmBi1bdtWBw8edLgfoQJIvxiaGmkRZywAIAkMHjxYW7ZsUf369TV69GjNmjVLzzzzjKRbfaFPnTqlPHnyOLlKACkJQ1MjrSFYAIAFcV/wkZGRKlGihE6cOKEFCxaoefPm99weQPrD0NRI6wgWAGCRMUaxsbF68803tWvXLq1atYrhQQE4YGhqpAdcYwEAFtlsNrm6uuqFF17Qtm3bNHv2bGeXBCCFOXz4sDw8PJQnTx6NGzdOXbp00fvvv6+jR49KkvLnz68OHTpo//79ypgxo0JDQ3Xw4EFFR0c7t3DgIXDGAgCSUKdOnXT48GEtW7ZMfn5+dFcAIImhqZE+ECwAIAnt379fFy5cUM2aNZ1dCoAUhqGpkdbRFQoAklDx4sUJFQASxNDUSOs4YwEAAJBMGJoaaRnBAgAA4BFjaGqkB4yHCAAA8IjZbDYZY+Tm5qaWLVtq165daty48T23B1IbrrEAAABIBgxNjbSOrlAAAADJjKGpkRYRLAAAAJIZQ1MjLSJYAAAAALCMaywAAAAAWEawAAAAAGAZwQIAAACAZQQLAAAAAJYRLAAAAABYRrAAAAAAYBnBAgAAAIBlBAsAAAAAlhEsAAAAAFhGsAAAAABgGcECAAAAgGX/B9MRVtagCNG+AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "labels = ['SSD, XTC', 'SSD, H5MD', 'AWS S3, H5MD', 'SSD, ZarrMD', 'AWS S3, ZarrMD']\n", + "values = [1.49, 4.76, 10.30,3.10, 6.53] \n", + "colors = ['#009e73', '#e69f00', '#e69f00','#56b4e9', '#56b4e9']\n", + "\n", + "plt.figure(figsize=(8, 6))\n", + "plt.bar(labels, values, color=colors)\n", + "\n", + "\n", + "plt.title('Comparison of Trajectory Iteration Speed by Storage Medium')\n", + "plt.ylabel('Time (minutes)')\n", + "\n", + "\n", + "plt.xticks(rotation=45, ha='right')\n", + "plt.tight_layout()\n", + "\n", + "plt.savefig('benchmark.png')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "zarrtraj", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/joss_paper/figure_2.ipynb b/joss_paper/figure_2.ipynb new file mode 100644 index 0000000..9bf43e5 --- /dev/null +++ b/joss_paper/figure_2.ipynb @@ -0,0 +1,336 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generate RMSD speed figure for JOSS paper. See \"zarrtra/data/write_aligned*.py\" scripts to see \n", + "trajectory writing code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import MDAnalysis as mda\n", + "import zarrtraj\n", + "import os\n", + "\n", + "os.environ[\"S3_REGION_NAME\"] = \"us-west-1\"\n", + "os.environ[\"AWS_PROFILE\"] = \"sample_profile\"\n", + "\n", + "TOPOL = \"/scratch/ljwoods2/workspace/zarrtraj/zarrtraj/data/yiip_equilibrium/YiiP_system.pdb\"\n", + "H5MD_DISK_TRAJ = \"/scratch/ljwoods2/workspace/zarrtraj/zarrtraj/data/yiip_aligned_compressed.h5md\"\n", + "H5MD_S3_TRAJ = \"s3://zarrtraj-test-data/yiip_aligned_compressed.h5md\"\n", + "ZARRMD_TRAJ_DISK = \"/scratch/ljwoods2/workspace/zarrtraj/zarrtraj/data/yiip_aligned_compressed.zarrmd\"\n", + "ZARRMD_TRAJ_S3 = \"s3://zarrtraj-test-data/yiip_aligned_compressed.zarrmd\"\n", + "XTC_TRAJ = \"/scratch/ljwoods2/workspace/zarrtraj/zarrtraj/data/yiip_equilibrium/YiiP_system_90ns_center_aligned.xtc\"\n", + "\n", + "h5md_disk_u = mda.Universe(TOPOL, H5MD_DISK_TRAJ)\n", + "h5md_s3_u = mda.Universe(TOPOL, H5MD_S3_TRAJ)\n", + "zarrmd_disk_u = mda.Universe(TOPOL, ZARRMD_TRAJ_DISK)\n", + "zarrm_s3_u = mda.Universe(TOPOL, ZARRMD_TRAJ_S3)\n", + "xtc_u = mda.Universe(TOPOL, XTC_TRAJ)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'xtc_u' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 11\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;66;03m## Dask \u001b[39;00m\n\u001b[1;32m 6\u001b[0m \n\u001b[1;32m 7\u001b[0m \u001b[38;5;66;03m### XTC \u001b[39;00m\n\u001b[1;32m 9\u001b[0m start \u001b[38;5;241m=\u001b[39m time\u001b[38;5;241m.\u001b[39mtime()\n\u001b[0;32m---> 11\u001b[0m R \u001b[38;5;241m=\u001b[39m RMSD(xtc_u, xtc_u,\n\u001b[1;32m 12\u001b[0m select\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbackbone\u001b[39m\u001b[38;5;124m\"\u001b[39m, \n\u001b[1;32m 13\u001b[0m )\n\u001b[1;32m 14\u001b[0m R\u001b[38;5;241m.\u001b[39mrun(backend\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdask\u001b[39m\u001b[38;5;124m'\u001b[39m, n_workers\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m4\u001b[39m)\n\u001b[1;32m 16\u001b[0m stop \u001b[38;5;241m=\u001b[39m time\u001b[38;5;241m.\u001b[39mtime()\n", + "\u001b[0;31mNameError\u001b[0m: name 'xtc_u' is not defined" + ] + } + ], + "source": [ + "from MDAnalysis.analysis.rms import RMSD\n", + "import time\n", + "\n", + "\n", + "## Dask \n", + "\n", + "### XTC \n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(xtc_u, xtc_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='dask', n_workers=4)\n", + "\n", + "stop = time.time()\n", + "\n", + "xtc_time_dask = stop-start\n", + "\n", + "### ZarrMD, disk\n", + "start = time.time()\n", + "\n", + "R = RMSD(zarrmd_disk_u, zarrmd_disk_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='dask', n_workers=4)\n", + "\n", + "stop = time.time()\n", + "\n", + "zarrmd_disk_time_dask = stop-start\n", + "\n", + "## H5MD, disk\n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(h5md_disk_u, h5md_disk_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='dask', n_workers=4)\n", + "\n", + "stop = time.time()\n", + "\n", + "h5md_disk_time_dask = stop-start\n", + "\n", + "## ZarrMD, S3\n", + "start = time.time()\n", + "\n", + "R = RMSD(zarrm_s3_u, zarrm_s3_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='dask', n_workers=4)\n", + "\n", + "stop = time.time()\n", + "\n", + "zarrmd_s3_time_dask = stop-start\n", + "\n", + "## H5MD, S3\n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(h5md_s3_u, h5md_s3_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='dask', n_workers=4)\n", + "\n", + "stop = time.time()\n", + "\n", + "h5md_s3_time_dask = stop-start\n", + "\n", + "## Serial\n", + "\n", + "## XTC\n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(xtc_u, xtc_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='serial')\n", + "\n", + "stop = time.time()\n", + "\n", + "xtc_time_serial = stop-start\n", + "\n", + "\n", + "## ZarrMD, disk\n", + "start = time.time()\n", + "\n", + "R = RMSD(zarrmd_disk_u, zarrmd_disk_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='serial')\n", + "\n", + "\n", + "stop = time.time()\n", + "\n", + "zarrmd_disk_time_serial = stop-start\n", + "\n", + "## H5MD, disk\n", + "\n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(h5md_disk_u, h5md_disk_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='serial')\n", + "\n", + "\n", + "stop = time.time()\n", + "\n", + "h5md_disk_time_serial = stop-start\n", + "\n", + "## ZarrMD, S3\n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(zarrm_s3_u, zarrm_s3_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='serial')\n", + "\n", + "stop = time.time()\n", + "\n", + "zarrm_s3_time_serial = stop-start\n", + "\n", + "## H5MD, S3\n", + "\n", + "start = time.time()\n", + "\n", + "R = RMSD(h5md_s3_u, h5md_s3_u,\n", + " select=\"backbone\", \n", + ")\n", + "R.run(backend='serial')\n", + "\n", + "\n", + "stop = time.time()\n", + "\n", + "h5md_s3_time_serial = stop-start" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.5079891562461853\n", + "1.5919220209121705\n", + "4.785086027781168\n", + "15.081525770823161\n", + "0.8399416049321492\n", + "1.9880372802416484\n", + "5.266235820452372\n", + "19.71386777162552\n", + "2.5871417999267576\n", + "5.6665968219439184\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_2862458/849001616.py:51: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.\n", + " ax1.set_xticklabels(labels, rotation=45, ha='right')\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABAEAAAMUCAYAAADNPz6+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC2EklEQVR4nOzdZ3gUZf/28XPTEwihJ6EFRFqQJh2EUKQ3EaTdUgQUQVSqwi1V0QhKUVSwISIKqNSbSJWqBBUBkSKCAgEMRRRCDSnX84Jn98+SQhI2ZnG+n+PYA3bK5rezM7Mz517XjM0YYwQAAAAAAP71PHK6AAAAAAAA8M8gBAAAAAAAwCIIAQAAAAAAsAhCAAAAAAAALIIQAAAAAAAAiyAEAAAAAADAIggBAAAAAACwCEIAAAAAAAAsghAAAAAAAACLcNsQYM+ePXrsscdUqlQp+fn5KXfu3Lr//vs1ZcoU/fXXXzldXrbr06ePSpYsmdNl3LFdu3YpIiJCQUFBstlsmjFjRprT2mw2p0eePHlUr149LViwIMW0c+fOdUy3adOmFOONMbr33ntls9nUqFEjp3Hnzp3T6NGjFR4erly5cikoKEjly5dXz549tWfPnlT/hs1mk5+fn0JCQtS4cWNFRkbqzJkzmVoWv//+uwYPHqyyZcvK399fAQEBqlixosaMGaOTJ09m6rVuru/o0aOZnjejbDabJkyYkOn5rly5ogkTJqT62fwTdaclISFB7777rmrWrKn8+fMrICBAYWFh6tChg5YuXfqP15MZR48elc1m09y5c2877ddff60aNWooV65cstlsWrZsWbbXZX94e3urQIECqlmzpoYOHap9+/almGfTpk2pbrszZ87UvffeKx8fH9lsNp0/f16SNGbMGJUoUUJeXl7Kmzdvtr2XO7V//35NmDAhU+v2d999p44dO6pEiRLy9fVVcHCw6tatq+HDhztN984772Tos/+3KFmyZIr977333qthw4bpzz//zLa/a183v/zyy2z7G5mRnfvLW79z03qkth/PjAkTJshms7mm6FRs27ZNEyZMcOwv3EmfPn3SXK4rV67M6fIy7auvvsrSMYE7K1mypPr06XPb6dLbRjIyvztI7zsqJ887Mnpcnt3b+meffZbueYorpXUc9E/yyrG/nI73339fgwYNUrly5TRy5EiFh4crISFBO3bs0OzZsxUdHe32B+x3auzYsXr22Wdzuow71rdvX12+fFkLFy5Uvnz5bruD6dy5s4YPHy5jjI4cOaJXXnlFPXr0kDFGPXr0SDF9YGCgPvzwwxQn+ps3b9Zvv/2mwMBAp+GXLl1SnTp1dOnSJY0cOVJVqlTR1atX9euvv2rJkiXavXu3Kleu7DTPRx99pPLlyyshIUFnzpzRN998o8mTJ+v111/XokWL9OCDD952OaxcuVLdunVTwYIFNXjwYFWrVk02m00///yz5syZo6ioKO3ateu2r3O3uHLliiZOnChJKT6bNm3aKDo6WqGhof94XT179tSSJUs0ZMgQTZw4Ub6+vvr999+1evVqrVmzRh07dvzHa3I1Y4y6dOmismXLasWKFcqVK5fKlSuX7X/36aefVo8ePZScnKzz589r165dmjNnjmbOnKnIyEiNHDnSMe3999+v6OhohYeHO4bt3r1bzzzzjPr376/evXvLy8tLgYGBWr58uV5++WW98MILatWqlXx9fbP9vWTV/v37NXHiRDVq1ChDB1NRUVFq3769GjVqpClTpig0NFSxsbHasWOHFi5cqKlTpzqmfeedd1SwYMG75mDTFerXr6/XX39dknT16lXt2LFDEyZM0JYtW7Rjx44cru7uFx0d7fT8pZde0saNG7Vhwwan4Tdvp1nRv39/tWzZ8o5eIz3btm3TxIkT1adPH7cMCf39/VMsU0kqX758DlRzZ7766iu9/fbb/7ogIKPsx6i3KlSoUA5Uk3npfUfl1HlHZo7Ls3tb/+yzz7R3714NGTLE5a99q9SOg/5xxs1s27bNeHp6mpYtW5pr166lGB8fH2+WL1+eA5X9My5fvpzTJbiUl5eXGThwYIamlWSeeuopp2FHjx41kkzDhg2dhn/00UdGkunfv7/x9/c3Fy5ccBr/6KOPmrp165qKFSuaiIgIx/A5c+YYSWbDhg2p1pCUlJTib/zwww8ppjt27JgpXry4CQwMNKdOnUr3ff3+++8mV65cplq1aub8+fMpxicnJ5vFixen+xqpsdd35MiRTM+bUZLM+PHjMz3f2bNnszxvdvn999+NJDNu3LhUx9/82bujI0eOGEnmo48+Sne6EydOGElm8uTJLvvbV65cMcnJyenW9dprr6U6X8uWLY0k89VXX6X7N+bPn28kme+++85p+KRJk4wkc/r06ay/gVtk1372iy++MJLMxo0bMzR9w4YNTenSpU1CQkKKcbeuj7fuy1whMTEx1e9ZdxAWFmbatGmTYvjYsWONJHPw4MFs+bsbN240kswXX3yRLa+fWf/Eft6ud+/eJleuXLedzt2OU1577bVsWUaueJ8ZXaZZ9U9/Fk899ZRxw1OHOxIWFmZ69+592+lSO0a922T2O+qfkJnj8sxu61euXMlULW3atDFhYWGZmudu5nbdAV555RXZbDa99957qf7i4+Pjo/bt2zueJycna8qUKSpfvrx8fX1VuHBh9erVSydOnHCar1GjRrrvvvsUHR2tevXqyd/fXyVLltRHH30k6cYvMvfff78CAgJUqVIlrV692ml+e3O2Xbt26eGHH1aePHkUFBSkRx99VGfPnnWadtGiRWrevLlCQ0Pl7++vChUqaNSoUbp8+bLTdH369FHu3Ln1888/q3nz5goMDFTTpk0d425N6b744gvVrl1bQUFBCggI0D333KO+ffs6TRMTE6NHH31UhQsXlq+vrypUqKCpU6cqOTnZMY29+e7rr7+uadOmqVSpUsqdO7fq1q2r7du3p/fxOOzdu1cdOnRQvnz55Ofnp6pVq+rjjz92jLc3YUxMTNSsWbMcTaYyKywsTIUKFdLp06dTHd+9e3dJcuoycOHCBS1evDjFspFuNDmSlOav0B4eGdskSpQooalTp+rixYt6991305122rRpunz5st555x0FBQWlGG+z2fTwww87nq9bt04dOnRQsWLFHE1gBwwYkOEmsKtXr1bTpk0d60mFChUUGRnpGN+oUaMUv85LGWsKdvbsWQ0aNEjh4eHKnTu3ChcurCZNmmjr1q2OaY4ePepIxSdOnJiiuVxazVvnzJmjKlWqyM/PT/nz51fHjh114MCBFDXmzp1bhw8fVuvWrZU7d24VL15cw4cPV3x8fLq1Z+aztzfTmj9/voYNG6aQkBD5+/srIiIi1RYbO3bsUPv27ZU/f375+fmpWrVq+vzzz1NMd+rUKQ0YMEDFihWTj4+PSpUqpYkTJyoxMdFpuj/++ENdunRRYGCggoKC1LVrV506dSrd9yfd2E8VK1ZMkvT888/LZrM5fabffPONmjZtqsDAQAUEBKhevXqKiopyeg3757N27Vr17dtXhQoVUkBAwG2Xb2r8/f314YcfytvbW6+99ppj+K3N4Bo1aqRH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Z5lWd4CMHKPeibS2afv3xq3Lb8qUKUaSY19hX85pHcNnNARI6/HJJ584TTt58mQjySxdutT07t3b+Pv7mz179jjGR0ZGGg8PjxTfd/bt2959bsuWLUaSeeGFF25b2+1CAGPS/4669RjQ/mPOlClTnKZbtGiRkWTee+89p7+Tkf1cWjJ6XJ5ed4CwsDDj6el5265iSUlJJiEhwcybN894eno6fRel1R0go/t8ezj4/PPPpzr/7UKAjB5P3nfffeahhx5K931mhNt1B8iMjRs3SlKKCyXVqlVLFSpU0Ndff+00PDQ0VNWrV3c8z58/vwoXLqyqVauqSJEijuEVKlSQpFSbEv7nP/9xet6lSxd5eXk5apFuXAW+R48eCgkJkaenp7y9vRURESFJKZo3S1KnTp1u+15r1qzp+Huff/55qleT37Bhg8LDw1WrVi2n4X369JExJsWFadq0aSNPT0/Hc/uFN1J737f+naZNm6p48eIp/s6VK1dSXGwoM9555x15e3vLx8dHZcuW1apVq7RgwQKnz+1WERERKl26tObMmaOff/5ZP/zwQ7pN0MaOHauYmBjNmTNHAwYMUO7cuTV79mxVr149001pjDGZmj4jzpw5oyeffFLFixeXl5eXvL29FRYWJin19cdu27ZtiouL06BBg7L1SsyzZ8/W/fffLz8/P0d9X3/9dbq1pSc6OlpXr15NsR0XL15cTZo0SbEd22w2tWvXzmlY5cqVb7veSlLr1q0VExOjpUuXasSIEapYsaKWLVum9u3ba/DgwSmm79Gjh9OyDAsLU7169Rzb++HDh/XLL7849guJiYmOR+vWrRUbG6uDBw9KunFxyMaNG6tIkSJO07Vq1UrSjYtZSjf2a4GBgU7dnuy1ZNXly5f13XffqXPnzsqdO7djuKenp3r27KkTJ0446rTLyH4po1y9naxZs0aJiYnq1auX07L08/NTREREqlfbvfX9rFy5Unnz5lW7du2cXqNq1aoKCQlJ8RpVq1ZViRIlHM/9/PxUtmzZDK13aSlQoIC2bt2qH374Qa+++qo6dOigX3/9VaNHj1alSpUy1AUos/v99u3bOzWlt/viiy9Uv3595c6d27Fdf/jhh07btX0d7dKli9O89m5ZdteuXdPXX3+tjh07KiAgIMV2ce3atQx1PXvggQf0ww8/6IcfftC3336rDz/8UGfPnlWTJk2cls2lS5f0/PPP695775WXl5e8vLyUO3duXb582an+VatWqXHjxo7v+PSsWbNGDRo0UMOGDbVu3Trlz5/fMS471p2NGzeqadOmCg4Odgzz9PRU165dU7yWj4+PnnjiCX388cf6/fffb/te7lS+fPnUpEmTFMMze6xjl9Ht99dff9Vvv/2mfv36yc/PL9N1Z/YYMa33+eyzz2r37t369ttvJd1oxv3JJ5+od+/eTvvTtPj7+zvWY/vjww8/dGmN0o3vt5u7EdnX8zZt2jhNZx8eExPjGJbVz9IuI8eomZHRbdru1u/LW49n7cs5rWP4jOrSpUuKz/KHH35Q69atnaYbOXKk2rRpo+7du+vjjz/WzJkzValSJcf4lStX6r777lPVqlWdtoEWLVo4NRFftWqVJOmpp57KcI2uYv/uuHXdfOSRR5QrV64U6+adfEe66ri8cuXKKlu2bIrhu3btUvv27VWgQAHH+t2rVy8lJSXp119/ve3rZnSfn9Z3ZOfOnW+7nmXmeLJWrVpatWqVRo0apU2bNunq1au3fQ+pcau7AxQsWFABAQE6cuRIhqZPrx9JkSJFUqx4N3+J2/n4+KQY7uPjI+nGgcytQkJCnJ57eXmpQIECjlouXbqkBg0ayM/PT5MmTVLZsmUVEBCg48eP6+GHH07xQQUEBChPnjy3e6tq2LChli1bpjfffFO9evVSfHy8KlasqBdeeMFxAHbu3LlU+3TbAw57jXYFChRwem6/BsPtVqZz586lucxT+zuZ0aVLF40cOVIJCQn6+eefNXr0aHXr1k07d+5UmTJlUp3HZrPpscce05tvvqlr166pbNmyatCgQbp/Jzg4WI899pgee+wxSdKWLVvUqlUrPfvssykOaNNy+fJlnTt3zmnHnpoSJUpkeJ1OTk5W8+bN9ccff2js2LGqVKmScuXKpeTkZNWpUyfdz8beL8neJzw7TJs2TcOHD9eTTz6pl156SQULFpSnp6fGjh2b5RDgdtvxunXrnIYFBASkOCD09fVNdXtNjb+/vx566CE99NBDkm4cCLVq1Upvv/22Bg4c6NTH89bt3T7sp59+kiRHP+IRI0ZoxIgRqf49+8nK6dOn9b///S/VE7Cbpzt37pzTiUB6tWTU33//LWNMprZbV9694dixY/L19U11H5wV9uVuP/C81a3X9khtP3v69GmdP3/esb+/1a0n4LfuL6Ub611Wv3xvVqNGDdWoUUPSjX6rzz//vKZPn64pU6ZoypQp6c6b2f1+ap/rkiVL1KVLFz3yyCMaOXKkQkJC5OXlpVmzZjn16Tx37py8vLxSfI63rq/nzp1TYmKiZs6cqZkzZ6Zad0YCjqCgIMdykaR69eopPDxcdevW1dSpUx3XOunRo4e+/vprjR07VjVr1lSePHlks9nUunVrp8/n7NmzGd4/Llu2TFevXtXAgQNTXJ8oO9adc+fOpbm/uVnp0qW1fv16TZkyRU899ZQuX76se+65R88880y2Xdk7tXUms8c6N8vo9nun32mZPUZMa5/XoUMHlSxZUm+//bbq16+vuXPn6vLlyxk+MfPw8HBaj7OjRinlMa59/bzdMe6dfJZ2GTlGzYyMbtN2tzuetS/ntI7hM6pQoUJpfpY3s1/XJioqSiEhIU7XApBubAOHDx++7fHA2bNn5enpeUff/1ll39/feucDm82mkJCQ255TSJn7jnTFcXlq20dMTIwaNGigcuXK6Y033lDJkiXl5+en77//Xk899VSG6svoPt++TG79TszIepaZ48k333xTxYoV06JFizR58mT5+fmpRYsWeu2119I8V0qNW4UAnp6eatq0qVatWqUTJ07cdsdvX6CxsbEppv3jjz9UsGBBl9d46tQpFS1a1PE8MTFR586dc9SyYcMG/fHHH9q0aZMjRZWU5j0tM/OLbYcOHdShQwfFx8dr+/btioyMVI8ePVSyZEnVrVtXBQoUUGxsbIr57BdHcdXyyM6/c/MOtm7duqpQoYIiIiI0dOjQdO+p26dPH40bN06zZ8/Wyy+/nOm/27BhQzVv3lzLli3TmTNnVLhw4dvOExUVpaSkpFQvsnezFi1aaObMmdq+fbvq1KmT7rR79+7VTz/9pLlz56p3796O4YcPH75tPfYd9a0XE7qVn5+fLly4kGJ4Rg7K58+fr0aNGmnWrFlOwy9evHjbedNy83Z8q+zajm9WokQJPfHEExoyZIj27dvnFAKkdjG+U6dOOWq21zZ69GinizvezH57voIFC6py5cpprp/2k7YCBQqkerGijFwYMC358uWTh4dHprZbV7UmOXnypH788UdFRERk6heX9Nhr/fLLLx2tZNKT2nuxX0Dq1ovA2t16e9F/ire3t8aPH6/p06dr7969t50+s/vj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jx7tmObdd99Vq1at1KJFC/Xp00dFixbVX3/9pQMHDmjnzp364osvJEkvvvii1q1bp3r16umZZ55RuXLldO3aNR09elRfffWVZs+erWLFiqlXr16aPn26evXqpZdfflllypTRV199pTVr1tzR+4+MjFSzZs3UuHFjjRgxQj4+PnrnnXe0d+9eLViw4I5/YYqJidH27duVnJysCxcuaNeuXZozZ46OHTumqVOnOv2SeqdKliypF198US+88IJ+//13tWzZUvny5dPp06f1/fffO1Ly9HTr1k2ffvqpWrdurWeffVa1atWSt7e3Tpw4oY0bN6pDhw7q2LFjpuq67777JEnvvfeeAgMD5efnp1KlSqV5wtuiRQsVK1ZM7dq1U/ny5ZWcnKzdu3dr6tSpyp07t1Pz7kqVKmnhwoVatGiR7rnnHvn5+alSpUou2e/bb6s0aNAgde7cWcePH9dLL72k0NBQHTp0yDFdy5YtVb9+fQ0fPlxxcXGqXr26oqOjNW/ePEnO3TDeeOMNPfDAA2rQoIEGDhyokiVL6uLFizp8+LD+97//pbhWQWrOnz/vuHZAQkKCDhw4oFdeeUW+vr6OFgZ58uRRw4YN9dprrzn2MZs3b9aHH36YItB98cUXtWrVKjVs2FD//e9/ValSJZ0/f16rV6/WsGHDVL58eafpvb29tWDBAvXv31+dO3fWvHnz1L1792xZd8aMGaMVK1aoSZMmGjdunAICAvT222+nuF3Y7NmztWHDBrVp00YlSpTQtWvXHL8mPvjgg47p+vTpo48//lhHjhy57a1fs+JOjnUys/2+/fbbateunerUqaOhQ4eqRIkSiomJ0Zo1axwndPYueW+88YZ69+4tb29vlStXzqXHiHnz5lWvXr00a9YshYWFpbguTVblxHHsrTLzWdqX9eTJk9WqVSt5enqqcuXKmjRp0m2PUaUbJ+ObN29O94eKzGzTGVWhQgU9+uijmjFjhry9vfXggw9q7969ev311zN07GN3+vTpVK9nkidPHoWHh0u6cYy8detWrV27ViEhIRo+fLg2b96sfv36qVq1aipVqpSGDBmixYsXq2HDhho6dKgqV66s5ORkxcTEaO3atRo+fLhq166tBg0aqGfPnpo0aZJOnz6ttm3bytfXV7t27VJAQICefvppSTeO68aOHatx48YpIiJC+/fv11tvvZXi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x03Lly831apVM23atEl1fpbnDZ988ol54oknTFxcnGPY9u3bzYABA8zFixdNQkKCqV+/vmnWrJn59ttvTalSpUzt2rUdx/bXr183xrA8rYwQwCJS+3I/c+aM6dmzpwkKCjJNmzY1y5cvN1evXjXXrl0zZcuWNdu3bzfGsINIz88//2zq1atnNm3aZPbs2WMqVqxo7r//fsdJVlRUlImIiDBFixY1RYsWNQsXLszhit2T/SDrzz//ND/99JM5cuSI4wT/wIEDJioqyiQkJDhOGGbOnGkiIiLMmTNncqxmd2ffbnft2mU6dOhgjDHmpZdeMp6enuatt95yHDjMnDnTFC5c2JQqVcoEBgayjqYjtf3oqVOnTLly5UzJkiVNw4YNzcKFC01ycrLZtm2bKVeunDl06FAOVHp3uHkdLVGihDl//rxZsWKFKVasmHniiSfMzz//bIwxZtu2bebee+81hQoVMsHBwayjGTBy5Ejz8MMPO57HxMSYmTNnmo8//tj89ttv5vLly+aFF14wZcqUMQcPHszBSt3XhAkTzBdffGGMcT4O+vrrr40xxvTu3ds8+OCD5sqVK+by5cumSpUqxmazmXbt2qWYB84OHz5sgoODzQsvvOB0EmvMjeX21VdfmTJlypg6deo4Dcf/WbZsmVm5cqUxxpj4+HjH8O+//94YY8ywYcNMo0aNzN9//22MMebhhx82NpvNhIWFmfj4eJYnDN0BLODmvoF79+7VuXPn1KZNGxUqVEjz5s3Tr7/+6tSX+r333pMxRoULF5ZEc+Bb2ZfnlStXNG7cONWuXVsRERGSblykrnv37nrggQf07bffqnXr1o5lm5ycrLJly9LU8hY3X1ytV69eunjxovz9/VW9enVNmDBB5cuXV/ny5SXdaC68ceNGjRgxQgsWLFChQoVyuHr3ZO9r+ddff+nZZ59VzZo1df36dY0ZM0aJiYl65plnZIzRoEGD1LdvX9WqVUsHDhxQxYoVVaNGDdbRVNi3+9jYWO3cuVN+fn4qVqyYypUrp++//15Hjx5V5cqVHdMfOHBA3t7e3Oc6DfZ1NC4uTm+++aY6deokX19ftWvXTklJSXr66adljNHIkSNVt25d7dixQz/99JNCQkLYj94GXalcg65U2YduVHeOblS4YzmZQCD73dzsqlixYqZ27domT548pmnTpubjjz92aua2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mbJixQrp3Lmzep1l3+bv0qVL4uTkJO+8847cu3dPTp8+LYqiyOzZs9V9Fi9eLI0aNZLBgwezWKWDUSrjYpTK8BijMhzGqMhYWAQownQnWLVr15aQkBD57rvvJC0tTX7++WeZMmWKKIoikyZNkoSEBLl79660bt1a6tWrJ82bN5fSpUvLxo0bTfwuCo/cF1rt3auYmBj14UAajUaaNWsmXbp0UR8OFB8fLyI50YCdO3cWXIMLOW1/JiQkiKurq/Tv3z/PtyWcP39evLy8pF69enL8+HEREYmIiJCBAwdK586d1eXVHBDkT9sv7733nvTr109EcgaqgYGB0q5dO73+jo+PlwEDBjBWkYvuddTT01N69+4t3377rVy8eFHOnj0r7du3l6pVq8rrr78uaWlpkpKSIj179pSKFStK165dxcrKihlrHbnPVe3fV65cKUFBQSIikpKSIoGBgfLyyy/nuVP1ySefyKFDhwqmsUUAo1TGxSjVi2OMyngYoyJjYhGgiIuPj5fq1atL3759JTs7Wx0waE/8WbNmiaIosnz5chERuXr1qnz00Ucyffp0tUrIi8SjgVZ6erpcuXJF72fz5s2TgIAASU5OlmbNmkmHDh3UAkBsbKxMnTpVLQSIsD91paamiqenpzRr1kzdlp2dLRqNRv1w+/3338XGxkZef/11vdempKSICO8KaOlOBnI/bblnz57y4YcfiohIkyZN9I7RDRs2yP79+9XX0iO5C1X5reRJTEyUoUOHirOzs6xatUpEclaszJw5U8aNGyfff/+93u8yZ7qRijt37ugdp2+++aYMHDhQRER8fHz0jtHt27erq4HoEUapjItRqhfHGJXxMEZFxsYiQBGWlZUlvr6+UrlyZb3tugOvtLQ0CQkJkSpVquT7lUCcYOWNVHTt2lXvgSrffPONtGnTRurVqyft2rXTGwiEhYVJv3795M6dOwXe7qJAGwMIDAyUXbt2qX2tPea0d1fGjx8vTZs2lQcPHqg/M/fjUpfuZGDUqFHyySef6N3lnzVrlnTu3Fm986f7HeDjx4+XV199VVJSUvh1lfnIyMiQli1biru7u1p40va39s+UlBSpX7++tG7dWu+1usequR+vutfRkJAQmThxot4y9K+//lpq1aolHh4e0qlTJ73C6bRp06Rv375y//79Am93YcUoVcFglOr5MUZlfIxR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hOuJiIiIioT4+Hhcv34dXl5enAwQmQGe8y8uJCQE5cqVQ3BwMPr06ZMnUsH+JHPEIgAREVERxDtYROaF5/yz0fbX+++/j7CwMAA5KwEGDhzIAgCZPcYBiIiIiiAOXonMC8/5Z8NIBdHjcSUAEREREREVS4xUEOXFIgARERERERV7jFQQ5eBXBBIRERERUbHHAgBRDhYBiIiIiIiIiMwEiwBEREREREREZoJFACIiIiIiIiIzwSIAERERERERkZlgEYCIiIiIiIjITLAIQERERERERGQmWAQgIiIiIiIiMhMsAhARERERERGZCRYBiIiIiIiIiMwEiwBEREREREREZoJFACIiIiIiIiIzwSIAERERERERkZlgEYCIiIiIiIjITLAIQERERERERGQmWAQgIiIiIiIiMhMsAhARERERERGZCRYBiIiIiIiIiMwEiwBEREREREREZoJFACIiIiIiIiIzwSIAERERERERkZlgEYCIiIiIiIjITLAIQERERERERGQmWATIR3p6OqZPn4709HRTN4WIiIjMDMchRERFQ1G9XisiIqZuRGGTkJAAW1tbxMfHo3z58qZuDhEREZkRjkOIiIqGonq95koAIiIiIiIiIjPBIgARERERERGRmbAydQMKI21CIiEhwcQtISIiInOjHX9wHEJEVLhpr9NFLWHPIkA+MjIyAACurq4mbgkRERGZK45DiIiKBu38sajggwHzISJITEw0dTOIiIiIiIiokCtXrhwURTF1M54aiwBEREREREREZoIPBiQiIiIiIiIyEywCEBEREREREZkJFgGIiIiIiIiIzASLAERklhRFwVdffWXwfXW1atUK69ate+bXGUqfPn3wySefmOzfJyKiouXQoUOwtLREUFCQ3vZ+/fqhU6dOetu++eYbKIqCadOm6W3/8MMP4eLiov79yy+/RPPmzWFra4ty5cqhXr16mDRp0hPbsWfPHgQGBsLe3h6lS5eGh4cHhg4diqysLADAuXPnEBgYCEdHR9jY2KBGjRqYOnUqMjMzX+TtE5kNFgGIqNgYNmwYFEWBoigoUaIEHB0d0b59e6xatQoajUZv3xs3buQZ0BjS9u3bcfPmTfTr10/d9ssvvyA4OBiVK1eGjY0N3N3dERoairt376r7/NtgKTIyUn2PlpaWsLOzQ/PmzTFjxgzEx8frteG9997DzJkz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VOmJryciIvO0fft2PHjwACNHjoStra3ez/r06YOVK1fitddeA5CzGuCzzz5DrVq14OjoqO7XunVrLFq0CDVq1FAfdgsA06dPR0pKCjp37gw3Nzc8fPgQCxcuRGZmJtq3b59vez7//HOcOHECPXv2RM2aNZGWloaoqCicOXNGfVDu2rVrUaJECTRo0ADW1tY4duwY3n77bYSGhqrfAkREj8ezhIiKFW3+XfcrAmfNmoXg4GAMGTIk39d8+umncHZ2RuPGjWFhYYHNmzfDyckJFSpU0NuvZ8+eiI6OxuDBg2FlZZVntYCWt7c3HBwccPDgQQQHBwPIGWRt2LAB/fr1Q+3atSEi+Prrr7Fz505EREQAePrBkojg5s2b6lcExsXFISwsDLa2tvjoo4/02hIbG4sOHTo8b3cSEVExt3LlSrRr1y5PAQAAevfujbCwMBw/fhxNmjRBYGAgoqKi1OcBaLVu3Rrh4eEICQnJs33JkiUYMmQIbt26BTs7O3h7e+O7776Dp6dnvu1p1qwZDhw4gP/85z/4+++/UbZsWdSrVw9fffWVuvrAysoKs2fPxvnz5yEicHNzw7hx455Y7CeiRxThmhkiKiaGDRuG1atXA8gZINjZ2aFRo0YYMGAAhg4dqneHXFEUbNmyBT169MCKFSvw2Wef4cKFC7C0tETTpk0xd+5ceHt759kXADZt2oShQ4di7dq16NWrV75tefvtt3HlyhX16/kuXbqEjz76CPv27cNff/0Fa2treHh44NVXX8WwYcMAAHv27MGSJUtw5MgRvcHSu+++i5YtWwIAIiMjMXz4cLVd5cuXh6enJ4KDgzFhwgSUL19ebUNaWhocHR2xa9cu9XudiYiIiMi8sQhARGQEt27dQr169XDs2DG4ubmZpA1LlizB1q1b833QIRERERGZJz4YkIjICBwdHbFy5Ur8+eefJmtDiRIl1PwkERERERHAlQBEREREREREZoMrAYiIiIiIiIjMBIsARERERERERGaCRQAiIiIiIiIiM8EiABEREREREZGZYBGAiIiIiIiIyEywCEBERERERERkJlgEICIiIiIiIjITLAIQERERERERmQkWAYiIiIiIiIjMxP8Do6PMwenURjUAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.ticker as ticker\n", + "\n", + "labels = [\n", + "'XTC, Dask', \n", + "'XTC, Serial', \n", + "'H5MD, Dask', \n", + "'H5MD, Serial', \n", + "'ZarrMD, Dask', \n", + "'ZarrMD, Serial', \n", + "# '',\n", + "'H5MD, Dask ', \n", + "'H5MD, Serial ', \n", + "'ZarrMD, Dask ', \n", + "'ZarrMD, Serial ']\n", + "\n", + "values = [\n", + "# XTC, disk\n", + "30.479349374771118 / 60.0,\n", + "95.51532125473022 / 60.0,\n", + "# H5MD, disk\n", + "287.1051616668701 / 60.0,\n", + "904.8915462493896 / 60.0,\n", + "# ZarrMD, disk\n", + "50.396496295928955 / 60.0,\n", + "119.2822368144989 / 60.0,\n", + "# Sep\n", + "# 0,\n", + "# H5MD, S3\n", + "315.97414922714233 / 60.0,\n", + "1182.8320662975311 / 60.0,\n", + "# ZarrMD, S3\n", + "155.22850799560547 / 60.0,\n", + "339.99580931663513 / 60.0,\n", + "]\n", + "\n", + "for value in values:\n", + " print(value)\n", + "colors = [\n", + "'#009e73', '#009e73', \n", + "'#e69f00', '#e69f00', \n", + "'#56b4e9', '#56b4e9', \n", + "# 'none',\n", + "'#e69f00', '#e69f00', \n", + "'#56b4e9', '#56b4e9']\n", + "\n", + "\n", + "fig1, ax1 = plt.subplots(figsize=(12, 8))\n", + "ax1.bar(labels, values, color=colors)\n", + "ax1.set_xticklabels(labels, rotation=45, ha='right')\n", + "ax1.set_ylabel('Time (minutes)')\n", + "\n", + "# Axis 2 (labels)\n", + "ax2 = ax1.twiny()\n", + "ax2.spines[\"bottom\"].set_position((\"axes\", -0.20))\n", + "ax2.tick_params('both', length=0, width=0, which='minor')\n", + "ax2.tick_params('both', direction='in', which='major')\n", + "ax2.xaxis.set_ticks_position(\"bottom\")\n", + "ax2.xaxis.set_label_position(\"bottom\")\n", + "\n", + "ax2.set_xticks([0.0, 0.6, 1.0])\n", + "ax2.xaxis.set_major_formatter(ticker.NullFormatter())\n", + "ax2.xaxis.set_minor_locator(ticker.FixedLocator([0.3, 0.8]))\n", + "ax2.xaxis.set_minor_formatter(ticker.FixedFormatter(['Disk (SSD)', 'AWS S3']))\n", + "\n", + "\n", + "\n", + "plt.title('Comparison of RMSD Calculation Speed for Different Storage Backends, Trajectory Formats, and Execution Strategies')\n", + "\n", + "plt.tight_layout()\n", + "\n", + "plt.savefig('RMSD.png')\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "zarrtraj", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/joss_paper/paper.bib b/joss_paper/paper.bib new file mode 100644 index 0000000..b742aae --- /dev/null +++ b/joss_paper/paper.bib @@ -0,0 +1,318 @@ +@article{FAIR:2019, + title = {Make scientific data FAIR}, + volume = {570}, + ISSN = {1476-4687}, + url = {http://dx.doi.org/10.1038/d41586-019-01720-7}, + DOI = {10.1038/d41586-019-01720-7}, + number = {7759}, + journal = {Nature}, + publisher = {Springer Science and Business Media LLC}, + author = {Stall, Shelley and Yarmey, Lynn and Cutcher-Gershenfeld, Joel and Hanson, Brooks and Lehnert, Kerstin and Nosek, Brian and Parsons, Mark and Robinson, Erin and Wyborn, Lesley}, + year = {2019}, + month = jun, + pages = {27–29} +} + +@misc{FoldingAtHome:2020, + title = {Foldingathome COVID-19 Datasets}, + url = {https://registry.opendata.aws/foldingathome-covid19}, + note = {Accessed: September 25, 2024} +} + +@article{GPCRmd:2019, + title = {Bringing Molecular Dynamics Simulation Data into View}, + volume = {44}, + ISSN = {0968-0004}, + url = {http://dx.doi.org/10.1016/j.tibs.2019.06.004}, + DOI = {10.1016/j.tibs.2019.06.004}, + number = {11}, + journal = {Trends in Biochemical Sciences}, + publisher = {Elsevier BV}, + author = {Hildebrand, Peter W. and Rose, Alexander S. and Tiemann, Johanna K.S.}, + year = {2019}, + month = nov, + pages = {902–913} +} + +@article{GPCRome:2020, + title = {GPCRmd uncovers the dynamics of the 3D-GPCRome}, + volume = {17}, + ISSN = {1548-7105}, + url = {http://dx.doi.org/10.1038/s41592-020-0884-y}, + DOI = {10.1038/s41592-020-0884-y}, + number = {8}, + journal = {Nature Methods}, + publisher = {Springer Science and Business Media LLC}, + author = {Rodríguez-Espigares, Ismael and Torrens-Fontanals, Mariona and Tiemann, Johanna K. S. and Aranda-García, David and Ramírez-Anguita, Juan Manuel and Stepniewski, Tomasz Maciej and Worp, Nathalie and Varela-Rial, Alejandro and Morales-Pastor, Adrián and Medel-Lacruz, Brian and Pándy-Szekeres, Gáspár and Mayol, Eduardo and Giorgino, Toni and Carlsson, Jens and Deupi, Xavier and Filipek, Slawomir and Filizola, Marta and Gómez-Tamayo, José Carlos and Gonzalez, Angel and Gutiérrez-de-Terán, Hugo and Jiménez-Rosés, Mireia and Jespers, Willem and Kapla, Jon and Khelashvili, George and Kolb, Peter and Latek, Dorota and Marti-Solano, Maria and Matricon, Pierre and Matsoukas, Minos-Timotheos and Miszta, Przemyslaw and Olivella, Mireia and Perez-Benito, Laura and Provasi, Davide and Ríos, Santiago and R. Torrecillas, Iván and Sallander, Jessica and Sztyler, Agnieszka and Vasile, Silvana and Weinstein, Harel and Zachariae, Ulrich and Hildebrand, Peter W. and De Fabritiis, Gianni and Sanz, Ferran and Gloriam, David E. and Cordomi, Arnau and Guixà-González, Ramon and Selent, Jana}, + year = {2020}, + month = jul, + pages = {777–787} +} + +@article{H5MD:2014, + title = {H5MD: A structured, efficient, and portable file format for molecular data}, + journal = {Computer Physics Communications}, + volume = {185}, + number = {6}, + pages = {1546-1553}, + year = {2014}, + issn = {0010-4655}, + doi = {https://doi.org/10.1016/j.cpc.2014.01.018}, + url = {https://www.sciencedirect.com/science/article/pii/S0010465514000447}, + author = {Pierre {de Buyl} and Peter H. Colberg and Felix Höfling}, + keywords = {Molecular simulation, HDF5}, + abstract = {We propose a new file format named “H5MD” for storing molecular simulation data, such as trajectories of particle positions and velocities, along with thermodynamic observables that are monitored during the course of the simulation. H5MD files are HDF5 (Hierarchical Data Format) files with a specific hierarchy and naming scheme. Thus, H5MD inherits many benefits of HDF5, e.g., structured layout of multi-dimensional datasets, data compression, fast and parallel I/O, and portability across many programming languages and hardware platforms. H5MD files are self-contained, and foster the reproducibility of scientific data and the interchange of data between researchers using different simulation programs and analysis software. In addition, the H5MD specification can serve for other kinds of data (e.g. experimental data) and is extensible to supplemental data, or may be part of an enclosing file structure.} +} + +@inproceedings{H5MDReader:2021, + address = {Austin, TX}, + title = {{MPI}-parallel {Molecular} {Dynamics} {Trajectory} {Analysis} with the {H5MD} {Format} in the {MDAnalysis} {Python} {Package}}, + url = {https://conference.scipy.org/proceedings/scipy2021/edis_jakupovic.html}, + doi = {10.25080/majora-1b6fd038-005}, + abstract = {Molecular dynamics (MD) computer simulations help elucidate details of the molecular processes in complex biological systems, from protein dynamics to drug discovery. One major issue is that these MD simulation files are now commonly terabytes in size, which means analyzing the data from these files becomes a painstakingly expensive task. In the age of national supercomputers, methods of parallel analysis are becoming a necessity for the efficient use of time and high performance computing (HPC) resources but for any approach to parallel analysis, simply reading the file from disk becomes the performance bottleneck that limits overall analysis speed. One promising way around this file I/O hurdle is to use a parallel message passing interface (MPI) implementation with the HDF5 (Hierarchical Data Format 5) file format to access a single file simultaneously with numerous processes on a parallel file system. Our previous feasibility study suggested that this combination can lead to favorable parallel scaling with hundreds of CPU cores, so we implemented a fast and user-friendly HDF5 reader (the H5MDReader class) that adheres to H5MD (HDF5 for Molecular Dynamics) specifications. We made H5MDReader (together with a H5MD output class H5MDWriter) available in the MDAnalysis library, a Python package that simplifies the process of reading and writing various popular MD file formats by providing a streamlined user-interface that is independent of any specific file format. We benchmarked H5MDReader's parallel file reading capabilities on three HPC clusters: ASU Agave, SDSC Comet, and PSC Bridges. The benchmark consisted of a simple split-apply-combine scheme of an I/O bound task that split a 90k frame (113 GiB) coordinate trajectory into chunks for processes, where each process performed the commonly used RMSD (root mean square distance after optimal structural superposition) calculation on their chunk of data, and then gathered the results back to the root process. For baseline performance, we found maximum I/O speedups at 2 full nodes, with Agave showing 20x, and a maximum computation speedup on Comet of 373x on 384 cores (all three HPCs scaled well in their computation task). We went on to test a series of optimizations attempting to speed up I/O performance, including adjusting file system stripe count, implementing a masked array feature that only loads relevant data for the computation task, front loading all I/O by loading the entire trajectory into memory, and manually adjusting the HDF5 dataset chunk shapes. We found the largest improvement in I/O performance by optimizing the chunk shape of the HDF5 datasets to match the iterative access pattern of our analysis benchmark. With respect to baseline serial performance, our best result was a 98x speedup at 112 cores on ASU Agave. In terms of absolute time saved, the analysis went from 4623 seconds in the baseline serial run to 47 seconds in the parallel, properly chunked run. Our results emphasize the fact that file I/O is not just dependent on the access pattern of the file, but more so the synergy between access pattern and the layout of the file on disk.}, + urldate = {2021-07-05}, + booktitle = {Proceedings of the 20th {Python} in {Science} {Conference}}, + author = {Jakupovic, Edis and Beckstein, Oliver}, + editor = {Agarwal, Meghann and Calloway, Chris and Niederhut, Dillon and Shupe, David}, + year = {2021}, + pages = {40--48}, +} + +@INPROCEEDINGS{MDAKits:2023, + title = "{MDAKits}: A framework for {FAIR-compliant} molecular + simulation analysis", + booktitle = "Proceedings of the Python in Science Conference", + author = "Alibay, Irfan and Wang, Lily and Naughton, Fiona and Kenney, + Ian and Barnoud, Jonathan and Gowers, Richard and Beckstein, + Oliver", + publisher = "SciPy", + pages = "76--84", + year = 2023, + conference = "Python in Science Conference", + location = "Austin, Texas" +} + + +@InProceedings{MDAnalysis:2016, + author = { {R}ichard {J}. {G}owers and {M}ax {L}inke and {J}onathan {B}arnoud and {T}yler {J}. {E}. {R}eddy and {M}anuel {N}. {M}elo and {S}ean {L}. {S}eyler and {J}an {D}omański and {D}avid {L}. {D}otson and {S}ébastien {B}uchoux and {I}an {M}. {K}enney and {O}liver {B}eckstein }, + title = { {M}{D}{A}nalysis: {A} {P}ython {P}ackage for the {R}apid {A}nalysis of {M}olecular {D}ynamics {S}imulations }, + booktitle = { {P}roceedings of the 15th {P}ython in {S}cience {C}onference }, + pages = { 98 - 105 }, + year = { 2016 }, + editor = { {S}ebastian {B}enthall and {S}cott {R}ostrup }, + doi = { 10.25080/Majora-629e541a-00e } +} + + +@article{MDAnalysis:2011, + author = {Michaud-Agrawal, Naveen and Denning, Elizabeth J. and Woolf, Thomas B. and Beckstein, Oliver}, + title = {MDAnalysis: A toolkit for the analysis of molecular dynamics simulations}, + journal = {Journal of Computational Chemistry}, + volume = {32}, + number = {10}, + pages = {2319-2327}, + keywords = {molecular dynamics simulations, analysis, proteins, object-oriented design, software, membrane systems, Python programming language}, + doi = {https://doi.org/10.1002/jcc.21787}, + url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/jcc.21787}, + eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/jcc.21787}, + abstract = {Abstract MDAnalysis is an object-oriented library for structural and temporal analysis of molecular dynamics (MD) simulation trajectories and individual protein structures. It is written in the Python language with some performance-critical code in C. It uses the powerful NumPy package to expose trajectory data as fast and efficient NumPy arrays. It has been tested on systems of millions of particles. Many common file formats of simulation packages including CHARMM, Gromacs, Amber, and NAMD and the Protein Data Bank format can be read and written. Atoms can be selected with a syntax similar to CHARMM's powerful selection commands. MDAnalysis enables both novice and experienced programmers to rapidly write their own analytical tools and access data stored in trajectories in an easily accessible manner that facilitates interactive explorative analysis. MDAnalysis has been tested on and works for most Unix-based platforms such as Linux and Mac OS X. It is freely available under the GNU General Public License from http://mdanalysis.googlecode.com. © 2011 Wiley Periodicals, Inc. J Comput Chem 2011}, + year = {2011} +} + +@misc{MDDB:2024, + title={The need to implement FAIR principles in biomolecular simulations}, + author={Rommie Amaro and Johan Åqvist and Ivet Bahar and Federica Battistini and Adam Bellaiche and Daniel Beltran and Philip C. Biggin and Massimiliano Bonomi and Gregory R. Bowman and Richard Bryce and Giovanni Bussi and Paolo Carloni and David Case and Andrea Cavalli and Chie-En A. Chang and Thomas E. Cheatham III au2 and Margaret S. Cheung and Cris Chipot and Lillian T. Chong and Preeti Choudhary and Gerardo Andres Cisneros and Cecilia Clementi and Rosana Collepardo-Guevara and Peter Coveney and Roberto Covino and T. Daniel Crawford and Matteo Dal Peraro and Bert de Groot and Lucie Delemotte and Marco De Vivo and Jonathan Essex and Franca Fraternali and Jiali Gao and Josep Lluís Gelpí and Francesco Luigi Gervasio and Fernando Danilo Gonzalez-Nilo and Helmut Grubmüller and Marina Guenza and Horacio V. Guzman and Sarah Harris and Teresa Head-Gordon and Rigoberto Hernandez and Adam Hospital and Niu Huang and Xuhui Huang and Gerhard Hummer and Javier Iglesias-Fernández and Jan H. Jensen and Shantenu Jha and Wanting Jiao and William L. Jorgensen and Shina Caroline Lynn Kamerlin and Syma Khalid and Charles Laughton and Michael Levitt and Vittorio Limongelli and Erik Lindahl and Kresten Lindorff-Larsen and Sharon Loverde and Magnus Lundborg and Yun Lyna Luo and Francisco Javier Luque and Charlotte I. Lynch and Alexander MacKerell and Alessandra Magistrato and Siewert J. Marrink and Hugh Martin and J. Andrew McCammon and Kenneth Merz and Vicent Moliner and Adrian Mulholland and Sohail Murad and Athi N. Naganathan and Shikha Nangia and Frank Noe and Agnes Noy and Julianna Oláh and Megan O'Mara and Mary Jo Ondrechen and José N. Onuchic and Alexey Onufriev and Silvia Osuna and Anna R. Panchenko and Sergio Pantano and Carol Parish and Michele Parrinello and Alberto Perez and Tomas Perez-Acle and Juan R. Perilla and B. Montgomery Pettitt and Adriana Pietropalo and Jean-Philip Piquemal and Adolfo Poma and Matej Praprotnik and Maria J. Ramos and Pengyu Ren and Nathalie Reuter and Adrian Roitberg and Edina Rosta and Carme Rovira and Benoit Roux and Ursula Röthlisberger and Karissa Y. Sanbonmatsu and Tamar Schlick and Alexey K. Shaytan and Carlos Simmerling and Jeremy C. Smith and Yuji Sugita and Katarzyna Świderek and Makoto Taiji and Peng Tao and Irina G. Tikhonova and Julian Tirado-Rives and Inaki Tunón and Marc W. Van Der Kamp and David Van der Spoel and Sameer Velankar and Gregory A. Voth and Rebecca Wade and Ariel Warshel and Valerie Vaissier Welborn and Stacey Wetmore and Chung F. Wong and Lee-Wei Yang and Martin Zacharias and Modesto Orozco}, + year={2024}, + eprint={2407.16584}, + archivePrefix={arXiv}, + primaryClass={q-bio.BM}, + url={https://arxiv.org/abs/2407.16584}, +} + +@article{MDsrv:2022, + author = {Kampfrath, Michelle and Staritzbichler, René and Hernández, Guillermo Pérez and Rose, Alexander S and Tiemann, Johanna K S and Scheuermann, Gerik and Wiegreffe, Daniel and Hildebrand, Peter W}, + title = "{MDsrv: visual sharing and analysis of molecular dynamics simulations}", + journal = {Nucleic Acids Research}, + volume = {50}, + number = {W1}, + pages = {W483-W489}, + year = {2022}, + month = {05}, + abstract = "{Molecular dynamics simulation is a proven technique for computing and visualizing the time-resolved motion of macromolecules at atomic resolution. The MDsrv is a tool that streams MD trajectories and displays them interactively in web browsers without requiring advanced skills, facilitating interactive exploration and collaborative visual analysis. We have now enhanced the MDsrv to further simplify the upload and sharing of MD trajectories and improve their online viewing and analysis. With the new instance, the MDsrv simplifies the creation of sessions, which allows the exchange of MD trajectories with preset representations and perspectives. An important innovation is that the MDsrv can now access and visualize trajectories from remote datasets, which greatly expands its applicability and use, as the data no longer needs to be accessible on a local server. In addition, initial analyses such as sequence or structure alignments, distance measurements, or RMSD calculations have been implemented, which optionally support visual analysis. Finally, based on Mol*, MDsrv now provides faster and more efficient visualization of even large trajectories compared to its predecessor tool NGL.}", + issn = {0305-1048}, + doi = {10.1093/nar/gkac398}, + url = {https://doi.org/10.1093/nar/gkac398}, + eprint = {https://academic.oup.com/nar/article-pdf/50/W1/W483/44375694/gkac398.pdf}, +} + + +@article {MDverse:2024, + article_type = {journal}, + title = {MDverse, shedding light on the dark matter of molecular dynamics simulations}, + author = {Tiemann, Johanna KS and Szczuka, Magdalena and Bouarroudj, Lisa and Oussaren, Mohamed and Garcia, Steven and Howard, Rebecca J and Delemotte, Lucie and Lindahl, Erik and Baaden, Marc and Lindorff-Larsen, Kresten and Chavent, Matthieu and Poulain, Pierre}, + editor = {Haider, Shozeb and Cui, Qiang}, + volume = 12, + year = 2024, + month = {aug}, + pub_date = {2024-08-30}, + pages = {RP90061}, + citation = {eLife 2024;12:RP90061}, + doi = {10.7554/eLife.90061}, + url = {https://doi.org/10.7554/eLife.90061}, + abstract = {The rise of open science and the absence of a global dedicated data repository for molecular dynamics (MD) simulations has led to the accumulation of MD files in generalist data repositories, constituting the \textit{dark matter of MD} — data that is technically accessible, but neither indexed, curated, or easily searchable. Leveraging an original search strategy, we found and indexed about 250,000 files and 2000 datasets from Zenodo, Figshare and Open Science Framework. With a focus on files produced by the Gromacs MD software, we illustrate the potential offered by the mining of publicly available MD data. We identified systems with specific molecular composition and were able to characterize essential parameters of MD simulation such as temperature and simulation length, and could identify model resolution, such as all-atom and coarse-grain. Based on this analysis, we inferred metadata to propose a search engine prototype to explore the MD data. To continue in this direction, we call on the community to pursue the effort of sharing MD data, and to report and standardize metadata to reuse this valuable matter.}, + keywords = {molecular dynamics, simulation, modeling, FAIR}, + journal = {eLife}, + issn = {2050-084X}, + publisher = {eLife Sciences Publications, Ltd}, +} + +@article{MLMDMethods:2023, +author = {Jackson, Nicholas E. and Savoie, Brett M. and Statt, Antonia and Webb, Michael A.}, +title = {Introduction to Machine Learning for Molecular Simulation}, +journal = {Journal of Chemical Theory and Computation}, +volume = {19}, +number = {14}, +pages = {4335-4337}, +year = {2023}, +doi = {10.1021/acs.jctc.3c00735}, + note ={PMID: 37489106}, +URL = { + https://doi.org/10.1021/acs.jctc.3c00735 +}, +eprint = { + https://doi.org/10.1021/acs.jctc.3c00735 +} +} + +@Article{NumPy:2020, + title = {Array programming with {NumPy}}, + author = {Charles R. Harris and K. Jarrod Millman and St{\'{e}}fan J. + van der Walt and Ralf Gommers and Pauli Virtanen and David + Cournapeau and Eric Wieser and Julian Taylor and Sebastian + Berg and Nathaniel J. Smith and Robert Kern and Matti Picus + and Stephan Hoyer and Marten H. van Kerkwijk and Matthew + Brett and Allan Haldane and Jaime Fern{\'{a}}ndez del + R{\'{i}}o and Mark Wiebe and Pearu Peterson and Pierre + G{\'{e}}rard-Marchant and Kevin Sheppard and Tyler Reddy and + Warren Weckesser and Hameer Abbasi and Christoph Gohlke and + Travis E. Oliphant}, + year = {2020}, + month = sep, + journal = {Nature}, + volume = {585}, + number = {7825}, + pages = {357--362}, + doi = {10.1038/s41586-020-2649-2}, + publisher = {Springer Science and Business Media {LLC}}, + url = {https://doi.org/10.1038/s41586-020-2649-2} +} + +@ARTICLE{PANGEO:2022, + AUTHOR={Stern, Charles and Abernathey, Ryan and Hamman, Joseph and Wegener, Rachel and Lepore, Chiara and Harkins, Sean and Merose, Alexander }, + + TITLE={Pangeo Forge: Crowdsourcing Analysis-Ready, Cloud Optimized Data Production}, + + JOURNAL={Frontiers in Climate}, + + VOLUME={3}, + + YEAR={2022}, + + URL={https://www.frontiersin.org/journals/climate/articles/10.3389/fclim.2021.782909}, + + DOI={10.3389/fclim.2021.782909}, + + ISSN={2624-9553}, + + ABSTRACT={

Pangeo Forge is a new community-driven platform that accelerates science by providing high-level recipe frameworks alongside cloud compute infrastructure for extracting data from provider archives, transforming it into analysis-ready, cloud-optimized (ARCO) data stores, and providing a human- and machine-readable catalog for browsing and loading. In abstracting the scientific domain logic of data recipes from cloud infrastructure concerns, Pangeo Forge aims to open a door for a broader community of scientists to participate in ARCO data production. A wholly open-source platform composed of multiple modular components, Pangeo Forge presents a foundation for the practice of reproducible, cloud-native, big-data ocean, weather, and climate science without relying on proprietary or cloud-vendor-specific tooling.

} +} + +@inproceedings{ParallelAnalysis:2010, + author = {Tu, Tiankai and Rendleman, Charles A. and Miller, Patrick J. and Sacerdoti, Federico and Dror, Ron O. and Shaw, David E.}, + title = {Accelerating parallel analysis of scientific simulation data via Zazen}, + year = {2010}, + publisher = {USENIX Association}, + address = {USA}, + abstract = {As a new generation of parallel supercomputers enables researchers to conduct scientific simulations of unprecedented scale and resolution, terabyte-scale simulation output has become increasingly commonplace. Analysis of such massive data sets is typically I/O-bound: many parallel analysis programs spend most of their execution time reading data from disk rather than performing useful computation. To overcome this I/O bottleneck, we have developed a new data access method. Our main idea is to cache a copy of simulation output files on the local disks of an analysis cluster's compute nodes, and to use a novel task-assignment protocol to co-locate data access with computation. We have implemented our methodology in a parallel disk cache system called Zazen. By avoiding the overhead associated with querying metadata servers and by reading data in parallel from local disks, Zazen is able to deliver a sustained read bandwidth of over 20 gigabytes per second on a commodity Linux cluster with 100 nodes, approaching the optimal aggregated I/O bandwidth attainable on these nodes. Compared with conventional NFS, PVFS2, and Hadoop/HDFS, respectively, Zazen is 75, 18, and 6 times faster for accessing large (1-GB) files, and 25, 13, and 85 times faster for accessing small (2-MB) files. We have deployed Zazen in conjunction with Anton--a special-purpose supercomputer that dramatically accelerates molecular dynamics (MD) simulations-- and have been able to accelerate the parallel analysis of terabyte-scale MD trajectories by about an order of magnitude.}, + booktitle = {Proceedings of the 8th USENIX Conference on File and Storage Technologies}, + pages = {10}, + numpages = {1}, + location = {San Jose, California}, + series = {FAST'10} +} + +@article{SharingMD:2019, +author = {Abraham, Mark and Apostolov, Rossen and Barnoud, Jonathan and Bauer, Paul and Blau, Christian and Bonvin, Alexandre M.J.J. and Chavent, Matthieu and Chodera, John and Čondić-Jurkić, Karmen and Delemotte, Lucie and Grubmüller, Helmut and Howard, Rebecca J. and Jordan, E. Joseph and Lindahl, Erik and Ollila, O. H. Samuli and Selent, Jana and Smith, Daniel G. A. and Stansfeld, Phillip J. and Tiemann, Johanna K.S. and Trellet, Mikael and Woods, Christopher and Zhmurov, Artem}, +title = {Sharing Data from Molecular Simulations}, +journal = {Journal of Chemical Information and Modeling}, +volume = {59}, +number = {10}, +pages = {4093-4099}, +year = {2019}, +doi = {10.1021/acs.jcim.9b00665}, + note ={PMID: 31525920}, +URL = { + https://doi.org/10.1021/acs.jcim.9b00665 +}, +eprint = { + https://doi.org/10.1021/acs.jcim.9b00665 +} +} + +@article{SplitApplyCombine:2011, + title={The Split-Apply-Combine Strategy for Data Analysis}, + volume={40}, + url={https://www.jstatsoft.org/index.php/jss/article/view/v040i01}, + doi={10.18637/jss.v040.i01}, + abstract={Many data analysis problems involve the application of a split-apply-combine strategy, where you break up a big problem into manageable pieces, operate on each piece independently and then put all the pieces back together. This insight gives rise to a new R package that allows you to smoothly apply this strategy, without having to worry about the type of structure in which your data is stored. The paper includes two case studies showing how these insights make it easier to work with batting records for veteran baseball players and a large 3d array of spatio-temporal ozone measurements.}, + number={1}, + journal={Journal of Statistical Software}, + author={Wickham, Hadley}, + year={2011}, + pages={1–29} +} + +@article{YiiP:2019, +author = "Shujie Fan and Oliver Beckstein", +title = "{Molecular Dynamics trajectories of membrane protein YiiP}", +year = "2019", +month = "5", +url = "https://figshare.com/articles/dataset/Molecular_Dynamics_trajectories_of_membrane_protein_YiiP/8202149", +doi = "10.6084/m9.figshare.8202149.v1" +} + +@misc{Zarr:2024, + doi = {10.5281/ZENODO.3773449}, + url = {https://zenodo.org/doi/10.5281/zenodo.3773449}, + author = {Alistair Miles, and jakirkham, and M Bussonnier, and Josh Moore, and Dimitri Papadopoulos Orfanos, and Davis Bennett, and David Stansby, and Joe Hamman, and James Bourbeau, and Andrew Fulton, and Gregory Lee, and Ryan Abernathey, and Norman Rzepka, and Zain Patel, and Mads R. B. Kristensen, and Sanket Verma, and Saransh Chopra, and Matthew Rocklin, and AWA BRANDON AWA, and Max Jones, and Martin Durant, and Elliott Sales de Andrade, and Vincent Schut, and raphael dussin, and Shivank Chaudhary, and Chris Barnes, and Juan Nunez-Iglesias, and shikharsg, }, + title = {zarr-developers/zarr-python: v3.0.0-alpha}, + publisher = {Zenodo}, + year = {2024}, + copyright = {Creative Commons Attribution 4.0 International} +} + +@misc{Zstandard:2021, + series = {Request for Comments}, + number = 8878, + howpublished = {RFC 8878}, + publisher = {RFC Editor}, + doi = {10.17487/RFC8878}, + url = {https://www.rfc-editor.org/info/rfc8878}, + author = {Yann Collet and Murray Kucherawy}, + title = {{Zstandard Compression and the 'application/zstd' Media Type}}, + pagetotal = 45, + year = 2021, + month = feb, + abstract = {Zstandard, or "zstd" (pronounced "zee standard"), is a lossless data compression mechanism. This document describes the mechanism and registers a media type, content encoding, and a structured syntax suffix to be used when transporting zstd-compressed content via MIME. Despite use of the word "standard" as part of Zstandard, readers are advised that this document is not an Internet Standards Track specification; it is being published for informational purposes only. This document replaces and obsoletes RFC 8478.}, +} + + +@article{Liu:2010, + author = {Liu, Pu and Agrafiotis, Dimitris K and Theobald, Douglas L}, + journal = {J Comput Chem}, + month = {May}, + number = 7, + pages = {1561-1563}, + title = {Fast determination of the optimal rotational matrix for macromolecular superpositions}, + volume = 31, + year = 2010} diff --git a/joss_paper/paper.md b/joss_paper/paper.md new file mode 100644 index 0000000..e442bbe --- /dev/null +++ b/joss_paper/paper.md @@ -0,0 +1,230 @@ +--- +title: 'Zarrtraj: A Python package for streaming molecular dynamics trajectories from cloud services' +tags: + - streaming + - molecular-dynamics + - file-format + - mdanalysis + - zarr +authors: + - name: Lawson Woods + orcid: 0009-0003-0713-4167 + affiliation: [1, 2] + - name: Hugo MacDermott-Opeskin + orcid: 0000-0002-7393-7457 + affiliation: [3] + - name: Edis Jakupovic + affiliation: [4, 5] + orcid: 0000-0001-8813-6356 + - name: Yuxuan Zhuang + orcid: 0000-0003-4390-8556 + affiliation: [6, 7] + - name: Richard J Gowers + orcid: 0000-0002-3241-1846 + affiliation: [8] + - name: Oliver Beckstein + orcid: 0000-0003-1340-0831 + affiliation: [4, 5] +affiliations: + - name: School of Computing and Augmented Intelligence, Arizona State University, Tempe, Arizona, United States of America + index: 1 + - name: School of Molecular Sciences, Arizona State University, Tempe, Arizona, United States of America + index: 2 + - name: Open Molecular Software Foundation, Davis, CA, United States of America + index: 3 + - name: Center for Biological Physics, Arizona State University, Tempe, AZ, United States of America + index: 4 + - name: Department of Physics, Arizona State University, Tempe, Arizona, United States of America + index: 5 + - name: Department of Computer Science, Stanford University, Stanford, CA 94305, USA. + index: 6 + - name: Departments of Molecular and Cellular Physiology and Structural Biology, Stanford University School of Medicine, Stanford, CA 94305, USA. + index: 7 + - name: Charm Therapeutics, London, United Kingdom + index: 8 +date: 23 October 2024 +bibliography: paper.bib +--- + +# Summary + +Molecular dynamics (MD) simulations provide a microscope into the behavior of +atomic-scale environments otherwise prohibitively difficult to observe. However, +the resulting trajectory data are too often siloed in a single institutions' +HPC environment, rendering it unusable by the broader scientific community. +Additionally, it is increasingly common for trajectory data to be entirely +stored in a cloud storage provider, rather than a traditional on-premise storage site. +*Zarrtraj* enables these trajectories to be read directly from cloud storage providers +like AWS, Google Cloud, and Microsoft Azure into MDAnalysis, a popular Python +package for analyzing trajectory data, providing a method to open up access to +trajectory data to anyone with an internet connection. Enabling cloud streaming +for MD trajectories empowers easier replication of published analysis results, +analyses of large, conglomerate datasets from different sources, and training +machine learning models without downloading and storing trajectory data. + +# Statement of need + +The computing power in HPC environments has increased to the point where +running simulation algorithms is often no longer the constraint in +obtaining scientific insights from molecular dynamics trajectory data. +Instead, the ability to process, analyze and share large volumes of data provide +new constraints on research in this field [@SharingMD:2019]. + +Other groups in the field recognize this same need for adherence to +FAIR principles [@FAIR:2019] including +MDsrv, a tool that can stream MD trajectories into a web browser for visual exploration [@MDsrv:2022], +GCPRmd, a web service that builds on MDsrv to provide a predefined set of analysis results and simple +geometric features for G-protein-coupled receptors [@GPCRmd:2019] [@GPCRome:2020], +MDDB (Molecular Dynamics Data Bank), an EU-scale +repository for bio-simulation data [@MDDB:2024], +and MDverse, a prototype search engine +for publicly-available GROMACS simulation data [@MDverse:2024]. + +While these efforts currently offer solutions for indexing, +searching, and visualizing MD trajectory data, the problem of distributing trajectories +in way that enables *NumPy*-like slicing and parallel reading for use in arbitrary analysis +tasks remains. + +Although exposing download links on the open internet offers a simple solution to this problem, +on-disk representations of molecular dynamics trajectories often range in size +up to TBs in scale [@ParallelAnalysis:2010] [@FoldingAtHome:2020], +so a solution which could prevent this +duplication of storage and unnecessary download step would provide greater utility +for the computational molecular sciences ecosystem, especially if it +provides access to slices or subsampled portions of these large files. + +To address this need, we developed *Zarrtraj* as a prototype for streaming +trajectories into analysis software using an established trajectory +format. *Zarrtraj* extends MDAnalysis [@MDAnalysis:2016], a popular +Python-based library for the analysis of molecular simulation data in a wide +range of formats, to also accept remote file locations for trajectories instead +of local filenames. Instead of being integrated directly into MDAnalysis, +*Zarrtraj* is built as an external MDAKit [@MDAKits:2023] that automatically +registers its capabilities with MDAnalysis on import and thus acts as a plugin. +*Zarrtraj* enables streaming MD trajectories in the popular HDF5-based H5MD format [@H5MD:2014] +from AWS S3, Google Cloud Buckets, and Azure Blob Storage and Data Lakes without ever downloading them. +*Zarrtraj* relies on the *Zarr* [@Zarr:2024] package for +streaming array-like data from a variety of storage mediums and on [Kerchunk](https://github.com/fsspec/kerchunk), +which extends the capability of *Zarr* by allowing it to read HDF5 files. +*Zarrtraj* leverages *Zarr*'s ability to read a slice of a file and to read a +file in parallel and it implements the standard MDAnalysis trajectory reader +API, which taken together make it compatible with analysis algorithms that use +the "split-apply-combine" parallelization strategy [@SplitApplyCombine:2011]. +In addition to the H5MD format, *Zarrtraj* can stream and write trajectories in +the experimental ZarrMD format, which ports the H5MD layout to the *Zarr* +file type. + +This work builds on the existing MDAnalysis `H5MDReader` +[@H5MDReader:2021], and uses *NumPy* [@NumPy:2020] as a common interface in-between MDAnalysis +and the file storage medium. *Zarrtraj* was inspired and made possible by similar efforts in the +geosciences community to align data practices with FAIR principles [@PANGEO:2022]. + +With *Zarrtraj*, we envision research groups making their data publicly available +via a cloud URL so that anyone can reuse their trajectories and reproduce their results. +Large databases, like MDDB and MDverse, can expose a URL associated with each +trajectory in their databases so that users can make a query and immediately use the resulting +trajectories to run an analysis on the hits that match their search. Groups seeking to +collect a large volume of trajectory data to train machine learning models [@MLMDMethods:2023] can make use +of our tool to efficiently and inexpensively obtain the data they need from these published +URLs. + +# Features and Benchmarks + +Once imported, *Zarrtraj* allows passing trajectory URLs just like ordinary files: +```python +import zarrtraj +import MDAnalysis as mda + +u = mda.Universe("topology.pdb", "s3://sample-bucket-name/trajectory.h5md") +``` + +Initial benchmarks show that *Zarrtraj* can iterate serially +through an AWS S3 cloud trajectory (load into memory one frame at a time) +at roughly 1/2 or 1/3 the speed it can iterate through the same trajectory from disk and roughly +1/5 to 1/10 the speed it can iterate through the same trajectory on disk in XTC format (\autoref{fig:benchmark}). +However, it should be noted that this speed is influenced by network bandwidth and that +writing parallelized algorithms can offset this loss of speed as in \autoref{fig:RMSD}. + +![Benchmarks performed on a machine with 2 Intel Xeon 2.00GHz CPUs, 32GB of RAM, and an SSD configured with RAID 0. The trajectory used for benchmarking was the YiiP trajectory from MDAnalysisData [@YiiP:2019], a 9000-frame (90ns), 111,815 particle simulation of a membrane-protein system. The original 3.47GB XTC trajectory was converted into an uncompressed 11.3GB H5MD trajectory and an uncompressed 11.3GB ZarrMD trajectory using the MDAnalysis `H5MDWriter` and *Zarrtraj* `ZarrMD` writers, respectively. XTC trajectory read using the MDAnalysis `XTCReader` for comparison. \label{fig:benchmark}](benchmark.png) + +![RMSD benchmarks performed on the same machine as \autoref{fig:benchmark}. YiiP trajectory aligned to first frame as reference using `MDAnalysis.analysis.align.AlignTraj` and converted to compressed, quantized H5MD (7.8GB) and ZarrMD (4.9GB) trajectories. RMSD performed using development branch of MDAnalysis (2.8.0dev) with "serial" and "dask" backends. See [this notebook](https://github.com/Becksteinlab/zarrtraj/blob/d4ab7710ec63813750d7224fe09bf5843e513570/joss_paper/figure_2.ipynb) for full benchmark codes. \label{fig:RMSD}](RMSD.png) + +*Zarrtraj* is capable of making use of *Zarr*'s powerful compression and quantization when writing ZarrMD trajectories. +The uncompressed MDAnalysisData YiiP trajectory in ZarrMD format is reduced from 11.3GB uncompressed +to just 4.9GB after compression with the Zstandard algorithm [@Zstandard:2021] +and quantization to 3 digits of precision. See [performance considerations](https://zarrtraj.readthedocs.io/en/latest/performance_considerations.html) +for more. + +# Example + +The YiiP membrane protein trajectory [@YiiP:2019] used for benchmarking in this +paper is publicly available for streaming from the Google Cloud Bucket +*gcs://zarrtraj-test-data/yiip.zarrmd*. The topology file in PDB format, which contains +information about the chemical composition of the system, can also be accessed +remotely from the same bucket (*gcs://zarrtraj-test-data/YiiP_system.pdb*) using +[fsspec](https://filesystem-spec.readthedocs.io/en/latest/), although this is +currently an experimental feature and details may change. + +In the following example (see also the [YiiP Example in the zarrtraj +docs](https://zarrtraj.readthedocs.io/en/latest/yiip_example.html)), we access +the topology file and the trajectory from the *gcs://zarrtraj-test-data* cloud +bucket. We initially create an `MDAnalysis.Universe`, the basic object in +MDAnalysis that ties static topology data and dynamic trajectory data together +and manages access to all data. We iterate through a slice of the trajectory, +starting from frame index 100 and skipping forward in steps of 20 frames: + +```python +import zarrtraj +import MDAnalysis as mda +import fsspec + +with fsspec.open("gcs://zarrtraj-test-data/YiiP_system.pdb", "r") as top: + u = mda.Universe(top, "gcs://zarrtraj-test-data/yiip.zarrmd", + topology_format="PDB") + + for timestep in u.trajectory[100::20]: + print(timestep) +``` + +Inside the loop over trajectory frames we print information for the current +frame `timestep` although in principle, any kind of analysis code can run here and +process the coordinates available in `u.atoms.positions`. + +The `Universe` object can be used as if the underlying trajectory file were a +local file. For example, we can use `u` from the preceeding example with one of +the standard analysis tools in MDAnalysis, the calculation of the root mean +square distance (RMSD) after optimal structural superposition [@Liu:2010] in +the `MDAnalysis.analysis.rms.RMSD` class. In the example below we select only the +C$_\alpha$ atoms of the protein with a MDAnalysis selection. We run the +analysis with the `.run()` method while stepping through the trajectory at +increments of 100 frames. We then print the first and last data point from the +results array: + +```python +>>> import MDAnalysis.analysis.rms +>>> R = MDAnalysis.analysis.rms.RMSD(u, select="protein and name CA").run( + step=100, verbose=True) +100%|██████████████████████████████████████████| 91/91 [00:28<00:00, 3.21it/s] +>>> print(f"Initial RMSD (frame={R.results.rmsd[0, 0]:g}): " + f"{R.results.rmsd[0, 2]:.3f} Å") +Initial RMSD (frame=0) : 0.000 Å +>>> print(f"Final RMSD (frame={R.results.rmsd[-1, 0]:g}): " + f"{R.results.rmsd[-1, 2]:.3f} Å") +Final RMSD (frame=9000) : 2.373 Å +``` + +This example demonstrates that the *Zarrtraj* interface enables seamless use of +cloud-hosted trajectories with the standard tools that are either available +with MDAnalysis itself, through MDAKits [@MDAKits:2023] (see the [MDAKit +registry](https://mdakits.mdanalysis.org/mdakits.html) for available packages), +or any script or package that uses MDAnalysis for file I/O. + + +# Acknowledgements + +We thank Dr. Jenna Swarthout Goddard for supporting the GSoC program at MDAnalysis and +Dr. Martin Durant, author of Kerchunk, for helping refine and merge features in his upstream code base +necessary for this project. LW was a participant in the Google Summer of Code 2024 program. +Some work on *Zarrtraj* was supported by the National Science Foundation under grant number 2311372. + +# References diff --git a/pyproject.toml b/pyproject.toml index 3996118..c3e52c4 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,3 +106,21 @@ line_length = 80 COLUMN_LIMIT = 80 INDENT_WIDTH = 4 USE_TABS = false + +classifiers = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + 'Operating System :: POSIX', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Bio-Informatics', + 'Topic :: Scientific/Engineering :: Chemistry', + 'Topic :: Software Development :: Libraries :: Python Modules', +] \ No newline at end of file