From 37a5ed8be0b6e4c4630fd49e111b19e9daf9de4e Mon Sep 17 00:00:00 2001 From: Luc van Vught <31657810+LucVV@users.noreply.github.com> Date: Wed, 26 Feb 2025 14:17:22 +0100 Subject: [PATCH] Fixed field parsing for RayFan() (#119) * Fixed field parsing for ray fan analysis * added test for to dataframe with multiple fields * Generate new reference data for the ray fan analysis --------- Co-authored-by: crnh <30109443+crnh@users.noreply.github.com> --- tests/analyses/test_raysandspots.py | 56 +++++++++++++++++++ ...esult[0-20-Aberration_X-Aberration_Y].json | 12 +++- ...esult[0-20-Aberration_Y-Aberration_X].json | 12 +++- ...esult[1-40-Aberration_Y-Aberration_X].json | 12 +++- ...esult[0-20-Aberration_X-Aberration_Y].json | 12 +++- ...esult[0-20-Aberration_Y-Aberration_X].json | 12 +++- ...esult[1-40-Aberration_Y-Aberration_X].json | 12 +++- zospy/analyses/raysandspots/ray_fan.py | 20 ++++--- 8 files changed, 123 insertions(+), 25 deletions(-) diff --git a/tests/analyses/test_raysandspots.py b/tests/analyses/test_raysandspots.py index de580ed8..aaffecfc 100644 --- a/tests/analyses/test_raysandspots.py +++ b/tests/analyses/test_raysandspots.py @@ -1,4 +1,5 @@ import pytest +from pandas.testing import assert_frame_equal from tests.helpers import assert_dataclass_equal from zospy.analyses.raysandspots import RayFan, SingleRayTrace @@ -69,6 +70,61 @@ def test_to_json(self, simple_system): result = RayFan().run(simple_system) assert result.from_json(result.to_json()).to_json() == result.to_json() + @pytest.mark.parametrize( + "fieldx,fieldy", [(0, 0), (5.5, 0), (0, 5.5), (5.5, 5.5), (-5.5, 0), (0, -5.5), (-5.5, -5.5)] + ) + def test_field_parsing(self, fieldx, fieldy, simple_system): + field1 = simple_system.SystemData.Fields.GetField(1) + field1.X = fieldx + field1.Y = fieldy + result = RayFan().run(simple_system) + + assert result.data.tangential[0].field_coordinate.value[0] == fieldx + assert result.data.tangential[0].field_coordinate.value[1] == fieldy + assert result.data.sagittal[0].field_coordinate.value[0] == fieldx + assert result.data.sagittal[0].field_coordinate.value[1] == fieldy + + @pytest.mark.parametrize("fields", [[(5.5, -5.5)], [(0, 0), (0.0, 5.5), (5.5, 0.0), (5.5, -5.5)]]) + def test_to_dataframe(self, fields, simple_system): + field1 = simple_system.SystemData.Fields.GetField(1) + field1.X = fields[0][0] + field1.Y = fields[0][1] + + for f in fields[1:]: + simple_system.SystemData.Fields.AddField(f[0], f[1], 1.0) + + result = RayFan().run(simple_system) + + df = result.data.to_dataframe() + + for r in result.data.tangential: + for wl in r.data.columns: + assert_frame_equal( + df.loc[ + (df["Direction"] == "Tangential") + & (df["Field Number"] == r.field_number) + & (df["FieldX"] == r.field_coordinate.value[0]) + & (df["FieldY"] == r.field_coordinate.value[1]) + & (df["Wavelength"] == wl), + ["Pupil", "Aberration"], + ].set_index("Pupil"), + r.data[wl].to_frame("Aberration"), + ) + + for r in result.data.sagittal: + for wl in r.data.columns: + assert_frame_equal( + df.loc[ + (df["Direction"] == "Sagittal") + & (df["Field Number"] == r.field_number) + & (df["FieldX"] == r.field_coordinate.value[0]) + & (df["FieldY"] == r.field_coordinate.value[1]) + & (df["Wavelength"] == wl), + ["Pupil", "Aberration"], + ].set_index("Pupil"), + r.data[wl].to_frame("Aberration"), + ) + @pytest.mark.parametrize( "plot_scale,number_of_rays,tangential,sagittal", [ diff --git a/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json b/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json index 22af8030..8b9240ad 100644 --- a/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json +++ b/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json @@ -4,7 +4,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -192,7 +195,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -390,7 +396,7 @@ "check_apertures": true }, "metadata": { - "DateTime": "2025-02-19T14:37:47.589000", + "DateTime": "2025-02-25T16:37:40.504000", "FeatureDescription": "Listing of Ray Fan Data\n\n", "LensFile": "REDACTED", "LensTitle": "" diff --git a/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json b/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json index 0c3f573d..ce2a6d46 100644 --- a/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json +++ b/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json @@ -4,7 +4,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -192,7 +195,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -390,7 +396,7 @@ "check_apertures": true }, "metadata": { - "DateTime": "2025-02-19T14:37:47.381000", + "DateTime": "2025-02-25T16:37:40.082000", "FeatureDescription": "Listing of Ray Fan Data\n\n", "LensFile": "REDACTED", "LensTitle": "" diff --git a/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json b/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json index b8287428..fe8cda61 100644 --- a/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json +++ b/tests/data/reference/24.1.3-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json @@ -4,7 +4,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -352,7 +355,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -710,7 +716,7 @@ "check_apertures": true }, "metadata": { - "DateTime": "2025-02-19T14:37:47.484000", + "DateTime": "2025-02-25T16:37:40.317000", "FeatureDescription": "Listing of Ray Fan Data\n\n", "LensFile": "REDACTED", "LensTitle": "" diff --git a/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json b/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json index 5165fcc6..8428d5c8 100644 --- a/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json +++ b/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_X-Aberration_Y].json @@ -4,7 +4,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -192,7 +195,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -390,7 +396,7 @@ "check_apertures": true }, "metadata": { - "DateTime": "2025-02-19T14:48:17.583000", + "DateTime": "2025-02-25T16:50:08.250000", "FeatureDescription": "Listing of Ray Fan Data\n\n", "LensFile": "REDACTED", "LensTitle": "" diff --git a/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json b/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json index 9a2f1da3..cb07c70f 100644 --- a/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json +++ b/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[0-20-Aberration_Y-Aberration_X].json @@ -4,7 +4,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -192,7 +195,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -390,7 +396,7 @@ "check_apertures": true }, "metadata": { - "DateTime": "2025-02-19T14:48:17.251000", + "DateTime": "2025-02-25T16:50:07.858000", "FeatureDescription": "Listing of Ray Fan Data\n\n", "LensFile": "REDACTED", "LensTitle": "" diff --git a/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json b/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json index 5cb23825..edee61fb 100644 --- a/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json +++ b/tests/data/reference/24.2.2-test_raysandspots.py-test_ray_fan_returns_correct_result[1-40-Aberration_Y-Aberration_X].json @@ -4,7 +4,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -352,7 +355,10 @@ { "field_number": 1, "field_coordinate": { - "value": 0.0, + "value": [ + 0.0, + 0.0 + ], "unit": "deg" }, "data": { @@ -710,7 +716,7 @@ "check_apertures": true }, "metadata": { - "DateTime": "2025-02-19T14:48:17.418000", + "DateTime": "2025-02-25T16:50:08.075000", "FeatureDescription": "Listing of Ray Fan Data\n\n", "LensFile": "REDACTED", "LensTitle": "" diff --git a/zospy/analyses/raysandspots/ray_fan.py b/zospy/analyses/raysandspots/ray_fan.py index 9b86f601..9d0f2e80 100644 --- a/zospy/analyses/raysandspots/ray_fan.py +++ b/zospy/analyses/raysandspots/ray_fan.py @@ -22,7 +22,7 @@ @analysis_result class FanData: field_number: Annotated[int, Field(ge=0)] - field_coordinate: UnitField[float | tuple[float, float]] + field_coordinate: UnitField[tuple[float, float]] data: ValidatedDataFrame def to_dataframe(self) -> DataFrame: @@ -34,7 +34,8 @@ def to_dataframe(self) -> DataFrame: ) stacked_df.insert(0, "Field Number", self.field_number) - stacked_df.insert(1, "Field", self.field_coordinate.value) + stacked_df.insert(1, "FieldX", self.field_coordinate.value[0]) + stacked_df.insert(2, "FieldY", self.field_coordinate.value[1]) return stacked_df @@ -173,10 +174,10 @@ def run_analysis(self) -> RayFanResult: def get_data_series(self) -> RayFanResult | None: """Get the data series from the Ray Fan analysis.""" - re_float = rf"\d+\{config.DECIMAL_POINT}\d+" + re_float = rf"-?\d+\{config.DECIMAL_POINT}\d+" ray_fan_description_regex = re.compile( rf"(?Psagittal|tangential) fan, field number (?P\d+) = " - rf"(?P{re_float})(?:, (?P{re_float}))? " + rf"(?P{re_float})(?:, (?P{re_float}))? " r"\((?P.+)\)", re.IGNORECASE, ) @@ -199,9 +200,14 @@ def get_data_series(self) -> RayFanResult | None: columns = [atox(label, float) for label in data_series.SeriesLabels] data = np.array(data_series.YData.Data) - coordinate_x = atox(match.group("coordinate_x"), float) - coordinate_y = atox(match.group("coordinate_y"), float) if match.group("coordinate_y") else None - coordinate = (coordinate_x, coordinate_y) if coordinate_y else coordinate_x + if match.group("field_2"): + field_x = atox(match.group("field_1"), float) + field_y = atox(match.group("field_2"), float) + else: # field 1 corresponds to y + field_x = 0.0 + field_y = atox(match.group("field_1"), float) + + coordinate = (field_x, field_y) fan_data = FanData( field_number=int(match.group("field")),