Source code for metabox.metrics
"""
This file contains the functions to calculate the metrics of the optical system.
"""
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
from typing import List
import numpy as np
import tensorflow as tf
from scipy import special
from metabox import propagation
[docs]def get_ideal_mtf_volume(
field_props: propagation.FieldProperties,
focal_length: float,
) -> np.ndarray:
"""Function to calculate the volume under the ideal MTF surface.
Args:
field_props (propagation.FieldProperties): the field properties.
focal_length: the focal length of the lens in meters.
Returns:
np.ndarray: the volume under the ideal MTF surface.
"""
lens_radius = field_props.n_pixels * field_props.period / 2 / field_props.upsampling
mtf_norm_arr = []
for wavelength in field_props.wavelength:
sampling_radius = lens_radius
x_max = (
2
* np.pi
/ wavelength
* sampling_radius
* np.sin(np.arctan(lens_radius / focal_length))
)
# The x-values we want
x = np.linspace(-x_max, x_max, field_props.n_pixels)
y = np.linspace(-x_max, x_max, field_props.n_pixels)
xv, yv = np.meshgrid(x, y)
# The normalized intensity
r = np.sqrt(xv**2 + yv**2)
intensity = 4 * (special.j1(r) / (r)) ** 2
# calculate the normalized intensity
total_energy = np.sum(intensity)
norm_intensity = intensity / total_energy
mtf = np.abs(np.fft.fftshift(np.fft.fft2(norm_intensity)))
# find the max in each MTF (for each angle)
mtf_max = np.amax(mtf)
mtf_norm = mtf / mtf_max
mtf_norm_arr.append(np.mean(mtf_norm))
return np.array(mtf_norm_arr)
[docs]def get_max_intensity(field: propagation.Field2D) -> tf.Tensor:
"""Defines function that returns the maximum intensity of the field.
Args:
field (propagation.Field2D): the field to calculate the maximum intensity of.
Returns:
tf.Tensor: The maximum intensity of the field.
"""
tensor = field.tensor
intensity = tf.math.abs(tensor) ** 2
return tf.math.reduce_max(intensity, axis=[-1, -2])
[docs]def get_mtf_volume(field: propagation.Field2D, use_log: bool = False) -> tf.Tensor:
"""Returns the volume under the MTF surface.
Args:
field (propagation.Field2D): the field to calculate the Strehl ratio of.
use_log (bool): use the log of the Strehl ratio for each sampling.
By enabling this, the Strehl ratio will be more uniform after the optimization.
Returns:
tf.Tensor: the Strehl ratio of the field.
"""
psf_tensor = field.get_intensity()
# reduce sum in pixelsX and pixelsY
norm_factor = tf.reduce_sum(psf_tensor, axis=[-2, -1], keepdims=True)
# norm_factor shape: [lambda_base, theta_base*phi_base, 1, 1]
norm_factor = tf.reshape(norm_factor, [len(field.wavelength), -1, 1, 1])
norm_psf = (
psf_tensor / norm_factor
) # norm_psf: [lambda_base, theta_base*phi_base, pixelsX, pixelsY]
norm_psf = tf.cast(norm_psf, dtype=tf.complex64)
# fft2d: Fourier transform over the inner-most 2 dimensions of input (the end of the shape array)
# [lambda_base, theta_base*phi_base, kX, kY]
mtf = tf.abs(tf.signal.fftshift(tf.signal.fft2d(norm_psf)))
# find the max in each MTF (for each angle)
mtf_max = tf.math.reduce_max(mtf, axis=[-1, -2])
# reshape for tensor division. mtf_max: [theta_base, phi_base, 1, 1]
mtf_max = tf.reshape(mtf_max, [len(field.wavelength), -1, 1, 1])
mtf_norm = mtf / mtf_max
volume = tf.math.reduce_mean(mtf_norm, axis=[-1, -2])
if use_log:
volume = tf.math.log(volume)
volume = tf.math.reduce_mean(volume)
return volume
[docs]def get_center_intensity(
field: propagation.Field2D, use_log: bool = False
) -> tf.Tensor:
"""Returns the center intensity of the field.
Args:
field (propagation.Field2D): the field to calculate the Strehl ratio of.
use_log (bool): use the log of the intensity for each sampling.
Returns:
tf.Tensor: the center intensity of the field.
"""
psf_tensor = field.get_intensity()
center_intensity = psf_tensor[:, :, field.n_pixels // 2, field.n_pixels // 2]
return center_intensity