Espectros de emisión en OghmaNano
Un espectro de emisión describe cómo se distribuye la luz emitida por un material en función de la longitud de onda. OghmaNano almacena estos espectros como parte de su base de datos de materiales, donde se pueden consultar, importar o actualizar. Esta página explica qué representa un espectro de emisión, cómo se almacena e importa, y, por último, cómo generar un espectro aproximado utilizando Python cuando no se dispone de datos medidos.
1. ¿Qué es un espectro de emisión?
Cuando los electrones y las «huecos» se recombinan mediante emisión radiativa, se emiten fotones en un rango de energías, en lugar de en una longitud de onda específica. Un espectro de emisión describe la intensidad relativa de esta luz en función de la longitud de onda. Puede obtenerse a partir de un experimento, como la fotoluminiscencia (PL) o la electroluminiscencia (EL), o de un modelo teórico. El espectro no es lo mismo que el coeficiente de absorción o el índice de refracción del material.
La forma del espectro depende de la estructura electrónica del semiconductor, la temperatura, la composición y, en un dispositivo real, de la concentración de portadores y de los efectos ópticos. Un espectro de emisión normalizado representa la distribución relativa de las longitudes de onda, y no la potencia óptica absoluta ni la eficiencia del LED.
2. Espectros de emisión en la base de datos de materiales.
Cada material tiene su propio directorio en la base de datos de materiales de OghmaNano. Cuando se dispone de datos de emisión, se almacenan en un archivo denominado emission.csv, junto con otros archivos como alpha.csv y n.csv. Por ejemplo, una carpeta de materiales puede contener:
materials/
GaAs/
emission.csv
alpha.csv
n.csv
La base de datos de materiales utilizada por las simulaciones se encuentra en el directorio oghma_local/materials del usuario. Para obtener una descripción de la estructura del directorio y los formatos de los archivos de materiales, consulte Base de datos de materiales: Parte C – Cómo se almacenan los materiales. Para obtener una introducción sobre cómo explorar y editar los materiales, consulte Base de datos de materiales: Parte A.
Un archivo emission.csv contiene la longitud de onda en metros y la intensidad de emisión relativa en unidades arbitrarias. Los archivos con formato OghmaNano también contienen una cabecera de metadatos; el valor y_len de la cabecera indica el número de pares de datos de longitud de onda e intensidad que siguen a continuación.
3. Importación de espectros de emisión medidos.
Cuando se dispone de datos de fotoluminiscencia (PL) o electroluminiscencia (EL), es preferible utilizarlos en lugar de un espectro generado de forma aproximada, siempre y cuando las mediciones sean adecuadas para el material y el dispositivo previsto. Abra el material correspondiente en la base de datos de materiales, seleccione su espectro de emisión y utilice la función de importación de datos para importar la curva medida. El proceso general para importar un conjunto de datos de dos columnas, seleccionar las unidades de longitud de onda y revisar los valores convertidos se describe en Base de datos de materiales: Parte A – Importación de datos.
Unidades: Los valores de longitud de onda almacenados se expresan en metros, aunque el gráfico se muestre en nanómetros. Utilice la intensidad normalizada (u.a.) para los datos representativos de la forma de la emisión. Verifique los metadatos del archivo y el número de puntos después de importar o generar un espectro.
4. Estimación de los espectros de emisión cuando no se dispone de datos medidos.
Si no se dispone de un espectro medido adecuado, el script de Python que se muestra a continuación puede generar un espectro de emisión normalizado temporal para semiconductores. Utiliza un modelo térmico simplificado de transición de banda a banda para materiales con banda prohibida directa:
\(S_E(E) \propto \sqrt{E-E_g}\,\exp[-(E-E_g)/(k_B T)]\) para \(E \geq E_g\).
Aquí, \(E_g\) representa la banda prohibida y \(T\) la temperatura. El script convierte el perfil de emisión calculado en el dominio de la energía a intensidad por unidad de longitud de onda, normaliza su valor máximo a uno y escribe emission.csv en el mismo formato de metadatos y datos que utiliza OghmaNano. De forma predeterminada, crea 2036 puntos que relacionan la longitud de onda con la intensidad a 300 K.
El script proporcionado crea un directorio para cada una de las 29 entradas distintas de semiconductores, que incluyen GaAs, AlGaAs, GaN, InGaN, InP e InGaAs. Los valores de la banda prohibida de los materiales son valores iniciales representativos; para las aleaciones cuya composición influye en sus propiedades, el espectro generado representa solo una composición nominal.
¿Cuándo es apropiado utilizar esto? Emplee estas curvas generadas como ejemplos de perfiles de emisión general, especialmente para probar la cadena de procesamiento de emisiones o hasta que se disponga de datos mejores o de un cálculo basado en la estructura de bandas. No son espectros medidos y no deben presentarse como predicciones cuantitativas de un LED en particular.
El modelo no incluye el confinamiento cuántico en pozos, el desorden de la aleación, las características excitónicas, la emisión debida a defectos, la tensión ni el acoplamiento óptico. Es especialmente inadecuado para las transiciones de banda indirecta, incluido el caso del GaP: la curva que genera representa una transición directa ilustrativa en lugar de la emisión convencional de un LED de GaP.
5. Ejemplo: generación de espectros de emisión con Python.
Guarde el código que se muestra a continuación con el nombre generate_emission_spectra.py y ejecútelo:
python3 generate_emission_spectra.py
El directorio de salida predeterminado es emission_generated. Para seleccionar otro directorio o cambiar el número de puntos:
python3 generate_emission_spectra.py --output my_emission_database --points 2036
Para modificar la temperatura de funcionamiento o utilizar sus propios valores de banda prohibida:
python3 generate_emission_spectra.py --temperature 300 --bandgap-file bandgaps.json
Un ejemplo de archivo bandgaps.json es:
{
"GaAs": 1.424,
"AlGaAs": 1.80,
"InGaN": 2.75
}
Examine los espectros resultantes antes de colocar los archivos emission.csv correspondientes en las carpetas de los materiales. Los valores de los espacios de banda en este script se especifican manualmente y son valores representativos; el script no utiliza los scripts de materiales de Lua para calcularlos.
6. Código fuente completo de Python.
A continuación, se presenta el código completo del generador, que utiliza únicamente la biblioteca estándar de Python. Puede copiar el código en un archivo de Python local para utilizarlo.
Muestre el generador completo del espectro de emisión de Python.
#!/usr/bin/env python3
"""Generate illustrative bulk band-edge emission.csv files for OghmaNano.
No external Python dependencies. Each directory contains a 2036-point
OghmaNano-format emission.csv. Spectra are normalised per wavelength and
are NOT measured or k.p-calculated spectra. Alloy bandgaps below represent
one nominal composition only; edit MATERIALS or use --bandgap-file.
"""
import argparse
import json
import math
from pathlib import Path
# Representative, approximate 300 K bulk DIRECT-transition gaps (eV).
# NOT universal values for alloys. Entries such as AlInP and AlInAs may be
# indirect at common compositions: their curves are illustrative ONLY.
# One directory per unique material; InSb was duplicated in the supplied list.
MATERIALS = {
"InP": (1.344, "InP/InP.lua"),
"GaP": (2.78, "GaP/GaP.lua"), # direct transition; GaP fundamental gap is indirect!
"AlGaInP": (2.05, "AlGaInP.lua"),
"GaAs": (1.424, "GaAs/GaAs.lua"),
"AlInP": (2.35, "AlInP.lua"), # illustrative; verify direct-gap composition
"InSb": (0.17, "InSb/InSb.lua"),
"GaSb": (0.726, "GaSb/GaSb.lua"),
"CdS": (2.42, "CdS/CdS.lua"),
"GaN": (3.40, "GaN/GaN.lua"),
"AlN": (6.03, "AlN.lua"),
"ZnS": (3.68, "ZnS.lua"),
"InAs": (0.354, "InAs/InAs.lua"),
"AlGaAs": (1.80, "AlGaAs/AlGaAs.lua"), # nominal Al x~0.3
"InAsP": (0.95, "InAsP.lua"),
"InGaAsP": (0.80, "InGaAsP.lua"),
"AlInAs": (1.45, "AlInAs.lua"), # composition-dependent and possibly indirect
"GaAsP": (1.90, "GaAsP.lua"),
"InGaAs": (0.75, "InGaAs/InGaAs.lua"), # nominal In0.53Ga0.47As
"ZnTe": (2.26, "ZnTe.lua"),
"InGaN": (2.75, "InGaN.lua"),
"GaInNAs": (0.95, "GaInNAs.lua"),
"InGaP": (1.90, "InGaP.lua"),
"GaInAs": (0.75, "GaInAs.lua"),
"InN": (0.69, "InN.lua"),
"AlGaN": (4.43, "AlGaN.lua"),
"AlGaInAs": (0.80, "AlGaInAs.lua"),
"CdSe": (1.74, "CdSe.lua"),
"ZnSe": (2.70, "ZnSe.lua"),
"CdTe": (1.50, "CdTe/CdTe.lua"),
}
HC_EV_NM = 1239.8419843320026
KB_EV_K = 8.617333262145e-5
def make_spectrum(eg, temp, points):
"""3D nondegenerate direct-gap model, intensity per unit wavelength.
S_E(E) ~ sqrt(E-Eg)*exp(-(E-Eg)/kT), E>=Eg.
Convert using dE/dlambda ~ 1/lambda**2, then peak-normalise.
Span covers Eg to Eg + max(0.35 eV, 12 kT); wavelength increases.
"""
kt = KB_EV_K * temp
upper = eg + max(0.35, 12.0 * kt)
lo_nm = HC_EV_NM / upper
hi_nm = HC_EV_NM / eg
values = []
for i in range(points):
wavelength_nm = lo_nm + (hi_nm - lo_nm) * i / (points - 1)
e = HC_EV_NM / wavelength_nm
de = max(0.0, e - eg)
intensity = math.sqrt(de) * math.exp(-de / kt) / (wavelength_nm * wavelength_nm)
values.append((wavelength_nm * 1e-9, intensity))
top = max(v for _, v in values)
return [(lam, value / top) for lam, value in values]
def generate(out, material, eg, lua_path, temp, points):
folder = out / material
folder.mkdir(parents=True, exist_ok=True)
metadata = {
"title": "Illustrative band-edge emission of " + material,
"type": "xy",
"y_label": "Wavelength",
"data_label": "Intensity",
"y_units": "nm",
"y_mul": 1e9,
"data_units": "au",
"icon": "spectra_file",
"time ": 0.0,
"Vexternal": 0.0,
"x_len": 1,
"y_len": points,
"z_len": 1,
"cols": "yd",
}
values = make_spectrum(eg, temp, points)
path = folder / "emission.csv"
with path.open("w", encoding="utf-8", newline="\n") as f:
f.write("#oghma_csv " + json.dumps(metadata, ensure_ascii=False, separators=(",", ":")) + "*\n")
for lam, value in values:
f.write(f"{lam:.12e}\t{value:.12e}\n")
return path
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, default=Path("emission_generated"))
parser.add_argument("--points", type=int, default=2036)
parser.add_argument("--temperature", type=float, default=300.0)
parser.add_argument("--bandgap-file", type=Path,
help='Optional JSON map e.g. {"GaAs":1.424,"AlGaAs":1.80} to override nominal gaps')
args = parser.parse_args()
if args.points < 3 or args.temperature <= 0:
parser.error("--points must be >=3 and --temperature must be positive")
gap_overrides = {}
if args.bandgap_file:
gap_overrides = json.loads(args.bandgap_file.read_text(encoding="utf-8"))
unknown = set(gap_overrides) - set(MATERIALS)
if unknown:
parser.error("Unknown material(s): " + ", ".join(sorted(unknown)))
args.output.mkdir(parents=True, exist_ok=True)
for material, (nominal_eg, lua_path) in MATERIALS.items():
eg = float(gap_overrides.get(material, nominal_eg))
if not math.isfinite(eg) or eg <= 0:
parser.error(f"Invalid bandgap for {material}: {eg}")
path = generate(args.output, material, eg, lua_path, args.temperature, args.points)
print(f"{path} Eg={eg:g} eV (source: {lua_path})")
print(f"Generated {len(MATERIALS)} unique material directories with {args.points} points each.")
print("CAUTION: nominal/illustrative bulk direct-transition profiles; validate alloy composition and gap type.")
if __name__ == "__main__":
main()
Nota: Los espectros de emisión generados son una solución provisional. Para simulaciones cuantitativas de dispositivos, es preferible utilizar datos de emisión medidos o espectros calculados con un modelo electrónico de estructura de bandas y de transición óptica adecuado, como un cálculo basado en el modelo k·p.