Python scripting
1. Introduction
In OghmaNano, Python scripting works by directly editing the simulation configuration files on disk
and then invoking the simulation engine (oghma_core.exe) to run the model.
This section describes this workflow in detail.
2. Running simulation files with Python
An OghmaNano simulation is defined entirely by a single JSON configuration file,
sim.json.
This file contains the complete state of the simulation, including the device structure,
material parameters, numerical settings, and output configuration.
In many cases, sim.json already exists as a normal file inside the simulation directory.
When driving OghmaNano from Python, the script reads sim.json into memory using Python’s
standard json library, modifies one or more parameters, and writes the updated JSON
back to disk.
Crucially, the Python script must be executed with its working directory set to the
simulation directory — that is, the directory containing sim.json (and
sim.oghma).
This is the directory from which oghma_core.exe is launched.
Once the JSON file has been updated, the script launches oghma_core.exe
from that same directory.
The simulation engine always reads its input files from the current working directory,
so if the working directory is incorrect, the simulation will fail or run the wrong model.
This separation of responsibilities is intentional and explicit: Python is responsible for defining what changes in the simulation, while OghmaNano is responsible for running the physics using the files present in the current directory.
The example below demonstrates this process by loading sim.json, modifying the carrier
mobility of the first device layer, writing the updated configuration back to disk, and then
executing the simulation from the simulation directory.
import json
import os
import sys
f=open('sim.json') #open the sim.json file
lines=f.readlines()
f.close()
lines="".join(lines) #convert the text to a python json object
data = json.loads(lines)
#Edit a value (use firefox as a json viewer
# to help you figure out which value to edit)
# this time we are editing the mobility of layer 1
data['epitaxy']['segment1']['shape_dos']['mue_y']=1.0
#convert the json object back to a string
jstr = json.dumps(data, sort_keys=False, indent='\t')
#write it back to disk
f=open('sim.json',"w")
f.write(jstr)
f.close()
#run the simulation using oghma_core
os.system("oghma_core.exe")
If the simulation in sim.json is set up to run a J–V curve, OghmaNano will write output files into the
simulation directory containing quantities such as PCE, fill factor, \(J_{sc}\) and
\(V_{oc}\).
The example below shows how to read a JSON output file and append the value of \(V_{oc}\) to a separate text file.
f=open('sim_info.dat')
lines=f.readlines()
f.close()
lines="".join(lines)
data = json.loads(lines)
f=open('out.dat',"a")
f.write(str(data["Voc"])+"\n");
f.close()
3. More complex simulations
In many scripting workflows you will want to run the same base simulation multiple times with different parameter values and keep each run isolated in its own directory. This is the simplest way to keep outputs clean and to avoid accidentally overwriting results.
The example below creates a set of directories corresponding to four mobilities
(1e-5, 1e-6, 1e-7, 1e-8). For each directory it:
- Creates the directory (if it does not already exist).
- Copies the current
sim.jsoninto that directory. - Edits the copied
sim.jsonto set the target mobility. - Changes the working directory to that directory.
- Runs the solver in that directory, producing outputs local to that run.
This pattern is the basis of batch scripting in OghmaNano: one directory per run, one configuration file per run, and a clean output folder for each parameter value.
import json
import os
import shutil
# The script must be started in the base simulation directory
# (i.e. the directory containing the reference sim.json).
base_dir = os.getcwd()
mobilities = [1e-5, 1e-6, 1e-7, 1e-8]
for mu in mobilities:
# Create a directory name that is easy to read and sorts nicely
run_dir = os.path.join(base_dir, f"mu_{mu:.0e}")
os.makedirs(run_dir, exist_ok=True)
# Copy the base sim.json into the run directory
src_sim = os.path.join(base_dir, "sim.json")
dst_sim = os.path.join(run_dir, "sim.json")
shutil.copyfile(src_sim, dst_sim)
# Load the copied sim.json and edit the mobility in THAT copy
f = open(dst_sim)
lines = f.readlines()
f.close()
lines = "".join(lines)
data = json.loads(lines)
# Edit a value: set the mobility for the first device layer
data['epitaxy']['segment1']['shape_dos']['mue_y'] = mu
jstr = json.dumps(data, sort_keys=False, indent='\t')
f = open(dst_sim, "w")
f.write(jstr)
f.close()
# Change into the run directory and execute the solver there
os.chdir(run_dir)
# If your command is "augment.nano" instead, replace the string below.
os.system("oghma_core.exe")
# Return to the base directory for the next run
os.chdir(base_dir)
After running, each directory will contain the outputs corresponding to that mobility value. In the next section we will extend this script to collect key results from each run and aggregate them into a single summary file.
Plotting \(V_{oc}\) against inverse mobility
Once you have run a batch of simulations in separate directories, the next step is usually to extract a key figure of
merit from each run and visualize the trend. A common example is to read the open-circuit voltage
\(V_{oc}\) from the JSON output file (sim_info.dat) and plot it against
inverse mobility \(1/\mu\).
The script below scans a set of run directories (for example mu_1e-05, mu_1e-06, ...)
and for each directory:
- Loads
sim_info.datas JSON. - Extracts
Voc. - Computes \(1/\mu\) using the mobility associated with that directory.
- Writes a small summary file (
voc_vs_inv_mobility.dat). - Plots \(V_{oc}\) vs \(1/\mu\).
import json
import os
# Optional: plotting (requires matplotlib)
import matplotlib.pyplot as plt
base_dir = os.getcwd()
# Mobilities must match the run directories you generated earlier
mobilities = [1e-5, 1e-6, 1e-7, 1e-8]
inv_mu = []
vocs = []
for mu in mobilities:
run_dir = os.path.join(base_dir, f"mu_{mu:.0e}")
info_path = os.path.join(run_dir, "sim_info.dat")
# Read the JSON output file and extract Voc
f = open(info_path)
lines = f.readlines()
f.close()
lines = "".join(lines)
data = json.loads(lines)
voc = data["Voc"]
inv_mu.append(1.0 / mu)
vocs.append(voc)
# Write a small summary table to disk
out = open("voc_vs_inv_mobility.dat", "w")
out.write("# inv_mobility(1/mu) Voc(V)\n")
for x, y in zip(inv_mu, vocs):
out.write(f"{x:.6e} {y}\n")
out.close()
# Plot Voc against inverse mobility
plt.plot(inv_mu, vocs, "o-")
plt.xlabel("Inverse mobility (1/μ)")
plt.ylabel("Open-circuit voltage Voc (V)")
plt.title("Voc vs inverse mobility")
plt.grid(True)
plt.tight_layout()
plt.show()
📺 Related video
The video below demonstrates how OghmaNano can be driven using Python scripting.
Note: The API and internal scripting interface shown in the video are now deprecated and are no longer
shipped, as they proved difficult to maintain. The current and recommended workflow is to use an external Python
script to edit the JSON simulation files directly and then invoke oghma_core.exe. This approach is
simpler, more robust, and easier to maintain. You may still find the video useful for understanding the overall workflow and typical use cases, but for the
up-to-date method please follow the steps described above.