Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Huygens MTF #55

Merged
merged 27 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e26e909
Adding analysis method for Huygens MTF from ZOSAPI.
noahrbn Dec 10, 2023
cab9bb5
Updating CHANGELOG.MD.
noahrbn Dec 10, 2023
9cfab21
Correct PR number
crnh Dec 11, 2023
d4837da
Changes to formatting by lintblack
noahrbn Dec 11, 2023
d2b2597
Reformatting for ruff lint.
noahrbn Dec 11, 2023
9383b8c
Adding unit testing and reference data for huygens_mtf analysis.
noahrbn Dec 11, 2023
5255bc4
Apply suggestions from code review relating to assert_frame_equal
noahrbn Dec 13, 2023
e400943
Changing naming of method variables.
noahrbn Dec 15, 2023
06c8dfa
Forgot maximum_frequency name change.
noahrbn Dec 15, 2023
7fb3f88
Remove bug fix for mtftype, unnecessary for huygens_mtf.
noahrbn Dec 15, 2023
ee11f8c
Adding requested variable name changes to unit tests and test referen…
noahrbn Dec 15, 2023
91ca6cf
Adding additional unit tests for huygens_mtf.
noahrbn Dec 15, 2023
30036dc
Forgot to change variable names in test specification
noahrbn Dec 15, 2023
b3b1de5
Formatting changes made by black
noahrbn Dec 15, 2023
c7f13ae
Merge branch 'huygensmtf' of github.com:noahrbn/ZOSPy into huygensmtf
noahrbn Dec 15, 2023
364d73b
Formatting changes
noahrbn Dec 15, 2023
1845b7a
Updating docstring with leading space before colon.
noahrbn Dec 15, 2023
ef9f57c
Clarifying documentation of maximum_frequency in huygens_mtf method.
noahrbn Dec 15, 2023
6b1892a
Rename test_can_run_fft_through_focus_mtf to test_can_run_huygens_mtf
crnh Dec 16, 2023
d566fe3
Adding test reference data specification (didn't get committed before…
noahrbn Dec 16, 2023
34e32c4
Merge branch 'huygensmtf' of github.com:noahrbn/ZOSPy into huygensmtf
noahrbn Dec 16, 2023
b5a2431
Typo fix in mtf.py
noahrbn Dec 16, 2023
8c1d3e6
Add test reference data for OpticStudio 23 R2.1
crnh Dec 18, 2023
8aed1f8
Merge branch 'main' into huygensmtf
crnh Dec 18, 2023
9676e42
added test reference data for version 20.3.2
LucVV Dec 18, 2023
000e6c5
added function test_huygens_mtf_matches_reference_data
LucVV Dec 18, 2023
bd5a1b7
Add reference data for OpticStudio 23.1.0
crnh Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
with one exception: small features that only simplify access to certain parts of the
ZOS-API can also be added in patch releases.

## [Unreleased]

### Added

- MTF analysis: `huygens_mtf` (#55)

## [[1.1.1]](https://github.com/MREYE-LUMC/ZOSPy/releases/tag/v1.1.1) - 2023-09-25

### Added
Expand Down
11 changes: 11 additions & 0 deletions scripts/generate_test_reference_data/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ tests:
frequency: 3
numberofsteps: 10
mtftype: Imaginary
- model: simple_system
analysis: mtf.huygens_mtf
file: test_mtf.py
test: test_huygens_mtf_returns_correct_result
parametrized: [pupil_sampling, image_sampling, image_delta, mtftype, maximum_frequency]
parameters:
- pupil_sampling: 64x64
image_sampling: 64x64
image_delta: 0.0
mtftype: Modulation
maximum_frequency: 150.0
- model: polarized_system
analysis: polarization.polarization_pupil_map
file: test_polarization.py
Expand Down
42 changes: 41 additions & 1 deletion tests/analyses/test_mtf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import numpy as np
import pytest
from pandas.testing import assert_frame_equal

from zospy.analyses.mtf import fft_through_focus_mtf, fft_through_focus_mtf_fromcfg
from zospy.analyses.mtf import (
fft_through_focus_mtf,
fft_through_focus_mtf_fromcfg,
huygens_mtf,
)

_FFT_THROUGH_FOCUS_MTF_MTFTYPE_EXPECTED_RETURN = {
# The expected return does not match constants.Analysis.Settings.Mtf.MtfTypes for fft_through_focus_mtf
Expand Down Expand Up @@ -86,3 +91,38 @@ def test_fft_through_focus_mtf_sets_mtftype_correctly(self, simple_system, mtfty
)

assert result.Settings["Type"] == _FFT_THROUGH_FOCUS_MTF_MTFTYPE_EXPECTED_RETURN[mtftype]


class TestHuygensMTF:
def test_can_run_fft_through_focus_mtf(self, simple_system):
result = huygens_mtf(simple_system)

assert result.Data is not None

def test_to_json(self, simple_system):
result = huygens_mtf(simple_system)

assert result.from_json(result.to_json())

@pytest.mark.parametrize(
"pupil_sampling,image_sampling,image_delta,mtftype,maximum_frequency",
[
("64x64", "64x64", 0.0, "Modulation", 150.0),
("32x32", "64x64", 1.0, "Modulation", 450.0),
("128x128", "128x128", 0.0, "Modulation", 314.5),
("32x32", "32x32", 0.0, "Modulation", 150.0),
],
)
def test_huygens_mtf_returns_correct_result(
self, simple_system, pupil_sampling, image_sampling, image_delta, mtftype, maximum_frequency, expected_data
):
result = huygens_mtf(
simple_system,
pupil_sampling=pupil_sampling,
image_sampling=image_sampling,
image_delta=image_delta,
mtftype=mtftype,
maximum_frequency=maximum_frequency,
)

assert_frame_equal(result.Data.astype(float), expected_data.Data.astype(float))
120 changes: 120 additions & 0 deletions zospy/analyses/mtf.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,123 @@ def fft_through_focus_mtf_fromcfg(
)

return analysis.complete(oncomplete, result)


def huygens_mtf(
oss: OpticStudioSystem,
pupil_sampling: str | int = "32x32",
image_sampling: str | int = "32x32",
image_delta: float = 0.0,
wavelength: str | int = "All",
field: str | int = "All",
mtftype: constants.Analysis.Settings.Mtf.HuygensMtfTypes | str = "Modulation",
maximum_frequency: float = 150.0,
use_polarization: bool = False,
use_dashes: bool = False,
oncomplete: OnComplete | str = OnComplete.Close,
) -> AnalysisResult:
"""Wrapper around the OpticStudio Huygens MTF.

For an in depth explanation of the parameters, see the Zemax OpticStudio user manual

Parameters
----------
oss : zospy.core.OpticStudioSystem
A ZOSPy OpticStudioSystem instance. Should be sequential.
pupil_sampling : str | int
The pupil sampling, either string (e.g. '64x64') or int.
The integer will be treated as a ZOSAPI Constants integer.
image_sampling : str | int
The image sampling, either string (e.g., '64x64') or int.
The integer will be treated as a ZOSAPI Constants integer.
image_delta : float
The Image Delta, defaults to 0.0.
wavelength : str | int
The wavelength to use in the MTF. Either 'All' or an integer specifying the wavelength number.
field : str | int
The field to use in the MTF. Either 'All' or an integer specifying the field number.
mtftype : zospy.constants.Analysis.Settings.Mtf.MtfTypes.Modulation
The MTF type (e.g. `Modulation`) that is calculated.
maximum_frequency : float
The maximum frequency at which the MTF is calculated.
Units are either cycles/mm or cycles/mr, depending on system setting).
Defaults to 150.0, which is more appropriate when units are set to cycles/mm.
use_polarization : bool
Use polarization. Defaults to False.
use_dashes : bool
Use dashes. Defaults to False.
oncomplete : OnComplete | str
Defines behaviour upon completion of the analysis. Should be one of ['Close', 'Release', 'Sustain']. If 'Close',
the analysis will be closed after completion. If 'Release', the analysis will remain open in OpticStudio, but
the link with python will be destroyed. If 'Sustain' the analysis will be kept open in OpticStudio and the link
with python will be sustained. To enable interaction when oncomplete == 'Sustain', the OpticStudio Analysis
instance will be available in the returned AnalysisResult through AnalysisResult.Analysis. Defaults to 'Close'.

Returns
-------
AnalysisResult
A HuygensMTF analysis result
"""
analysis_type = constants.Analysis.AnalysisIDM.HuygensMtf

# Create analysis
analysis = new_analysis(oss, analysis_type)

# Apply settings
analysis.Settings.PupilSampleSize = getattr(
constants.Analysis.SampleSizes, utils.zputils.standardize_sampling(pupil_sampling)
)
analysis.Settings.ImageSampleSize = getattr(
constants.Analysis.SampleSizes, utils.zputils.standardize_sampling(image_sampling)
)
analysis.Settings.ImageDelta = image_delta
analysis.set_wavelength(wavelength)
analysis.set_field(field)
analysis.Settings.Type = constants.process_constant(constants.Analysis.Settings.Mtf.HuygensMtfTypes, mtftype)
analysis.Settings.UsePolarization = use_polarization
analysis.Settings.UseDashes = use_dashes
analysis.Settings.MaximumFrequency = maximum_frequency

# Calculate
analysis.ApplyAndWaitForCompletion()

# Get headerdata, metadata and messages
headerdata = analysis.get_header_data()
metadata = analysis.get_metadata()
messages = analysis.get_messages()

# Get settings
settings = pd.Series(name="Settings", dtype=object)

settings.loc["PupilSampling"] = str(analysis.Settings.PupilSampleSize)
settings.loc["ImageSampling"] = str(analysis.Settings.ImageSampleSize)
settings.loc["ImageDelta"] = analysis.Settings.ImageDelta
settings.loc["Wavelength"] = analysis.get_wavelength()
settings.loc["Field"] = analysis.get_field()
settings.loc["Type"] = str(analysis.Settings.Type)
settings.loc["UsePolarization"] = analysis.Settings.UsePolarization
settings.loc["UseDashes"] = analysis.Settings.UseDashes
settings.loc["MaximumFrequency"] = analysis.Settings.MaximumFrequency

# Get data and unpack
data = []
for ii in range(analysis.Results.NumberOfDataSeries):
data.append(utils.zputils.unpack_dataseries(analysis.Results.DataSeries[ii]))

if not len(data):
data = pd.DataFrame()
elif len(data) == 1:
data = data[0]
else:
data = pd.concat(data, axis=1)

result = AnalysisResult(
analysistype=str(analysis_type),
data=data,
settings=settings,
metadata=metadata,
headerdata=headerdata,
messages=messages,
)

return analysis.complete(oncomplete, result)