Published March 4, 2026 | Version v1
Dataset Restricted

Thermodynamics of bouncing grains & Büttiker-Landauer motor

  • 1. ROR icon Institut de physique du globe de Paris
  • 2. ROR icon University of Belgrade
  • 3. ROR icon Massachusetts Institute of Technology

Description

Experiments

Sand grains bouncing over a vibrated plate diffuse across the plate's surface. Their diffusivity depends on the plate's surface properties, on the frequency and amplitude of its vibration, and on the grains' shape. This phenomenon generates the well-known Chladni figure.

This archive contains the trajectories of bouncing grains recorded during the experiments presented in two articles:

  • A granular Büttiker-Landauer motor, O. Devauchelle, P. Popović, P. Szymczak, A. Abramian & A. Lazarus, PRL (2026), DOI: 10.1103/1ggv-8f83;
  • Thermodynamics of bouncing grains, O. Devauchelle, P. Popović, P. Szymczak, A. Abramian & A. Lazarus (2026), https://doi.org/10.1103/zlsc-w7m4.

File content

Codes

Python codes used to produce and analyse the trajectories.

Processed data

  • Trajectory files *.traj. Text files intended to be read with the BrownTrack library. See code sample below for illustration.
  • Background image background.jpg.
  • Geometry of the setup in the corresponding movie frame domains.json.
  • Parameter files *.json.

Raw data

Data files created during the experiment.

  • Tilt of the vibrating plate angle_*.csv.
  • Metadata of all movies in specific run movie_metadata_*.json.
  • Raw parameter file parameters_*.json.

Names of experimental runs

The runs are referred to with letters in the articles, according to the following table.

Run name

File

A

big_grains_40Hz

B

siloxane_multiple_quick_3

C

siloxane_big_grains_large_swing

D

siloxane_small_low_freq

E

siloxane_multiple_quick

F

siloxane_multiple_quick_2

G

siloxane_small_grains

H

siloxane_small_large_amp

I

diffusivity_hard_30Hz_1_5_mm

J

diffusivity_soft_30Hz_2_5_4_mm

K

diffusivity_hard_30Hz_2_5_4_mm

L

diffusivity_soft_30Hz_6_mm

M

diffusivity_hard_30Hz_6_mm

N

diffusivity_soft_30Hz_1_5_mm

Example

The code sample below loads all the trajectories of a specific experimental movie, and plots some of them.

Methods

####################################
#
# A granular Büttiker-Landauer motor, O. Devauchelle, P. Popović, P. Szymczak, A. Abramian & A. Lazarus (2026)
# Thermodynamics of bouncing grains, O. Devauchelle, P. Popović, P. Szymczak, A. Abramian & A. Lazarus (2026)  
# https://www.doi.org/10.5281/zenodo.18862698
# https://github.com/odevauchelle/BrownTrack
#
####################################

from pylab import *
from glob import glob
import json

sys.path.append('/home/olivier/git/BrownTrack/') 
import BrownTrack as BT

####################
#
# parameters & data
#
###################

file_to_letter = {'big_grains_40Hz': 'A', 'siloxane_multiple_quick_3': 'B', 'siloxane_big_grains_large_swing': 'C', 'siloxane_small_low_freq': 'D', 'siloxane_multiple_quick': 'E', 'siloxane_multiple_quick_2': 'F', 'siloxane_small_grains': 'G', 'siloxane_small_large_amp': 'H', 'diffusivity_hard_30Hz_1_5_mm': 'I', 'diffusivity_soft_30Hz_2_5_4_mm': 'J', 'diffusivity_hard_30Hz_2_5_4_mm': 'K', 'diffusivity_soft_30Hz_6_mm': 'L', 'diffusivity_hard_30Hz_6_mm': 'M', 'diffusivity_soft_30Hz_1_5_mm': 'N'}

experiment = 'siloxane_small_low_freq'
movie_index = 4


data_path = '/home/olivier/no_backup/ganzeville/Bureau/bouncing_grains/BL_ratchet_paper_data/BLR_experiment/'
experiment_folder = data_path + experiment
processed_data_path = experiment_folder + '/processed_data/'

with open( processed_data_path + 'parameters.json' ) as the_file :
	p = json.load( the_file )

with open( processed_data_path + 'domains.json' ) as the_file :
	domains_data = json.load( the_file )

###############################
#
# show domains
#
###############################

fig = figure( figsize = (6,6), layout = 'tight' )
ax_phys = gca()

domains = {}

for name, domain in domains_data['domains'].items() :

	patch_style = dict( facecolor = 'none', linestyle = '--' )

	if name == 'main' :
		patch_style['linestyle'] = '-'

	domains[name] = BT.domain( **domain )

	ax_phys.add_patch( domains[name].get_patch( label = name.capitalize(), **patch_style ) )

radius = domains['main'].boundary['radius']
disk_diameter = 8 # cm
disk_diameter *= 1e-2 #m
meters_per_pixel = disk_diameter/radius

###################
#
# draw scale bar
#
###################

px_per_cm = 1e-2/meters_per_pixel

bar_size = 1 #cm

bar_pos = array( domains['main'].boundary['xy'] ) + .92*array([1,-1])*radius

bar_points = array( [ bar_pos - array([bar_size*px_per_cm, 0]), bar_pos + array([bar_size*px_per_cm, 0]) ] )

bar_color = 'k'
ax_phys.plot( *bar_points.T, '-|', color = bar_color )
ax_phys.text( *( mean(bar_points, axis = 0) + array([0, 0]) ), str(bar_size) + r'$\,$cm' + '\n', color = bar_color, ha = 'center' )

####################
#
# plot trajectories
#
####################

def is_acceptable( traj, **criteria ) :

    output = True

    try :
        output = output and len(traj.x) > criteria['min_length']
    except :
        pass

    try :
        output = output and norm( array( [ ( traj.x[-1] - traj.x[0] ), ( traj.y[-1] - traj.y[0] ) ] ) ) > criteria['min_displacement']*p['grain_size']
    except :
        pass

    return output

data_file = glob( processed_data_path + 'bunch_*.traj' )[movie_index]

with open( data_file ) as the_file :
	p_traj = json.loads( the_file.readline() )
	traj_movie_name = p_traj['movie_file'].replace( 'diffusion_', '' )
	trajectories = BT.load_trajectories( the_file )

plotted_length = 0
max_plotted_length = 3000

for traj in trajectories[::-1] :

	if is_acceptable( traj,  min_length = 5, min_displacement = 10 ) :

		if plotted_length < max_plotted_length : # plot some trajectories
			step = 1
			i_max = max_plotted_length - plotted_length
			ax_phys.plot( traj.x[:i_max], traj.y[:i_max], alpha = .5 )
			plotted_length += len(traj.x)

		index = arange( len( traj.x ) )

####################
#
# adjust plot
#
####################

ax_phys.axis('scaled')
ax_phys.set_xticks([])
ax_phys.set_yticks([])
ax_phys.axis('off')
ax_phys.set_title( 'Run ' + file_to_letter[experiment] )

fig.savefig('trajectories.pdf')

show()

Files

Restricted

The record is publicly accessible, but files are restricted. Log in to check if you have access.

Additional details

Software

Repository URL
https://github.com/odevauchelle/BrownTrack
Programming language
Python