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.
Files changed (121) hide show
  1. fiberfusing-1.9.7/FiberFusing/_version.py +5 -0
  2. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/background.py +2 -3
  3. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/connection.py +3 -2
  4. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/connection_optimization.py +17 -2
  5. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/coordinate_system.py +4 -2
  6. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/generic_fiber.py +18 -9
  7. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/__init__.py +0 -8
  8. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/base_class.py +2 -1
  9. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/linestring.py +4 -2
  10. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/point.py +9 -7
  11. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/polygon.py +6 -6
  12. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometry.py +113 -75
  13. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/helper.py +0 -28
  14. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/optical_structure.py +7 -5
  15. fiberfusing-1.9.7/FiberFusing/plottings.py +145 -0
  16. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/profile.py +116 -79
  17. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/circle.py +7 -2
  18. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/ellipse.py +8 -3
  19. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/structures.py +20 -0
  20. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/utils.py +11 -0
  21. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/PKG-INFO +125 -88
  22. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/SOURCES.txt +4 -48
  23. fiberfusing-1.9.7/FiberFusing.egg-info/requires.txt +28 -0
  24. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/PKG-INFO +125 -88
  25. fiberfusing-1.9.7/README.rst +210 -0
  26. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/pyproject.toml +31 -40
  27. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_cooordinate_system.py +0 -1
  28. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_fused_structure.py +2 -2
  29. fiberfusing-1.9.7/tests/test_packaging.py +42 -0
  30. fiberfusing-1.9.7/tests/test_plotting.py +127 -0
  31. fiberfusing-1.9.7/tests/test_profile_invariants.py +213 -0
  32. fiberfusing-1.9.4/.flake8 +0 -6
  33. fiberfusing-1.9.4/.github/dependabot.yml +0 -11
  34. fiberfusing-1.9.4/.github/workflows/deploy_PyPi.yml +0 -20
  35. fiberfusing-1.9.4/.github/workflows/deploy_anaconda.yml +0 -21
  36. fiberfusing-1.9.4/.github/workflows/deploy_coverage.yml +0 -25
  37. fiberfusing-1.9.4/.github/workflows/deploy_documentation.yml +0 -24
  38. fiberfusing-1.9.4/.gitignore +0 -28
  39. fiberfusing-1.9.4/FiberFusing/_version.py +0 -34
  40. fiberfusing-1.9.4/FiberFusing/plottings.py +0 -43
  41. fiberfusing-1.9.4/FiberFusing.egg-info/requires.txt +0 -20
  42. fiberfusing-1.9.4/README.rst +0 -180
  43. fiberfusing-1.9.4/docs/Makefile +0 -19
  44. fiberfusing-1.9.4/docs/examples/README.rst +0 -7
  45. fiberfusing-1.9.4/docs/examples/extra/README.rst +0 -4
  46. fiberfusing-1.9.4/docs/examples/extra/custom_graded_index_fiber.py +0 -46
  47. fiberfusing-1.9.4/docs/examples/extra/custom_step_index_fiber.py +0 -40
  48. fiberfusing-1.9.4/docs/examples/extra/loading_fiber.py +0 -18
  49. fiberfusing-1.9.4/docs/examples/extra/randomize_core.py +0 -31
  50. fiberfusing-1.9.4/docs/examples/extra/structure_shift.py +0 -28
  51. fiberfusing-1.9.4/docs/examples/geometry/README.rst +0 -4
  52. fiberfusing-1.9.4/docs/examples/geometry/plot_line_3.py +0 -54
  53. fiberfusing-1.9.4/docs/examples/geometry/plot_line_4.py +0 -47
  54. fiberfusing-1.9.4/docs/examples/geometry/plot_line_5.py +0 -47
  55. fiberfusing-1.9.4/docs/examples/geometry/plot_ring_10.py +0 -47
  56. fiberfusing-1.9.4/docs/examples/geometry/plot_ring_2.py +0 -48
  57. fiberfusing-1.9.4/docs/examples/geometry/plot_ring_3.py +0 -47
  58. fiberfusing-1.9.4/docs/examples/geometry/plot_ring_4.py +0 -51
  59. fiberfusing-1.9.4/docs/examples/geometry/plot_ring_5.py +0 -48
  60. fiberfusing-1.9.4/docs/examples/geometry/plot_ring_7.py +0 -48
  61. fiberfusing-1.9.4/docs/examples/geometry/plot_single.py +0 -45
  62. fiberfusing-1.9.4/docs/images/example_3x3.png +0 -0
  63. fiberfusing-1.9.4/docs/images/logo.png +0 -0
  64. fiberfusing-1.9.4/docs/make.bat +0 -35
  65. fiberfusing-1.9.4/docs/source/_static/default.css +0 -19
  66. fiberfusing-1.9.4/docs/source/_static/thumbnail.png +0 -0
  67. fiberfusing-1.9.4/docs/source/code/components.rst +0 -10
  68. fiberfusing-1.9.4/docs/source/code/coordinate_system.rst +0 -12
  69. fiberfusing-1.9.4/docs/source/code/geometry.rst +0 -12
  70. fiberfusing-1.9.4/docs/source/code/shapes.rst +0 -9
  71. fiberfusing-1.9.4/docs/source/code/utils.rst +0 -19
  72. fiberfusing-1.9.4/docs/source/code.rst +0 -17
  73. fiberfusing-1.9.4/docs/source/conf.py +0 -175
  74. fiberfusing-1.9.4/docs/source/gallery/index.rst +0 -331
  75. fiberfusing-1.9.4/docs/source/index.rst +0 -13
  76. fiberfusing-1.9.4/docs/source/references.rst +0 -6
  77. fiberfusing-1.9.4/docs/source/sg_execution_times.rst +0 -79
  78. fiberfusing-1.9.4/docs/source/theory.rst +0 -5
  79. fiberfusing-1.9.4/meta.yaml +0 -65
  80. fiberfusing-1.9.4/notebook.ipynb +0 -202
  81. fiberfusing-1.9.4/tests/__init__.py +0 -0
  82. fiberfusing-1.9.4/tests/fiber_loader.py +0 -91
  83. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/__init__.py +0 -0
  84. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/__init__.py +0 -0
  85. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/connection/utils.py +0 -0
  86. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/directories.py +0 -0
  87. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/__init__.py +0 -0
  88. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/1550BHP.yaml +0 -0
  89. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF13.yaml +0 -0
  90. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_20.yaml +0 -0
  91. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_26.yaml +0 -0
  92. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_33.yaml +0 -0
  93. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/DCF1300S_42.yaml +0 -0
  94. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2028M12.yaml +0 -0
  95. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2028M21.yaml +0 -0
  96. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2028M24.yaml +0 -0
  97. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2058G1.yaml +0 -0
  98. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/F2058L1.yaml +0 -0
  99. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/HI1060.yaml +0 -0
  100. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/HP630.yaml +0 -0
  101. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/SM1950.yaml +0 -0
  102. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/SMF28.yaml +0 -0
  103. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/fluorine_doped_1%_capillary.yaml +0 -0
  104. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/fluorine_doped_2%_capillary.yaml +0 -0
  105. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/test_fiber.yaml +0 -0
  106. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/fiber_files/test_multimode_fiber.yaml +0 -0
  107. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/fiber/loader.py +0 -0
  108. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/geometries/utils.py +0 -0
  109. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/graded_index.py +0 -0
  110. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/__init__.py +0 -0
  111. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/base.py +0 -0
  112. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing/shapes/square.py +0 -0
  113. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/dependency_links.txt +0 -0
  114. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/FiberFusing.egg-info/top_level.txt +0 -0
  115. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/LICENSE +0 -0
  116. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/setup.cfg +0 -0
  117. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_boundaries.py +0 -0
  118. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_fiber.py +0 -0
  119. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_generic_fiber.py +0 -0
  120. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_geometries.py +0 -0
  121. {fiberfusing-1.9.4 → fiberfusing-1.9.7}/tests/test_polygon.py +0 -0
@@ -0,0 +1,5 @@
1
+ """Release version maintained by martinpdes-maintenance."""
2
+
3
+ __version__ = version = "1.9.7"
4
+ __version_tuple__ = version_tuple = tuple(int(part) for part in version.split("."))
5
+ __commit_id__ = commit_id = None
@@ -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=ConfigDict(extra='forbid', kw_only=True))
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
- logging.warning(f"Optimization failed: {result.message}")
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 = len(self.fiber_list) * self.fiber_list[0].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 ConfigDict, field_validator
7
+ from pydantic import field_validator
8
8
  from dataclasses import field
9
9
 
10
+ from FiberFusing.utils import config_dict
10
11
 
11
- @dataclass(config=ConfigDict(extra='forbid', strict=True, kw_only=True, arbitrary_types_allowed=True, frozen=False))
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
- @_plot_helper
512
- def plot(self, ax: plt.Axes = None) -> None:
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=ax, polygon=structure.polygon._shapely_object)
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
- @_plot_helper
569
- def plot_raster(self, coordinate_system, ax: plt.Axes = None) -> None:
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
- ax.pcolormesh(coordinate_system.x_vector, coordinate_system.y_vector, mesh, cmap='Blues')
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 Tuple, Callable, Self
4
+ from typing import Tuple, Callable
5
+ from typing_extensions import Self
5
6
  import copy
6
7
  from shapely import affinity
7
8
 
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
- from typing import Optional, Tuple, Self
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=geometries.config_dict)
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, Self
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=geometries.config_dict)
16
+ @dataclass(config=config_dict)
15
17
  class Point(geometries.base_class.Alteration):
16
- position: Optional[Tuple[float, float]] = None
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
- @_plot_helper
110
- def plot(self, ax: plt.Axes = None, marker: str = 'x', size: int = 20, label: str = None) -> None:
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
- ax.scatter(self.x, self.y, label=label, marker=marker, s=size)
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, Self
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
- @_plot_helper
146
- def plot(self, ax: plt.Axes, **kwargs) -> None:
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=ax, polygon=self._shapely_object, **kwargs)
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 MPSPlots.styles import mps
14
- from FiberFusing.helper import _plot_helper
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=ConfigDict(extra='forbid', kw_only=True, arbitrary_types_allowed=True))
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('resolution')
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('Resolution must be positive')
85
+ raise ValueError("Resolution must be positive")
82
86
  return v
83
87
 
84
- @field_validator('boundary_pad_factor')
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('Boundary pad factor must be positive')
93
+ raise ValueError("Boundary pad factor must be positive")
90
94
  return v
91
95
 
92
- @field_validator('index_scrambling')
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('Index scrambling must be non-negative')
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, x_max=x_max, y_min=y_min, y_max=y_max, nx=self.resolution, ny=self.resolution
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, x_max=x_max, y_min=y_min, y_max=y_max, nx=self.resolution, ny=self.resolution
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('x_bounds')
146
+ @field_validator("x_bounds")
133
147
  @classmethod
134
- def validate_x_bounds(cls, v: Union[DomainAlignment, Tuple[float, float]]) -> Union[DomainAlignment, Tuple[float, float]]:
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('y_bounds')
161
+ @field_validator("y_bounds")
146
162
  @classmethod
147
- def validate_y_bounds(cls, v: Union[DomainAlignment, Tuple[float, float]]) -> Union[DomainAlignment, Tuple[float, float]]:
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, 'coordinate_system') and self.coordinate_system is not None:
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 = self.x_bounds
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 = self.y_bounds
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 = [obj for obj in self.structure_list if not isinstance(obj, FiberFusing.background.BackGround)]
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('No structures provided (other than background) for computing the mesh.')
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(refractive_index for obj in self.structure_list for refractive_index in obj.refractive_index_list)
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(refractive_index for obj in self.structure_list if not isinstance(obj, FiberFusing.background.BackGround) for refractive_index in obj.refractive_index_list)
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 = structure.refractive_index * self.refractive_index_scrambling * numpy.random.rand() * random_factor
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(mesh=mesh, coordinate_system=self.coordinate_system)
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, 'coordinate_system'):
320
- raise AttributeError("Coordinate system has not been generated. Call generate_coordinate_system() first.")
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
- @_plot_helper
330
- def plot_patch(self, ax: plt.Axes = None, show: bool = True) -> None:
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(ax=ax, show=False, show_added=False, show_removed=False, show_centers=False, show_fibers=True)
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(ax=ax, show=False)
406
+ structure.plot(axes=axes, show=False)
354
407
 
355
- ax.set(title='Fiber structure', xlabel=r'x-distance [m]', ylabel=r'y-distance [m]')
356
- ax.ticklabel_format(axis='both', style='sci', scilimits=(-6, -6), useOffset=False)
408
+ axes.set(
409
+ title="Fiber structure", xlabel=r"x-distance [m]", ylabel=r"y-distance [m]"
410
+ )
357
411
 
358
- @_plot_helper
359
- def plot_raster(self, ax: plt.Axes = None, gamma: float = 5) -> None:
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 = ax.pcolormesh(
430
+ image = axes.pcolormesh(
377
431
  self.coordinate_system.x_vector,
378
432
  self.coordinate_system.y_vector,
379
433
  self.mesh,
380
- cmap='Blues',
381
- norm=colors.PowerNorm(gamma=gamma)
434
+ cmap="Blues",
435
+ norm=colors.PowerNorm(gamma=gamma),
382
436
  )
383
437
 
384
- divider = make_axes_locatable(ax)
385
- cax = divider.append_axes('right', size='5%', pad=0.05)
386
- ax.get_figure().colorbar(image, cax=cax, orientation='vertical')
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
- ax.set(title='Fiber structure', xlabel=r'x-distance [m]', ylabel=r'y-distance [m]')
389
- ax.ticklabel_format(axis='both', style='sci', scilimits=(-6, -6), useOffset=False)
442
+ axes.set(
443
+ title="Fiber structure", xlabel=r"x-distance [m]", ylabel=r"y-distance [m]"
444
+ )
390
445
 
391
- def plot(self, show_patch: bool = True, show_mesh: bool = True, show: bool = True, gamma: float = 5) -> plt.Figure:
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
- n_ax = bool(show_patch) + bool(show_mesh)
412
- unit_size = numpy.array([1, n_ax])
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
- if show:
434
- plt.show()
472
+ self.plot_patch(axes=axes[0], show=False)
435
473
 
436
- return figure
474
+ self.plot_raster(axes=axes[1], show=False, gamma=gamma)