FiberFusing 1.9.4__tar.gz → 1.9.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fiberfusing-1.9.7/FiberFusing/_version.py +5 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/background.py +2 -3
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/connection.py +3 -2
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/connection_optimization.py +17 -2
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/coordinate_system.py +4 -2
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/generic_fiber.py +18 -9
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/__init__.py +0 -8
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/base_class.py +2 -1
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/linestring.py +4 -2
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/point.py +9 -7
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/polygon.py +6 -6
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometry.py +113 -75
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/helper.py +0 -28
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/optical_structure.py +7 -5
- fiberfusing-1.9.7/FiberFusing/plottings.py +145 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/profile.py +116 -79
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/circle.py +7 -2
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/ellipse.py +8 -3
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/structures.py +20 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/utils.py +11 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/PKG-INFO +125 -88
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/SOURCES.txt +4 -48
- fiberfusing-1.9.7/FiberFusing.egg-info/requires.txt +28 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/PKG-INFO +125 -88
- fiberfusing-1.9.7/README.rst +210 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/pyproject.toml +31 -40
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_cooordinate_system.py +0 -1
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_fused_structure.py +2 -2
- fiberfusing-1.9.7/tests/test_packaging.py +42 -0
- fiberfusing-1.9.7/tests/test_plotting.py +127 -0
- fiberfusing-1.9.7/tests/test_profile_invariants.py +213 -0
- fiberfusing-1.9.4/.flake8 +0 -6
- fiberfusing-1.9.4/.github/dependabot.yml +0 -11
- fiberfusing-1.9.4/.github/workflows/deploy_PyPi.yml +0 -20
- fiberfusing-1.9.4/.github/workflows/deploy_anaconda.yml +0 -21
- fiberfusing-1.9.4/.github/workflows/deploy_coverage.yml +0 -25
- fiberfusing-1.9.4/.github/workflows/deploy_documentation.yml +0 -24
- fiberfusing-1.9.4/.gitignore +0 -28
- fiberfusing-1.9.4/FiberFusing/_version.py +0 -34
- fiberfusing-1.9.4/FiberFusing/plottings.py +0 -43
- fiberfusing-1.9.4/FiberFusing.egg-info/requires.txt +0 -20
- fiberfusing-1.9.4/README.rst +0 -180
- fiberfusing-1.9.4/docs/Makefile +0 -19
- fiberfusing-1.9.4/docs/examples/README.rst +0 -7
- fiberfusing-1.9.4/docs/examples/extra/README.rst +0 -4
- fiberfusing-1.9.4/docs/examples/extra/custom_graded_index_fiber.py +0 -46
- fiberfusing-1.9.4/docs/examples/extra/custom_step_index_fiber.py +0 -40
- fiberfusing-1.9.4/docs/examples/extra/loading_fiber.py +0 -18
- fiberfusing-1.9.4/docs/examples/extra/randomize_core.py +0 -31
- fiberfusing-1.9.4/docs/examples/extra/structure_shift.py +0 -28
- fiberfusing-1.9.4/docs/examples/geometry/README.rst +0 -4
- fiberfusing-1.9.4/docs/examples/geometry/plot_line_3.py +0 -54
- fiberfusing-1.9.4/docs/examples/geometry/plot_line_4.py +0 -47
- fiberfusing-1.9.4/docs/examples/geometry/plot_line_5.py +0 -47
- fiberfusing-1.9.4/docs/examples/geometry/plot_ring_10.py +0 -47
- fiberfusing-1.9.4/docs/examples/geometry/plot_ring_2.py +0 -48
- fiberfusing-1.9.4/docs/examples/geometry/plot_ring_3.py +0 -47
- fiberfusing-1.9.4/docs/examples/geometry/plot_ring_4.py +0 -51
- fiberfusing-1.9.4/docs/examples/geometry/plot_ring_5.py +0 -48
- fiberfusing-1.9.4/docs/examples/geometry/plot_ring_7.py +0 -48
- fiberfusing-1.9.4/docs/examples/geometry/plot_single.py +0 -45
- fiberfusing-1.9.4/docs/images/example_3x3.png +0 -0
- fiberfusing-1.9.4/docs/images/logo.png +0 -0
- fiberfusing-1.9.4/docs/make.bat +0 -35
- fiberfusing-1.9.4/docs/source/_static/default.css +0 -19
- fiberfusing-1.9.4/docs/source/_static/thumbnail.png +0 -0
- fiberfusing-1.9.4/docs/source/code/components.rst +0 -10
- fiberfusing-1.9.4/docs/source/code/coordinate_system.rst +0 -12
- fiberfusing-1.9.4/docs/source/code/geometry.rst +0 -12
- fiberfusing-1.9.4/docs/source/code/shapes.rst +0 -9
- fiberfusing-1.9.4/docs/source/code/utils.rst +0 -19
- fiberfusing-1.9.4/docs/source/code.rst +0 -17
- fiberfusing-1.9.4/docs/source/conf.py +0 -175
- fiberfusing-1.9.4/docs/source/gallery/index.rst +0 -331
- fiberfusing-1.9.4/docs/source/index.rst +0 -13
- fiberfusing-1.9.4/docs/source/references.rst +0 -6
- fiberfusing-1.9.4/docs/source/sg_execution_times.rst +0 -79
- fiberfusing-1.9.4/docs/source/theory.rst +0 -5
- fiberfusing-1.9.4/meta.yaml +0 -65
- fiberfusing-1.9.4/notebook.ipynb +0 -202
- fiberfusing-1.9.4/tests/__init__.py +0 -0
- fiberfusing-1.9.4/tests/fiber_loader.py +0 -91
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/__init__.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/__init__.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/utils.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/directories.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/__init__.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/1550BHP.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF13.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_20.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_26.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_33.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_42.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2028M12.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2028M21.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2028M24.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2058G1.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2058L1.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/HI1060.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/HP630.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/SM1950.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/SMF28.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/fluorine_doped_1%_capillary.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/fluorine_doped_2%_capillary.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/test_fiber.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/test_multimode_fiber.yaml +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/loader.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/utils.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/graded_index.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/__init__.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/base.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/square.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/dependency_links.txt +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/top_level.txt +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/LICENSE +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/setup.cfg +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_boundaries.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_fiber.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_generic_fiber.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_geometries.py +0 -0
- {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_polygon.py +0 -0
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
from typing import Tuple, Optional
|
|
5
5
|
from pydantic.dataclasses import dataclass
|
|
6
|
-
from pydantic import ConfigDict
|
|
7
6
|
import numpy as np
|
|
8
7
|
|
|
9
8
|
from FiberFusing.coordinate_system import CoordinateSystem
|
|
10
9
|
from FiberFusing.helper import OverlayStructureBaseClass
|
|
11
|
-
from FiberFusing.utils import NameSpace
|
|
10
|
+
from FiberFusing.utils import NameSpace, config_dict
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
@dataclass(config=
|
|
13
|
+
@dataclass(config=config_dict)
|
|
15
14
|
class BackGround(OverlayStructureBaseClass):
|
|
16
15
|
"""
|
|
17
16
|
Represents a background structure overlayed on a mesh, characterized by a circular shape.
|
|
@@ -380,14 +380,15 @@ class Connection:
|
|
|
380
380
|
options={'xatol': tolerance}
|
|
381
381
|
)
|
|
382
382
|
|
|
383
|
-
if result.success:
|
|
383
|
+
if result.success and np.isfinite(result.x) and np.isfinite(result.fun):
|
|
384
|
+
self._compute_area_mismatch_cost(result.x)
|
|
384
385
|
logging.info(f"Optimization successful: cost={result.fun:.2e}")
|
|
385
386
|
# Apply optimized shifts
|
|
386
387
|
if self.core_shift:
|
|
387
388
|
self.fiber0.shifted_core += self.core_shift[0]
|
|
388
389
|
self.fiber1.shifted_core += self.core_shift[1]
|
|
389
390
|
else:
|
|
390
|
-
|
|
391
|
+
raise RuntimeError(f"Core optimization failed: {result.message}")
|
|
391
392
|
|
|
392
393
|
def _compute_area_mismatch_cost(self, x: float) -> float:
|
|
393
394
|
"""
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import numpy as np
|
|
2
3
|
from scipy.optimize import minimize_scalar
|
|
3
4
|
from itertools import combinations
|
|
4
5
|
from FiberFusing.connection.connection import Connection
|
|
@@ -92,6 +93,7 @@ class ConnectionOptimization:
|
|
|
92
93
|
virtual_shift : float
|
|
93
94
|
The shift value for the virtual circles.
|
|
94
95
|
"""
|
|
96
|
+
self._added_section = self._removed_section = None
|
|
95
97
|
self.virtual_shift = virtual_shift
|
|
96
98
|
topology = self.get_overall_topology()
|
|
97
99
|
for connection in self.connected_fibers:
|
|
@@ -132,7 +134,7 @@ class ConnectionOptimization:
|
|
|
132
134
|
float
|
|
133
135
|
The total removed area.
|
|
134
136
|
"""
|
|
135
|
-
disconnected_area =
|
|
137
|
+
disconnected_area = sum(fiber.area for fiber in self.fiber_list)
|
|
136
138
|
connected_area = utils.union_geometries(*self.fiber_list).area
|
|
137
139
|
return disconnected_area - connected_area
|
|
138
140
|
|
|
@@ -140,6 +142,7 @@ class ConnectionOptimization:
|
|
|
140
142
|
"""
|
|
141
143
|
Initializes the connections between pairs of fibers in the structure.
|
|
142
144
|
"""
|
|
145
|
+
self._added_section = self._removed_section = None
|
|
143
146
|
self.connected_fibers = [Connection(f0, f1) for f0, f1 in self.iterate_over_connected_fibers()]
|
|
144
147
|
|
|
145
148
|
def get_overall_topology(self) -> str:
|
|
@@ -206,12 +209,18 @@ class ConnectionOptimization:
|
|
|
206
209
|
core_distance = self.connected_fibers[0].distance_between_cores
|
|
207
210
|
bounds = (0, core_distance * 1e3) if bounds is None else bounds
|
|
208
211
|
|
|
212
|
+
if np.shape(bounds) != (2,) or not np.all(np.isfinite(bounds)) or not 0 <= bounds[0] < bounds[1]:
|
|
213
|
+
raise ValueError("bounds must be finite, nonnegative, and strictly increasing.")
|
|
214
|
+
|
|
209
215
|
result = minimize_scalar(
|
|
210
216
|
self.get_cost_value,
|
|
211
217
|
bounds=bounds,
|
|
212
218
|
method='bounded',
|
|
213
219
|
options={'xatol': core_distance * self.tolerance_factor}
|
|
214
220
|
)
|
|
221
|
+
if not result.success or not np.isfinite(result.x) or not np.isfinite(result.fun):
|
|
222
|
+
raise RuntimeError(f"Fusion optimization failed: {result.message}")
|
|
223
|
+
self.shift_connections(result.x)
|
|
215
224
|
return result.x
|
|
216
225
|
|
|
217
226
|
def compute_optimal_structure(self, bounds: tuple = None) -> None:
|
|
@@ -224,8 +233,14 @@ class ConnectionOptimization:
|
|
|
224
233
|
The boundaries for the virtual shift.
|
|
225
234
|
"""
|
|
226
235
|
logging.info("Computing the optimal structure geometry")
|
|
227
|
-
self.find_optimal_virtual_shift(bounds)
|
|
236
|
+
shift = self.find_optimal_virtual_shift(bounds)
|
|
237
|
+
self.shift_connections(shift)
|
|
228
238
|
self._clad_structure = self.compute_shifted_geometry()
|
|
239
|
+
if not self._clad_structure._shapely_object.is_valid or not np.isfinite(self._clad_structure.area):
|
|
240
|
+
raise RuntimeError("Fusion optimization produced invalid geometry.")
|
|
241
|
+
target_area = sum(fiber.area for fiber in self.fiber_list)
|
|
242
|
+
self.area_residual = abs(self._clad_structure.area - target_area)
|
|
243
|
+
self.relative_area_residual = self.area_residual / target_area
|
|
229
244
|
self.optimize_core_positions()
|
|
230
245
|
|
|
231
246
|
def compute_shifted_geometry(self) -> Polygon:
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from typing import Tuple, Optional
|
|
6
6
|
from pydantic.dataclasses import dataclass
|
|
7
|
-
from pydantic import
|
|
7
|
+
from pydantic import field_validator
|
|
8
8
|
from dataclasses import field
|
|
9
9
|
|
|
10
|
+
from FiberFusing.utils import config_dict
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
@dataclass(config=config_dict)
|
|
12
14
|
class CoordinateSystem:
|
|
13
15
|
"""
|
|
14
16
|
A 2D Cartesian coordinate system for fiber optics simulations.
|
|
@@ -6,12 +6,12 @@ import numpy
|
|
|
6
6
|
import pprint
|
|
7
7
|
from copy import deepcopy
|
|
8
8
|
import matplotlib.pyplot as plt
|
|
9
|
+
from FiberFusing.plottings import plotting
|
|
9
10
|
|
|
10
11
|
from FiberFusing import Circle, CircleOpticalStructure
|
|
11
12
|
from FiberFusing.geometries.point import Point
|
|
12
13
|
from FiberFusing.coordinate_system import CoordinateSystem
|
|
13
14
|
from FiberFusing.plottings import plot_polygon
|
|
14
|
-
from FiberFusing.helper import _plot_helper
|
|
15
15
|
from FiberFusing.graded_index import GradedIndex
|
|
16
16
|
|
|
17
17
|
pp = pprint.PrettyPrinter(indent=4, sort_dicts=False, compact=True, width=1)
|
|
@@ -508,8 +508,8 @@ class GenericFiber():
|
|
|
508
508
|
coordinate_system=coordinate_system
|
|
509
509
|
)
|
|
510
510
|
|
|
511
|
-
@
|
|
512
|
-
def plot(self,
|
|
511
|
+
@plotting(nrows=1, ncols=1)
|
|
512
|
+
def plot(self, axes: plt.Axes = None) -> None:
|
|
513
513
|
"""
|
|
514
514
|
Plot the fiber geometry representation including patch and raster-mesh.
|
|
515
515
|
|
|
@@ -519,7 +519,7 @@ class GenericFiber():
|
|
|
519
519
|
Resolution for rasterizing structures. Default is 300.
|
|
520
520
|
"""
|
|
521
521
|
for structure in self.fiber_structure:
|
|
522
|
-
plot_polygon(ax=
|
|
522
|
+
plot_polygon(ax=axes, polygon=structure.polygon._shapely_object)
|
|
523
523
|
|
|
524
524
|
def get_structures_boundaries(self) -> numpy.ndarray:
|
|
525
525
|
"""
|
|
@@ -565,8 +565,8 @@ class GenericFiber():
|
|
|
565
565
|
"""
|
|
566
566
|
return self.get_structure_max_min_boundaries()
|
|
567
567
|
|
|
568
|
-
@
|
|
569
|
-
def plot_raster(self,
|
|
568
|
+
@plotting(nrows=1, ncols=1)
|
|
569
|
+
def plot_raster(self, axes: plt.Axes, coordinate_system) -> None:
|
|
570
570
|
"""
|
|
571
571
|
Render the rasterized representation of the geometry onto a given matplotlib axis.
|
|
572
572
|
|
|
@@ -582,7 +582,16 @@ class GenericFiber():
|
|
|
582
582
|
None
|
|
583
583
|
"""
|
|
584
584
|
mesh = self.get_raster_mesh(coordinate_system=coordinate_system)
|
|
585
|
-
|
|
585
|
+
axes.pcolormesh(
|
|
586
|
+
coordinate_system.x_vector,
|
|
587
|
+
coordinate_system.y_vector,
|
|
588
|
+
mesh,
|
|
589
|
+
cmap='Blues'
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
axes.set(
|
|
593
|
+
title='Fiber structure',
|
|
594
|
+
xlabel=r'x-distance',
|
|
595
|
+
ylabel=r'y-distance'
|
|
596
|
+
)
|
|
586
597
|
|
|
587
|
-
ax.set(title='Fiber structure', xlabel=r'x-distance [m]', ylabel=r'y-distance [m]')
|
|
588
|
-
ax.ticklabel_format(axis='both', style='sci', scilimits=(-6, -6), useOffset=False)
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
from pydantic import ConfigDict
|
|
2
|
-
|
|
3
|
-
config_dict = ConfigDict(
|
|
4
|
-
extra='forbid',
|
|
5
|
-
arbitrary_types_allowed=True,
|
|
6
|
-
kw_only=True
|
|
7
|
-
)
|
|
8
|
-
|
|
9
1
|
from .base_class import Alteration # noqa: F401
|
|
10
2
|
from .point import Point # noqa: F401
|
|
11
3
|
from .linestring import LineString # noqa: F401
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
-
from typing import Optional, Tuple
|
|
4
|
+
from typing import Optional, Tuple
|
|
5
|
+
from typing_extensions import Self
|
|
5
6
|
import numpy as np
|
|
6
7
|
from pydantic.dataclasses import dataclass
|
|
7
8
|
from dataclasses import field
|
|
@@ -9,9 +10,10 @@ from shapely.affinity import scale
|
|
|
9
10
|
import shapely.geometry as geo
|
|
10
11
|
|
|
11
12
|
from FiberFusing.geometries.base_class import Alteration
|
|
13
|
+
from FiberFusing.utils import config_dict
|
|
12
14
|
from FiberFusing import geometries
|
|
13
15
|
|
|
14
|
-
@dataclass(config=
|
|
16
|
+
@dataclass(config=config_dict)
|
|
15
17
|
class LineString(Alteration):
|
|
16
18
|
coordinates: Optional[Tuple] = field(default=None, repr=False)
|
|
17
19
|
instance: Optional[geo.LineString] = field(default=None, repr=False)
|
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
4
|
# Built-in imports
|
|
5
|
-
from typing import Optional, Tuple,
|
|
5
|
+
from typing import Optional, Tuple, List
|
|
6
|
+
from typing_extensions import Self
|
|
6
7
|
import numpy
|
|
7
8
|
from pydantic.dataclasses import dataclass
|
|
8
9
|
import shapely.geometry as geo
|
|
9
10
|
import matplotlib.pyplot as plt
|
|
11
|
+
from FiberFusing.plottings import plotting
|
|
10
12
|
|
|
11
|
-
from FiberFusing.helper import _plot_helper
|
|
12
13
|
from FiberFusing import geometries
|
|
14
|
+
from FiberFusing.utils import config_dict
|
|
13
15
|
|
|
14
|
-
@dataclass(config=
|
|
16
|
+
@dataclass(config=config_dict)
|
|
15
17
|
class Point(geometries.base_class.Alteration):
|
|
16
|
-
position:
|
|
18
|
+
position: Tuple[float, float] | List[float] | None = None
|
|
17
19
|
instance: Optional[geo.Point] = None
|
|
18
20
|
index: Optional[float] = None
|
|
19
21
|
|
|
@@ -106,8 +108,8 @@ class Point(geometries.base_class.Alteration):
|
|
|
106
108
|
"""
|
|
107
109
|
return numpy.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
|
|
108
110
|
|
|
109
|
-
@
|
|
110
|
-
def plot(self,
|
|
111
|
+
@plotting(nrows=1, ncols=1)
|
|
112
|
+
def plot(self, axes: plt.Axes, marker: str = 'x', size: int = 20, label: str = None) -> None:
|
|
111
113
|
"""
|
|
112
114
|
Renders this point on the given axis, optionally with text.
|
|
113
115
|
|
|
@@ -122,4 +124,4 @@ class Point(geometries.base_class.Alteration):
|
|
|
122
124
|
label : str
|
|
123
125
|
The label for the point, if any.
|
|
124
126
|
"""
|
|
125
|
-
|
|
127
|
+
axes.scatter(self.x, self.y, label=label, marker=marker, s=size)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
-
from typing import Iterable, List, Optional, Union, Tuple
|
|
4
|
+
from typing import Iterable, List, Optional, Union, Tuple
|
|
5
|
+
from typing_extensions import Self
|
|
5
6
|
import numpy as np
|
|
6
7
|
from matplotlib.path import Path
|
|
7
8
|
import shapely.geometry as geo
|
|
@@ -10,8 +11,7 @@ from shapely.ops import split
|
|
|
10
11
|
from shapely import affinity
|
|
11
12
|
|
|
12
13
|
from FiberFusing.coordinate_system import CoordinateSystem
|
|
13
|
-
from FiberFusing.plottings import plot_polygon
|
|
14
|
-
from FiberFusing.helper import _plot_helper
|
|
14
|
+
from FiberFusing.plottings import plot_polygon, plotting
|
|
15
15
|
from FiberFusing import geometries
|
|
16
16
|
|
|
17
17
|
class Polygon(geometries.base_class.Alteration):
|
|
@@ -142,8 +142,8 @@ class Polygon(geometries.base_class.Alteration):
|
|
|
142
142
|
# Points are inside if they are in the exterior but not in any hole
|
|
143
143
|
return exterior_mask & ~hole_mask
|
|
144
144
|
|
|
145
|
-
@
|
|
146
|
-
def plot(self,
|
|
145
|
+
@plotting(nrows=1, ncols=1)
|
|
146
|
+
def plot(self, axes: plt.Axes, **kwargs) -> None:
|
|
147
147
|
"""
|
|
148
148
|
Plots the polygon on the given Matplotlib axis.
|
|
149
149
|
|
|
@@ -154,7 +154,7 @@ class Polygon(geometries.base_class.Alteration):
|
|
|
154
154
|
**kwargs
|
|
155
155
|
Additional keyword arguments passed to the plotting function.
|
|
156
156
|
"""
|
|
157
|
-
plot_polygon(ax=
|
|
157
|
+
plot_polygon(ax=axes, polygon=self._shapely_object, **kwargs)
|
|
158
158
|
|
|
159
159
|
def rasterize(self, coordinate_system: CoordinateSystem) -> np.ndarray:
|
|
160
160
|
"""
|
|
@@ -7,16 +7,20 @@ import numpy
|
|
|
7
7
|
from scipy.ndimage import gaussian_filter
|
|
8
8
|
import FiberFusing
|
|
9
9
|
import matplotlib.pyplot as plt
|
|
10
|
-
from FiberFusing.coordinate_system import CoordinateSystem
|
|
11
|
-
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
12
10
|
import matplotlib.colors as colors
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from pydantic import field_validator, ConfigDict
|
|
11
|
+
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
12
|
+
from pydantic import field_validator
|
|
16
13
|
from pydantic.dataclasses import dataclass
|
|
14
|
+
from FiberFusing.plottings import plotting
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from FiberFusing.coordinate_system import CoordinateSystem
|
|
18
|
+
from FiberFusing.utils import config_dict
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
class DomainAlignment(Enum):
|
|
19
22
|
"""Boundary positioning modes."""
|
|
23
|
+
|
|
20
24
|
AUTO = "auto"
|
|
21
25
|
LEFT = "left"
|
|
22
26
|
RIGHT = "right"
|
|
@@ -25,8 +29,8 @@ class DomainAlignment(Enum):
|
|
|
25
29
|
CENTERING = "centering"
|
|
26
30
|
|
|
27
31
|
|
|
28
|
-
@dataclass(config=
|
|
29
|
-
class Geometry
|
|
32
|
+
@dataclass(config=config_dict)
|
|
33
|
+
class Geometry:
|
|
30
34
|
"""
|
|
31
35
|
Represents the refractive index (RI) geometric profile including background and fiber structures.
|
|
32
36
|
|
|
@@ -73,28 +77,28 @@ class Geometry():
|
|
|
73
77
|
"""
|
|
74
78
|
self.structure_list.extend(structure)
|
|
75
79
|
|
|
76
|
-
@field_validator(
|
|
80
|
+
@field_validator("resolution")
|
|
77
81
|
@classmethod
|
|
78
82
|
def validate_resolution(cls, v: int) -> int:
|
|
79
83
|
"""Validate resolution is positive."""
|
|
80
84
|
if v <= 0:
|
|
81
|
-
raise ValueError(
|
|
85
|
+
raise ValueError("Resolution must be positive")
|
|
82
86
|
return v
|
|
83
87
|
|
|
84
|
-
@field_validator(
|
|
88
|
+
@field_validator("boundary_pad_factor")
|
|
85
89
|
@classmethod
|
|
86
90
|
def validate_boundary_pad_factor(cls, v: float) -> float:
|
|
87
91
|
"""Validate boundary pad factor is positive."""
|
|
88
92
|
if v <= 0:
|
|
89
|
-
raise ValueError(
|
|
93
|
+
raise ValueError("Boundary pad factor must be positive")
|
|
90
94
|
return v
|
|
91
95
|
|
|
92
|
-
@field_validator(
|
|
96
|
+
@field_validator("index_scrambling")
|
|
93
97
|
@classmethod
|
|
94
98
|
def validate_index_scrambling(cls, v: float) -> float:
|
|
95
99
|
"""Validate index scrambling is non-negative."""
|
|
96
100
|
if v < 0:
|
|
97
|
-
raise ValueError(
|
|
101
|
+
raise ValueError("Index scrambling must be non-negative")
|
|
98
102
|
return v
|
|
99
103
|
|
|
100
104
|
def initialize(self):
|
|
@@ -105,7 +109,12 @@ class Geometry():
|
|
|
105
109
|
x_min, y_min, x_max, y_max = self.get_boundaries()
|
|
106
110
|
|
|
107
111
|
self.coordinate_system = CoordinateSystem(
|
|
108
|
-
x_min=x_min,
|
|
112
|
+
x_min=x_min,
|
|
113
|
+
x_max=x_max,
|
|
114
|
+
y_min=y_min,
|
|
115
|
+
y_max=y_max,
|
|
116
|
+
nx=self.resolution,
|
|
117
|
+
ny=self.resolution,
|
|
109
118
|
)
|
|
110
119
|
|
|
111
120
|
self.coordinate_system.center(factor=self.boundary_pad_factor)
|
|
@@ -124,14 +133,21 @@ class Geometry():
|
|
|
124
133
|
x_min, y_min, x_max, y_max = self.get_boundaries()
|
|
125
134
|
|
|
126
135
|
self.coordinate_system = CoordinateSystem(
|
|
127
|
-
x_min=x_min,
|
|
136
|
+
x_min=x_min,
|
|
137
|
+
x_max=x_max,
|
|
138
|
+
y_min=y_min,
|
|
139
|
+
y_max=y_max,
|
|
140
|
+
nx=self.resolution,
|
|
141
|
+
ny=self.resolution,
|
|
128
142
|
)
|
|
129
143
|
self.coordinate_system.center(factor=self.boundary_pad_factor)
|
|
130
144
|
self.apply_boundary_settings()
|
|
131
145
|
|
|
132
|
-
@field_validator(
|
|
146
|
+
@field_validator("x_bounds")
|
|
133
147
|
@classmethod
|
|
134
|
-
def validate_x_bounds(
|
|
148
|
+
def validate_x_bounds(
|
|
149
|
+
cls, v: Union[DomainAlignment, Tuple[float, float]]
|
|
150
|
+
) -> Union[DomainAlignment, Tuple[float, float]]:
|
|
135
151
|
"""Validate x_bounds parameter."""
|
|
136
152
|
if isinstance(v, (list, tuple)):
|
|
137
153
|
if len(v) != 2:
|
|
@@ -142,9 +158,11 @@ class Geometry():
|
|
|
142
158
|
raise ValueError("x_bounds must be a tuple or DomainAlignment")
|
|
143
159
|
return v
|
|
144
160
|
|
|
145
|
-
@field_validator(
|
|
161
|
+
@field_validator("y_bounds")
|
|
146
162
|
@classmethod
|
|
147
|
-
def validate_y_bounds(
|
|
163
|
+
def validate_y_bounds(
|
|
164
|
+
cls, v: Union[DomainAlignment, Tuple[float, float]]
|
|
165
|
+
) -> Union[DomainAlignment, Tuple[float, float]]:
|
|
148
166
|
"""Validate y_bounds parameter."""
|
|
149
167
|
if isinstance(v, (list, tuple)):
|
|
150
168
|
if len(v) != 2:
|
|
@@ -157,14 +175,18 @@ class Geometry():
|
|
|
157
175
|
|
|
158
176
|
def apply_boundary_settings(self) -> None:
|
|
159
177
|
"""Apply boundary settings to coordinate system."""
|
|
160
|
-
if hasattr(self,
|
|
178
|
+
if hasattr(self, "coordinate_system") and self.coordinate_system is not None:
|
|
161
179
|
if isinstance(self.x_bounds, (list, tuple)):
|
|
162
|
-
self.coordinate_system.x_min, self.coordinate_system.x_max =
|
|
180
|
+
self.coordinate_system.x_min, self.coordinate_system.x_max = (
|
|
181
|
+
self.x_bounds
|
|
182
|
+
)
|
|
163
183
|
elif isinstance(self.x_bounds, DomainAlignment):
|
|
164
184
|
self._apply_x_boundary_mode(self.x_bounds)
|
|
165
185
|
|
|
166
186
|
if isinstance(self.y_bounds, (list, tuple)):
|
|
167
|
-
self.coordinate_system.y_min, self.coordinate_system.y_max =
|
|
187
|
+
self.coordinate_system.y_min, self.coordinate_system.y_max = (
|
|
188
|
+
self.y_bounds
|
|
189
|
+
)
|
|
168
190
|
elif isinstance(self.y_bounds, DomainAlignment):
|
|
169
191
|
self._apply_y_boundary_mode(self.y_bounds)
|
|
170
192
|
|
|
@@ -206,10 +228,16 @@ class Geometry():
|
|
|
206
228
|
The boundaries as (x_min, y_min, x_max, y_max).
|
|
207
229
|
|
|
208
230
|
"""
|
|
209
|
-
filtered_structures = [
|
|
231
|
+
filtered_structures = [
|
|
232
|
+
obj
|
|
233
|
+
for obj in self.structure_list
|
|
234
|
+
if not isinstance(obj, FiberFusing.background.BackGround)
|
|
235
|
+
]
|
|
210
236
|
|
|
211
237
|
if len(filtered_structures) == 0:
|
|
212
|
-
raise ValueError(
|
|
238
|
+
raise ValueError(
|
|
239
|
+
"No structures provided (other than background) for computing the mesh."
|
|
240
|
+
)
|
|
213
241
|
|
|
214
242
|
x_min, y_min, x_max, y_max = zip(
|
|
215
243
|
*(obj.get_structure_max_min_boundaries() for obj in filtered_structures)
|
|
@@ -226,7 +254,11 @@ class Geometry():
|
|
|
226
254
|
float
|
|
227
255
|
Maximum refractive index.
|
|
228
256
|
"""
|
|
229
|
-
return max(
|
|
257
|
+
return max(
|
|
258
|
+
refractive_index
|
|
259
|
+
for obj in self.structure_list
|
|
260
|
+
for refractive_index in obj.refractive_index_list
|
|
261
|
+
)
|
|
230
262
|
|
|
231
263
|
@property
|
|
232
264
|
def refractive_index_minimum(self) -> float:
|
|
@@ -238,7 +270,12 @@ class Geometry():
|
|
|
238
270
|
float
|
|
239
271
|
Minimum refractive index.
|
|
240
272
|
"""
|
|
241
|
-
return min(
|
|
273
|
+
return min(
|
|
274
|
+
refractive_index
|
|
275
|
+
for obj in self.structure_list
|
|
276
|
+
if not isinstance(obj, FiberFusing.background.BackGround)
|
|
277
|
+
for refractive_index in obj.refractive_index_list
|
|
278
|
+
)
|
|
242
279
|
|
|
243
280
|
def get_index_range(self) -> List[float]:
|
|
244
281
|
"""
|
|
@@ -276,7 +313,12 @@ class Geometry():
|
|
|
276
313
|
"""
|
|
277
314
|
for fiber in self.fiber_list:
|
|
278
315
|
for structure in fiber.inner_structure:
|
|
279
|
-
adjustment =
|
|
316
|
+
adjustment = (
|
|
317
|
+
structure.refractive_index
|
|
318
|
+
* self.refractive_index_scrambling
|
|
319
|
+
* numpy.random.rand()
|
|
320
|
+
* random_factor
|
|
321
|
+
)
|
|
280
322
|
structure.refractive_index += adjustment
|
|
281
323
|
|
|
282
324
|
self.mesh = self.generate_mesh()
|
|
@@ -298,7 +340,9 @@ class Geometry():
|
|
|
298
340
|
mesh = numpy.zeros(self.coordinate_system.shape)
|
|
299
341
|
|
|
300
342
|
for structure in self.structure_list:
|
|
301
|
-
structure.overlay_structures_on_mesh(
|
|
343
|
+
structure.overlay_structures_on_mesh(
|
|
344
|
+
mesh=mesh, coordinate_system=self.coordinate_system
|
|
345
|
+
)
|
|
302
346
|
|
|
303
347
|
return mesh
|
|
304
348
|
|
|
@@ -316,8 +360,10 @@ class Geometry():
|
|
|
316
360
|
AttributeError
|
|
317
361
|
If the coordinate system has not been generated before calling this method.
|
|
318
362
|
"""
|
|
319
|
-
if not hasattr(self,
|
|
320
|
-
raise AttributeError(
|
|
363
|
+
if not hasattr(self, "coordinate_system"):
|
|
364
|
+
raise AttributeError(
|
|
365
|
+
"Coordinate system has not been generated. Call generate_coordinate_system() first."
|
|
366
|
+
)
|
|
321
367
|
|
|
322
368
|
mesh = self.rasterize_polygons()
|
|
323
369
|
|
|
@@ -326,8 +372,8 @@ class Geometry():
|
|
|
326
372
|
|
|
327
373
|
return mesh
|
|
328
374
|
|
|
329
|
-
@
|
|
330
|
-
def plot_patch(self,
|
|
375
|
+
@plotting(nrows=1, ncols=1)
|
|
376
|
+
def plot_patch(self, axes: plt.Axes) -> None:
|
|
331
377
|
"""
|
|
332
378
|
Render the patch representation of the geometry onto a given matplotlib axis.
|
|
333
379
|
|
|
@@ -347,16 +393,24 @@ class Geometry():
|
|
|
347
393
|
continue
|
|
348
394
|
|
|
349
395
|
if isinstance(structure, FiberFusing.profile.Profile):
|
|
350
|
-
structure.plot(
|
|
396
|
+
structure.plot(
|
|
397
|
+
axes=axes,
|
|
398
|
+
show=False,
|
|
399
|
+
show_added=False,
|
|
400
|
+
show_removed=False,
|
|
401
|
+
show_centers=False,
|
|
402
|
+
show_fibers=True,
|
|
403
|
+
)
|
|
351
404
|
continue
|
|
352
405
|
|
|
353
|
-
structure.plot(
|
|
406
|
+
structure.plot(axes=axes, show=False)
|
|
354
407
|
|
|
355
|
-
|
|
356
|
-
|
|
408
|
+
axes.set(
|
|
409
|
+
title="Fiber structure", xlabel=r"x-distance [m]", ylabel=r"y-distance [m]"
|
|
410
|
+
)
|
|
357
411
|
|
|
358
|
-
@
|
|
359
|
-
def plot_raster(self,
|
|
412
|
+
@plotting(nrows=1, ncols=1)
|
|
413
|
+
def plot_raster(self, axes: plt.Axes, gamma: float = 5) -> None:
|
|
360
414
|
"""
|
|
361
415
|
Render the rasterized representation of the geometry onto a given matplotlib axis.
|
|
362
416
|
|
|
@@ -373,31 +427,35 @@ class Geometry():
|
|
|
373
427
|
-------
|
|
374
428
|
None
|
|
375
429
|
"""
|
|
376
|
-
image =
|
|
430
|
+
image = axes.pcolormesh(
|
|
377
431
|
self.coordinate_system.x_vector,
|
|
378
432
|
self.coordinate_system.y_vector,
|
|
379
433
|
self.mesh,
|
|
380
|
-
cmap=
|
|
381
|
-
norm=colors.PowerNorm(gamma=gamma)
|
|
434
|
+
cmap="Blues",
|
|
435
|
+
norm=colors.PowerNorm(gamma=gamma),
|
|
382
436
|
)
|
|
383
437
|
|
|
384
|
-
divider = make_axes_locatable(
|
|
385
|
-
cax = divider.append_axes(
|
|
386
|
-
|
|
438
|
+
divider = make_axes_locatable(axes)
|
|
439
|
+
cax = divider.append_axes("right", size="5%", pad=0.05)
|
|
440
|
+
axes.get_figure().colorbar(image, cax=cax, orientation="vertical")
|
|
387
441
|
|
|
388
|
-
|
|
389
|
-
|
|
442
|
+
axes.set(
|
|
443
|
+
title="Fiber structure", xlabel=r"x-distance [m]", ylabel=r"y-distance [m]"
|
|
444
|
+
)
|
|
390
445
|
|
|
391
|
-
|
|
446
|
+
@plotting(
|
|
447
|
+
nrows=1,
|
|
448
|
+
ncols=2,
|
|
449
|
+
subplot_kw=dict(
|
|
450
|
+
aspect="equal", xlabel="x-distance [m]", ylabel="y-distance [m]"
|
|
451
|
+
),
|
|
452
|
+
)
|
|
453
|
+
def plot(self, axes, gamma: float = 5) -> plt.Figure:
|
|
392
454
|
"""
|
|
393
455
|
Plot the different representations (patch and mesh) of the geometry.
|
|
394
456
|
|
|
395
457
|
Parameters
|
|
396
458
|
----------
|
|
397
|
-
show_patch : bool, optional
|
|
398
|
-
Whether to display the patch representation of the geometry. Default is True.
|
|
399
|
-
show_mesh : bool, optional
|
|
400
|
-
Whether to display the mesh (rasterized) representation of the geometry. Default is True.
|
|
401
459
|
show : bool, optional
|
|
402
460
|
Whether to immediately show the plot. Default is True.
|
|
403
461
|
gamma : float, optional
|
|
@@ -408,29 +466,9 @@ class Geometry():
|
|
|
408
466
|
plt.Figure
|
|
409
467
|
The matplotlib figure encompassing all the axes used in the plot.
|
|
410
468
|
"""
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
with plt.style.context(mps):
|
|
415
|
-
figure, axes = plt.subplots(
|
|
416
|
-
*unit_size,
|
|
417
|
-
figsize=5 * numpy.flip(unit_size),
|
|
418
|
-
sharex=True,
|
|
419
|
-
sharey=True,
|
|
420
|
-
subplot_kw=dict(aspect='equal', xlabel='x-distance [m]', ylabel='y-distance [m]'),
|
|
421
|
-
)
|
|
422
|
-
|
|
423
|
-
axes_iter = iter(axes.flatten())
|
|
424
|
-
|
|
425
|
-
if show_patch:
|
|
426
|
-
ax = next(axes_iter)
|
|
427
|
-
self.plot_patch(ax, show=False)
|
|
428
|
-
|
|
429
|
-
if show_mesh:
|
|
430
|
-
ax = next(axes_iter)
|
|
431
|
-
self.plot_raster(ax, show=False, gamma=gamma)
|
|
469
|
+
axes[0].sharex(axes[1])
|
|
470
|
+
axes[0].sharey(axes[1])
|
|
432
471
|
|
|
433
|
-
|
|
434
|
-
plt.show()
|
|
472
|
+
self.plot_patch(axes=axes[0], show=False)
|
|
435
473
|
|
|
436
|
-
|
|
474
|
+
self.plot_raster(axes=axes[1], show=False, gamma=gamma)
|