lammpskit.ecellmodel.plot_atomic_charge_distribution
- lammpskit.ecellmodel.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()))