Tech notes/using adda with python
<h2>How to simulate the plasmon resonance using discrete dipole approximation</h2>
<p>Many years ago, I used ADDA to calculate plasmon resonances of gold nanorods while they were etched by potassium cyanide (DOI: <a href="https://doi.org/10.1039/C6CP01679K">https://doi.org/10.1039/C6CP01679K</a>). I wrote the code in Python and is available here (<a href="https://github.com/aquilesC/plasmon_adda">https://github.com/aquilesC/plasmon_adda</a>). </p>
<p>This tutorial explains the architecture and design decisions behind the <code>plasmon-adda</code> package. It is intended for users who want to understand how to automate ADDA simulations using Python.</p>
<h2>Architecture Overview</h2>
<p>The project is structured as a standard Python package to ensure modularity, ease of installation, and reusability.</p>
<pre class="codehilite"><code>plasmon_adda/
├── src/
│ └── plasmon_adda/
│ ├── __init__.py # Package marker
│ ├── cli.py # Command-Line Interface entry points
│ ├── simulation.py # Core logic for single simulations
│ ├── batch.py # Logic for parallel batch processing
│ ├── plotting.py # Logic for data visualization
│ └── indexes.py # Material properties (Refractive Indices)
├── pyproject.toml # Package metadata and dependencies
└── README.md # General usage instructions
</code></pre>
<h2>Core Components</h2>
<h3>1. Material Properties (<code>indexes.py</code>)</h3>
<p><strong>Design Goal</strong>: Encapsulate complex refractive index data handling.</p>
<p>Instead of hardcoding values or parsing files repeatedly, we use a <code>RefractiveIndex</code> base class. This class handles:</p>
<ul>
<li>Reading raw data files (Wavelength vs Real/Imaginary Index).</li>
<li>Interpolating the data using <code>scipy.interpolate</code> to allow querying the refractive index at <em>any</em> arbitrary wavelength.</li>
</ul>
<p><strong>Extensibility</strong>: To add a new material (e.g., Silver), you simply subclass <code>RefractiveIndex</code> and point it to the new data file.</p>
<pre class="codehilite"><code class="language-python">class SilverIndex(RefractiveIndex):
def __init__(self):
super().__init__('METALS_Silver_Johnson.txt')
</code></pre>
<h3>2. Adaptive Simulation (<code>simulation.py</code>)</h3>
<p><strong>Design Goal</strong>: Efficiently resolve spectral peaks without wasting computation on flat regions.</p>
<p>The <code>run_simulation</code> function implements an adaptive step-size algorithm ("Smart Search"):
1. <strong>Initialization</strong>: It starts at a given wavelength (e.g., 550nm).
2. <strong>Step Adjustment</strong>: It calculates the slope ("difference") of the Extinction Efficiency (<code>Q_ext</code>) between previous steps.
- <strong>Fast Change (Peak)</strong>: If the slope is steep, the step size decreases (down to 2nm) to capture fine details.
- <strong>Slow Change</strong>: If the slope is flat, the step size increases (up to 15nm) to speed up the process.
3. <strong>Peak Detection</strong>: It tracks if it has passed a peak (slope sign change) and stops the simulation 100nm after the peak is confirmed.</p>
<p><strong>ADDA Integration</strong>:
We use the <code>subprocess</code> module to call the binary <code>adda</code> tool. This separates the physics engine (ADDA) from the control logic (Python).
- <strong>Command Construction</strong>: Arguments like <code>-shape</code>, <code>-size</code>, and <code>-m</code> (refractive index) are dynamically built.
- <strong>Output Parsing</strong>: We capture standard output (<code>stdout</code>) and robustly parse the key metrics (<code>Q_ext</code>, <code>C_ext</code>, etc.).</p>
<h3>3. Parallel Batch Processing (<code>batch.py</code>)</h3>
<p><strong>Design Goal</strong>: Scale simulations across multiple CPU cores.</p>
<p>Since ADDA simulations for different geometries are independent, they are "embarrassingly parallel."
- We use <code>concurrent.futures.ProcessPoolExecutor</code> to spawn a pool of worker processes.
- Each worker runs an instance of <code>run_simulation</code> for a specific configuration (e.g., a specific Length).
- This drastically reduces total computation time compared to a sequential loop.</p>
<h3>4. Command-Line Interface (<code>cli.py</code>)</h3>
<p><strong>Design Goal</strong>: Make the tools accessible from the terminal without modifying code.</p>
<p>We use <code>argparse</code> to define user-friendly commands.
- <strong>Entry Points</strong>: <code>pyproject.toml</code> maps these functions to commands like <code>plasmon-run</code>.
- This allows you to run simulations on a remote server or cluster just by typing a command, making it easy to script and automate.</p>
<h2>How to Extend</h2>
<p>If you want to simulate different shapes or materials:
1. <strong>Modify <code>indexes.py</code></strong>: Add your new material class.
2. <strong>Update <code>simulation.py</code></strong>:
- Change <code>AuIndex()</code> to your new material class.
- Update the <code>cmd</code> list to use different shapes (e.g., change <code>-shape capsule</code> to <code>-shape sphere</code>).</p>
<h2>Conclusion</h2>
<p>This architecture separates concerns: <strong>Data</strong> (indexes), <strong>Logic</strong> (simulation), <strong>Scaling</strong> (batch), and <strong>Interface</strong> (CLI). This makes the code easier to test, debug, and extend.</p>
Backlinks
These are the other notes that link to this one.
Nothing links here, how did you reach this page then?
Comment
Share your thoughts on this note. Comments are not public, they are
messages sent directly to my inbox.