lammpskit.ecellmodel.filament_layer_analysis module
Specialized analysis functions for filament formation and connectivity evolution in electrochemical devices. This module provides comprehensive tools for analyzing conductive filament formation, percolation pathways, and device switching behavior in molecular dynamics simulations.
Key Functions
The module provides specialized functions for:
Displacement data reading from LAMMPS trajectory files
Atomic distribution plotting with statistical analysis
Charge distribution plotting for electrical characterization
Displacement comparison analysis across multiple datasets
Cluster analysis for connectivity determination
Filament evolution tracking over time
Time series plotting for temporal analysis
Filament Analysis Workflow
Complete workflow for filament formation analysis:
from lammpskit.ecellmodel.filament_layer_analysis import (
analyze_filament_connectivity,
track_filament_evolution,
classify_device_state,
plot_connectivity_evolution
)
# 1. Analyze connectivity across all timesteps
connectivity_data = analyze_filament_connectivity(
trajectory_data,
distance_threshold=2.5, # Å, connectivity cutoff
electrode_regions={
'bottom': (0, 5), # Bottom electrode z-range
'top': (45, 50) # Top electrode z-range
},
min_cluster_size=3, # Minimum atoms per cluster
periodic_boundaries=True # Account for PBC
)
# 2. Track temporal evolution
evolution_metrics = track_filament_evolution(
connectivity_data,
analysis_window=100, # Timesteps for local analysis
trend_detection=True, # Detect formation/dissolution trends
switching_detection=True # Identify switching events
)
# 3. Classify device states
device_states = classify_device_state(
connectivity_data,
hrs_threshold=0.1, # Low connectivity = HRS
lrs_threshold=0.8, # High connectivity = LRS
intermediate_range=(0.1, 0.8) # Intermediate resistance states
)
# 4. Visualize results
plot_connectivity_evolution(
connectivity_data,
device_states=device_states,
save_path='filament_analysis.pdf',
include_statistics=True
)
Connectivity Analysis Examples
Basic Connectivity Analysis:
# Analyze atomic connectivity with distance-based criteria
connectivity = analyze_filament_connectivity(
trajectory_data,
distance_threshold=2.5, # Connectivity distance (Å)
analysis_type='full', # Full connectivity analysis
include_clusters=True, # Include cluster analysis
include_pathways=True # Include percolation pathways
)
# Results include:
# - connectivity_matrix: atom-atom connectivity
# - cluster_labels: cluster assignments for each atom
# - pathway_analysis: percolation pathway identification
# - connectivity_ratio: fraction of connected atoms
Advanced Connectivity with Electrode Regions:
# Define electrode regions for device analysis
electrode_config = {
'bottom': {
'z_range': (0, 5), # Bottom electrode region
'material': 'Ag', # Electrode material
'bias': -1.0 # Applied bias (V)
},
'top': {
'z_range': (45, 50), # Top electrode region
'material': 'Ag', # Electrode material
'bias': 1.0 # Applied bias (V)
}
}
# Analyze electrode-to-electrode connectivity
device_connectivity = find_electrode_connections(
trajectory_data,
electrode_config,
pathway_algorithm='shortest_path', # Pathfinding algorithm
resistance_model='ohmic', # Resistance calculation model
include_intermediate=True # Include intermediate states
)
Cluster Analysis with Multiple Algorithms:
# Compare different clustering approaches
cluster_comparison = analyze_cluster_distribution(
connectivity_data,
algorithms=['dbscan', 'connected_components', 'hierarchical'],
dbscan_params={'eps': 2.5, 'min_samples': 3},
hierarchical_params={'distance_threshold': 3.0},
comparison_metrics=['silhouette', 'davies_bouldin', 'calinski_harabasz']
)
Temporal Evolution Analysis
Filament Formation Tracking:
# Track filament growth and dissolution dynamics
evolution = track_filament_evolution(
connectivity_time_series,
detection_params={
'formation_threshold': 0.1, # Connectivity increase threshold
'dissolution_threshold': 0.9, # Connectivity decrease threshold
'minimum_duration': 50, # Minimum event duration (timesteps)
'noise_filter': True # Apply noise filtering
},
analysis_metrics=[
'growth_rate', # Filament formation speed
'dissolution_rate', # Filament dissolution speed
'stability_index', # State stability measure
'switching_frequency' # Number of switches per unit time
]
)
Gap Evolution Analysis:
# Analyze gap formation and healing in partially connected devices
gap_evolution = track_gap_evolution(
trajectory_data,
gap_definition={
'max_distance': 5.0, # Maximum gap distance (Å)
'min_gap_size': 1.0, # Minimum detectable gap (Å)
'electrode_separation': 50.0 # Device thickness (Å)
},
tracking_params={
'temporal_resolution': 10, # Analysis every 10 timesteps
'spatial_resolution': 0.5, # Spatial binning (Å)
'persistence_threshold': 20 # Minimum gap lifetime (timesteps)
}
)
Device State Classification
Multi-State Classification:
# Classify device into multiple resistance states
state_classification = classify_device_state(
connectivity_data,
classification_scheme={
'HRS': (0.0, 0.1), # High resistance state
'IRS1': (0.1, 0.4), # Intermediate resistance state 1
'IRS2': (0.4, 0.7), # Intermediate resistance state 2
'LRS': (0.7, 1.0) # Low resistance state
},
state_stability={
'minimum_duration': 100, # Minimum state duration (timesteps)
'transition_hysteresis': 0.05, # Hysteresis for state transitions
'noise_tolerance': 0.02 # Noise tolerance for classification
}
)
Switching Event Detection:
# Detect and characterize switching events
switching_analysis = calculate_switching_metrics(
device_states,
switching_criteria={
'minimum_state_change': 2, # Minimum state difference for switching
'transition_window': 50, # Window for transition detection (timesteps)
'confirmation_window': 20 # Confirmation of stable new state
},
performance_metrics=[
'switching_speed', # Time for state transition
'switching_voltage', # Voltage at switching
'energy_consumption', # Energy per switching event
'reliability_index' # Switching success rate
]
)
Gap Analysis and Critical Path Finding
Comprehensive Gap Analysis:
# Analyze gap distribution and critical bottlenecks
gap_analysis = analyze_gap_distribution(
trajectory_data,
gap_parameters={
'search_radius': 5.0, # Search radius for gaps (Å)
'gap_threshold': 2.0, # Minimum gap size (Å)
'electrode_distance': 50.0 # Total electrode separation (Å)
},
analysis_options={
'include_histograms': True, # Generate gap size histograms
'include_spatial_maps': True, # Create spatial gap maps
'include_critical_paths': True, # Find shortest connection paths
'temporal_tracking': True # Track gap evolution over time
}
)
Critical Path Analysis:
# Find critical gaps that prevent electrode connection
critical_gaps = find_critical_gaps(
connectivity_data,
pathfinding_params={
'algorithm': 'dijkstra', # Pathfinding algorithm
'weight_function': 'distance', # Edge weight calculation
'max_search_depth': 100, # Maximum search iterations
'path_optimization': True # Optimize found paths
},
gap_criteria={
'minimum_impact': 0.1, # Minimum connectivity impact
'spatial_clustering': True, # Group nearby gaps
'temporal_persistence': 50 # Minimum gap lifetime (timesteps)
}
)
Visualization and Reporting
Connectivity Evolution Plots:
# Create comprehensive connectivity evolution plots
plot_connectivity_evolution(
connectivity_data,
plot_options={
'include_states': True, # Show device state regions
'include_statistics': True, # Add statistical summaries
'include_events': True, # Mark switching events
'color_scheme': 'professional' # Publication-ready colors
},
time_series_config={
'temporal_smoothing': 10, # Smoothing window (timesteps)
'confidence_intervals': True, # Show confidence bands
'trend_analysis': True # Include trend lines
}
)
Filament Structure Visualization:
# Visualize filament structure and connectivity
plot_filament_structure(
connectivity_data,
visualization_params={
'view_angle': 'side', # Viewing perspective
'highlight_pathways': True, # Highlight percolation pathways
'color_by_cluster': True, # Color atoms by cluster
'show_electrodes': True # Show electrode regions
},
export_options={
'format': 'pdf', # Output format
'resolution': 300, # DPI for raster elements
'vector_graphics': True # Use vector graphics when possible
}
)
Performance Optimization
Memory-Efficient Analysis:
# Analyze large trajectories with memory management
for timestep_batch in process_trajectory_batches(
trajectory_file,
batch_size=1000, # Process 1000 timesteps at once
overlap=100, # Overlap between batches
memory_limit='8GB' # Maximum memory usage
):
batch_connectivity = analyze_filament_connectivity(
timestep_batch,
distance_threshold=2.5,
parallel_processing=True,
n_cores=4
)
Parallel Processing:
# Parallelize analysis across multiple CPU cores
parallel_results = analyze_connectivity_parallel(
trajectory_data,
n_cores=8, # Number of CPU cores
chunk_size=500, # Timesteps per chunk
load_balancing=True, # Enable dynamic load balancing
shared_memory=True # Use shared memory for efficiency
)
Research Applications
- Mechanism Studies:
Filament formation kinetics analysis
Ion migration pathway identification
Electric field effect characterization
Temperature dependence investigation
- Device Optimization:
Electrode material selection
Device geometry optimization
Operating condition determination
Reliability enhancement strategies
- Material Design:
Electrolyte property correlation
Interface engineering analysis
Defect impact assessment
Dopant concentration optimization
Integration Examples
With Data Processing Module:
from lammpskit.ecellmodel.data_processing import load_trajectory_data
# Load and preprocess trajectory data
trajectory = load_trajectory_data('simulation.lammpstrj')
processed_data = preprocess_for_filament_analysis(trajectory)
# Perform filament analysis
connectivity = analyze_filament_connectivity(processed_data)
With Plotting System:
from lammpskit.plotting import create_dual_axis_plot
# Create dual-axis plot for connectivity vs. temperature
fig, ax1, ax2 = create_dual_axis_plot(
x_data=time,
primary_y_data=connectivity_ratio,
secondary_y_data=temperature,
title='Filament Connectivity vs Temperature',
primary_ylabel='Connectivity Ratio',
secondary_ylabel='Temperature (K)'
)
Module Documentation
- lammpskit.ecellmodel.filament_layer_analysis.read_displacement_data(filepath, loop_start, loop_end, repeat_count=0)[source]
Read spatially-binned displacement data from LAMMPS thermodynamic output files.
Parses binwise averaged displacement and mobility analysis data generated by LAMMPS simulations, extracting loop-specific datasets for temporal analysis of ion migration, atomic redistribution, and electrochemical switching dynamics in HfTaO memory devices. Handles multi-loop data structures with automatic chunk detection and validation.
- Parameters:
filepath (str) – Path to LAMMPS thermodynamic output file containing binwise averaged data. Expected format: Header lines (3) + Nchunks line + loop data blocks. Common files: “Hf_mobility.dat”, “O_mobility.dat”, “temperature.dat”
loop_start (int) – Starting loop index (inclusive) for data extraction. Corresponds to simulation timestep ranges or voltage cycle iterations. Minimum value: 0.
loop_end (int) – Ending loop index (inclusive) for data extraction. Must be ≥ loop_start. Determines the temporal range of analysis.
repeat_count (int, optional) – Number of times the first timestep is repeated in the data file structure. Used for correcting file parsing when initial conditions are duplicated. Default: 0 (no repetition correction applied).
- Returns:
thermo – List of displacement data arrays, one per requested loop. Each array has shape (Nchunks, N_columns) where:
Nchunks: Number of spatial bins along analysis direction (typically z-axis)
N_columns: Data columns per bin (typically 6-8 columns)
Standard Column Structure: - Column 0: Chunk/bin ID number - Column 1: Spatial position (Angstroms) - Column 2: Atom count per bin - Column 3: Total displacement magnitude (Angstroms) - Column 4: Z-direction displacement (Angstroms) - Column 5: Lateral displacement (Angstroms) - Column 6: Outward displacement vector (Angstroms) - Column 7: Temperature or density (context-dependent)
- Return type:
list of np.ndarray
- Raises:
FileNotFoundError – If the specified filepath does not exist or is not accessible.
ValueError – If loop_start > loop_end or if file contains invalid data structure.
EOFError – If expected loop data is missing, truncated, or has insufficient rows.
TypeError – If Nchunks header line cannot be parsed as integer or is malformed.
Notes
File Format Requirements: - Header Structure: 3 comment lines (starting with #) followed by Nchunks line - Nchunks Line: Format “# <unused> <Nchunks>” where Nchunks = spatial bins - Loop Blocks: Each loop contains Nchunks data rows + 4 separator lines - Data Validation: Automatic verification of chunk count consistency
Performance Characteristics: - Memory Usage: O(N_loops × Nchunks × N_columns) for output storage - I/O Efficiency: Streaming read with selective row extraction - Error Resilience: Comprehensive validation with descriptive error messages - Typical Speed: ~1-10 MB/s depending on file size and chunk count
Electrochemical Analysis Context: Essential for processing mobility analysis outputs where spatial discretization enables tracking of: - Ion Migration: Species-specific displacement patterns along electric field - Thermal Effects: Temperature-dependent mobility and diffusion coefficients - Interface Dynamics: Electrode-oxide boundary phenomena and charge injection - Temporal Evolution: Loop-by-loop analysis of switching cycle progression
Integration with LAMMPSKit Workflow: - Input Generation: LAMMPS compute ave/chunk commands generate compatible files - Species Processing: Combine with extract_element_label_from_filename() for automation - Visualization: Output arrays integrate with plot_displacement_timeseries() - Analysis Pipelines: Essential component of plot_displacement_comparison() workflow
Examples
Basic displacement data loading:
>>> from lammpskit.ecellmodel.filament_layer_analysis import read_displacement_data >>> # Load mobility data for 100 simulation loops >>> displacement_data = read_displacement_data("Hf_mobility.dat", ... loop_start=0, loop_end=99) >>> print(f"Loaded {len(displacement_data)} loops") >>> print(f"Each loop contains {displacement_data[0].shape[0]} spatial bins") >>> print(f"Data columns per bin: {displacement_data[0].shape[1]}")
Temporal analysis workflow:
>>> # Analyze displacement evolution across switching cycles >>> early_loops = read_displacement_data("O_mobility.dat", 0, 49) # First 50 loops >>> late_loops = read_displacement_data("O_mobility.dat", 950, 999) # Last 50 loops >>> >>> # Compare initial vs final displacement profiles >>> early_avg = np.mean([loop[:, 4] for loop in early_loops], axis=0) # Z-displacement >>> late_avg = np.mean([loop[:, 4] for loop in late_loops], axis=0) >>> displacement_change = late_avg - early_avg >>> print(f"Max displacement change: {displacement_change.max():.3f} Å")
Error handling and validation:
>>> # Robust file processing with error handling >>> try: ... data = read_displacement_data("temperature.dat", 10, 15) ... print(f"Successfully loaded temperature data: {len(data)} loops") ... except FileNotFoundError: ... print("Temperature file not found - skipping thermal analysis") ... except EOFError as e: ... print(f"Incomplete data file: {e}") ... except TypeError as e: ... print(f"File format error: {e}")
Batch processing integration:
>>> # Process multiple species files in automated workflow >>> mobility_files = ["Hf_mobility.dat", "Ta_mobility.dat", "O_mobility.dat"] >>> species_labels = ["Hf", "Ta", "O"] >>> loop_range = (0, 100) >>> >>> all_displacement_data = {} >>> for filename, species in zip(mobility_files, species_labels): ... try: ... data = read_displacement_data(filename, *loop_range) ... all_displacement_data[species] = data ... print(f"{species}: {len(data)} loops, {data[0].shape[0]} bins") ... except Exception as e: ... print(f"Failed to process {species}: {e}")
Data structure analysis:
>>> # Examine file structure and spatial binning >>> data = read_displacement_data("Hf_mobility.dat", 0, 0) # Single loop >>> loop_data = data[0] >>> >>> spatial_positions = loop_data[:, 1] # Bin center positions >>> bin_spacing = spatial_positions[1] - spatial_positions[0] >>> analysis_range = spatial_positions[-1] - spatial_positions[0] >>> >>> print(f"Spatial resolution: {bin_spacing:.2f} Å per bin") >>> print(f"Analysis range: {analysis_range:.1f} Å") >>> print(f"Electrode separation: ~{analysis_range:.0f} Å")
Integration with time series plotting:
>>> # Prepare data for displacement time series visualization >>> loop_data = read_displacement_data("O_mobility.dat", 0, 50) >>> z_displacements = np.array([loop[:, 4] for loop in loop_data]) # Shape: (51, Nbins) >>> >>> # Extract time evolution for specific spatial bin (e.g., center of device) >>> center_bin = z_displacements.shape[1] // 2 >>> center_evolution = z_displacements[:, center_bin] >>> print(f"Center bin displacement range: {center_evolution.min():.3f} to {center_evolution.max():.3f} Å")
- lammpskit.ecellmodel.filament_layer_analysis.plot_atomic_distribution(file_list, labels, skip_rows, z_bins, analysis_name, output_dir=os.getcwd(), columns_to_read=None, **kwargs)[source]
Generate comprehensive atomic distribution analysis for HfTaO electrochemical devices.
Processes multiple trajectory files to compute and visualize spatial distributions of atomic species along the electrode-to-electrode axis. Provides both individual species analysis and stoichiometric composition profiling essential for understanding filament formation, material redistribution, and interface phenomena in resistive memory devices.
- Parameters:
file_list (list of str) – List of file paths to LAMMPS trajectory files (.lammpstrj, .dump). Files should contain atomic coordinates with consistent format and time ordering. Example: [“initial_state.lammpstrj”, “final_state.lammpstrj”]
labels (list of str) – Descriptive labels for each trajectory file, used in plot legends and filenames. Must have same length as file_list. Example: [“Initial”, “SET”, “RESET”]
skip_rows (int) – Number of header rows to skip before atomic coordinate data in trajectory files. Typical LAMMPS values: 9 (standard dump format), varies with custom output.
z_bins (int) – Number of spatial bins for z-direction discretization. Recommended: 15-100 bins for optimal balance between resolution and statistical significance.
analysis_name (str) – Base name used for output file generation and plot titles. Should be descriptive of the analysis context. Example: “HfTaO_switching_analysis”
output_dir (str, optional) – Directory path for saving generated plots. Default: current working directory. Creates directory if it doesn’t exist.
columns_to_read (tuple, optional) – Column indices to read from trajectory files. If None, uses DEFAULT_COLUMNS_TO_READ from config module. Standard format: (id, type, charge, x, y, z).
**kwargs –
Additional keyword arguments passed to plot_multiple_cases() for plot customization:
yaxis: Y-axis starting position for plot alignment
linewidth: Line thickness for distribution curves
alpha: Transparency level for overlapping distributions
colors: Custom color scheme for different cases
markers: Marker styles for data points
- Returns:
distribution_figures – Dictionary of matplotlib Figure objects for different analysis types:
Stoichiometric Analysis: - ‘stoichiometry’: Final-state composition (Hf_a-Ta_c-O_b stoichiometry) - ‘initial_stoichiometry’: Initial-state composition for comparison
Species-Specific Distributions: - ‘Hf’: Hafnium atom spatial distribution (conductive species) - ‘Ta’: Tantalum atom spatial distribution (matrix material) - ‘O’: Oxygen atom spatial distribution (vacancy formation species) - ‘metal’: Combined metallic species (Hf + Ta) distribution
All figures are saved to output_dir with descriptive filenames for publication use.
- Return type:
Notes
Implementation Details: The analysis implements the atom type system: - Type 2: Hf atoms - Odd types: O atoms - Even types (≠2): Ta atoms - Proportional factor 3.5: Normalization for analysis
Performance Characteristics: - Memory Usage: O(N_files × N_atoms × N_columns) for coordinate storage - Processing Speed: ~1-10s per file depending on atom count and bin resolution - Output Size: ~1-5 MB per figure depending on data complexity - Scalability: Efficient for batch processing of multiple trajectories
Integration with LAMMPSKit Ecosystem: - Data Processing: Uses calculate_atomic_distributions() for species separation - Coordinate Reading: Integrates with read_coordinates() for trajectory parsing - Visualization: Uses plot_multiple_cases() for publication-quality plotting - Validation: Employs config module functions for parameter verification
Examples
Basic device analysis workflow:
>>> from lammpskit.ecellmodel.filament_layer_analysis import plot_atomic_distribution >>> # Compare initial vs SET state distributions >>> trajectory_files = ["initial.lammpstrj", "set_state.lammpstrj"] >>> case_labels = ["Initial", "SET"] >>> figures = plot_atomic_distribution(trajectory_files, case_labels, ... skip_rows=9, z_bins=50, ... analysis_name="switching_analysis") >>> print(f"Generated {len(figures)} distribution plots")
Advanced stoichiometric analysis:
>>> # Multi-state comparison with custom parameters >>> files = ["pristine.lammpstrj", "forming.lammpstrj", "set.lammpstrj", "reset.lammpstrj"] >>> labels = ["Pristine", "Forming", "SET", "RESET"] >>> >>> # High-resolution analysis with custom styling >>> figures = plot_atomic_distribution(files, labels, skip_rows=9, z_bins=100, ... analysis_name="full_cycle_analysis", ... output_dir="./analysis_output", ... linewidth=2.0, alpha=0.8) >>> >>> # Examine stoichiometric evolution >>> stoich_fig = figures['stoichiometry'] >>> initial_stoich_fig = figures['initial_stoichiometry'] >>> print("Stoichiometric analysis completed")
Species-specific filament analysis:
>>> # Focus on conductive species evolution >>> trajectory_files = ["before_switching.lammpstrj", "after_switching.lammpstrj"] >>> labels = ["Before", "After"] >>> >>> figures = plot_atomic_distribution(trajectory_files, labels, skip_rows=9, ... z_bins=75, analysis_name="filament_formation") >>> >>> # Analyze individual species >>> hf_distribution = figures['Hf'] # Conductive filament species >>> o_distribution = figures['O'] # Vacancy formation species >>> metal_distribution = figures['metal'] # Combined metallic species >>> >>> print("Filament formation analysis completed")
Interface and electrode analysis:
>>> # High-resolution interface study >>> interface_files = ["electrode_interface.lammpstrj"] >>> interface_labels = ["Interface"] >>> >>> # Fine-grained binning for interface resolution >>> figures = plot_atomic_distribution(interface_files, interface_labels, ... skip_rows=9, z_bins=150, ... analysis_name="interface_analysis", ... yaxis=0) # Start y-axis at zero >>> >>> # Examine composition near electrodes >>> species_distributions = [figures['Hf'], figures['Ta'], figures['O']] >>> print("Interface composition analysis completed")
Batch processing with error handling:
>>> import os >>> # Process multiple device conditions >>> conditions = ["low_voltage", "medium_voltage", "high_voltage"] >>> all_figures = {} >>> >>> for condition in conditions: ... try: ... files = [f"{condition}_initial.lammpstrj", f"{condition}_final.lammpstrj"] ... labels = ["Initial", "Final"] ... ... if all(os.path.exists(f) for f in files): ... figures = plot_atomic_distribution(files, labels, skip_rows=9, ... z_bins=50, ... analysis_name=f"{condition}_analysis") ... all_figures[condition] = figures ... print(f"Completed analysis for {condition}") ... else: ... print(f"Missing files for {condition} - skipping") ... except Exception as e: ... print(f"Error processing {condition}: {e}") >>> >>> print(f"Successfully analyzed {len(all_figures)} conditions")
Custom visualization with publication styling:
>>> # Publication-ready plots with custom styling >>> trajectory_files = ["experimental_data.lammpstrj"] >>> labels = ["Experimental"] >>> >>> # Custom styling parameters >>> custom_kwargs = { ... 'linewidth': 3.0, ... 'alpha': 0.9, ... 'colors': ['blue', 'red', 'green'], ... 'yaxis': 0 ... } >>> >>> figures = plot_atomic_distribution(trajectory_files, labels, skip_rows=9, ... z_bins=60, analysis_name="publication_analysis", ... output_dir="./publication_figures", ... **custom_kwargs) >>> >>> print("Publication-ready figures generated")
Integration with other LAMMPSKit functions:
>>> # Combine with charge analysis for comprehensive characterization >>> from lammpskit.ecellmodel.filament_layer_analysis import plot_atomic_charge_distribution >>> >>> files = ["switching_cycle.lammpstrj"] >>> labels = ["Switching"] >>> >>> # Atomic distribution analysis >>> dist_figures = plot_atomic_distribution(files, labels, skip_rows=9, z_bins=50, ... analysis_name="comprehensive_analysis") >>> >>> # Charge distribution analysis >>> charge_figures = plot_atomic_charge_distribution(files, labels, skip_rows=9, ... z_bins=50, ... analysis_name="comprehensive_analysis") >>> >>> total_figures = len(dist_figures) + len(charge_figures) >>> print(f"Comprehensive analysis generated {total_figures} figures")
- lammpskit.ecellmodel.filament_layer_analysis.plot_atomic_charge_distribution(file_list, labels, skip_rows, z_bins, analysis_name, output_dir=os.getcwd(), columns_to_read=None, **kwargs)[source]
Generate electrostatic charge distribution analysis for HfTaO electrochemical devices.
Computes and visualizes spatial charge profiles across the electrode-to-electrode axis to analyze electrostatic field formation, charge redistribution during switching cycles, and ionic polarization effects in resistive memory devices. Provides both total charge density and species-specific mean charge characterization essential for understanding resistance switching mechanisms and device reliability.
- Parameters:
file_list (list of str) – List of file paths to LAMMPS trajectory files containing atomic coordinates and charges. Files must include charge information (column 2) for electrostatic analysis. Example: [“initial_state.lammpstrj”, “charged_state.lammpstrj”]
labels (list of str) – Descriptive labels for each trajectory file, used in plot legends and output filenames. Must match file_list length. Example: [“Initial”, “Polarized”, “Relaxed”]
skip_rows (int) – Number of header rows to skip before atomic data in trajectory files. Standard LAMMPS dump format typically uses 9 header rows.
z_bins (int) – Number of spatial bins for z-direction charge profile discretization. Recommended: 15-100 bins for optimal field resolution vs. statistical significance.
analysis_name (str) – Base identifier for output files and plot titles. Should describe analysis context. Example: “voltage_cycling_analysis”, “field_redistribution_study”
output_dir (str, optional) – Directory path for saving generated charge distribution plots. Default: current working directory. Creates directory if non-existent.
columns_to_read (tuple, optional) – Column indices for trajectory file parsing. If None, uses DEFAULT_COLUMNS_TO_READ. Standard format: (id, type, charge, x, y, z) with charge in column 2.
**kwargs –
Additional visualization parameters passed to plot_multiple_cases():
ylimithi/xlimithi: Upper axis limits for charge magnitude scaling
ylimitlo/xlimitlo: Lower axis limits for field detail resolution
yaxis: Y-axis origin position for bipolar charge visualization
markerindex: Marker style index for multi-case differentiation
- Returns:
charge_figures – Dictionary of matplotlib Figure objects for comprehensive charge analysis:
Total Charge Analysis: - ‘net_charge’: All-frame total charge density profiles - ‘initial_net_charge’: First-frame charge distribution baseline - ‘final_net_charge’: Last-frame charge distribution for comparison
Species-Specific Mean Charge: - ‘metal_charge’: Mean charge per metal atom (Hf + Ta combined) - ‘initial_metal_charge’: Initial metal charge state reference - ‘oxygen_charge’: Mean charge per oxygen atom (ion migration indicator) - ‘initial_oxygen_charge’: Initial oxygen charge state reference
All figures include publication-ready formatting with axis labels, legends, and consistent styling for comparative analysis across experimental conditions.
- Return type:
Notes
Electrostatic Field Physics: - Total Charge: Reveals space charge regions and internal field gradients - Metal Charges: Indicate oxidation state changes and electron transfer - Oxygen Charges: Track ionic polarization and vacancy formation regions - Temporal Evolution: Initial vs. final states show switching-induced changes
Mathematical Foundation:
Raw charge distribution calculation:
Q(z) = Σ q_i × δ(z_i - z_bin)
Mean charge per species:
<q>_species(z) = Q_species(z) / N_species(z)
Where safe division prevents numerical errors when N_species(z) = 0.
Performance Characteristics: - Memory Complexity: O(N_files × N_atoms × N_bins) for charge histogram storage - Processing Speed: ~2-15s per file depending on atom count and bin resolution - Output Quality: Publication-ready SVG figures with customizable styling - Numerical Stability: Robust handling of zero-atom bins and extreme charge values
Integration with LAMMPSKit Analysis Pipeline: - Charge Calculation: Uses calculate_charge_distributions() for species separation - Coordinate Processing: Integrates with read_coordinates() for trajectory parsing - Atomic Context: Requires calculate_atomic_distributions() for normalization - Visualization: Leverages plot_multiple_cases() for consistent scientific plotting
Examples
Basic charge redistribution analysis:
>>> from lammpskit.ecellmodel.filament_layer_analysis import plot_atomic_charge_distribution >>> # Analyze charge evolution during switching >>> trajectory_files = ["before_switch.lammpstrj", "after_switch.lammpstrj"] >>> state_labels = ["Before", "After"] >>> >>> charge_figures = plot_atomic_charge_distribution(trajectory_files, state_labels, ... skip_rows=9, z_bins=50, ... analysis_name="switching_charges") >>> print(f"Generated {len(charge_figures)} charge analysis plots")
Voltage-dependent charge analysis:
>>> # Multi-voltage charge redistribution study >>> voltage_files = ["0V.lammpstrj", "1V.lammpstrj", "2V.lammpstrj", "3V.lammpstrj"] >>> voltage_labels = ["0V", "1V", "2V", "3V"] >>> >>> # High-resolution field analysis >>> charge_figures = plot_atomic_charge_distribution(voltage_files, voltage_labels, ... skip_rows=9, z_bins=100, ... analysis_name="voltage_dependence", ... output_dir="./charge_analysis") >>> >>> # Examine field gradient evolution >>> net_charge_fig = charge_figures['net_charge'] >>> metal_charge_fig = charge_figures['metal_charge'] >>> print("Voltage-dependent field analysis completed")
Temporal charge evolution tracking:
>>> # Long-term charge stability analysis >>> time_series_files = [f"time_{i}ps.lammpstrj" for i in [0, 100, 500, 1000, 5000]] >>> time_labels = ["0ps", "100ps", "500ps", "1000ps", "5000ps"] >>> >>> charge_figures = plot_atomic_charge_distribution(time_series_files, time_labels, ... skip_rows=9, z_bins=75, ... analysis_name="temporal_stability") >>> >>> # Compare initial vs. final charge states >>> initial_charge = charge_figures['initial_net_charge'] >>> final_charge = charge_figures['final_net_charge'] >>> print("Temporal charge stability analysis completed")
Species-specific charge state analysis:
>>> # Detailed oxidation state characterization >>> trajectory_files = ["pristine.lammpstrj", "oxidized.lammpstrj"] >>> condition_labels = ["Pristine", "Oxidized"] >>> >>> # Custom axis limits for charge detail resolution >>> custom_limits = { ... 'ylimithi': 100, # Extended charge range ... 'ylimitlo': -100, # Bipolar charge visualization ... 'xlimitlo': -15, # Full device width ... 'xlimithi': 25, ... 'yaxis': 0 # Center y-axis at zero charge ... } >>> >>> charge_figures = plot_atomic_charge_distribution(trajectory_files, condition_labels, ... skip_rows=9, z_bins=60, ... analysis_name="oxidation_study", ... **custom_limits) >>> >>> # Analyze species-specific charge states >>> metal_charges = charge_figures['metal_charge'] # Metal oxidation states >>> oxygen_charges = charge_figures['oxygen_charge'] # Oxygen polarization >>> initial_metal = charge_figures['initial_metal_charge'] # Reference state >>> print("Oxidation state analysis completed")
Interface charge characterization:
>>> # Electrode-oxide interface charge analysis >>> interface_files = ["bottom_electrode.lammpstrj", "top_electrode.lammpstrj"] >>> interface_labels = ["Bottom", "Top"] >>> >>> # Focus on interface region with appropriate limits >>> interface_params = { ... 'xlimitlo': -5, # Near bottom electrode ... 'xlimithi': 35, # Near top electrode ... 'ylimithi': 50, # Moderate charge range ... 'yaxis': 0, # Bipolar visualization ... 'markerindex': 1 # Distinctive markers ... } >>> >>> charge_figures = plot_atomic_charge_distribution(interface_files, interface_labels, ... skip_rows=9, z_bins=80, ... analysis_name="interface_charges", ... **interface_params) >>> print("Interface charge analysis completed")
Comparative analysis with atomic distributions:
>>> # Combined atomic and charge distribution analysis >>> from lammpskit.ecellmodel.filament_layer_analysis import plot_atomic_distribution >>> >>> trajectory_files = ["device_state.lammpstrj"] >>> analysis_labels = ["Device"] >>> >>> # Atomic distribution analysis >>> atomic_figures = plot_atomic_distribution(trajectory_files, analysis_labels, ... skip_rows=9, z_bins=50, ... analysis_name="comprehensive_study") >>> >>> # Charge distribution analysis >>> charge_figures = plot_atomic_charge_distribution(trajectory_files, analysis_labels, ... skip_rows=9, z_bins=50, ... analysis_name="comprehensive_study") >>> >>> # Correlate atomic and charge profiles >>> total_analyses = len(atomic_figures) + len(charge_figures) >>> print(f"Comprehensive analysis: {total_analyses} figures generated") >>> print("Atomic composition:", list(atomic_figures.keys())) >>> print("Charge distributions:", list(charge_figures.keys()))
Batch processing for parameter studies:
>>> import os >>> # Systematic parameter variation study >>> parameters = ["low_field", "medium_field", "high_field"] >>> all_charge_analyses = {} >>> >>> for param in parameters: ... try: ... param_files = [f"{param}_initial.lammpstrj", f"{param}_final.lammpstrj"] ... param_labels = ["Initial", "Final"] ... ... if all(os.path.exists(f) for f in param_files): ... figures = plot_atomic_charge_distribution(param_files, param_labels, ... skip_rows=9, z_bins=50, ... analysis_name=f"{param}_charges", ... output_dir=f"./analysis_{param}") ... all_charge_analyses[param] = figures ... print(f"Completed charge analysis for {param}") ... else: ... print(f"Missing trajectory files for {param}") ... except Exception as e: ... print(f"Error analyzing {param}: {e}") >>> >>> successful_analyses = len(all_charge_analyses) >>> print(f"Successfully completed {successful_analyses} parameter studies")
Advanced visualization customization:
>>> # Publication-quality charge analysis with custom styling >>> publication_files = ["experiment_data.lammpstrj"] >>> publication_labels = ["Experimental"] >>> >>> # Publication-specific parameters >>> pub_styling = { ... 'ylimithi': 80, # Appropriate charge scale ... 'ylimitlo': -80, # Symmetric bipolar range ... 'xlimitlo': -10, # Device boundaries ... 'xlimithi': 40, ... 'yaxis': 0, # Zero-centered ... 'markerindex': 2 # Publication markers ... } >>> >>> charge_figures = plot_atomic_charge_distribution(publication_files, publication_labels, ... skip_rows=9, z_bins=75, ... analysis_name="publication_charges", ... output_dir="./publication_figures", ... **pub_styling) >>> >>> print("Publication-ready charge distribution figures generated") >>> print("Available figures:", list(charge_figures.keys()))
- lammpskit.ecellmodel.filament_layer_analysis.plot_displacement_comparison(file_list, loop_start, loop_end, labels, analysis_name, repeat_count=0, output_dir=os.getcwd())[source]
Generate comparative displacement analysis for multi-species ion migration in HfTaO devices.
Processes multiple thermodynamic output files to extract and visualize final displacement patterns across different atomic species, experimental conditions, or temporal states. Provides comprehensive mobility characterization essential for understanding ion migration mechanisms, filament formation pathways, and electrochemical switching dynamics in resistive memory devices through spatial displacement profiling.
- Parameters:
List of file paths to LAMMPS thermodynamic output files containing spatially-binned displacement data. Each file represents a different analysis case (species, condition, or temporal state). Common examples:
Species Files: [“Hf_mobility.dat”, “Ta_mobility.dat”, “O_mobility.dat”]
Condition Files: [“low_field.dat”, “medium_field.dat”, “high_field.dat”]
Temporal Files: [“early.dat”, “middle.dat”, “late.dat”]
Files must follow thermodynamic output format with consistent spatial binning.
loop_start (int) – Starting loop index (inclusive) for temporal analysis range. Corresponds to simulation timestep ranges or voltage cycle iterations. Minimum value: 0. Used to select specific time windows within displacement evolution data.
loop_end (int) – Ending loop index (inclusive) for temporal analysis range. Must be ≥ loop_start. Defines the final temporal point for displacement analysis.
Descriptive identifiers for each file in file_list, used in plot legends and output filenames. Must match file_list length. Examples:
Species Labels: [“Hf”, “Ta”, “O”] for multi-species comparison
Condition Labels: [“Low Field”, “High Field”] for parametric studies
State Labels: [“Initial”, “Intermediate”, “Final”] for temporal analysis
analysis_name (str) – Base identifier for output file naming and plot titles. Should describe the comparative analysis context. Examples: “species_mobility”, “field_dependence”, “temporal_evolution”
repeat_count (int, optional) – Number of times the first timestep is repeated in data files. Used for file format correction when initial conditions are duplicated. Default: 0.
output_dir (str, optional) – Directory path for saving generated displacement comparison plots. Default: current working directory. Creates directory if non-existent.
- Returns:
displacement_figures – Dictionary of matplotlib Figure objects for comprehensive displacement analysis:
Z-Direction Analysis: - ‘z_displacement’: Signed z-displacement profiles showing migration direction - ‘z_magnitude’: Absolute z-displacement magnitude for migration extent analysis
Lateral Displacement Analysis: - ‘lateral_displacement’: Radial displacement patterns indicating spreading behavior
All figures include publication-ready formatting with comparative legends, axis labels, and consistent styling for multi-case analysis visualization.
- Return type:
Notes
Data Processing Methodology:
Final State Extraction: The function processes the last temporal frame (loop_end) from each file:
` z_displacement = data[file_index, -1, :, -3] # Column -3: Z-displacement lateral_displacement = data[file_index, -1, :, -2] # Column -2: Lateral displacement bin_position = data[file_index, -1, :, 1] # Column 1: Spatial position atom_count = data[file_index, -1, :, 2] # Column 2: Atoms per bin `Spatial Binning Framework: - Uniform z-direction discretization across electrode separation - Bin centers represent average displacement for contained atoms - Statistical significance ensured through atom count validation - Cross-file spatial alignment for comparative analysis
Performance Characteristics: - Memory Usage: O(N_files × N_loops × N_bins × N_columns) for data storage - Processing Speed: ~2-8s per file depending on loop range and bin count - Output Quality: Publication-ready SVG figures with comparative legends - Scalability: Efficient batch processing for parametric studies
Integration with LAMMPSKit Analysis Pipeline: - Data Reading: Uses read_displacement_data() for robust file parsing - Visualization: Leverages plot_multiple_cases() for comparative plotting - Error Handling: Inherits validation from displacement data reading functions - Workflow Integration: Compatible with plot_displacement_timeseries() analysis
Examples
Multi-species mobility comparison:
>>> from lammpskit.ecellmodel.filament_layer_analysis import plot_displacement_comparison >>> # Compare displacement patterns of different atomic species >>> mobility_files = ["Hf_mobility.dat", "Ta_mobility.dat", "O_mobility.dat"] >>> species_labels = ["Hf", "Ta", "O"] >>> >>> displacement_figures = plot_displacement_comparison(mobility_files, ... loop_start=0, loop_end=99, ... labels=species_labels, ... analysis_name="species_mobility") >>> print(f"Generated {len(displacement_figures)} displacement comparison plots") >>> >>> # Analyze species-specific migration behavior >>> z_disp_fig = displacement_figures['z_displacement'] # Migration direction >>> lateral_fig = displacement_figures['lateral_displacement'] # Spreading behavior >>> magnitude_fig = displacement_figures['z_magnitude'] # Migration extent
Field-dependent displacement analysis:
>>> # Parametric study of electric field effects on mobility >>> field_files = ["low_field_Hf.dat", "medium_field_Hf.dat", "high_field_Hf.dat"] >>> field_labels = ["Low Field", "Medium Field", "High Field"] >>> >>> field_displacement = plot_displacement_comparison(field_files, ... loop_start=50, loop_end=150, ... labels=field_labels, ... analysis_name="field_dependence", ... output_dir="./field_study") >>> >>> print("Field-dependent mobility analysis completed") >>> print("Available plots:", list(field_displacement.keys()))
Temporal evolution analysis:
>>> # Time-resolved displacement evolution study >>> temporal_files = ["early_mobility.dat", "mid_mobility.dat", "late_mobility.dat"] >>> time_labels = ["Early (0-100ps)", "Mid (500-600ps)", "Late (1900-2000ps)"] >>> >>> temporal_analysis = plot_displacement_comparison(temporal_files, ... loop_start=0, loop_end=50, ... labels=time_labels, ... analysis_name="temporal_evolution") >>> >>> # Examine long-term migration trends >>> z_evolution = temporal_analysis['z_displacement'] >>> lateral_evolution = temporal_analysis['lateral_displacement'] >>> print("Temporal displacement evolution analysis completed")
Device condition comparison:
>>> # Compare displacement under different device conditions >>> condition_files = ["pristine_mobility.dat", "cycled_mobility.dat", "degraded_mobility.dat"] >>> condition_labels = ["Pristine", "After 1000 Cycles", "Degraded"] >>> >>> condition_analysis = plot_displacement_comparison(condition_files, ... loop_start=80, loop_end=120, ... labels=condition_labels, ... analysis_name="device_degradation") >>> >>> print("Device condition comparison completed")
Comprehensive multi-case analysis:
>>> # Large-scale comparative study with multiple parameters >>> import os >>> analysis_cases = { ... "species_comparison": { ... "files": ["Hf_mobility.dat", "Ta_mobility.dat", "O_mobility.dat"], ... "labels": ["Hf", "Ta", "O"] ... }, ... "voltage_dependence": { ... "files": ["1V_mobility.dat", "2V_mobility.dat", "3V_mobility.dat"], ... "labels": ["1V", "2V", "3V"] ... }, ... "temperature_effects": { ... "files": ["300K_mobility.dat", "400K_mobility.dat", "500K_mobility.dat"], ... "labels": ["300K", "400K", "500K"] ... } ... } >>> >>> all_displacement_analyses = {} >>> for case_name, case_params in analysis_cases.items(): ... try: ... if all(os.path.exists(f) for f in case_params["files"]): ... figures = plot_displacement_comparison(case_params["files"], ... loop_start=0, loop_end=75, ... labels=case_params["labels"], ... analysis_name=case_name, ... output_dir=f"./analysis_{case_name}") ... all_displacement_analyses[case_name] = figures ... print(f"Completed {case_name}: {len(figures)} figures") ... else: ... print(f"Missing files for {case_name}") ... except Exception as e: ... print(f"Error in {case_name}: {e}") >>> >>> total_figures = sum(len(figs) for figs in all_displacement_analyses.values()) >>> print(f"Total displacement analyses: {total_figures} figures across {len(all_displacement_analyses)} cases")
Advanced data extraction and analysis:
>>> # Detailed analysis of displacement patterns >>> mobility_files = ["oxygen_mobility.dat"] >>> labels = ["O Mobility"] >>> >>> displacement_data = plot_displacement_comparison(mobility_files, 0, 50, labels, ... "detailed_oxygen_analysis") >>> >>> # Access raw displacement data for further analysis >>> print("Available displacement analyses:", list(displacement_data.keys())) >>> >>> # Examine displacement characteristics >>> # Note: To access raw data, you would typically use read_displacement_data directly >>> from lammpskit.ecellmodel.filament_layer_analysis import read_displacement_data >>> raw_data = read_displacement_data("oxygen_mobility.dat", 0, 50) >>> final_frame = raw_data[-1] # Last temporal frame >>> >>> z_positions = final_frame[:, 1] # Spatial bin centers >>> z_displacements = final_frame[:, -3] # Z-direction displacements >>> lateral_displacements = final_frame[:, -2] # Lateral displacements >>> >>> print(f"Spatial resolution: {z_positions[1] - z_positions[0]:.2f} Å") >>> print(f"Max z-displacement: {z_displacements.max():.3f} Å") >>> print(f"Max lateral displacement: {lateral_displacements.max():.3f} Å")
Error handling and validation:
>>> # Robust displacement analysis with comprehensive error handling >>> mobility_files = ["species1.dat", "species2.dat", "species3.dat"] >>> species_labels = ["Species1", "Species2", "Species3"] >>> >>> try: ... # Validate file existence before analysis ... existing_files = [f for f in mobility_files if os.path.exists(f)] ... existing_labels = [species_labels[i] for i, f in enumerate(mobility_files) if os.path.exists(f)] ... ... if len(existing_files) >= 2: # Require at least 2 files for comparison ... displacement_figures = plot_displacement_comparison(existing_files, ... loop_start=0, loop_end=100, ... labels=existing_labels, ... analysis_name="validated_comparison") ... print(f"Successfully analyzed {len(existing_files)} displacement files") ... else: ... print("Insufficient files for comparative analysis") ... except FileNotFoundError as e: ... print(f"File access error: {e}") ... except ValueError as e: ... print(f"Data format error: {e}") ... except Exception as e: ... print(f"Analysis error: {e}")
Integration with other LAMMPSKit functions:
>>> # Combine displacement comparison with time series analysis >>> from lammpskit.ecellmodel.filament_layer_analysis import plot_displacement_timeseries >>> >>> # Step 1: Comparative final displacement analysis >>> comparative_files = ["condition_A.dat", "condition_B.dat"] >>> comparative_labels = ["Condition A", "Condition B"] >>> >>> final_displacement = plot_displacement_comparison(comparative_files, 0, 100, ... comparative_labels, "comparison_study") >>> >>> # Step 2: Detailed time series for selected condition >>> time_series = plot_displacement_timeseries(["condition_A.dat"], 0, 100, ["Condition A"], ... "detailed_timeseries", time_step=0.0002, ... dump_interval_steps=5000) >>> >>> print("Comprehensive displacement analysis completed:") >>> print(f" Comparative analysis: {len(final_displacement)} figures") >>> print(f" Time series analysis: {len(time_series)} figures")
- lammpskit.ecellmodel.filament_layer_analysis.analyze_clusters(filepath, z_filament_lower_limit=5, z_filament_upper_limit=23, thickness=21)[source]
Perform comprehensive OVITO-based cluster analysis for metallic filament characterization.
Conducts advanced structural analysis of HfTaO electrochemical memory devices using OVITO’s cluster detection algorithms to identify, characterize, and quantify conductive filament formation. Provides detailed connectivity analysis, size distribution characterization, and radial distribution function (RDF) analysis essential for understanding resistive switching mechanisms and device reliability in electrochemical memory applications.
- Parameters:
filepath (str) – Path to LAMMPS trajectory file (.lammpstrj, .dump) containing atomic coordinates and species information. File must be compatible with OVITO import functionality and contain sufficient atomic data for cluster detection. Example: “filament_formation.lammpstrj”, “switching_cycle.dump”
z_filament_lower_limit (float, optional) – Lower z-coordinate boundary (Angstroms) defining the bottom electrode interface for filament connectivity analysis. Represents the lower threshold for complete electrode-to-electrode conductive bridge formation. Default: 5.0 Å. Typical range: 3-8 Å depending on device geometry.
z_filament_upper_limit (float, optional) – Upper z-coordinate boundary (Angstroms) defining the top electrode interface for filament connectivity analysis. Represents the upper threshold for complete conductive bridge formation. Default: 23.0 Å. Typical range: 20-30 Å depending on oxide thickness.
thickness (float, optional) – Characteristic filament thickness parameter (Angstroms) used for structural analysis and RDF calculations. Influences cluster size classification and connectivity determination. Default: 21.0 Å. Optimization range: 15-30 Å based on device dimensions.
- Returns:
cluster_analysis_results – Comprehensive cluster analysis data containing connectivity metrics, size distributions, and structural characterization:
Temporal Information: - timestep (int): Simulation timestep from trajectory file header
Connectivity Analysis: - connection (int): Binary connectivity state (1=connected, 0=disconnected) - separation (float): Minimum inter-cluster distance (Å) when disconnected - gap (float): Z-direction gap between cluster boundaries (Å)
Lower Filament Characterization: - fil_size_down (int): Number of atoms in bottom filament cluster - fil_height (float): Maximum z-coordinate of bottom cluster (Å) - rdf_down (np.ndarray): Radial distribution function data (shape: [N_bins, 2])
Column 0: Radial distance (Å)
Column 1: RDF intensity
Upper Filament Characterization: - fil_size_up (int): Number of atoms in top filament cluster - fil_depth (float): Minimum z-coordinate of top cluster (Å) - rdf_up (np.ndarray): Upper cluster RDF data (shape: [N_bins, 2])
- Return type:
tuple[int, int, int, float, np.ndarray, int, float, np.ndarray, float, float]
- Raises:
FileNotFoundError – If the specified filepath does not exist or is not accessible to OVITO.
ValueError – If no metallic clusters are detected in the trajectory file or if file format is incompatible with OVITO cluster analysis pipelines.
TypeError – If input parameters have invalid types or values outside acceptable ranges.
Notes
OVITO Cluster Analysis Pipeline:
Multi-Pipeline Architecture: The function employs four specialized OVITO pipelines for comprehensive analysis:
Primary Cluster Pipeline: Largest cluster identification and analysis
Secondary Cluster Pipeline: Second-largest cluster characterization
Lower Filament Pipeline: Bottom electrode region analysis
Upper Filament Pipeline: Top electrode region analysis
Coordination Analysis Framework:
Cutoff Distance: 2.7 Å (first neighbor shell for metallic bonds) Coordination Threshold: <6 (under-coordinated atoms for filament detection) Clustering Cutoff: 3.9 Å (metallic cluster connectivity distance)
Species Selection Criteria: The analysis targets metallic and under-coordinated species: - Type 2: Hafnium atoms (matrix metal component) - Type 4, 8: Tantalum atoms (matrix metal component) - Type 8: Additional Ta metal species (device-specific) - Type 1, 3, 7: Oxygen atoms (matrix component) - Types 5, 6, 9, 10: Electrode species
Connectivity Determination Algorithm:
if z_min < z_lower_limit and z_max > z_upper_limit: connection_state = 1 # Continuous bridge formed separation = 0.0 gap = 0.0 else: connection_state = 0 # Discontinuous filament separation = min_distance_between_clusters - cutoff_distance gap = z_direction_gap_between_clusters
Radial Distribution Function (RDF) Analysis: Characterizes local atomic structure and bonding environment
Performance Characteristics:
Processing Time: 5-30s per trajectory frame depending on atom count
Memory Usage: O(N_atoms × N_neighbors) for coordination analysis
Accuracy: Sub-angstrom precision for cluster boundary detection
Scalability: Efficient for trajectories up to ~10⁶ atoms per frame
Integration with LAMMPSKit Ecosystem:
File Compatibility: Direct integration with LAMMPS trajectory formats
Parameter Validation: Uses config module validation functions
Pipeline Integration: Compatible with track_filament_evolution() workflows
Data Export: Results integrate with time series plotting functions
Examples
Basic filament connectivity analysis:
>>> from lammpskit.ecellmodel.filament_layer_analysis import analyze_clusters >>> # Analyze single trajectory frame for filament formation >>> trajectory_file = "switching_process.lammpstrj" >>> >>> cluster_results = analyze_clusters(trajectory_file, ... z_filament_lower_limit=5, ... z_filament_upper_limit=23) >>> >>> # Extract connectivity information >>> timestep, connection, size_down, height, rdf_down, size_up, depth, rdf_up, separation, gap = cluster_results >>> >>> print(f"Timestep: {timestep}") >>> print(f"Filament Connected: {'Yes' if connection else 'No'}") >>> if connection: ... print(f"Complete bridge formed with {size_down + size_up} atoms") ... else: ... print(f"Gap: {gap:.2f} Å, Separation: {separation:.2f} Å")
Device geometry optimization:
>>> # Parametric study of device geometry effects on connectivity >>> trajectory_file = "device_optimization.lammpstrj" >>> >>> # Test different electrode separations >>> geometries = [ ... {"lower": 3, "upper": 20, "name": "thin_oxide"}, ... {"lower": 5, "upper": 25, "name": "medium_oxide"}, ... {"lower": 7, "upper": 30, "name": "thick_oxide"} ... ] >>> >>> connectivity_results = {} >>> for geom in geometries: ... try: ... results = analyze_clusters(trajectory_file, ... z_filament_lower_limit=geom["lower"], ... z_filament_upper_limit=geom["upper"]) ... connectivity_results[geom["name"]] = { ... "connected": results[1], ... "total_atoms": results[2] + results[5], ... "gap": results[9] if not results[1] else 0.0 ... } ... print(f"{geom['name']}: Connected={results[1]}, Atoms={results[2] + results[5]}") ... except Exception as e: ... print(f"Error analyzing {geom['name']}: {e}") >>> >>> print("Geometry optimization completed")
Detailed structural characterization:
>>> # Comprehensive filament structure analysis >>> trajectory_file = "filament_structure.lammpstrj" >>> >>> cluster_data = analyze_clusters(trajectory_file, z_filament_lower_limit=4, ... z_filament_upper_limit=24, thickness=20) >>> >>> timestep, connection, size_down, height, rdf_down, size_up, depth, rdf_up, separation, gap = cluster_data >>> >>> # Analyze RDF characteristics >>> rdf_distances = rdf_down[:, 0] # Radial distances >>> rdf_intensities = rdf_down[:, 1] # RDF values >>> >>> # Find first coordination shell peak >>> first_peak_idx = np.argmax(rdf_intensities[:50]) # Search first 5 Å >>> first_peak_position = rdf_distances[first_peak_idx] >>> first_peak_intensity = rdf_intensities[first_peak_idx] >>> >>> print(f"Structural Analysis Results:") >>> print(f" Lower cluster: {size_down} atoms, height: {height:.2f} Å") >>> print(f" Upper cluster: {size_up} atoms, depth: {depth:.2f} Å") >>> print(f" First RDF peak: {first_peak_position:.2f} Å (intensity: {first_peak_intensity:.3f})") >>> >>> if connection: ... total_filament_size = size_down + size_up ... filament_span = height - depth ... print(f" Complete filament: {total_filament_size} atoms spanning {filament_span:.2f} Å") ... else: ... print(f" Disconnected filament with {gap:.2f} Å gap")
Time series connectivity analysis:
>>> # Process multiple trajectory frames for evolution tracking >>> import glob >>> trajectory_files = sorted(glob.glob("time_series_*.lammpstrj")) >>> >>> connectivity_evolution = [] >>> filament_size_evolution = [] >>> gap_evolution = [] >>> >>> for i, traj_file in enumerate(trajectory_files): ... try: ... results = analyze_clusters(traj_file, z_filament_lower_limit=5, ... z_filament_upper_limit=23) ... ... timestep, connection, size_down, height, rdf_down, size_up, depth, rdf_up, separation, gap = results ... ... connectivity_evolution.append(connection) ... filament_size_evolution.append(size_down + size_up) ... gap_evolution.append(gap if not connection else 0.0) ... ... if i % 10 == 0: # Progress reporting ... print(f"Processed frame {i+1}/{len(trajectory_files)}") ... ... except Exception as e: ... print(f"Error processing {traj_file}: {e}") ... connectivity_evolution.append(0) ... filament_size_evolution.append(0) ... gap_evolution.append(float('inf')) >>> >>> # Analyze connectivity statistics >>> connectivity_fraction = np.mean(connectivity_evolution) >>> avg_connected_size = np.mean([size for size, conn in zip(filament_size_evolution, connectivity_evolution) if conn]) >>> avg_gap_when_disconnected = np.mean([gap for gap, conn in zip(gap_evolution, connectivity_evolution) if not conn and gap < float('inf')]) >>> >>> print(f"Time Series Analysis Results:") >>> print(f" Connectivity fraction: {connectivity_fraction:.3f}") >>> print(f" Average connected filament size: {avg_connected_size:.1f} atoms") >>> print(f" Average gap when disconnected: {avg_gap_when_disconnected:.2f} Å")
Advanced RDF analysis and comparison:
>>> # Compare RDF characteristics between different states >>> connected_file = "connected_state.lammpstrj" >>> disconnected_file = "disconnected_state.lammpstrj" >>> >>> # Analyze connected state >>> conn_results = analyze_clusters(connected_file) >>> conn_rdf_down = conn_results[4] >>> >>> # Analyze disconnected state >>> disconn_results = analyze_clusters(disconnected_file) >>> disconn_rdf_down = disconn_results[4] >>> >>> # Compare structural order >>> conn_first_peak = np.max(conn_rdf_down[:50, 1]) >>> disconn_first_peak = np.max(disconn_rdf_down[:50, 1]) >>> >>> print(f"RDF Comparison:") >>> print(f" Connected state first peak intensity: {conn_first_peak:.3f}") >>> print(f" Disconnected state first peak intensity: {disconn_first_peak:.3f}") >>> print(f" Structural order change: {(conn_first_peak - disconn_first_peak)/disconn_first_peak*100:.1f}%")
Error handling and validation:
>>> # Robust cluster analysis with comprehensive error handling >>> trajectory_files = ["test1.lammpstrj", "test2.lammpstrj", "test3.lammpstrj"] >>> >>> successful_analyses = [] >>> failed_analyses = [] >>> >>> for traj_file in trajectory_files: ... try: ... # Validate file exists before processing ... import os ... if not os.path.exists(traj_file): ... print(f"File not found: {traj_file}") ... failed_analyses.append((traj_file, "FileNotFoundError")) ... continue ... ... # Perform cluster analysis ... results = analyze_clusters(traj_file, ... z_filament_lower_limit=5, ... z_filament_upper_limit=23) ... ... # Validate results ... if len(results) == 10: ... successful_analyses.append((traj_file, results)) ... print(f"Successfully analyzed: {traj_file}") ... else: ... failed_analyses.append((traj_file, "Invalid results format")) ... ... except ValueError as e: ... print(f"Cluster detection failed for {traj_file}: {e}") ... failed_analyses.append((traj_file, f"ValueError: {e}")) ... except Exception as e: ... print(f"Unexpected error for {traj_file}: {e}") ... failed_analyses.append((traj_file, f"Exception: {e}")) >>> >>> print(f"Analysis Summary:") >>> print(f" Successful: {len(successful_analyses)}") >>> print(f" Failed: {len(failed_analyses)}")
Integration with filament evolution tracking:
>>> # Use with track_filament_evolution for comprehensive time series analysis >>> from lammpskit.ecellmodel.filament_layer_analysis import track_filament_evolution >>> >>> # Single-frame cluster analysis >>> single_frame_file = "snapshot.lammpstrj" >>> cluster_analysis = analyze_clusters(single_frame_file) >>> >>> print(f"Single Frame Analysis:") >>> print(f" Connectivity: {cluster_analysis[1]}") >>> print(f" Total atoms: {cluster_analysis[2] + cluster_analysis[5]}") >>> >>> # Multi-frame evolution tracking >>> time_series_files = ["evolution.lammpstrj"] >>> evolution_plots = track_filament_evolution(time_series_files, "evolution_study", ... time_step=0.0002, dump_interval_steps=5000) >>> >>> print(f"Evolution tracking completed with {len(evolution_plots)} plots")
- lammpskit.ecellmodel.filament_layer_analysis.track_filament_evolution(file_list, analysis_name, time_step, dump_interval_steps, output_dir=os.getcwd())[source]
Comprehensive temporal tracking of filament formation and evolution dynamics.
Processes time-series trajectory data to analyze and visualize the complete evolution of conductive filament formation, connectivity transitions, and structural characteristics in HfTaO electrochemical memory devices. Provides detailed time-resolved analysis essential for understanding switching mechanisms, device reliability, and optimization of resistive memory operation through systematic cluster analysis and visualization.
- Parameters:
List of file paths to sequential trajectory files representing temporal evolution. Files should be ordered and contain atomic coordinates with species information compatible with OVITO cluster analysis. Examples:
Single Trajectory: [“complete_switching_cycle.lammpstrj”]
Multi-Frame Sequence: [“frame_0001.lammpstrj”, “frame_0002.lammpstrj”, …]
Process Segments: [“formation.lammpstrj”, “retention.lammpstrj”, “dissolution.lammpstrj”]
analysis_name (str) – Base identifier for output file naming and analysis organization. Used to generate descriptive filenames for all output plots and data files. Should reflect the temporal analysis context. Examples: “switching_cycle”, “formation_dynamics”, “retention_study”, “dissolution_analysis”
time_step (float) – Simulation time step in picoseconds (ps) used for temporal axis conversion. Defines the fundamental temporal resolution of molecular dynamics simulation. Typical values: 0.0001-0.002 ps for atomic-scale MD simulations.
dump_interval_steps (int) – Number of simulation steps between consecutive trajectory frame outputs. Used to calculate actual time intervals between analyzed frames. Example: dump_interval_steps=5000 with time_step=0.0002 gives 1 ps between frames.
output_dir (str, optional) – Directory path for saving generated time series plots and analysis figures. Default: current working directory. Creates directory structure if non-existent. Recommended: dedicated analysis subdirectory for organized output management.
- Returns:
evolution_figures – Comprehensive dictionary of matplotlib Figure objects for temporal analysis:
Connectivity Analysis: - ‘connection’: Binary connectivity state evolution (1=connected, 0=broken) - ‘gap’: Filament gap evolution showing discontinuity magnitude (Å) - ‘separation’: Inter-cluster separation distance when disconnected (Å)
Filament Morphology Evolution: - ‘filament_gap_and_size’: Dual-axis plot of gap vs. atom count - ‘filament_lower_part’: Lower filament height and size evolution - ‘filament_upper_part’: Upper filament depth and size evolution
Detailed Structural Characteristics: - ‘filament_height’: Lower filament penetration depth (scatter plot) - ‘filament_depth’: Upper filament penetration depth (scatter plot) - ‘filament_size_up’: Upper filament atom count evolution - ‘filament_size_down’: Lower filament atom count evolution
All figures include statistical analysis (mean ± std) and frequency analysis with publication-ready formatting for scientific reporting.
- Return type:
- Raises:
FileNotFoundError – If any trajectory file in file_list does not exist or is inaccessible.
ValueError – If OVITO cluster analysis fails for any trajectory frame or if temporal sequence contains inconsistent data structures.
RuntimeError – If memory limitations are exceeded during large-scale time series analysis.
Notes
Temporal Analysis Framework:
Time Axis Calculation:
` actual_time = timestep × time_step × dump_interval_steps `Where timestep is extracted from LAMMPS trajectory headers.Connectivity State Analysis: - Connected State: Filament spans complete electrode separation (connection = 1) - Broken State: Incomplete filament with measurable gap (connection = 0) - Frequency Analysis: Percentage of time spent in connected state - Switching Events: Transitions between connected/disconnected states
Statistical Characterization: Each temporal series includes comprehensive statistical analysis: - Mean Values: Average behavior over analysis time window - Standard Deviation: Variability and stability assessment - Frequency Metrics: State occupation probabilities - Trend Analysis: Long-term evolution patterns
Performance Characteristics: - Processing Speed: ~1-5 minutes per trajectory file depending on complexity - Memory Usage: O(N_frames × N_atoms) for trajectory sequence processing - Temporal Resolution: Limited by dump_interval_steps and simulation time_step - Statistical Accuracy: Requires sufficient frames for reliable statistics (≥50 frames)
Integration with LAMMPSKit Analysis Pipeline: - Cluster Analysis: Uses analyze_clusters() for frame-by-frame structural analysis - Visualization: Leverages plotting utilities for publication-ready time series - Data Processing: Compatible with displacement analysis and atomic distribution functions - Workflow Integration: Supports batch processing and automated analysis pipelines
Examples
Basic filament evolution tracking:
>>> from lammpskit.ecellmodel.filament_layer_analysis import track_filament_evolution >>> # Analyze complete switching cycle evolution >>> trajectory_files = ["switching_cycle.lammpstrj"] >>> >>> evolution_figures = track_filament_evolution(trajectory_files, ... analysis_name="switching_dynamics", ... time_step=0.0002, # 0.2 fs ... dump_interval_steps=5000) # 1 ps intervals >>> >>> print(f"Generated {len(evolution_figures)} temporal analysis plots") >>> >>> # Examine connectivity evolution >>> connection_fig = evolution_figures['connection'] >>> gap_fig = evolution_figures['gap'] >>> print("Basic filament evolution analysis completed")
Multi-phase switching analysis:
>>> # Analyze distinct switching phases with high temporal resolution >>> phase_files = ["formation.lammpstrj", "retention.lammpstrj", "dissolution.lammpstrj"] >>> phase_names = ["formation", "retention", "dissolution"] >>> >>> all_phase_analyses = {} >>> for phase_file, phase_name in zip(phase_files, phase_names): ... try: ... phase_figures = track_filament_evolution([phase_file], ... analysis_name=f"{phase_name}_analysis", ... time_step=0.0001, # High resolution ... dump_interval_steps=1000, # 0.1 ps intervals ... output_dir=f"./analysis_{phase_name}") ... all_phase_analyses[phase_name] = phase_figures ... print(f"Completed {phase_name} phase analysis: {len(phase_figures)} plots") ... except Exception as e: ... print(f"Error analyzing {phase_name} phase: {e}") >>> >>> total_plots = sum(len(figs) for figs in all_phase_analyses.values()) >>> print(f"Multi-phase analysis completed: {total_plots} total plots")
Long-term cycling study:
>>> # Extended cycling analysis for endurance characterization >>> cycling_files = [f"cycle_{i:03d}.lammpstrj" for i in range(1, 101)] # 100 cycles >>> >>> cycling_evolution = track_filament_evolution(cycling_files, ... analysis_name="endurance_study", ... time_step=0.0005, ... dump_interval_steps=10000, # 5 ps intervals ... output_dir="./endurance_analysis") >>> >>> print(f"Endurance study completed: {len(cycling_evolution)} analysis plots") >>> >>> # Focus on key endurance metrics >>> connection_evolution = cycling_evolution['connection'] >>> gap_evolution = cycling_evolution['gap'] >>> size_evolution = cycling_evolution['filament_gap_and_size'] >>> print("Long-term cycling analysis available for device optimization")
High-resolution formation dynamics:
>>> # Detailed analysis of initial filament formation with fine temporal resolution >>> formation_files = ["initial_formation.lammpstrj"] >>> >>> # Ultra-high temporal resolution for formation kinetics >>> formation_analysis = track_filament_evolution(formation_files, ... analysis_name="formation_kinetics", ... time_step=0.00005, # 0.05 fs steps ... dump_interval_steps=500, # 0.025 ps intervals ... output_dir="./formation_study") >>> >>> print("High-resolution formation dynamics analysis:") >>> print(f" Total figures: {len(formation_analysis)}") >>> print(" Available analyses:", list(formation_analysis.keys())) >>> >>> # Examine detailed morphology evolution >>> lower_evolution = formation_analysis['filament_lower_part'] >>> upper_evolution = formation_analysis['filament_upper_part'] >>> height_evolution = formation_analysis['filament_height'] >>> depth_evolution = formation_analysis['filament_depth']
Comparative evolution analysis:
>>> # Compare filament evolution under different conditions >>> conditions = ["low_voltage", "medium_voltage", "high_voltage"] >>> evolution_comparison = {} >>> >>> for condition in conditions: ... try: ... condition_file = f"{condition}_evolution.lammpstrj" ... condition_analysis = track_filament_evolution([condition_file], ... analysis_name=f"{condition}_evolution", ... time_step=0.0002, ... dump_interval_steps=2500, # 0.5 ps intervals ... output_dir=f"./comparison_{condition}") ... evolution_comparison[condition] = condition_analysis ... print(f"Completed {condition} evolution analysis") ... except Exception as e: ... print(f"Error analyzing {condition}: {e}") >>> >>> print(f"Comparative evolution analysis: {len(evolution_comparison)} conditions") >>> print("Available for voltage-dependent switching optimization")
Advanced statistical analysis integration:
>>> # Detailed analysis with custom statistical processing >>> trajectory_files = ["device_operation.lammpstrj"] >>> >>> evolution_data = track_filament_evolution(trajectory_files, ... analysis_name="statistical_analysis", ... time_step=0.0001, ... dump_interval_steps=5000, ... output_dir="./statistical_study") >>> >>> print("Statistical Evolution Analysis Results:") >>> print(f" Connection evolution: {evolution_data['connection']}") >>> print(f" Gap evolution: {evolution_data['gap']}") >>> print(f" Separation evolution: {evolution_data['separation']}") >>> print(f" Lower filament evolution: {evolution_data['filament_lower_part']}") >>> print(f" Upper filament evolution: {evolution_data['filament_upper_part']}") >>> >>> # Note: Statistical summaries are automatically included in plot legends >>> # as mean ± std and frequency analysis for connectivity states
Error handling and validation:
>>> # Robust evolution tracking with comprehensive error handling >>> import os >>> import glob >>> >>> # Auto-discover trajectory files >>> trajectory_pattern = "evolution_*.lammpstrj" >>> discovered_files = sorted(glob.glob(trajectory_pattern)) >>> >>> if len(discovered_files) >= 5: # Require minimum frames for statistics ... try: ... evolution_analysis = track_filament_evolution(discovered_files, ... analysis_name="auto_discovery", ... time_step=0.0002, ... dump_interval_steps=5000) ... print(f"Auto-discovery analysis: {len(evolution_analysis)} plots generated") ... print(f"Processed {len(discovered_files)} trajectory frames") ... except FileNotFoundError as e: ... print(f"File access error: {e}") ... except ValueError as e: ... print(f"Cluster analysis error: {e}") ... except Exception as e: ... print(f"Unexpected error: {e}") ... else: ... print(f"Insufficient files found: {len(discovered_files)} (minimum: 5)")
Integration with other LAMMPSKit functions:
>>> # Combine evolution tracking with single-frame analysis >>> from lammpskit.ecellmodel.filament_layer_analysis import analyze_clusters >>> >>> # Time series evolution analysis >>> time_series_files = ["evolution.lammpstrj"] >>> evolution_plots = track_filament_evolution(time_series_files, "evolution_study", ... time_step=0.0002, dump_interval_steps=5000) >>> >>> # Detailed single-frame analysis for specific timepoints >>> key_frames = ["initial.lammpstrj", "intermediate.lammpstrj", "final.lammpstrj"] >>> frame_analyses = {} >>> >>> for frame_file in key_frames: ... try: ... frame_name = frame_file.replace(".lammpstrj", "") ... cluster_data = analyze_clusters(frame_file) ... frame_analyses[frame_name] = { ... "timestep": cluster_data[0], ... "connected": cluster_data[1], ... "total_atoms": cluster_data[2] + cluster_data[5], ... "gap": cluster_data[9] ... } ... print(f"Frame analysis {frame_name}: Connected={cluster_data[1]}") ... except Exception as e: ... print(f"Error analyzing {frame_file}: {e}") >>> >>> print("Comprehensive Analysis Summary:") >>> print(f" Evolution tracking: {len(evolution_plots)} time series plots") >>> print(f" Frame-by-frame analysis: {len(frame_analyses)} detailed snapshots") >>> >>> # Results provide both temporal trends and detailed structural snapshots >>> # for complete filament formation and switching characterization
Performance optimization for large datasets:
>>> # Efficient processing of extensive time series data >>> import numpy as np >>> >>> # Process large trajectory sequence with progress monitoring >>> large_trajectory_files = [f"large_study_{i:04d}.lammpstrj" for i in range(1, 1001)] # 1000 frames >>> >>> # Check file existence before processing >>> existing_files = [f for f in large_trajectory_files if os.path.exists(f)] >>> >>> if len(existing_files) >= 100: # Proceed if sufficient data ... try: ... print(f"Processing {len(existing_files)} trajectory files...") ... large_evolution = track_filament_evolution(existing_files[:100], # Limit for demonstration ... analysis_name="large_scale_study", ... time_step=0.001, ... dump_interval_steps=10000, ... output_dir="./large_scale_analysis") ... print(f"Large-scale evolution analysis completed: {len(large_evolution)} plots") ... except MemoryError: ... print("Memory limitation reached - consider processing in batches") ... except Exception as e: ... print(f"Processing error: {e}") ... else: ... print(f"Insufficient data files: {len(existing_files)} available")
- lammpskit.ecellmodel.filament_layer_analysis.plot_displacement_timeseries(file_list, datatype, dataindex, Nchunks, loop_start, loop_end, output_dir=os.getcwd())[source]
Generate comprehensive spatially-resolved displacement time series visualization.
Creates multi-panel grid plots showing temporal evolution of displacement data across spatial bins and multiple atomic species or experimental conditions. Provides detailed visualization of ion migration patterns, thermal effects, and spatial heterogeneity essential for understanding electrochemical switching dynamics in HfTaO memory devices through systematic time-resolved displacement analysis.
- Parameters:
List of file paths to LAMMPS thermodynamic output files containing spatially-binned displacement data. Each file represents different atomic species, experimental conditions, or temporal phases. Files must have consistent spatial binning and temporal sampling. Examples:
Species Analysis: [“Hf_mobility.dat”, “Ta_mobility.dat”, “O_mobility.dat”]
Condition Comparison: [“low_field.dat”, “high_field.dat”]
Temperature Study: [“300K.dat”, “400K.dat”, “500K.dat”]
datatype (str) – Descriptive identifier for the type of displacement analysis being performed. Used in plot titles, axis labels, and output filename generation. Should reflect the physical context of the analysis. Examples: “mobility”, “temperature”, “field_dependence”, “species_comparison”
dataindex (int) –
Column index specifying the displacement component to visualize from thermodynamic output files. Maps to specific physical quantities:
0: ‘abs total disp’ - Total displacement magnitude (Angstroms)
1: ‘density - mass’ - Local mass density (atomic units)
2: ‘temp (K)’ - Local temperature (Kelvin)
3: ‘z disp (A)’ - Z-direction displacement (Angstroms, field direction)
4: ‘lateral disp (A)’ - Lateral displacement magnitude (Angstroms)
5: ‘outward disp vector (A)’ - Radial outward displacement (Angstroms)
Nchunks (int) – Number of spatial bins along the z-direction (electrode-to-electrode axis). Determines the spatial resolution of displacement analysis and number of subplot rows. Typical range: 15-100 bins depending on device thickness and desired resolution.
loop_start (int) – Starting loop index (inclusive) for temporal analysis window. Corresponds to simulation timestep ranges or voltage cycle iterations. Defines the beginning of the time series analysis period.
loop_end (int) – Ending loop index (inclusive) for temporal analysis window. Must be ≥ loop_start. Defines the end of the time series analysis period.
output_dir (str, optional) – Directory path for saving generated time series visualization plots. Default: current working directory. Creates directory structure if non-existent. Recommended: dedicated analysis subdirectory for organized output management.
- Returns:
timeseries_figures – Dictionary containing the generated matplotlib Figure object:
Time Series Visualization: - ‘displacement_timeseries’: Multi-panel grid plot with comprehensive spatially-resolved temporal analysis
Plot Structure: - Rows: Each row represents a spatial bin (z-position) - Columns: Each column represents a different file/species/condition - Layout: Bottom row = electrode interface, top row = opposite electrode - Legends: Species identification and spatial bin numbering - Styling: Publication-ready formatting with consistent color schemes
The figure includes automatic layout optimization, shared axis labeling, and integrated legend systems for clear interpretation of multi-dimensional data.
- Return type:
- Raises:
FileNotFoundError – If any file in file_list does not exist or is inaccessible.
ValueError – If dataindex is outside valid range (0-5) or if displacement data format is incompatible with expected thermodynamic output structure.
IndexError – If Nchunks exceeds available spatial bins in displacement data files.
Notes
Visualization Framework & Layout:
Multi-Panel Grid Structure:
` Grid Layout: [Nchunks rows × 4 columns maximum] Row Assignment: Bottom = Bin 1 (electrode), Top = Bin Nchunks (opposite electrode) Column Assignment: File order from file_list (left to right) Color Scheme: ['blue', 'red', 'green', 'black'] cycling for multiple files `Spatial Bin Interpretation: - Bin 1 (Bottom Row): Electrode interface region (z ≈ z_min) - Bin N (Top Row): Opposite electrode interface (z ≈ z_max) - Middle Bins: Oxide bulk regions with varying properties - Bin Numbering: 1-based indexing for intuitive spatial reference
Temporal Axis Configuration: - X-Axis: Loop indices corresponding to simulation timesteps - Time Conversion: Actual time = loop × time_step × dump_interval_steps - Resolution: Determined by dump frequency in simulation output - Range: Defined by loop_start to loop_end parameters
Data Index Configuration: - Index 0,3,4,5: Displacement magnitude components - Index 1,2: Density and temperature data
Performance Characteristics: - Memory Usage: O(N_files × N_loops × N_chunks × N_columns) for data storage - Processing Speed: ~5-30s depending on data size and chunk count - Output Quality: Publication-ready SVG figures with high resolution - Scalability: Efficient for datasets up to ~10⁶ data points per subplot
Integration with LAMMPSKit Analysis Pipeline: - Data Reading: Uses read_displacement_data() for robust file parsing - Species Identification: Leverages extract_element_label_from_filename() - Parameter Validation: Employs config module validation functions - Workflow Compatibility: Integrates with plot_displacement_comparison()
Examples
Multi-species z-displacement time series:
>>> from lammpskit.ecellmodel.filament_layer_analysis import plot_displacement_timeseries >>> # Visualize z-direction displacement evolution for different species >>> mobility_files = ["Hf_mobility.dat", "Ta_mobility.dat", "O_mobility.dat"] >>> >>> timeseries_plots = plot_displacement_timeseries(mobility_files, ... datatype="species_mobility", ... dataindex=3, # Z-displacement ... Nchunks=50, ... loop_start=0, ... loop_end=100) >>> >>> z_displacement_fig = timeseries_plots['displacement_timeseries'] >>> print("Z-displacement time series generated for species comparison")
Temperature evolution analysis:
>>> # Analyze spatial temperature evolution during switching >>> temperature_files = ["temperature_switching.dat"] >>> >>> temp_timeseries = plot_displacement_timeseries(temperature_files, ... datatype="thermal_analysis", ... dataindex=2, # Temperature ... Nchunks=75, ... loop_start=50, ... loop_end=200, ... output_dir="./thermal_study") >>> >>> print("Temperature evolution time series analysis completed")
Lateral displacement comparison:
>>> # Compare lateral spreading under different field conditions >>> field_files = ["low_field_mobility.dat", "high_field_mobility.dat"] >>> >>> lateral_analysis = plot_displacement_timeseries(field_files, ... datatype="field_dependence", ... dataindex=4, # Lateral displacement ... Nchunks=60, ... loop_start=0, ... loop_end=150) >>> >>> lateral_fig = lateral_analysis['displacement_timeseries'] >>> print("Lateral displacement field dependence analysis completed")
Total displacement magnitude tracking:
>>> # Track overall ion migration with high spatial resolution >>> migration_files = ["ion_migration.dat"] >>> >>> total_displacement = plot_displacement_timeseries(migration_files, ... datatype="migration_analysis", ... dataindex=0, # Total displacement ... Nchunks=100, # High resolution ... loop_start=10, ... loop_end=90) >>> >>> print("High-resolution total displacement tracking completed")
Multi-condition comprehensive analysis:
>>> # Systematic analysis across multiple displacement components >>> condition_files = ["condition_A.dat", "condition_B.dat", "condition_C.dat"] >>> displacement_components = { ... 0: "total_magnitude", ... 3: "z_direction", ... 4: "lateral_spreading", ... 5: "radial_outward" ... } >>> >>> all_displacement_analyses = {} >>> for dataindex, component_name in displacement_components.items(): ... try: ... analysis_figures = plot_displacement_timeseries(condition_files, ... datatype=f"multi_condition_{component_name}", ... dataindex=dataindex, ... Nchunks=50, ... loop_start=0, ... loop_end=120, ... output_dir=f"./analysis_{component_name}") ... all_displacement_analyses[component_name] = analysis_figures ... print(f"Completed {component_name} displacement analysis") ... except Exception as e: ... print(f"Error analyzing {component_name}: {e}") >>> >>> total_figures = len(all_displacement_analyses) >>> print(f"Multi-component analysis: {total_figures} displacement types analyzed")
Fine-grained temporal resolution study:
>>> # High-resolution temporal analysis for formation dynamics >>> formation_files = ["formation_dynamics.dat"] >>> >>> # Analyze z-displacement with fine temporal and spatial resolution >>> fine_resolution = plot_displacement_timeseries(formation_files, ... datatype="formation_kinetics", ... dataindex=3, # Z-displacement ... Nchunks=80, # Fine spatial resolution ... loop_start=0, ... loop_end=500, # Extended time window ... output_dir="./formation_kinetics") >>> >>> print("Fine-grained formation kinetics analysis completed") >>> print("Available for detailed filament nucleation and growth characterization")
Density redistribution analysis:
>>> # Track mass density evolution during switching cycles >>> density_files = ["density_evolution.dat"] >>> >>> density_timeseries = plot_displacement_timeseries(density_files, ... datatype="density_redistribution", ... dataindex=1, # Density ... Nchunks=40, ... loop_start=20, ... loop_end=180) >>> >>> density_fig = density_timeseries['displacement_timeseries'] >>> print("Mass density redistribution time series generated") >>> print("Available for vacancy formation and material transport analysis")
Error handling and parameter validation:
>>> # Robust time series analysis with comprehensive validation >>> import os >>> >>> test_files = ["species1.dat", "species2.dat", "species3.dat"] >>> valid_files = [f for f in test_files if os.path.exists(f)] >>> >>> if len(valid_files) >= 1: ... try: ... # Validate dataindex range ... valid_dataindices = [0, 3, 4, 5] # Focus on displacement components ... ... for dataindex in valid_dataindices: ... try: ... component_names = {0: "total", 3: "z_direction", 4: "lateral", 5: "radial"} ... component_name = component_names[dataindex] ... ... timeseries_result = plot_displacement_timeseries(valid_files, ... datatype=f"validated_{component_name}", ... dataindex=dataindex, ... Nchunks=30, # Moderate resolution ... loop_start=0, ... loop_end=50) ... print(f"Successfully analyzed {component_name} displacement") ... except ValueError as e: ... print(f"Data error for index {dataindex}: {e}") ... except Exception as e: ... print(f"Unexpected error for index {dataindex}: {e}") ... ... except FileNotFoundError as e: ... print(f"File access error: {e}") ... else: ... print(f"No valid files found from {test_files}")
Integration with displacement comparison analysis:
>>> # Combine time series with comparative final displacement analysis >>> from lammpskit.ecellmodel.filament_layer_analysis import plot_displacement_comparison >>> >>> analysis_files = ["evolution_study.dat"] >>> species_labels = ["Evolution"] >>> >>> # Step 1: Time series analysis for detailed temporal evolution >>> timeseries_analysis = plot_displacement_timeseries(analysis_files, ... datatype="comprehensive_study", ... dataindex=3, # Z-displacement ... Nchunks=50, ... loop_start=0, ... loop_end=100) >>> >>> # Step 2: Final displacement comparison for endpoint analysis >>> comparison_analysis = plot_displacement_comparison(analysis_files, ... loop_start=0, loop_end=100, ... labels=species_labels, ... analysis_name="comprehensive_study") >>> >>> print("Comprehensive Analysis Summary:") >>> print(f" Time series plots: {len(timeseries_analysis)}") >>> print(f" Comparison plots: {len(comparison_analysis)}") >>> print(" Total analysis: Detailed temporal + final state characterization")
Advanced data interpretation and analysis:
>>> # Extract and analyze specific spatial regions from time series >>> mobility_files = ["detailed_mobility.dat"] >>> >>> # Generate time series for detailed analysis >>> detailed_timeseries = plot_displacement_timeseries(mobility_files, ... datatype="detailed_analysis", ... dataindex=3, # Z-displacement ... Nchunks=60, ... loop_start=0, ... loop_end=200) >>> >>> print("Detailed Time Series Analysis:") >>> print(" Generated multi-panel visualization for:") >>> print(" - Electrode interface regions (bottom/top rows)") >>> print(" - Oxide bulk regions (middle rows)") >>> print(" - Temporal evolution patterns (x-axis progression)") >>> print(" - Species-specific migration characteristics (color coding)") >>> >>> # Note: For quantitative analysis of time series data, >>> # use read_displacement_data() directly to access raw numerical values >>> from lammpskit.ecellmodel.filament_layer_analysis import read_displacement_data >>> raw_timeseries = read_displacement_data("detailed_mobility.dat", 0, 200) >>> print(f"Raw data available: {len(raw_timeseries)} time points, " ... f"{raw_timeseries[0].shape[0]} spatial bins")
- lammpskit.ecellmodel.filament_layer_analysis.main()[source]
Minimal demonstration of LAMMPSKit basic functionality.
For complete examples with real data, see: https://github.com/simantalahkar/lammpskit/tree/main/usage/ecellmodel