Useful Scripts & Companion Tools
This section offers external tools and scripts developed to extend functionality, enhance, or automate TipTop workflows.
Run N TipTop Simulations with Different Parameter Values
Here, we provide an example script to run N TipTop simulations with different values of two parameters:
ZenithAngle
and Seeing
. The script can easily be adapted to sweep over a single parameter or more than two.
Download the script: 📥.
Running N TipTop simulations: TIPTOP_RUN_N_SIM.py
"""
Created on Thu Jul 10 10:00:34 2025
Run N TIPTOP simulations by sweeping over different parameter values.
@author: astro-tiptop-services
"""
#%% =============================================================================
# Import necessary libraries
from tiptop.tiptop import overallSimulation
import numpy as np
import configparser
from itertools import product
import os
#%% =============================================================================
# === File Paths ===
#💡 Adapt these paths to your environment
inputDir = "../MICADO/" # Folder containing the .ini parameter file
inTag = "MICADO_SCAO" # Name of the input parameter file (without extension)
outputDir = "./" # Folder where the output FITS files will be saved
tempDir = inputDir # Temporary folder for modified .ini files
tempName = 'file_temp' # Name of the temporary file
# Construct the full path to the input and temporary config files
inputFile = os.path.join(inputDir, f"{inTag}.ini")
tempFile = os.path.join(tempDir, f"{tempName}.ini")
# Base name for output files
outTagBase = "MICADO"
#%% =============================================================================
# === Define parameters to sweep ===
# 💡 Format: (section_name, key_name, list_of_values : min, max, number of values)
param_sweep = [
("atmosphere", "Seeing", np.linspace(0.8, 2.0, 3)),
("telescope", "ZenithAngle", np.linspace(0.0, 60.0, 3)),
# Add more parameters as needed
]
#%% =============================================================================
# === Read base configuration file ===
parser = configparser.ConfigParser()
parser.optionxform = str # Keep the original case of parameter keys
parser.read(inputFile)
#%% =============================================================================
# === Build and run combinations ===
# Extract parameter names and values
param_names = [f"{sec}.{key}" for sec, key, _ in param_sweep]
param_values = [vals for _, _, vals in param_sweep]
# Generate all combinations
for combo in product(*param_values):
# Update .ini with current parameter values
for (section, key, _), value in zip(param_sweep, combo):
parser.set(section, key, str(value))
# Write updated config to temporary .ini file
with open(tempFile, "w") as configfile:
parser.write(configfile)
# Build output file tag
tag_parts = [f"{name.split('.')[-1]}{val:.2f}" for name, val in zip(param_names, combo)]
tag = f"{outTagBase}_{'_'.join(tag_parts)}"
print(f"🔄 Running simulation with: {dict(zip(param_names, combo))}")
# Run TipTop simulation
overallSimulation(
path2param=tempDir,
parametersFile=tempName,
outputDir=outputDir,
outputFile=tag,
addSrAndFwhm=True
)
Rename Output FITS Files and Keep Backups
This script renames PSF FITS files (e.g., those generated by the previous script)
to facilitate their use in other routines, while creating backups of the originals.
For example, it renames FITS files in your folder starting with the tag MICADO
to simple, easily readable names like MICADO_PSF1.fits
, MICADO_PSF2.fits
, and so on.
Download the script: 📥
Rename output PSF FITS files: TIPTOP_rename_psf_files.py
"""
Created on Thu Jul 10 14:15:45 2025
Rename output PSF FITS files sequentially for easier access and keep original copies.
@author: astro-tiptop-services
"""
#%% =============================================================================
# Import necessary libraries
import os
import shutil
#%% =============================================================================
# === Configuration ===
#💡 Adapt these paths to your environment
inputDir = "./" # Directory containing the generated FITS files
backupDir = "Original_PSFs" # Directory to store backups of original files
outputDir = "Renamed_PSFs" # Directory where renamed files will be moved (must exist)
TagBase = "MICADO" # Base name used in the output FITS filenames
# Create output and backup directories if they do not exist
os.makedirs(outputDir, exist_ok=True)
os.makedirs(backupDir, exist_ok=True)
#%% =============================================================================
# === Gather and sort relevant FITS files ===
output_files = sorted(
[f for f in os.listdir(inputDir) if f.startswith(TagBase) and f.endswith(".fits")]
)
# Check if any matching files are found
if not output_files:
print("[INFO] No matching FITS files found for renaming.")
else:
print(f"[INFO] Found {len(output_files)} FITS files to rename.")
#%% =============================================================================
# === Copy and Rename files sequentially as TagBase_PSF1.fits, MICADO_PSF2.fits, etc. ===
for i, fname in enumerate(output_files):
src = os.path.join(inputDir, fname)
dst = os.path.join(outputDir, f"{TagBase}_PSF{i+1}.fits")
backup = os.path.join(backupDir, fname)
try:
# Copy the original file to the backup folder
shutil.copy2(src, backup)
# Rename (and move) the file to the new destination
os.rename(src, dst)
print(f"[INFO] Renamed: {fname} → {TagBase}_PSF{i+1}.fits (Backup saved)")
except PermissionError:
print(f"[⚠️ WARNING] File in use and cannot be renamed: {fname}")
except Exception as e:
print(f"[❌ ERROR] Failed to rename {fname} → {e}")
#%% =============================================================================
print(f"\n✅ Process complete. {len(output_files)} files renamed and backed up.")
Sum rotated PSF
This script combines multiple PSFs into a single image by rotating each individual PSF
by a specified field rotation angle before summation.
Used together with the two previous scripts, this process can be
especially useful, for example, when simulating long integrations to reproduce the impact of pupil rotation and field rotation on the final image.
✏️ Note: You need to manually define the list of field rotation angles in the script — one angle per PSF FITS file.
Feel free to adapt this script to suit TipTop simulations involving multiple science sources.
The example provided here illustrates its use with the input parameter file
MICADO.ini.
Download the script: 📥
Sum rotated AO PSFs: TIPTOP_rotated_PSF_sum.py
"""
Created on Thu Jul 10 15:01:55 2025
Sum rotated PSFs.
Each PSF is rotated by a specified angle before summation.
@author: astro-tiptop-services
"""
#%% =============================================================================
# Import necessary libraries
import os
import numpy as np
from scipy.ndimage import rotate
from astropy.io import fits
#%% =============================================================================
# === Configuration ===
inputDir = "Renamed_PSFs" # Folder where the PSF FITS files are stored
TagBase = "MICADO" # Base tag for the PSF files
outputFile = "PSF_sum.fits" # Filename for the combined output
# Rotation angles (degrees) applied to each PSF before summation
rotation_angles = [...] # 💡 To be defined
N = len(rotation_angles) # Should match the number of PSF files available
#%% =============================================================================
# === Load and combine rotated PSFs ===
first_filename = os.path.join(inputDir, f"{TagBase}_PSF1.fits")
if not os.path.exists(first_filename):
raise FileNotFoundError(f"Base file {first_filename} is missing. Check input directory.")
first_img = fits.getdata(first_filename)
accumulated = np.zeros_like(first_img, dtype=float) # Accumulator for summing rotated PSFs
valid_count = 0 # Count how many files were found and used
# Loop over all images and angles
for i in range(N):
filename = os.path.join(inputDir, f'{TagBase}_PSF{i+1}.fits')
# Check if file exists
if not os.path.exists(filename):
print(f"[WARNING] Missing file: {filename}")
continue
print(f"[INFO] Loading file: {filename}")
img = fits.getdata(filename)
# Rotate the image by the corresponding angle
rotated = rotate(img, angle=rotation_angles[i], reshape=False)
# Accumulate the rotated image
accumulated += rotated
valid_count += 1
#%% =============================================================================
# === Normalize and save result ===
if valid_count > 0:
accumulated /= valid_count
# Save PSF sum to a FITS file, overwrite if exists
fits.writeto(outputFile, accumulated, overwrite=True)
print(f"[INFO] Combined PSF saved to: {outputFile}")
else:
print("[ERROR] No valid PSF files found. No output generated.")