simcats 1.1.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
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.
- simcats/__init__.py +4 -3
- simcats/_default_configs.py +129 -13
- simcats/_simulation.py +451 -69
- simcats/config_samplers/_GaAs_v1_random_variations_v3_config_sampler.py +1059 -0
- simcats/config_samplers/__init__.py +9 -0
- simcats/distortions/_distortion_interfaces.py +1 -1
- simcats/distortions/_dot_jumps.py +8 -6
- simcats/distortions/_random_telegraph_noise.py +4 -4
- simcats/distortions/_transition_blurring.py +5 -5
- simcats/distortions/_white_noise.py +2 -2
- simcats/ideal_csd/geometric/_generate_lead_transition_mask.py +3 -3
- simcats/ideal_csd/geometric/_get_electron_occupation.py +5 -5
- simcats/ideal_csd/geometric/_ideal_csd_geometric.py +5 -5
- simcats/ideal_csd/geometric/_ideal_csd_geometric_class.py +9 -9
- simcats/ideal_csd/geometric/_tct_bezier.py +5 -5
- simcats/sensor/__init__.py +10 -6
- simcats/sensor/{_generic_sensor.py → _sensor_generic.py} +1 -1
- simcats/sensor/_sensor_interface.py +164 -11
- simcats/sensor/_sensor_rise_glf.py +229 -0
- simcats/sensor/_sensor_scan_sensor_generic.py +929 -0
- simcats/sensor/barrier_function/__init__.py +9 -0
- simcats/sensor/barrier_function/_barrier_function_glf.py +280 -0
- simcats/sensor/barrier_function/_barrier_function_interface.py +43 -0
- simcats/sensor/barrier_function/_barrier_function_multi_glf.py +157 -0
- simcats/sensor/deformation/__init__.py +9 -0
- simcats/sensor/deformation/_sensor_peak_deformation_circle.py +109 -0
- simcats/sensor/deformation/_sensor_peak_deformation_interface.py +65 -0
- simcats/sensor/deformation/_sensor_peak_deformation_linear.py +77 -0
- simcats/support_functions/__init__.py +11 -3
- simcats/support_functions/_generalized_logistic_function.py +146 -0
- simcats/support_functions/_linear_algebra.py +171 -0
- simcats/support_functions/_parameter_sampling.py +108 -19
- simcats/support_functions/_pixel_volt_transformation.py +24 -0
- simcats/support_functions/_reset_offset_mu_sens.py +43 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info}/METADATA +93 -29
- simcats-2.0.0.dist-info/RECORD +53 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info}/WHEEL +1 -1
- simcats-1.1.0.dist-info/RECORD +0 -37
- /simcats/sensor/{_gaussian_sensor_peak.py → _sensor_peak_gaussian.py} +0 -0
- /simcats/sensor/{_lorentzian_sensor_peak.py → _sensor_peak_lorentzian.py} +0 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""This module contains the interface for simulating deformations of sensor peaks in scans.
|
|
2
|
+
|
|
3
|
+
@author: b.papajewski
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
__all__ = []
|
|
12
|
+
|
|
13
|
+
from simcats.sensor import SensorInterface
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SensorPeakDeformationInterface(ABC):
|
|
17
|
+
"""General interface for sensor peak deformations.
|
|
18
|
+
|
|
19
|
+
Classes that implement this abstract class realize different types of deformation of a single wavefront.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def calc_mu(self, dist: Union[float, np.ndarray], mu0: float) -> float:
|
|
24
|
+
"""
|
|
25
|
+
Method that calculates the deformed potential value of mu0 of a sensor peak that is moved based on
|
|
26
|
+
the distance (`dist`) to a middle line from which the deformation originates. The middle line follows
|
|
27
|
+
the direction of the sensor peaks (i.e., the sensor dot potential) and is perpendicular to the wavefronts
|
|
28
|
+
defined by the peaks.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
dist (Union[float, np.ndarray]): Distance from the middle line, from which the deformation originates. If a
|
|
32
|
+
point is above the middle line the distance is positive and if it is below, the distance is negative.
|
|
33
|
+
The parameter can either be a np.ndarray that contains multiple distances or a single distance as a
|
|
34
|
+
float.
|
|
35
|
+
mu0 (float): Potential offset for calculation of the deformed potential. Usually this should be the
|
|
36
|
+
potential offset of the sensor peak, that should be deformed.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Union[float, np.ndarray]: Deformed potential value mu0 of a sensor peak. The same type as the type of
|
|
40
|
+
`dist` is returned.
|
|
41
|
+
"""
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def sensor(self) -> SensorInterface:
|
|
46
|
+
"""
|
|
47
|
+
Returns the sensor object to which the deformed sensor peak belongs.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
SensorInterface: Sensor object of the deformed sensor peak.
|
|
51
|
+
"""
|
|
52
|
+
if self.__sensor:
|
|
53
|
+
return self.__sensor
|
|
54
|
+
else:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
@sensor.setter
|
|
58
|
+
def sensor(self, sensor: SensorInterface) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Sets the sensor object to which the deformed sensor peak belongs.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
sensor (SensorInterface): Sensor object of the deformed sensor peak.
|
|
64
|
+
"""
|
|
65
|
+
self.__sensor = sensor
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""This module contains the class for simulating a linear deformation of sensor peaks in a scan.
|
|
2
|
+
|
|
3
|
+
@author: b.papajewski
|
|
4
|
+
"""
|
|
5
|
+
import math
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from simcats.sensor.deformation import SensorPeakDeformationInterface
|
|
11
|
+
|
|
12
|
+
__all__ = []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SensorPeakDeformationLinear(SensorPeakDeformationInterface):
|
|
16
|
+
"""Linear deformation implementation of the SensorPeakDeformationInterface."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, angle: float) -> None:
|
|
19
|
+
"""Initialization of an object of the class LinearSensorPeakDeformation.
|
|
20
|
+
This kind of deformation uses a single line for the deformation of a sensor peak (tilting the wavefront).
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
angle (float): Angle of the line that is used for the linear deformation of the sensor peak. This Angle
|
|
24
|
+
is specified in radians. The angle between the two straight lines is specified, which lies above the
|
|
25
|
+
deformation line and to the right of the middle line.
|
|
26
|
+
"""
|
|
27
|
+
super().__init__()
|
|
28
|
+
|
|
29
|
+
self.angle = angle
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def angle(self) -> float:
|
|
33
|
+
"""Returns the angle of the line used for deforming the sensor peak.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
float: Angle of the line that is used for the linear deformation of the sensor peak.
|
|
37
|
+
"""
|
|
38
|
+
return self.__angle
|
|
39
|
+
|
|
40
|
+
@angle.setter
|
|
41
|
+
def angle(self, angle: float):
|
|
42
|
+
self.__angle = angle
|
|
43
|
+
|
|
44
|
+
def calc_mu(self, dist: Union[float, np.ndarray], mu0: float) -> Union[float, np.ndarray]:
|
|
45
|
+
"""
|
|
46
|
+
Method that calculates the deformed potential value for mu0 of mu0 of a sensor peak that is moved based on the
|
|
47
|
+
distance (`dist`) to a middle line, from which the deformation originates, using a line. The middle line follows
|
|
48
|
+
the direction of the sensor peaks (i.e., the sensor dot potential) and is perpendicular to the wavefronts
|
|
49
|
+
defined by the peaks.
|
|
50
|
+
|
|
51
|
+
For the deformation, the potential value of the point of the line that has the given distance to the center line
|
|
52
|
+
is used.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
dist (Union[float, np.ndarray]): Distance from the middle line, from which the deformation originates. If a
|
|
56
|
+
point is above the middle line the distance is positive and if it is below, the distance is negative.
|
|
57
|
+
The parameter can either be a np.ndarray that contains multiple distances or a single distance as a
|
|
58
|
+
float.
|
|
59
|
+
mu0 (float): Potential offset for calculation of the deformed potential. Usually this should be the
|
|
60
|
+
potential offset of the sensor peak, that should be deformed.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Union[float, np.ndarray]: Deformed potential value mu0 of a sensor peak. The same type as the type of
|
|
64
|
+
`dist` is returned.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
# Calculation of the gradient of the deformation line
|
|
68
|
+
m = 1 / math.tan(self.angle)
|
|
69
|
+
|
|
70
|
+
dif = m * dist
|
|
71
|
+
dif *= np.linalg.norm(self.sensor.alpha_sensor_gate[0])
|
|
72
|
+
return mu0 + dif
|
|
73
|
+
|
|
74
|
+
def __repr__(self):
|
|
75
|
+
return (
|
|
76
|
+
self.__class__.__name__ + f"(angle={self.angle})"
|
|
77
|
+
)
|
|
@@ -3,14 +3,22 @@ SimCATS subpackage with support functions that are not assigned to any specific
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from simcats.support_functions._parameter_sampling import ParameterSamplingInterface, NormalSamplingRange, \
|
|
6
|
-
LogNormalSamplingRange, UniformSamplingRange
|
|
6
|
+
LogNormalSamplingRange, UniformSamplingRange, ExponentialSamplingRange
|
|
7
7
|
from simcats.support_functions._fermi_filter1d import fermi_filter1d, fermi_dirac_derivative
|
|
8
8
|
from simcats.support_functions._cumulative_distribution_functions import cauchy_cdf, multi_cauchy_cdf, sigmoid_cdf, \
|
|
9
9
|
multi_sigmoid_cdf
|
|
10
|
+
from simcats.support_functions._generalized_logistic_function import glf, inverse_glf, multi_glf
|
|
11
|
+
from simcats.support_functions._reset_offset_mu_sens import reset_offset_mu_sens
|
|
10
12
|
from simcats.support_functions._signed_dist_points_line import signed_dist_points_line
|
|
13
|
+
from simcats.support_functions._linear_algebra import line_line_intersection
|
|
14
|
+
from simcats.support_functions._linear_algebra import line_circle_intersection
|
|
15
|
+
from simcats.support_functions._linear_algebra import is_point_below_line
|
|
16
|
+
from simcats.support_functions._pixel_volt_transformation import pixel_to_volt_1d
|
|
11
17
|
from simcats.support_functions._rotate_points import rotate_points
|
|
12
18
|
from simcats.support_functions._plotting import plot_csd
|
|
13
19
|
|
|
14
20
|
__all__ = ["ParameterSamplingInterface", "NormalSamplingRange", "LogNormalSamplingRange", "UniformSamplingRange",
|
|
15
|
-
"fermi_filter1d", "fermi_dirac_derivative", "cauchy_cdf", "multi_cauchy_cdf",
|
|
16
|
-
"multi_sigmoid_cdf", "
|
|
21
|
+
"ExponentialSamplingRange", "fermi_filter1d", "fermi_dirac_derivative", "cauchy_cdf", "multi_cauchy_cdf",
|
|
22
|
+
"sigmoid_cdf", "multi_sigmoid_cdf", "glf", "inverse_glf", "multi_glf", "reset_offset_mu_sens",
|
|
23
|
+
"signed_dist_points_line", "line_line_intersection", "line_circle_intersection", "is_point_below_line",
|
|
24
|
+
"pixel_to_volt_1d", "rotate_points", "plot_csd"]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""This module contains an implementation of the generalized logistic function (GLF) used for sensor barriers.
|
|
2
|
+
|
|
3
|
+
@author: b.papajewski
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def glf(potential: Union[float, np.ndarray],
|
|
12
|
+
asymptote_left: float,
|
|
13
|
+
asymptote_right: float,
|
|
14
|
+
growth_rate: float,
|
|
15
|
+
asymmetry: float,
|
|
16
|
+
shape_factor: float,
|
|
17
|
+
denominator_offset: float = 1,
|
|
18
|
+
offset: float = 0
|
|
19
|
+
) -> Union[float, np.ndarray]:
|
|
20
|
+
"""Function implementing the generalized logistic function.
|
|
21
|
+
For further information see: https://en.wikipedia.org/wiki/Generalised_logistic_function
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
potential (Union[float, np.ndarray]): Originally called t. The potential is the variable of the GLF for which
|
|
25
|
+
the value of the function should be calculated.
|
|
26
|
+
asymptote_left (float): Originally called A. This parameter is the left horizontal asymptote of the function.
|
|
27
|
+
Any rational number can be used as the left asymptote. This parameter may take any rational number.
|
|
28
|
+
asymptote_right (float): Originally called K. Specifies the right horizontal asymptote of the function
|
|
29
|
+
when denominator_offset=1. If asymptote_left=0 and denominator_offset=1 then this parameter is also called
|
|
30
|
+
the carrying capacity. This parameter may take any rational number.
|
|
31
|
+
growth_rate (float): Originally called B. The growth rate of the function. The value must be a float and can be
|
|
32
|
+
any rational number. Be careful with negative values, because the function is mirrored on a vertical
|
|
33
|
+
straight line for these. This line passes through the point where the potential equals `offset`.
|
|
34
|
+
asymmetry (float): Originally called nu. This parameter introduces skew and affects symmetry. It also affects
|
|
35
|
+
near which asymptote maximum growth occurs. The value of asymmetry must be a rational number greater than
|
|
36
|
+
zero. \n
|
|
37
|
+
- `asymmetry > 1`: the curve rises more gradually before the midpoint and more sharply after. \n
|
|
38
|
+
- `asymmetry < 1`: the curve rises quickly early on and levels off more slowly.
|
|
39
|
+
shape_factor (float): Originally called Q. is related to the value Y(0) and adjusts the curve’s value at the
|
|
40
|
+
y-intercept. Thereby it changes the shape of the function without changing the asymptotes. The shape factor
|
|
41
|
+
can be any rational number.
|
|
42
|
+
denominator_offset (float): Originally called C. A constant added to the denominator inside the power. Controls
|
|
43
|
+
the initial level of the denominator.This parameter must be a rational number.
|
|
44
|
+
It typically takes a value of 1. Otherwise, the upper asymptote is
|
|
45
|
+
asymptote_left + (asymptote_right-asymptote_left)/(denominator_offset^(1/asymmetry)).
|
|
46
|
+
offset (float): Parameter that shifts the function starting from the zero point. If the offset is positive, the
|
|
47
|
+
function is shifted to the right and if it is negative, it is shifted to the left.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Union[float, np.ndarray]: Value of the GLF at the given potential (originally time t) for a given set of
|
|
51
|
+
parameters. The returned datatype is the same as the type of `potential`.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
return asymptote_left + ((asymptote_right - asymptote_left) / (
|
|
55
|
+
np.power(denominator_offset + shape_factor * np.exp(-growth_rate * (potential - offset)), (1 / asymmetry))))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def inverse_glf(
|
|
59
|
+
value: Union[float, np.ndarray],
|
|
60
|
+
asymptote_left: float,
|
|
61
|
+
asymptote_right: float,
|
|
62
|
+
growth_rate: float,
|
|
63
|
+
asymmetry: float,
|
|
64
|
+
shape_factor: float,
|
|
65
|
+
denominator_offset: float = 1,
|
|
66
|
+
offset: float = 0
|
|
67
|
+
) -> Union[float, np.ndarray]:
|
|
68
|
+
"""
|
|
69
|
+
Inverse of the function `glf` which computes the inverse of a generalized logistic function for a set of parameters.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
value: The input value(s) for which to compute the inverse GLF. Can be a single float or a numpy array.
|
|
73
|
+
asymptote_left (float): Originally called A. This parameter is the left horizontal asymptote of the function.
|
|
74
|
+
Any rational number can be used as the left asymptote. This parameter may take any rational number.
|
|
75
|
+
asymptote_right (float): Originally called K. Specifies the right horizontal asymptote of the function
|
|
76
|
+
when denominator_offset=1. If asymptote_left=0 and denominator_offset=1 then this parameter is also called
|
|
77
|
+
the carrying capacity. This parameter may take any rational number.
|
|
78
|
+
growth_rate (float): Originally called B. The growth rate of the function. The value must be a float and can be
|
|
79
|
+
any rational number. Be careful with negative values, because the function is mirrored on a vertical
|
|
80
|
+
straight line for these. This line passes through the point where the potential equals `offset`.
|
|
81
|
+
asymmetry (float): Originally called nu. This parameter introduces skew and affects symmetry. It also affects
|
|
82
|
+
near which asymptote maximum growth occurs. The value of asymmetry must be a rational number greater than
|
|
83
|
+
zero. \n
|
|
84
|
+
- `asymmetry > 1`: the curve rises more gradually before the midpoint and more sharply after. \n
|
|
85
|
+
- `asymmetry < 1`: the curve rises quickly early on and levels off more slowly.
|
|
86
|
+
shape_factor (float): Originally called Q. is related to the value Y(0) and adjusts the curve’s value at the
|
|
87
|
+
y-intercept. Thereby it changes the shape of the function without changing the asymptotes. The shape factor
|
|
88
|
+
can be any rational number.
|
|
89
|
+
denominator_offset (float): Originally called C. A constant added to the denominator inside the power. Controls
|
|
90
|
+
the initial level of the denominator.This parameter must be a rational number.
|
|
91
|
+
It typically takes a value of 1. Otherwise, the upper asymptote is \n
|
|
92
|
+
asymptote_left + (asymptote_right-asymptote_left)/(denominator_offset^(1/asymmetry)).
|
|
93
|
+
offset (float): Parameter that shifts the function starting from the zero point. If the offset is positive, the
|
|
94
|
+
function is shifted to the right and if it is negative, it is shifted to the left.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Union[float, np.ndarray]: Potential (originally time t) of the inverse GLF at the given value for a given set of
|
|
98
|
+
parameters. The returned datatype is the same as the type of `value`.
|
|
99
|
+
"""
|
|
100
|
+
return (growth_rate * offset - np.log((((asymptote_left - asymptote_right) / (
|
|
101
|
+
asymptote_left - value)) ** asymmetry - denominator_offset) / shape_factor)) / growth_rate
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def multi_glf(potential: Union[float, np.ndarray], *params: float) -> Union[float, np.ndarray]:
|
|
105
|
+
"""Function that combines several GLFs as a sum into one function.
|
|
106
|
+
For further information see: https://en.wikipedia.org/wiki/Generalised_logistic_function
|
|
107
|
+
|
|
108
|
+
Each GLF has the following parameters: \n
|
|
109
|
+
- asymptote_left (float): Originally called A. This parameter is the left horizontal asymptote of the function.
|
|
110
|
+
Any rational number can be used as the left asymptote.
|
|
111
|
+
- asymptote_right (float): Originally called K. Specifies the right horizontal asymptote of the function
|
|
112
|
+
when denominator_offset=1. If asymptote_left=0 and denominator_offset=1 then this parameter is also called the
|
|
113
|
+
carrying capacity. This parameter may take any rational number.
|
|
114
|
+
- growth_rate (float): Originally called B. The growth rate of the function. The value must be a float and can be
|
|
115
|
+
any rational number. Be careful with negative values, because the function is mirrored on a vertical straight
|
|
116
|
+
line for these. This line passes through the point where the potential equals `offset`.
|
|
117
|
+
- asymmetry (float): Originally called nu. This parameter introduces skew and affects symmetry. It also affects
|
|
118
|
+
near which asymptote maximum growth occurs. The value of asymmetry must be a rational number greater than zero. \n
|
|
119
|
+
- If `asymmetry > 1`: the curve rises more gradually before the midpoint and more sharply after. \n
|
|
120
|
+
- If `asymmetry < 1`: the curve rises quickly early on and levels off more slowly.
|
|
121
|
+
- shape_factor (float): Originally called Q. is related to the value Y(0) and adjusts the curve’s value at the
|
|
122
|
+
y-intercept. Thereby it changes the shape of the function without changing the asymptotes. The shape factor
|
|
123
|
+
can be any rational number.
|
|
124
|
+
- denominator_offset (float): Originally called C. A constant added to the denominator inside the power. Controls
|
|
125
|
+
the initial level of the denominator.This parameter must be a rational number.
|
|
126
|
+
It typically takes a value of 1. Otherwise, the upper asymptote is \n
|
|
127
|
+
asymptote_left + (asymptote_right - asymptote_left) / (denominator_offset^(1 / asymmetry)).
|
|
128
|
+
- offset (float): Potential offset, that shifts the function starting from the zero point. If the offset is
|
|
129
|
+
positive, the function is shifted to the right and if it is negative, it is shifted to the left.
|
|
130
|
+
|
|
131
|
+
The number of GLFs is specified by the number of parameters. To do this, the parameter count must be divisible by
|
|
132
|
+
seven and a GLF is added for every seven other parameters.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
potential (Union[float, np.ndarray]): Originally called t. The potential is the variable of the GLF for which
|
|
136
|
+
the value of the function should be calculated.
|
|
137
|
+
*params: Additional positional arguments representing the GLF parameters. The number of additional parameters
|
|
138
|
+
must be divisible by seven and determines the number of GLFs that are used for the Multi-GLF. All parameters
|
|
139
|
+
consist of sequential groups of seven floats that each represent a single GLF. All individual parameters are
|
|
140
|
+
described above and are in the same order as they are described.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Union[float, np.ndarray]: Value of the GLF at the given potential (originally time t).
|
|
144
|
+
"""
|
|
145
|
+
assert not (len(params) % 7)
|
|
146
|
+
return sum([glf(potential, *params[i: i + 7]) for i in range(0, len(params), 7)])
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""This module provides functionality based on linear algebra for evaluating geometric relationships, such as between
|
|
2
|
+
two lines or between a point and a line.
|
|
3
|
+
|
|
4
|
+
@author: b.papajewski
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import numbers
|
|
8
|
+
from typing import Union, Sequence, Tuple, List
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_point_below_line(point: Tuple[float, float], line: Union[Tuple[float, float], float]) -> bool:
|
|
14
|
+
"""Method for evaluating whether a point is below a line.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
point (Tuple[float, float]): Point to be evaluated.
|
|
18
|
+
line (Union[Tuple[float, float], float]: Either a straight line (m*x+b) given as a tuple (m,b) or as a single
|
|
19
|
+
value if it is a horizontal line. This then specifies the height b of the line.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
bool: True if the point is below the line, False otherwise.
|
|
23
|
+
"""
|
|
24
|
+
x, y = point
|
|
25
|
+
|
|
26
|
+
# If the straight line is specified as a single float, it is a horizontal line
|
|
27
|
+
if isinstance(line, int):
|
|
28
|
+
b = line
|
|
29
|
+
return y < b
|
|
30
|
+
# If the straight line is specified as a tuple (m, b)
|
|
31
|
+
elif isinstance(line, tuple) and len(line) == 2:
|
|
32
|
+
m, b = line
|
|
33
|
+
y_line = m * x + b
|
|
34
|
+
return y < y_line
|
|
35
|
+
else:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
"The straight line must be specified either as a tuple (m, b) or as a single integer for a horizontal line.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def line_line_intersection(line1: Union[Sequence, np.ndarray, numbers.Real],
|
|
41
|
+
line2: Union[Sequence, np.ndarray, numbers.Real]):
|
|
42
|
+
"""Method for calculating the intersection of two lines.
|
|
43
|
+
Both lines can be specified either by a straight line equation (m*x+b) or by a single number if it is a vertical
|
|
44
|
+
line. The single number gives the x coordinate of the vertical line.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
line1 (Union[Sequence, np.ndarray, numbers.Real]): A two element sequence when it's a straight line equation
|
|
48
|
+
(m,b) or a single number when line1 is a vertical line. In this case, the number indicates the x coordinate
|
|
49
|
+
of the vertical line.
|
|
50
|
+
line2 (Union[Sequence, np.ndarray, numbers.Real]): A two element sequence when it's a straight line equation
|
|
51
|
+
(m,b) or a single number when line1 is a vertical line. In this case, the number indicates the x coordinate
|
|
52
|
+
of the vertical line.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The x and y coordinates of the intersection of the two lines. If there is no intersection, an exception
|
|
56
|
+
is thrown.
|
|
57
|
+
"""
|
|
58
|
+
for i, line in enumerate((line1, line2), start=1):
|
|
59
|
+
if not (isinstance(line, numbers.Real) or len(line) == 2):
|
|
60
|
+
raise ValueError(f"Invalid parameter - line{i} must either be a single number or an sequence with two elements!")
|
|
61
|
+
|
|
62
|
+
# Both lines are straight line equations
|
|
63
|
+
if isinstance(line1, (Sequence, np.ndarray)) and isinstance(line2, (Sequence, np.ndarray)):
|
|
64
|
+
m1, b1 = line1
|
|
65
|
+
m2, b2 = line2
|
|
66
|
+
|
|
67
|
+
# Check if lines are parallel
|
|
68
|
+
if m1 == m2:
|
|
69
|
+
if b1 == b2:
|
|
70
|
+
raise ValueError("The lines are the same.")
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError("The lines are parallel and do not intersect.")
|
|
73
|
+
|
|
74
|
+
# Compute intersection point
|
|
75
|
+
x = (b2 - b1) / (m1 - m2)
|
|
76
|
+
y = m1 * x + b1
|
|
77
|
+
|
|
78
|
+
return np.array([x, y])
|
|
79
|
+
|
|
80
|
+
# Line1 is a straight line equation and line2 is a vertical line
|
|
81
|
+
elif isinstance(line1, (Sequence, np.ndarray)):
|
|
82
|
+
m1, b1 = line1
|
|
83
|
+
return np.array([line2, m1 * line2 + b1])
|
|
84
|
+
|
|
85
|
+
# Line1 is a vertical line and line2 is a straight line equation
|
|
86
|
+
elif isinstance(line2, (Sequence, np.ndarray)):
|
|
87
|
+
m2, b2 = line2
|
|
88
|
+
return np.array([line1, m2 * line1 + b2])
|
|
89
|
+
|
|
90
|
+
# Both lines are vertical lines und therefore also parallel
|
|
91
|
+
else:
|
|
92
|
+
if line1 == line2:
|
|
93
|
+
raise ValueError("The lines are the same.")
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError("The lines are parallel and do not intersect.")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def line_circle_intersection(line: Union[float, Tuple], circle_center: Tuple, radius: float):
|
|
99
|
+
"""Method for calculating the intersection of a line and a circle.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
line (Union(float,Tuple)): The line with which the intersection points are to be calculated. This line can
|
|
103
|
+
either be given as a tuple with the slope and y-intercept in the form (m,b) or as a float if it is a
|
|
104
|
+
vertical line.
|
|
105
|
+
circle_center (Tuple): The center of the circle. The center is specified as a tuple (x-center, y-center).
|
|
106
|
+
radius (float): The radius of the circle. The radius is specified as float that can be any rational number
|
|
107
|
+
greater than 0.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
List[Tuple]: This method returns a list of the intersection points of the line and a circle. The points are
|
|
111
|
+
returned as a tuple with the form (x-coordinate,y-coordinate). The list of intersection points can contain zero
|
|
112
|
+
to two tuples. Accordingly, an empty list is returned if the line and the circle have no intersection.
|
|
113
|
+
"""
|
|
114
|
+
if radius <= 0:
|
|
115
|
+
raise ValueError("The radius of the circle must be greater than 0.")
|
|
116
|
+
|
|
117
|
+
center_x, center_y = circle_center
|
|
118
|
+
|
|
119
|
+
# Check whether the line is a vertical line
|
|
120
|
+
if isinstance(line, tuple):
|
|
121
|
+
m, b = line
|
|
122
|
+
|
|
123
|
+
# Calculation of the coefficients of the quadratic equation system
|
|
124
|
+
A = 1 + m ** 2
|
|
125
|
+
B = 2 * (m * b - m * center_y - center_x)
|
|
126
|
+
C = center_x ** 2 + center_y ** 2 + b ** 2 - 2 * b * center_y - radius ** 2
|
|
127
|
+
|
|
128
|
+
# Calculation of the discriminant
|
|
129
|
+
discriminant = B ** 2 - 4 * A * C
|
|
130
|
+
|
|
131
|
+
if discriminant < 0:
|
|
132
|
+
# No intersection
|
|
133
|
+
return []
|
|
134
|
+
elif discriminant == 0:
|
|
135
|
+
# Single intersection
|
|
136
|
+
x = -B / (2 * A)
|
|
137
|
+
y = m * x + b
|
|
138
|
+
return [(x, y)]
|
|
139
|
+
else:
|
|
140
|
+
# Two intersections
|
|
141
|
+
sqrt_discriminant = np.sqrt(discriminant)
|
|
142
|
+
x1 = (-B + sqrt_discriminant) / (2 * A)
|
|
143
|
+
y1 = m * x1 + b
|
|
144
|
+
x2 = (-B - sqrt_discriminant) / (2 * A)
|
|
145
|
+
y2 = m * x2 + b
|
|
146
|
+
return [(x1, y1), (x2, y2)]
|
|
147
|
+
else:
|
|
148
|
+
# Vertical line x = c
|
|
149
|
+
c = line
|
|
150
|
+
|
|
151
|
+
# Calculation of the y-values
|
|
152
|
+
A = 1
|
|
153
|
+
B = -2 * center_y
|
|
154
|
+
C = center_y ** 2 + (c - center_x) ** 2 - radius ** 2
|
|
155
|
+
|
|
156
|
+
# Calculation of the discriminant
|
|
157
|
+
discriminant = B ** 2 - 4 * A * C
|
|
158
|
+
|
|
159
|
+
if discriminant < 0:
|
|
160
|
+
# No intersection
|
|
161
|
+
return []
|
|
162
|
+
elif discriminant == 0:
|
|
163
|
+
# Single intersection
|
|
164
|
+
y = -B / (2 * A)
|
|
165
|
+
return [(c, y)]
|
|
166
|
+
else:
|
|
167
|
+
# Two intersections
|
|
168
|
+
sqrt_discriminant = np.sqrt(discriminant)
|
|
169
|
+
y1 = (-B + sqrt_discriminant) / (2 * A)
|
|
170
|
+
y2 = (-B - sqrt_discriminant) / (2 * A)
|
|
171
|
+
return [(c, y1), (c, y2)]
|