Home Examples Screenshots User manual Bluesky logo YouTube 中文
OghmaNano Simulate organic/Perovskite Solar Cells, OFETs, and OLEDs DOWNLOAD

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:

  1. Creates the directory (if it does not already exist).
  2. Copies the current sim.json into that directory.
  3. Edits the copied sim.json to set the target mobility.
  4. Changes the working directory to that directory.
  5. 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:

  1. Loads sim_info.dat as JSON.
  2. Extracts Voc.
  3. Computes \(1/\mu\) using the mobility associated with that directory.
  4. Writes a small summary file (voc_vs_inv_mobility.dat).
  5. 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.

Python scripting of a perovskite solar cell simulation in OghmaNano