fakecbed 0.2.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.
- fakecbed/__init__.py +46 -0
- fakecbed/discretized.py +2554 -0
- fakecbed/shapes.py +3510 -0
- fakecbed/tds.py +268 -0
- fakecbed/version.py +16 -0
- fakecbed-0.2.0.dist-info/LICENSE +674 -0
- fakecbed-0.2.0.dist-info/METADATA +59 -0
- fakecbed-0.2.0.dist-info/RECORD +10 -0
- fakecbed-0.2.0.dist-info/WHEEL +5 -0
- fakecbed-0.2.0.dist-info/top_level.txt +1 -0
fakecbed/shapes.py
ADDED
|
@@ -0,0 +1,3510 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright 2024 Matthew Fitzpatrick.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
# the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
# Foundation, version 3.
|
|
7
|
+
#
|
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
10
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License along with
|
|
13
|
+
# this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
14
|
+
r"""For creating undistorted geometric shapes.
|
|
15
|
+
|
|
16
|
+
This module contains classes that represent the intensity patterns of different
|
|
17
|
+
undistorted geometric shapes that can be combined to construct intensity
|
|
18
|
+
patterns that imitate convergent beam diffraction beam (CBED) patterns. As a
|
|
19
|
+
shorthand, we refer to these intensity patterns that imitate CBED patterns as
|
|
20
|
+
"fake CBED patterns".
|
|
21
|
+
|
|
22
|
+
Users can create images of fake CBED patterns using the
|
|
23
|
+
:mod:`fakecbed.discretized` module. An image of a fake CBED pattern is formed by
|
|
24
|
+
specifying a series of parameters, with the most important parameters being: the
|
|
25
|
+
set of intensity patterns of undistorted shapes that determine the undistorted
|
|
26
|
+
noiseless non-blurred uncorrupted fake CBED pattern; and a distortion model
|
|
27
|
+
which transforms the undistorted noiseless non-blurred uncorrupted fake CBED
|
|
28
|
+
pattern into a distorted noiseless non-blurred uncorrupted fake CBED
|
|
29
|
+
pattern. The remaining parameters determine whether additional images effects
|
|
30
|
+
are applied, like e.g. shot noise or blur effects. Note that in the case of the
|
|
31
|
+
aforementioned shapes, we expand the notion of intensity patterns to mean a 2D
|
|
32
|
+
real-valued function, i.e. it can be negative. To be clear, we do not apply this
|
|
33
|
+
generalized notion of intensity patterns to the fake CBED patterns: in such
|
|
34
|
+
cases intensity patterns mean 2D real-valued functions that are strictly
|
|
35
|
+
nonnegative.
|
|
36
|
+
|
|
37
|
+
Let :math:`u_{x}` and :math:`u_{y}` be the fractional horizontal and vertical
|
|
38
|
+
coordinates, respectively, of a point in an undistorted image, where
|
|
39
|
+
:math:`\left(u_{x},u_{y}\right)=\left(0,0\right)` is the bottom left corner of
|
|
40
|
+
the image. Secondly, let :math:`q_{x}` and :math:`q_{y}` be the fractional
|
|
41
|
+
horizontal and vertical coordinates, respectively, of a point in a distorted
|
|
42
|
+
image, where :math:`\left(q_{x},q_{y}\right)=\left(0,0\right)` is the bottom
|
|
43
|
+
left corner of the image. When users specify a distortion model, represented by
|
|
44
|
+
an :obj:`distoptica.DistortionModel` object, they also specify a coordinate
|
|
45
|
+
transformation which maps a given coordinate pair
|
|
46
|
+
:math:`\left(u_{x},u_{y}\right)` to a corresponding coordinate pair
|
|
47
|
+
:math:`\left(q_{x},q_{y}\right)`, and implicitly a right-inverse to said
|
|
48
|
+
coordinate transformation that maps a coordinate pair
|
|
49
|
+
:math:`\left(q_{x},q_{y}\right)` to a corresponding coordinate pair
|
|
50
|
+
:math:`\left(u_{x},u_{y}\right)`, when such a relationship exists for
|
|
51
|
+
:math:`\left(q_{x},q_{y}\right)`.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
#####################################
|
|
58
|
+
## Load libraries/packages/modules ##
|
|
59
|
+
#####################################
|
|
60
|
+
|
|
61
|
+
# For accessing attributes of functions.
|
|
62
|
+
import inspect
|
|
63
|
+
|
|
64
|
+
# For randomly selecting items in dictionaries.
|
|
65
|
+
import random
|
|
66
|
+
|
|
67
|
+
# For performing deep copies.
|
|
68
|
+
import copy
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# For general array handling.
|
|
73
|
+
import numpy as np
|
|
74
|
+
import torch
|
|
75
|
+
|
|
76
|
+
# For calculating factorials.
|
|
77
|
+
import math
|
|
78
|
+
|
|
79
|
+
# For validating and converting objects.
|
|
80
|
+
import czekitout.check
|
|
81
|
+
import czekitout.convert
|
|
82
|
+
|
|
83
|
+
# For defining classes that support enforced validation, updatability,
|
|
84
|
+
# pre-serialization, and de-serialization.
|
|
85
|
+
import fancytypes
|
|
86
|
+
|
|
87
|
+
# For validating, pre-serializing, and de-pre-serializing certain objects.
|
|
88
|
+
import distoptica
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
##################################
|
|
93
|
+
## Define classes and functions ##
|
|
94
|
+
##################################
|
|
95
|
+
|
|
96
|
+
# List of public objects in module.
|
|
97
|
+
__all__ = ["BaseShape",
|
|
98
|
+
"Circle",
|
|
99
|
+
"Ellipse",
|
|
100
|
+
"Peak",
|
|
101
|
+
"Band",
|
|
102
|
+
"PlaneWave",
|
|
103
|
+
"Arc",
|
|
104
|
+
"GenericBlob",
|
|
105
|
+
"Orbital",
|
|
106
|
+
"Lune",
|
|
107
|
+
"NonuniformBoundedShape"]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _check_and_convert_cartesian_coords(params):
|
|
112
|
+
current_func_name = inspect.stack()[0][3]
|
|
113
|
+
char_idx = 19
|
|
114
|
+
obj_name = current_func_name[char_idx:]
|
|
115
|
+
obj = params[obj_name]
|
|
116
|
+
|
|
117
|
+
u_x, u_y = obj
|
|
118
|
+
|
|
119
|
+
params["real_torch_matrix"] = u_x
|
|
120
|
+
params["name_of_alias_of_real_torch_matrix"] = "u_x"
|
|
121
|
+
u_x = _check_and_convert_real_torch_matrix(params)
|
|
122
|
+
|
|
123
|
+
params["real_torch_matrix"] = u_y
|
|
124
|
+
params["name_of_alias_of_real_torch_matrix"] = "u_y"
|
|
125
|
+
u_y = _check_and_convert_real_torch_matrix(params)
|
|
126
|
+
|
|
127
|
+
del params["real_torch_matrix"]
|
|
128
|
+
del params["name_of_alias_of_real_torch_matrix"]
|
|
129
|
+
|
|
130
|
+
if u_x.shape != u_y.shape:
|
|
131
|
+
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
132
|
+
err_msg = unformatted_err_msg.format("u_x", "u_y")
|
|
133
|
+
raise ValueError(err_msg)
|
|
134
|
+
|
|
135
|
+
cartesian_coords = (u_x, u_y)
|
|
136
|
+
|
|
137
|
+
return cartesian_coords
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _check_and_convert_real_torch_matrix(params):
|
|
142
|
+
current_func_name = inspect.stack()[0][3]
|
|
143
|
+
char_idx = 19
|
|
144
|
+
obj_name = current_func_name[char_idx:]
|
|
145
|
+
obj = params[obj_name]
|
|
146
|
+
|
|
147
|
+
name_of_alias_of_real_torch_matrix = \
|
|
148
|
+
params["name_of_alias_of_real_torch_matrix"]
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
if not isinstance(obj, torch.Tensor):
|
|
152
|
+
kwargs = {"obj": obj,
|
|
153
|
+
"obj_name": name_of_alias_of_real_torch_matrix}
|
|
154
|
+
obj = czekitout.convert.to_real_numpy_matrix(**kwargs)
|
|
155
|
+
|
|
156
|
+
obj = torch.tensor(obj,
|
|
157
|
+
dtype=torch.float32,
|
|
158
|
+
device=params["device"])
|
|
159
|
+
|
|
160
|
+
if len(obj.shape) != 2:
|
|
161
|
+
raise
|
|
162
|
+
|
|
163
|
+
real_torch_matrix = obj.to(device=params["device"], dtype=torch.float32)
|
|
164
|
+
|
|
165
|
+
except:
|
|
166
|
+
unformatted_err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
167
|
+
err_msg = unformatted_err_msg.format(name_of_alias_of_real_torch_matrix)
|
|
168
|
+
raise TypeError(err_msg)
|
|
169
|
+
|
|
170
|
+
return real_torch_matrix
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _check_and_convert_device(params):
|
|
175
|
+
params["name_of_obj_alias_of_torch_device_obj"] = "device"
|
|
176
|
+
device = _check_and_convert_torch_device_obj(params)
|
|
177
|
+
|
|
178
|
+
del params["name_of_obj_alias_of_torch_device_obj"]
|
|
179
|
+
|
|
180
|
+
return device
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _check_and_convert_torch_device_obj(params):
|
|
185
|
+
obj_name = params["name_of_obj_alias_of_torch_device_obj"]
|
|
186
|
+
obj = params[obj_name]
|
|
187
|
+
|
|
188
|
+
if obj is None:
|
|
189
|
+
torch_device_obj = torch.device("cuda"
|
|
190
|
+
if torch.cuda.is_available()
|
|
191
|
+
else "cpu")
|
|
192
|
+
else:
|
|
193
|
+
kwargs = {"obj": obj,
|
|
194
|
+
"obj_name": obj_name,
|
|
195
|
+
"accepted_types": (torch.device, type(None))}
|
|
196
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
197
|
+
torch_device_obj = obj
|
|
198
|
+
|
|
199
|
+
return torch_device_obj
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _check_and_convert_skip_validation_and_conversion(params):
|
|
204
|
+
current_func_name = inspect.stack()[0][3]
|
|
205
|
+
obj_name = current_func_name[19:]
|
|
206
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
207
|
+
skip_validation_and_conversion = czekitout.convert.to_bool(**kwargs)
|
|
208
|
+
|
|
209
|
+
return skip_validation_and_conversion
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
_default_u_x = ((0.5,),)
|
|
214
|
+
_default_u_y = _default_u_x
|
|
215
|
+
_default_device = None
|
|
216
|
+
_default_skip_validation_and_conversion = False
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class BaseShape(fancytypes.PreSerializableAndUpdatable):
|
|
221
|
+
r"""The intensity pattern of an undistorted geometric shape.
|
|
222
|
+
|
|
223
|
+
See the summary documentation of the module :mod:`fakecbed.shapes` for
|
|
224
|
+
additional context.
|
|
225
|
+
|
|
226
|
+
One cannot construct an instance of the class
|
|
227
|
+
:class:`fakecbed.shapes.BaseShape`, only subclasses of itself defined in
|
|
228
|
+
:mod:`fakecbed` library.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
ctor_params : `dict`
|
|
233
|
+
The construction parameters of the subclass.
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
def __init__(self, ctor_params):
|
|
237
|
+
if type(self) is BaseShape:
|
|
238
|
+
self._eval(u_x=None, u_y=None)
|
|
239
|
+
else:
|
|
240
|
+
kwargs = ctor_params
|
|
241
|
+
kwargs["skip_cls_tests"] = True
|
|
242
|
+
fancytypes.PreSerializableAndUpdatable.__init__(self, **kwargs)
|
|
243
|
+
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@classmethod
|
|
249
|
+
def get_validation_and_conversion_funcs(cls):
|
|
250
|
+
validation_and_conversion_funcs = \
|
|
251
|
+
cls._validation_and_conversion_funcs_.copy()
|
|
252
|
+
|
|
253
|
+
return validation_and_conversion_funcs
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@classmethod
|
|
258
|
+
def get_pre_serialization_funcs(cls):
|
|
259
|
+
pre_serialization_funcs = \
|
|
260
|
+
cls._pre_serialization_funcs_.copy()
|
|
261
|
+
|
|
262
|
+
return pre_serialization_funcs
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def get_de_pre_serialization_funcs(cls):
|
|
268
|
+
de_pre_serialization_funcs = \
|
|
269
|
+
cls._de_pre_serialization_funcs_.copy()
|
|
270
|
+
|
|
271
|
+
return de_pre_serialization_funcs
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def eval(self,
|
|
276
|
+
u_x=\
|
|
277
|
+
_default_u_x,
|
|
278
|
+
u_y=\
|
|
279
|
+
_default_u_y,
|
|
280
|
+
device=\
|
|
281
|
+
_default_device,
|
|
282
|
+
skip_validation_and_conversion=\
|
|
283
|
+
_default_skip_validation_and_conversion):
|
|
284
|
+
r"""Evaluate the intensity pattern of the undistorted shape.
|
|
285
|
+
|
|
286
|
+
Let :math:`u_{x}` and :math:`u_{y}` be the fractional horizontal and
|
|
287
|
+
vertical coordinates, respectively, of a point in an undistorted image.
|
|
288
|
+
We adopt the convention where the fractional coordinate pair
|
|
289
|
+
:math:`\left(u_{x},u_{y}\right)=\left(0,0\right)` is the bottom left
|
|
290
|
+
corner of an image.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
u_x : `torch.Tensor` (`float`, ndim=2), optional
|
|
295
|
+
The fractional horizontal coordinates of the positions at which to
|
|
296
|
+
evaluate the intensity pattern of the undistorted shape.
|
|
297
|
+
u_y : `torch.Tensor` (`float`, shape=``u_x.shape``), optional
|
|
298
|
+
The fractional vertical coordinates of the positions at which to
|
|
299
|
+
evaluate the intensity pattern of the undistorted shape.
|
|
300
|
+
device : `torch.device` | `None`, optional
|
|
301
|
+
This parameter specifies the device to be used to perform
|
|
302
|
+
computationally intensive calls to PyTorch functions. If ``device``
|
|
303
|
+
is of the type :class:`torch.device`, then ``device`` represents the
|
|
304
|
+
device to be used. If ``device`` is set to ``None`` and a GPU device
|
|
305
|
+
is available, then a GPU device is to be used. Otherwise, the CPU is
|
|
306
|
+
used.
|
|
307
|
+
skip_validation_and_conversion : `bool`, optional
|
|
308
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then
|
|
309
|
+
validations and conversions are performed on the above parameters.
|
|
310
|
+
|
|
311
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
312
|
+
no validations and conversions are performed on the above
|
|
313
|
+
parameters. This option is desired primarily when the user wants to
|
|
314
|
+
avoid potentially expensive validation and/or conversion operations.
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
result : `torch.Tensor` (`float`, shape=``u_x.shape``)
|
|
319
|
+
The values of the intensity pattern at the positions specified by
|
|
320
|
+
``u_x`` and ``u_y``. For every pair of nonnegative integers ``(i,
|
|
321
|
+
j)`` that does not raise an ``IndexError`` exception upon calling
|
|
322
|
+
``result[i, j]``, ``result[i, j]`` is the value of the intensity
|
|
323
|
+
pattern at the position ``(u_x[i, j], u_y[i, j])`` of the
|
|
324
|
+
undistorted shape.
|
|
325
|
+
|
|
326
|
+
"""
|
|
327
|
+
params = locals()
|
|
328
|
+
|
|
329
|
+
func_alias = _check_and_convert_skip_validation_and_conversion
|
|
330
|
+
skip_validation_and_conversion = func_alias(params)
|
|
331
|
+
|
|
332
|
+
if (skip_validation_and_conversion == False):
|
|
333
|
+
params = {"cartesian_coords": (u_x, u_y), "device": device}
|
|
334
|
+
device = _check_and_convert_device(params)
|
|
335
|
+
u_x, u_y = _check_and_convert_cartesian_coords(params)
|
|
336
|
+
|
|
337
|
+
result = self._eval(u_x, u_y)
|
|
338
|
+
|
|
339
|
+
return result
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _eval(self, u_x, u_y):
|
|
344
|
+
raise NotImplementedError(_base_shape_err_msg_1)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _check_and_convert_center(params):
|
|
349
|
+
current_func_name = inspect.stack()[0][3]
|
|
350
|
+
obj_name = current_func_name[19:]
|
|
351
|
+
|
|
352
|
+
cls_alias = \
|
|
353
|
+
distoptica.CoordTransformParams
|
|
354
|
+
validation_and_conversion_funcs = \
|
|
355
|
+
cls_alias.get_validation_and_conversion_funcs()
|
|
356
|
+
validation_and_conversion_func = \
|
|
357
|
+
validation_and_conversion_funcs[obj_name]
|
|
358
|
+
center = \
|
|
359
|
+
validation_and_conversion_func(params)
|
|
360
|
+
|
|
361
|
+
return center
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _pre_serialize_center(center):
|
|
366
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
367
|
+
current_func_name = inspect.stack()[0][3]
|
|
368
|
+
obj_name = current_func_name[15:]
|
|
369
|
+
|
|
370
|
+
cls_alias = \
|
|
371
|
+
distoptica.CoordTransformParams
|
|
372
|
+
pre_serialization_funcs = \
|
|
373
|
+
cls_alias.get_pre_serialization_funcs()
|
|
374
|
+
pre_serialization_func = \
|
|
375
|
+
pre_serialization_funcs[obj_name]
|
|
376
|
+
serializable_rep = \
|
|
377
|
+
pre_serialization_func(obj_to_pre_serialize)
|
|
378
|
+
|
|
379
|
+
return serializable_rep
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _de_pre_serialize_center(serializable_rep):
|
|
384
|
+
current_func_name = inspect.stack()[0][3]
|
|
385
|
+
obj_name = current_func_name[18:]
|
|
386
|
+
|
|
387
|
+
cls_alias = \
|
|
388
|
+
distoptica.CoordTransformParams
|
|
389
|
+
de_pre_serialization_funcs = \
|
|
390
|
+
cls_alias.get_de_pre_serialization_funcs()
|
|
391
|
+
de_pre_serialization_func = \
|
|
392
|
+
de_pre_serialization_funcs[obj_name]
|
|
393
|
+
center = \
|
|
394
|
+
de_pre_serialization_func(serializable_rep)
|
|
395
|
+
|
|
396
|
+
return center
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _check_and_convert_radius(params):
|
|
401
|
+
current_func_name = inspect.stack()[0][3]
|
|
402
|
+
obj_name = current_func_name[19:]
|
|
403
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
404
|
+
radius = czekitout.convert.to_positive_float(**kwargs)
|
|
405
|
+
|
|
406
|
+
return radius
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _pre_serialize_radius(radius):
|
|
411
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
412
|
+
serializable_rep = obj_to_pre_serialize
|
|
413
|
+
|
|
414
|
+
return serializable_rep
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _de_pre_serialize_radius(serializable_rep):
|
|
419
|
+
radius = serializable_rep
|
|
420
|
+
|
|
421
|
+
return radius
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _check_and_convert_intra_shape_val(params):
|
|
426
|
+
current_func_name = inspect.stack()[0][3]
|
|
427
|
+
obj_name = current_func_name[19:]
|
|
428
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
429
|
+
intra_shape_val = czekitout.convert.to_float(**kwargs)
|
|
430
|
+
|
|
431
|
+
return intra_shape_val
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _pre_serialize_intra_shape_val(intra_shape_val):
|
|
436
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
437
|
+
serializable_rep = obj_to_pre_serialize
|
|
438
|
+
|
|
439
|
+
return serializable_rep
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _de_pre_serialize_intra_shape_val(serializable_rep):
|
|
444
|
+
intra_shape_val = serializable_rep
|
|
445
|
+
|
|
446
|
+
return intra_shape_val
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
_default_center = (0.5, 0.5)
|
|
451
|
+
_default_radius = 0.05
|
|
452
|
+
_default_intra_shape_val = 1
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class Circle(BaseShape):
|
|
457
|
+
r"""The intensity pattern of a circle.
|
|
458
|
+
|
|
459
|
+
Let :math:`\left(u_{x;c;\text{C}},u_{y;c;\text{C}}\right)`, and
|
|
460
|
+
:math:`R_{\text{C}}` be the center, and the radius of the circle
|
|
461
|
+
respectively. Furthermore, let :math:`A_{\text{C}}` be the value of the
|
|
462
|
+
intensity pattern inside the circle. The undistorted intensity pattern of
|
|
463
|
+
the circle is given by:
|
|
464
|
+
|
|
465
|
+
.. math ::
|
|
466
|
+
\mathcal{I}_{\text{C}}\left(u_{x},u_{y}\right)=
|
|
467
|
+
A_{\text{C}}\Theta\left(R_{\text{C}}
|
|
468
|
+
-\sqrt{\left(u_{x}-u_{x;c;\text{C}}\right)^{2}
|
|
469
|
+
+\left(u_{y}-u_{y;c;\text{C}}\right)^{2}}\right),
|
|
470
|
+
:label: intensity_pattern_of_circle__1
|
|
471
|
+
|
|
472
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
473
|
+
coordinates of the undistorted intensity pattern of the circle respectively,
|
|
474
|
+
and :math:`\Theta\left(\cdots\right)` is the Heaviside step function.
|
|
475
|
+
|
|
476
|
+
Parameters
|
|
477
|
+
----------
|
|
478
|
+
center : `array_like` (`float`, shape=(``2``,)), optional
|
|
479
|
+
The center of the circle, :math:`\left(u_{x;c;\text{C}},
|
|
480
|
+
u_{y;c;\text{C}}\right)`.
|
|
481
|
+
radius : `float`, optional
|
|
482
|
+
The radius of the circle, :math:`R_{\text{C}}`. Must be positive.
|
|
483
|
+
intra_shape_val : `float`, optional
|
|
484
|
+
The value of the intensity pattern inside the circle.
|
|
485
|
+
skip_validation_and_conversion : `bool`, optional
|
|
486
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
487
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
488
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
489
|
+
being `dict` objects.
|
|
490
|
+
|
|
491
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
492
|
+
representation of the constructor parameters excluding the parameter
|
|
493
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
494
|
+
different constructor parameter name, excluding the name
|
|
495
|
+
``"skip_validation_and_conversion"``, and
|
|
496
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
497
|
+
constructor parameter with the name given by ``key``.
|
|
498
|
+
|
|
499
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
500
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
501
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
502
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
503
|
+
|
|
504
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
505
|
+
then ``core_attrs`` is set to
|
|
506
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
507
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
508
|
+
and/or conversions of the `dict` values of
|
|
509
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
510
|
+
copies or conversions are made in this case.
|
|
511
|
+
|
|
512
|
+
"""
|
|
513
|
+
ctor_param_names = ("center",
|
|
514
|
+
"radius",
|
|
515
|
+
"intra_shape_val")
|
|
516
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
517
|
+
"ctor_param_names": ctor_param_names}
|
|
518
|
+
|
|
519
|
+
_validation_and_conversion_funcs_ = \
|
|
520
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
521
|
+
_pre_serialization_funcs_ = \
|
|
522
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
523
|
+
_de_pre_serialization_funcs_ = \
|
|
524
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
525
|
+
|
|
526
|
+
del ctor_param_names, kwargs
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def __init__(self,
|
|
531
|
+
center=\
|
|
532
|
+
_default_center,
|
|
533
|
+
radius=\
|
|
534
|
+
_default_radius,
|
|
535
|
+
intra_shape_val=\
|
|
536
|
+
_default_intra_shape_val,
|
|
537
|
+
skip_validation_and_conversion=\
|
|
538
|
+
_default_skip_validation_and_conversion):
|
|
539
|
+
ctor_params = {key: val
|
|
540
|
+
for key, val in locals().items()
|
|
541
|
+
if (key not in ("self", "__class__"))}
|
|
542
|
+
BaseShape.__init__(self, ctor_params)
|
|
543
|
+
|
|
544
|
+
self.execute_post_core_attrs_update_actions()
|
|
545
|
+
|
|
546
|
+
return None
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def execute_post_core_attrs_update_actions(self):
|
|
551
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
552
|
+
for self_core_attr_name in self_core_attrs:
|
|
553
|
+
attr_name = "_"+self_core_attr_name
|
|
554
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
555
|
+
setattr(self, attr_name, attr)
|
|
556
|
+
|
|
557
|
+
return None
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def update(self,
|
|
562
|
+
new_core_attr_subset_candidate,
|
|
563
|
+
skip_validation_and_conversion=\
|
|
564
|
+
_default_skip_validation_and_conversion):
|
|
565
|
+
super().update(new_core_attr_subset_candidate,
|
|
566
|
+
skip_validation_and_conversion)
|
|
567
|
+
self.execute_post_core_attrs_update_actions()
|
|
568
|
+
|
|
569
|
+
return None
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def _eval(self, u_x, u_y):
|
|
574
|
+
u_x_c, u_y_c = self._center
|
|
575
|
+
R = self._radius
|
|
576
|
+
A = self._intra_shape_val
|
|
577
|
+
|
|
578
|
+
delta_u_x = u_x-u_x_c
|
|
579
|
+
delta_u_y = u_y-u_y_c
|
|
580
|
+
|
|
581
|
+
u_r = torch.sqrt(delta_u_x*delta_u_x + delta_u_y*delta_u_y)
|
|
582
|
+
|
|
583
|
+
result = A * (u_r <= R)
|
|
584
|
+
|
|
585
|
+
return result
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _check_and_convert_semi_major_axis(params):
|
|
590
|
+
current_func_name = inspect.stack()[0][3]
|
|
591
|
+
obj_name = current_func_name[19:]
|
|
592
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
593
|
+
semi_major_axis = czekitout.convert.to_positive_float(**kwargs)
|
|
594
|
+
|
|
595
|
+
return semi_major_axis
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _pre_serialize_semi_major_axis(semi_major_axis):
|
|
600
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
601
|
+
serializable_rep = obj_to_pre_serialize
|
|
602
|
+
|
|
603
|
+
return serializable_rep
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def _de_pre_serialize_semi_major_axis(serializable_rep):
|
|
608
|
+
semi_major_axis = serializable_rep
|
|
609
|
+
|
|
610
|
+
return semi_major_axis
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _check_and_convert_eccentricity(params):
|
|
615
|
+
current_func_name = inspect.stack()[0][3]
|
|
616
|
+
obj_name = current_func_name[19:]
|
|
617
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
618
|
+
eccentricity = czekitout.convert.to_nonnegative_float(**kwargs)
|
|
619
|
+
|
|
620
|
+
if eccentricity > 1:
|
|
621
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
622
|
+
raise ValueError(err_msg)
|
|
623
|
+
|
|
624
|
+
return eccentricity
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def _pre_serialize_eccentricity(eccentricity):
|
|
629
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
630
|
+
serializable_rep = obj_to_pre_serialize
|
|
631
|
+
|
|
632
|
+
return serializable_rep
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _de_pre_serialize_eccentricity(serializable_rep):
|
|
637
|
+
eccentricity = serializable_rep
|
|
638
|
+
|
|
639
|
+
return eccentricity
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _check_and_convert_rotation_angle(params):
|
|
644
|
+
current_func_name = inspect.stack()[0][3]
|
|
645
|
+
obj_name = current_func_name[19:]
|
|
646
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
647
|
+
rotation_angle = czekitout.convert.to_float(**kwargs) % (2*np.pi)
|
|
648
|
+
|
|
649
|
+
return rotation_angle
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _pre_serialize_rotation_angle(rotation_angle):
|
|
654
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
655
|
+
serializable_rep = obj_to_pre_serialize
|
|
656
|
+
|
|
657
|
+
return serializable_rep
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def _de_pre_serialize_rotation_angle(serializable_rep):
|
|
662
|
+
rotation_angle = serializable_rep
|
|
663
|
+
|
|
664
|
+
return rotation_angle
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
_default_semi_major_axis = _default_radius
|
|
669
|
+
_default_eccentricity = 0
|
|
670
|
+
_default_rotation_angle = 0
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
class Ellipse(BaseShape):
|
|
675
|
+
r"""The intensity pattern of a ellipse.
|
|
676
|
+
|
|
677
|
+
Let :math:`\left(u_{x;c;\text{E}},u_{y;c;\text{E}}\right)`,
|
|
678
|
+
:math:`a_{\text{E}}`, :math:`e_{\text{E}}`, and :math:`\theta_{\text{E}}` be
|
|
679
|
+
the center, the semi-major axis, the eccentricity, and the rotation angle of
|
|
680
|
+
the ellipse respectively. Furthermore, let :math:`A_{\text{E}}` be the value
|
|
681
|
+
of the intensity pattern inside the ellipse. The undistorted intensity
|
|
682
|
+
pattern of the ellipse is given by:
|
|
683
|
+
|
|
684
|
+
.. math ::
|
|
685
|
+
\mathcal{I}_{\text{E}}\left(u_{x},u_{y}\right)=
|
|
686
|
+
A_{\text{E}}\Theta\left(\Theta_{\arg;\text{E}}\left(u_{x},
|
|
687
|
+
u_{y}\right)\right),
|
|
688
|
+
:label: intensity_pattern_of_ellipse__1
|
|
689
|
+
|
|
690
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
691
|
+
coordinates of the undistorted intensity pattern of the ellipse
|
|
692
|
+
respectively, :math:`\Theta\left(\cdots\right)` is the Heaviside step
|
|
693
|
+
function, and
|
|
694
|
+
|
|
695
|
+
.. math ::
|
|
696
|
+
&\Theta_{\arg;\text{E}}\left(u_{x},u_{y}\right)\\&\quad=
|
|
697
|
+
\left\{ 1-e_{\text{E}}^{2}\right\} a_{\text{E}}^{2}\\
|
|
698
|
+
&\quad\quad-\left\{ 1-e_{\text{E}}^{2}\right\}
|
|
699
|
+
\left\{ \left[u_{x}-u_{x;c;\text{E}}\right]
|
|
700
|
+
\cos\left(\theta_{\text{E}}\right)
|
|
701
|
+
-\left[u_{y}-u_{y;c;\text{E}}\right]
|
|
702
|
+
\sin\left(\theta_{\text{E}}\right)\right\} ^{2}\\
|
|
703
|
+
&\quad\quad-\left\{ \left[u_{x}-
|
|
704
|
+
u_{x;c;\text{E}}\right]\sin\left(\theta_{\text{E}}\right)+\left[u_{y}-
|
|
705
|
+
u_{y;c;\text{E}}\right]\cos\left(\theta_{\text{E}}\right)\right\}^{2}.
|
|
706
|
+
:label: ellipse_support_arg__1
|
|
707
|
+
|
|
708
|
+
Parameters
|
|
709
|
+
----------
|
|
710
|
+
center : `array_like` (`float`, shape=(``2``,)), optional
|
|
711
|
+
The center of the ellipse, :math:`\left(u_{x;c;\text{E}},
|
|
712
|
+
u_{y;c;\text{E}}\right)`.
|
|
713
|
+
semi_major_axis : `float`, optional
|
|
714
|
+
The semi-major axis of the ellipse, :math:`a_{\text{E}}`. Must be
|
|
715
|
+
positive.
|
|
716
|
+
eccentricity : `float`, optional
|
|
717
|
+
The eccentricity of the ellipse, :math:`e_{\text{E}}`. Must be a
|
|
718
|
+
nonnegative number less than or equal to unity.
|
|
719
|
+
rotation_angle : `float`, optional
|
|
720
|
+
The rotation angle of the ellipse, :math:`\theta_{\text{E}}`.
|
|
721
|
+
intra_shape_val : `float`, optional
|
|
722
|
+
The value of the intensity pattern inside the ellipse.
|
|
723
|
+
skip_validation_and_conversion : `bool`, optional
|
|
724
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
725
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
726
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
727
|
+
being `dict` objects.
|
|
728
|
+
|
|
729
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
730
|
+
representation of the constructor parameters excluding the parameter
|
|
731
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
732
|
+
different constructor parameter name, excluding the name
|
|
733
|
+
``"skip_validation_and_conversion"``, and
|
|
734
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
735
|
+
constructor parameter with the name given by ``key``.
|
|
736
|
+
|
|
737
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
738
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
739
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
740
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
741
|
+
|
|
742
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
743
|
+
then ``core_attrs`` is set to
|
|
744
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
745
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
746
|
+
and/or conversions of the `dict` values of
|
|
747
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
748
|
+
copies or conversions are made in this case.
|
|
749
|
+
|
|
750
|
+
"""
|
|
751
|
+
ctor_param_names = ("center",
|
|
752
|
+
"semi_major_axis",
|
|
753
|
+
"eccentricity",
|
|
754
|
+
"rotation_angle",
|
|
755
|
+
"intra_shape_val")
|
|
756
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
757
|
+
"ctor_param_names": ctor_param_names}
|
|
758
|
+
|
|
759
|
+
_validation_and_conversion_funcs_ = \
|
|
760
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
761
|
+
_pre_serialization_funcs_ = \
|
|
762
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
763
|
+
_de_pre_serialization_funcs_ = \
|
|
764
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
765
|
+
|
|
766
|
+
del ctor_param_names, kwargs
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def __init__(self,
|
|
771
|
+
center=\
|
|
772
|
+
_default_center,
|
|
773
|
+
semi_major_axis=\
|
|
774
|
+
_default_semi_major_axis,
|
|
775
|
+
eccentricity=\
|
|
776
|
+
_default_eccentricity,
|
|
777
|
+
rotation_angle=\
|
|
778
|
+
_default_rotation_angle,
|
|
779
|
+
intra_shape_val=\
|
|
780
|
+
_default_intra_shape_val,
|
|
781
|
+
skip_validation_and_conversion=\
|
|
782
|
+
_default_skip_validation_and_conversion):
|
|
783
|
+
ctor_params = {key: val
|
|
784
|
+
for key, val in locals().items()
|
|
785
|
+
if (key not in ("self", "__class__"))}
|
|
786
|
+
BaseShape.__init__(self, ctor_params)
|
|
787
|
+
|
|
788
|
+
self.execute_post_core_attrs_update_actions()
|
|
789
|
+
|
|
790
|
+
return None
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def execute_post_core_attrs_update_actions(self):
|
|
795
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
796
|
+
for self_core_attr_name in self_core_attrs:
|
|
797
|
+
attr_name = "_"+self_core_attr_name
|
|
798
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
799
|
+
setattr(self, attr_name, attr)
|
|
800
|
+
|
|
801
|
+
return None
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
def update(self,
|
|
806
|
+
new_core_attr_subset_candidate,
|
|
807
|
+
skip_validation_and_conversion=\
|
|
808
|
+
_default_skip_validation_and_conversion):
|
|
809
|
+
super().update(new_core_attr_subset_candidate,
|
|
810
|
+
skip_validation_and_conversion)
|
|
811
|
+
self.execute_post_core_attrs_update_actions()
|
|
812
|
+
|
|
813
|
+
return None
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
def _eval(self, u_x, u_y):
|
|
818
|
+
A = self._intra_shape_val
|
|
819
|
+
support_arg = self._calc_support_arg(u_x, u_y)
|
|
820
|
+
one = torch.tensor(1.0, device=support_arg.device)
|
|
821
|
+
result = A * torch.heaviside(support_arg, one)
|
|
822
|
+
|
|
823
|
+
return result
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def _calc_support_arg(self, u_x, u_y):
|
|
828
|
+
u_x_c, u_y_c = self._center
|
|
829
|
+
a = self._semi_major_axis
|
|
830
|
+
e = self._eccentricity
|
|
831
|
+
theta = torch.tensor(self._rotation_angle, dtype=u_x.dtype)
|
|
832
|
+
|
|
833
|
+
delta_u_x = u_x-u_x_c
|
|
834
|
+
delta_u_y = u_y-u_y_c
|
|
835
|
+
|
|
836
|
+
e_sq = e*e
|
|
837
|
+
a_sq = a*a
|
|
838
|
+
b_sq = (1-e_sq)*a_sq
|
|
839
|
+
|
|
840
|
+
cos_theta = torch.cos(theta)
|
|
841
|
+
sin_theta = torch.sin(theta)
|
|
842
|
+
|
|
843
|
+
delta_u_x_prime = delta_u_x*cos_theta - delta_u_y*sin_theta
|
|
844
|
+
delta_u_x_prime_sq = delta_u_x_prime*delta_u_x_prime
|
|
845
|
+
|
|
846
|
+
delta_u_y_prime = delta_u_x*sin_theta + delta_u_y*cos_theta
|
|
847
|
+
delta_u_y_prime_sq = delta_u_y_prime*delta_u_y_prime
|
|
848
|
+
|
|
849
|
+
support_arg = (b_sq
|
|
850
|
+
- (b_sq/a_sq)*delta_u_x_prime_sq
|
|
851
|
+
- delta_u_y_prime_sq)
|
|
852
|
+
|
|
853
|
+
return support_arg
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def _check_and_convert_widths(params):
|
|
858
|
+
current_func_name = inspect.stack()[0][3]
|
|
859
|
+
obj_name = current_func_name[19:]
|
|
860
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
861
|
+
widths = czekitout.convert.to_quadruplet_of_positive_floats(**kwargs)
|
|
862
|
+
|
|
863
|
+
return widths
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def _pre_serialize_widths(widths):
|
|
868
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
869
|
+
serializable_rep = obj_to_pre_serialize
|
|
870
|
+
|
|
871
|
+
return serializable_rep
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
def _de_pre_serialize_widths(serializable_rep):
|
|
876
|
+
widths = serializable_rep
|
|
877
|
+
|
|
878
|
+
return widths
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def _check_and_convert_val_at_center(params):
|
|
883
|
+
current_func_name = inspect.stack()[0][3]
|
|
884
|
+
obj_name = current_func_name[19:]
|
|
885
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
886
|
+
val_at_center = czekitout.convert.to_float(**kwargs)
|
|
887
|
+
|
|
888
|
+
return val_at_center
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def _pre_serialize_val_at_center(val_at_center):
|
|
893
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
894
|
+
serializable_rep = obj_to_pre_serialize
|
|
895
|
+
|
|
896
|
+
return serializable_rep
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
def _de_pre_serialize_val_at_center(serializable_rep):
|
|
901
|
+
val_at_center = serializable_rep
|
|
902
|
+
|
|
903
|
+
return val_at_center
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def _check_and_convert_functional_form(params):
|
|
908
|
+
current_func_name = inspect.stack()[0][3]
|
|
909
|
+
obj_name = current_func_name[19:]
|
|
910
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
911
|
+
functional_form = czekitout.convert.to_str_from_str_like(**kwargs)
|
|
912
|
+
|
|
913
|
+
kwargs["obj"] = functional_form
|
|
914
|
+
kwargs["accepted_strings"] = ("asymmetric_gaussian",
|
|
915
|
+
"asymmetric_exponential",
|
|
916
|
+
"asymmetric_lorentzian")
|
|
917
|
+
czekitout.check.if_one_of_any_accepted_strings(**kwargs)
|
|
918
|
+
|
|
919
|
+
return functional_form
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def _pre_serialize_functional_form(functional_form):
|
|
924
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
925
|
+
serializable_rep = obj_to_pre_serialize
|
|
926
|
+
|
|
927
|
+
return serializable_rep
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def _de_pre_serialize_functional_form(serializable_rep):
|
|
932
|
+
functional_form = serializable_rep
|
|
933
|
+
|
|
934
|
+
return functional_form
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
_default_widths = 4*(0.05,)
|
|
939
|
+
_default_val_at_center = 1
|
|
940
|
+
_default_functional_form = "asymmetric_gaussian"
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
class Peak(BaseShape):
|
|
945
|
+
r"""The intensity pattern of a peak.
|
|
946
|
+
|
|
947
|
+
Let :math:`\left(u_{x;c;\text{P}},u_{y;c;\text{P}}\right)`,
|
|
948
|
+
:math:`\left(W_{1;1;\text{P}},W_{1;2;\text{P}},
|
|
949
|
+
W_{2;1;\text{P}},W_{2;2;\text{P}}\right)`, and :math:`\theta_{\text{P}}` be
|
|
950
|
+
the center, the widths factors, and the rotation angle of the peak
|
|
951
|
+
respectively. Furthermore, let :math:`A_{\text{P}}` be the value of the
|
|
952
|
+
intensity pattern at the center of the peak. The undistorted intensity
|
|
953
|
+
pattern of the peak is given by:
|
|
954
|
+
|
|
955
|
+
.. math ::
|
|
956
|
+
\mathcal{I}_{\text{P}}\left(u_{x},u_{y}\right)=
|
|
957
|
+
A_{\text{P}}F_{\beta;\text{P}}\left(
|
|
958
|
+
\sqrt{\sum_{\alpha=1}^{2}\left\{ \frac{
|
|
959
|
+
z_{\alpha;\text{P}}\left(u_{x},u_{y}\right)}{
|
|
960
|
+
W_{\alpha;\text{P}}\left(u_{x},u_{y}\right)}\right\}^{2}}\right),
|
|
961
|
+
:label: intensity_pattern_of_peak__1
|
|
962
|
+
|
|
963
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
964
|
+
coordinates of the undistorted intensity pattern of the peak respectively,
|
|
965
|
+
|
|
966
|
+
.. math ::
|
|
967
|
+
F_{\beta;\text{P}}\left(\omega\right)=\begin{cases}
|
|
968
|
+
e^{-\frac{1}{2}\omega^{2}}, & \text{if }\beta=\text{A.G.},\\
|
|
969
|
+
e^{-\omega}, & \text{if }\beta=\text{A.E.},\\
|
|
970
|
+
\left(1+\omega^{2}\right)^{-\frac{3}{2}}, & \text{if }\beta=\text{A.L.},
|
|
971
|
+
\end{cases}
|
|
972
|
+
:label: functional_form_of_peak__1
|
|
973
|
+
|
|
974
|
+
with A.G., A.E., and A.L. being abbreviations of “asymmetric Gaussian”,
|
|
975
|
+
“asymmetric exponential”, and “asymmetric Lorentzian” respectively, and
|
|
976
|
+
:math:`\beta` specifying the functional form of the intensity pattern;
|
|
977
|
+
|
|
978
|
+
.. math ::
|
|
979
|
+
z_{\alpha=1;\text{P}}\left(u_{x},u_{y}\right)=
|
|
980
|
+
\left(u_{x}-u_{x;c;\text{P}}\right)
|
|
981
|
+
\cos\left(\theta_{\text{P}}\right)
|
|
982
|
+
-\left(u_{y}
|
|
983
|
+
-u_{y;c;\text{P}}\right)\sin\left(\theta_{\text{P}}\right);
|
|
984
|
+
:label: z_alpha_peak__1
|
|
985
|
+
|
|
986
|
+
.. math ::
|
|
987
|
+
z_{\alpha=2;\text{P}}\left(u_{x},u_{y}\right)=
|
|
988
|
+
\left(u_{x}-u_{x;c;\text{P}}\right)
|
|
989
|
+
\sin\left(\theta_{\text{P}}\right)
|
|
990
|
+
+\left(u_{y}
|
|
991
|
+
-u_{y;c;\text{P}}\right)\cos\left(\theta_{\text{P}}\right);
|
|
992
|
+
:label: z_alpha_peak__2
|
|
993
|
+
|
|
994
|
+
and
|
|
995
|
+
|
|
996
|
+
.. math ::
|
|
997
|
+
W_{\alpha;\text{P}}\left(u_{x},u_{y}\right)=
|
|
998
|
+
\sum_{\nu=1}^{2}W_{\alpha;\nu;\text{P}}\left[\left\{\nu-1\right\}
|
|
999
|
+
+\left\{ -1\right\}^{\nu+1}
|
|
1000
|
+
\Theta\left(z_{\alpha;\text{P}}\left(u_{x},
|
|
1001
|
+
u_{y}\right)\right)\right],
|
|
1002
|
+
:label: W_alpha_peak__1
|
|
1003
|
+
|
|
1004
|
+
with :math:`\Theta\left(\cdots\right)` being the Heaviside step function.
|
|
1005
|
+
|
|
1006
|
+
Parameters
|
|
1007
|
+
----------
|
|
1008
|
+
center : `array_like` (`float`, shape=(``2``,)), optional
|
|
1009
|
+
The center of the peak,
|
|
1010
|
+
:math:`\left(u_{x;c;\text{P}},u_{y;c;\text{P}}\right)`.
|
|
1011
|
+
widths : `array_like` (`float`, shape=(``4``,)), optional
|
|
1012
|
+
The width factors of the peak,
|
|
1013
|
+
:math:`\left(W_{1;1;\text{P}},W_{1;2;\text{P}},
|
|
1014
|
+
W_{2;1;\text{P}},W_{2;2;\text{P}}\right)`. Must be a quadruplet of
|
|
1015
|
+
positive numbers.
|
|
1016
|
+
rotation_angle : `float`, optional
|
|
1017
|
+
The rotation angle of the peak, :math:`\theta_{\text{P}}`.
|
|
1018
|
+
val_at_center : `float`, optional
|
|
1019
|
+
The value of the intensity pattern at the center of the peak,
|
|
1020
|
+
:math:`A_{\text{P}}`.
|
|
1021
|
+
functional_form : ``"asymmetric_gaussian"`` | ``"asymmetric_exponential"`` | ``"asymmetric_lorentzian"``, optional
|
|
1022
|
+
The functional form of the peak. If
|
|
1023
|
+
``functional_form==asymmetric_gaussian``, then :math:`\beta`, which
|
|
1024
|
+
appears in Eq. :eq:`functional_form_of_peak__1`, is set to "A.G."; else
|
|
1025
|
+
if ``functional_form==asymmetric_exponential``, then :math:`\beta` is
|
|
1026
|
+
set to "A.E."; else :math:`\beta` is set to "A.L.".
|
|
1027
|
+
skip_validation_and_conversion : `bool`, optional
|
|
1028
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
1029
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
1030
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
1031
|
+
being `dict` objects.
|
|
1032
|
+
|
|
1033
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
1034
|
+
representation of the constructor parameters excluding the parameter
|
|
1035
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
1036
|
+
different constructor parameter name, excluding the name
|
|
1037
|
+
``"skip_validation_and_conversion"``, and
|
|
1038
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
1039
|
+
constructor parameter with the name given by ``key``.
|
|
1040
|
+
|
|
1041
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
1042
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
1043
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
1044
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
1045
|
+
|
|
1046
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
1047
|
+
then ``core_attrs`` is set to
|
|
1048
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
1049
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
1050
|
+
and/or conversions of the `dict` values of
|
|
1051
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
1052
|
+
copies or conversions are made in this case.
|
|
1053
|
+
|
|
1054
|
+
"""
|
|
1055
|
+
ctor_param_names = ("center",
|
|
1056
|
+
"widths",
|
|
1057
|
+
"rotation_angle",
|
|
1058
|
+
"val_at_center",
|
|
1059
|
+
"functional_form")
|
|
1060
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
1061
|
+
"ctor_param_names": ctor_param_names}
|
|
1062
|
+
|
|
1063
|
+
_validation_and_conversion_funcs_ = \
|
|
1064
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
1065
|
+
_pre_serialization_funcs_ = \
|
|
1066
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
1067
|
+
_de_pre_serialization_funcs_ = \
|
|
1068
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
1069
|
+
|
|
1070
|
+
del ctor_param_names, kwargs
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
def __init__(self,
|
|
1075
|
+
center=\
|
|
1076
|
+
_default_center,
|
|
1077
|
+
widths=\
|
|
1078
|
+
_default_widths,
|
|
1079
|
+
rotation_angle=\
|
|
1080
|
+
_default_rotation_angle,
|
|
1081
|
+
val_at_center=\
|
|
1082
|
+
_default_val_at_center,
|
|
1083
|
+
functional_form=\
|
|
1084
|
+
_default_functional_form,
|
|
1085
|
+
skip_validation_and_conversion=\
|
|
1086
|
+
_default_skip_validation_and_conversion):
|
|
1087
|
+
ctor_params = {key: val
|
|
1088
|
+
for key, val in locals().items()
|
|
1089
|
+
if (key not in ("self", "__class__"))}
|
|
1090
|
+
BaseShape.__init__(self, ctor_params)
|
|
1091
|
+
|
|
1092
|
+
self.execute_post_core_attrs_update_actions()
|
|
1093
|
+
|
|
1094
|
+
return None
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
def execute_post_core_attrs_update_actions(self):
|
|
1099
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
1100
|
+
for self_core_attr_name in self_core_attrs:
|
|
1101
|
+
attr_name = "_"+self_core_attr_name
|
|
1102
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
1103
|
+
setattr(self, attr_name, attr)
|
|
1104
|
+
|
|
1105
|
+
theta = torch.tensor(self._rotation_angle)
|
|
1106
|
+
self._cos_theta = torch.cos(theta)
|
|
1107
|
+
self._sin_theta = torch.sin(theta)
|
|
1108
|
+
|
|
1109
|
+
functional_form = self._functional_form
|
|
1110
|
+
if functional_form == "asymmetric_gaussian":
|
|
1111
|
+
self._eval = self._eval_asymmetric_gaussian
|
|
1112
|
+
elif functional_form == "asymmetric_exponential":
|
|
1113
|
+
self._eval = self._eval_asymmetric_exponential
|
|
1114
|
+
else:
|
|
1115
|
+
self._eval = self._eval_asymmetric_lorentzian
|
|
1116
|
+
|
|
1117
|
+
return None
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
def update(self,
|
|
1122
|
+
new_core_attr_subset_candidate,
|
|
1123
|
+
skip_validation_and_conversion=\
|
|
1124
|
+
_default_skip_validation_and_conversion):
|
|
1125
|
+
super().update(new_core_attr_subset_candidate,
|
|
1126
|
+
skip_validation_and_conversion)
|
|
1127
|
+
self.execute_post_core_attrs_update_actions()
|
|
1128
|
+
|
|
1129
|
+
return None
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
def _eval_asymmetric_gaussian(self, u_x, u_y):
|
|
1134
|
+
u_x_c, u_y_c = self._center
|
|
1135
|
+
delta_u_x_c = u_x-u_x_c
|
|
1136
|
+
delta_u_y_c = u_y-u_y_c
|
|
1137
|
+
|
|
1138
|
+
cos_theta = self._cos_theta
|
|
1139
|
+
sin_theta = self._sin_theta
|
|
1140
|
+
|
|
1141
|
+
A = self._val_at_center
|
|
1142
|
+
W_1_1, W_1_2, W_2_1, W_2_2 = self._widths
|
|
1143
|
+
|
|
1144
|
+
z_1 = delta_u_x_c*cos_theta - delta_u_y_c*sin_theta
|
|
1145
|
+
mask_1 = (z_1 >= 0)
|
|
1146
|
+
W_1 = W_1_1*mask_1 + W_1_2*(~mask_1)
|
|
1147
|
+
z_1_over_W_1 = z_1/W_1
|
|
1148
|
+
|
|
1149
|
+
z_2 = delta_u_x_c*sin_theta + delta_u_y_c*cos_theta
|
|
1150
|
+
mask_2 = (z_2 >= 0)
|
|
1151
|
+
W_2 = W_2_1*mask_2 + W_2_2*(~mask_2)
|
|
1152
|
+
z_2_over_W_2 = z_2/W_2
|
|
1153
|
+
|
|
1154
|
+
result = A*torch.exp(-0.5*(z_1_over_W_1*z_1_over_W_1
|
|
1155
|
+
+ z_2_over_W_2*z_2_over_W_2))
|
|
1156
|
+
|
|
1157
|
+
return result
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
def _eval_asymmetric_exponential(self, u_x, u_y):
|
|
1162
|
+
u_x_c, u_y_c = self._center
|
|
1163
|
+
delta_u_x_c = u_x-u_x_c
|
|
1164
|
+
delta_u_y_c = u_y-u_y_c
|
|
1165
|
+
|
|
1166
|
+
cos_theta = self._cos_theta
|
|
1167
|
+
sin_theta = self._sin_theta
|
|
1168
|
+
|
|
1169
|
+
A = self._val_at_center
|
|
1170
|
+
w_1_1, w_1_2, w_2_1, w_2_2 = self._widths
|
|
1171
|
+
|
|
1172
|
+
z_1 = delta_u_x_c*cos_theta - delta_u_y_c*sin_theta
|
|
1173
|
+
mask_1 = (z_1 >= 0)
|
|
1174
|
+
w_1 = w_1_1*mask_1 + w_1_2*(~mask_1)
|
|
1175
|
+
z_1_over_w_1 = z_1/w_1
|
|
1176
|
+
|
|
1177
|
+
z_2 = delta_u_x_c*sin_theta + delta_u_y_c*cos_theta
|
|
1178
|
+
mask_2 = (z_2 >= 0)
|
|
1179
|
+
w_2 = w_2_1*mask_2 + w_2_2*(~mask_2)
|
|
1180
|
+
z_2_over_w_2 = z_2/w_2
|
|
1181
|
+
|
|
1182
|
+
result = A*torch.exp(-torch.sqrt(z_1_over_w_1*z_1_over_w_1
|
|
1183
|
+
+ z_2_over_w_2*z_2_over_w_2))
|
|
1184
|
+
|
|
1185
|
+
return result
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
def _eval_asymmetric_lorentzian(self, u_x, u_y):
|
|
1190
|
+
u_x_c, u_y_c = self._center
|
|
1191
|
+
delta_u_x_c = u_x-u_x_c
|
|
1192
|
+
delta_u_y_c = u_y-u_y_c
|
|
1193
|
+
|
|
1194
|
+
cos_theta = self._cos_theta
|
|
1195
|
+
sin_theta = self._sin_theta
|
|
1196
|
+
|
|
1197
|
+
A = self._val_at_center
|
|
1198
|
+
w_1_1, w_1_2, w_2_1, w_2_2 = self._widths
|
|
1199
|
+
|
|
1200
|
+
z_1 = delta_u_x_c*cos_theta - delta_u_y_c*sin_theta
|
|
1201
|
+
mask_1 = (z_1 >= 0)
|
|
1202
|
+
w_1 = w_1_1*mask_1 + w_1_2*(~mask_1)
|
|
1203
|
+
z_1_over_w_1 = z_1/w_1
|
|
1204
|
+
|
|
1205
|
+
z_2 = delta_u_x_c*sin_theta + delta_u_y_c*cos_theta
|
|
1206
|
+
mask_2 = (z_2 >= 0)
|
|
1207
|
+
w_2 = w_2_1*mask_2 + w_2_2*(~mask_2)
|
|
1208
|
+
z_2_over_w_2 = z_2/w_2
|
|
1209
|
+
|
|
1210
|
+
denom_factor = torch.sqrt(1
|
|
1211
|
+
+ z_1_over_w_1*z_1_over_w_1
|
|
1212
|
+
+ z_2_over_w_2*z_2_over_w_2)
|
|
1213
|
+
|
|
1214
|
+
result = A / denom_factor / denom_factor / denom_factor
|
|
1215
|
+
|
|
1216
|
+
return result
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
def _check_and_convert_end_pt_1(params):
|
|
1221
|
+
current_func_name = inspect.stack()[0][3]
|
|
1222
|
+
obj_name = current_func_name[19:]
|
|
1223
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1224
|
+
end_pt_1 = czekitout.convert.to_pair_of_floats(**kwargs)
|
|
1225
|
+
|
|
1226
|
+
return end_pt_1
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def _pre_serialize_end_pt_1(end_pt_1):
|
|
1231
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1232
|
+
serializable_rep = obj_to_pre_serialize
|
|
1233
|
+
|
|
1234
|
+
return serializable_rep
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
def _de_pre_serialize_end_pt_1(serializable_rep):
|
|
1239
|
+
end_pt_1 = serializable_rep
|
|
1240
|
+
|
|
1241
|
+
return end_pt_1
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
def _check_and_convert_end_pt_2(params):
|
|
1246
|
+
current_func_name = inspect.stack()[0][3]
|
|
1247
|
+
obj_name = current_func_name[19:]
|
|
1248
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1249
|
+
end_pt_2 = czekitout.convert.to_pair_of_floats(**kwargs)
|
|
1250
|
+
|
|
1251
|
+
return end_pt_2
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
def _pre_serialize_end_pt_2(end_pt_2):
|
|
1256
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1257
|
+
serializable_rep = obj_to_pre_serialize
|
|
1258
|
+
|
|
1259
|
+
return serializable_rep
|
|
1260
|
+
|
|
1261
|
+
|
|
1262
|
+
|
|
1263
|
+
def _de_pre_serialize_end_pt_2(serializable_rep):
|
|
1264
|
+
end_pt_2 = serializable_rep
|
|
1265
|
+
|
|
1266
|
+
return end_pt_2
|
|
1267
|
+
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
def _check_and_convert_width(params):
|
|
1271
|
+
current_func_name = inspect.stack()[0][3]
|
|
1272
|
+
obj_name = current_func_name[19:]
|
|
1273
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1274
|
+
width = czekitout.convert.to_positive_float(**kwargs)
|
|
1275
|
+
|
|
1276
|
+
return width
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
def _pre_serialize_width(width):
|
|
1281
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1282
|
+
serializable_rep = obj_to_pre_serialize
|
|
1283
|
+
|
|
1284
|
+
return serializable_rep
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
def _de_pre_serialize_width(serializable_rep):
|
|
1289
|
+
width = serializable_rep
|
|
1290
|
+
|
|
1291
|
+
return width
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
_default_end_pt_1 = (0, 0.5)
|
|
1296
|
+
_default_end_pt_2 = (1, 0.5)
|
|
1297
|
+
_default_width = 0.05
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
class Band(BaseShape):
|
|
1302
|
+
r"""The intensity pattern of a band.
|
|
1303
|
+
|
|
1304
|
+
Let :math:`\left(u_{x;\text{B};1},u_{y;\text{B};1}\right)`,
|
|
1305
|
+
:math:`\left(u_{x;\text{B};2},u_{y;\text{B};2}\right)`, and
|
|
1306
|
+
:math:`W_{\text{B}}` be the first end point, the second end point, and the
|
|
1307
|
+
width of the band respectively. Furthermore, let :math:`A_{\text{B}}` be the
|
|
1308
|
+
maximum value of the peak. The undistorted intensity pattern of the band is
|
|
1309
|
+
given by:
|
|
1310
|
+
|
|
1311
|
+
.. math ::
|
|
1312
|
+
\mathcal{I}_{\text{B}}\left(u_{x},u_{y}\right)=
|
|
1313
|
+
A_{\text{B}}\Theta\left(\frac{W_{\text{B}}}{2}
|
|
1314
|
+
-d_{\text{B};1}\left(u_{x},u_{y}\right)\right)\Theta\left(
|
|
1315
|
+
\frac{L_{\text{B}}}{2}
|
|
1316
|
+
-d_{\text{B};2}\left(u_{x},u_{y}\right)\right),
|
|
1317
|
+
:label: intensity_pattern_of_band__1
|
|
1318
|
+
|
|
1319
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
1320
|
+
coordinates of the undistorted intensity pattern of the band respectively,
|
|
1321
|
+
|
|
1322
|
+
.. math ::
|
|
1323
|
+
\Theta\left(\omega\right)=\begin{cases}
|
|
1324
|
+
1, & \text{if }\omega\ge0,\\
|
|
1325
|
+
0, & \text{otherwise};
|
|
1326
|
+
\end{cases}
|
|
1327
|
+
:label: heaviside_step_function__1
|
|
1328
|
+
|
|
1329
|
+
.. math ::
|
|
1330
|
+
L_{\text{B}}=\sqrt{\left(u_{x;\text{B};2}
|
|
1331
|
+
-u_{x;\text{B};1}\right)^{2}+\left(u_{y;\text{B};2}
|
|
1332
|
+
-u_{y;\text{B};1}\right)^{2}};
|
|
1333
|
+
:label: length_of_band__1
|
|
1334
|
+
|
|
1335
|
+
.. math ::
|
|
1336
|
+
d_{\text{B};1}\left(u_{x},u_{y}\right)=
|
|
1337
|
+
\frac{a_{\text{B};1}u_{x}+b_{\text{B};1}u_{y}
|
|
1338
|
+
+c_{\text{B};1}}{\sqrt{a_{\text{B};1}^{2}+b_{\text{B};1}^{2}}};
|
|
1339
|
+
:label: d_1_of_band__1
|
|
1340
|
+
|
|
1341
|
+
with
|
|
1342
|
+
|
|
1343
|
+
.. math ::
|
|
1344
|
+
a_{\text{B};1}=\begin{cases}
|
|
1345
|
+
u_{y;\text{B};2}-u_{y;\text{B};1},
|
|
1346
|
+
& \text{if }u_{x;\text{B};1}\neq u_{x;\text{B};2},\\
|
|
1347
|
+
1, & \text{otherwise},
|
|
1348
|
+
\end{cases}
|
|
1349
|
+
:label: a_1_of_band__1
|
|
1350
|
+
|
|
1351
|
+
.. math ::
|
|
1352
|
+
b_{\text{B};1}=u_{x;\text{B};1}-u_{x;\text{B};2},
|
|
1353
|
+
:label: b_1_of_band__1
|
|
1354
|
+
|
|
1355
|
+
.. math ::
|
|
1356
|
+
c_{\text{B};1}=\begin{cases}
|
|
1357
|
+
u_{x;\text{B};2}u_{y;\text{B};1}
|
|
1358
|
+
-u_{x;\text{B};1}u_{y;\text{B};2},
|
|
1359
|
+
& \text{if }u_{x;\text{B};1}\neq u_{x;\text{B};2},\\
|
|
1360
|
+
-u_{x;\text{B};1}, & \text{otherwise};
|
|
1361
|
+
\end{cases}
|
|
1362
|
+
:label: c_1_of_band__1
|
|
1363
|
+
|
|
1364
|
+
.. math ::
|
|
1365
|
+
d_{\text{B};2}\left(u_{x},u_{y}\right)=
|
|
1366
|
+
\frac{a_{\text{B};2}u_{x}+b_{\text{B};2}u_{y}
|
|
1367
|
+
+c_{\text{B};2}}{\sqrt{a_{\text{B};2}^{2}+b_{\text{B};2}^{2}}},
|
|
1368
|
+
:label: d_2_of_band__1
|
|
1369
|
+
|
|
1370
|
+
with
|
|
1371
|
+
|
|
1372
|
+
.. math ::
|
|
1373
|
+
a_{\text{B};2}=\begin{cases}
|
|
1374
|
+
u_{y;\text{B};4}-u_{y;\text{B};3},
|
|
1375
|
+
& \text{if }u_{x;\text{B};3}\neq u_{x;\text{B};4},\\
|
|
1376
|
+
1, & \text{otherwise},
|
|
1377
|
+
\end{cases}
|
|
1378
|
+
:label: a_2_of_band__1
|
|
1379
|
+
|
|
1380
|
+
.. math ::
|
|
1381
|
+
u_{x;\text{B};3}=u_{x;\text{B};1}
|
|
1382
|
+
+\frac{L_{\text{B}}}{2}\cos\left(\theta_{\text{B}}\right),
|
|
1383
|
+
:label: u_x_3_of_band__1
|
|
1384
|
+
|
|
1385
|
+
.. math ::
|
|
1386
|
+
u_{y;\text{B};3}=u_{y;\text{B};1}
|
|
1387
|
+
+\frac{L_{\text{B}}}{2}\sin\left(\theta_{\text{B}}\right),
|
|
1388
|
+
:label: u_y_3_of_band__1
|
|
1389
|
+
|
|
1390
|
+
.. math ::
|
|
1391
|
+
u_{x;\text{B};4}=u_{x;\text{B};3}
|
|
1392
|
+
+\frac{L_{\text{B}}}{2}\cos\left(\phi_{\text{B}}\right),
|
|
1393
|
+
:label: u_x_4_of_band__1
|
|
1394
|
+
|
|
1395
|
+
.. math ::
|
|
1396
|
+
u_{y;\text{B};4}=u_{y;\text{B};3}
|
|
1397
|
+
+\frac{L_{\text{B}}}{2}\sin\left(\phi_{\text{B}}\right),
|
|
1398
|
+
:label: u_y_4_of_band__1
|
|
1399
|
+
|
|
1400
|
+
.. math ::
|
|
1401
|
+
\theta_{\text{B}}=\tan^{-1}\left(\frac{u_{y;\text{B};2}
|
|
1402
|
+
-u_{y;\text{B};1}}{u_{x;\text{B};2}-u_{x;\text{B};1}}\right),
|
|
1403
|
+
:label: theta_of_band__1
|
|
1404
|
+
|
|
1405
|
+
.. math ::
|
|
1406
|
+
\phi_{\text{B}}=\theta_{\text{B}}+\frac{\pi}{2},
|
|
1407
|
+
:label: phi_of_band__1
|
|
1408
|
+
|
|
1409
|
+
.. math ::
|
|
1410
|
+
b_{\text{B};2}=u_{x;\text{B};3}-u_{x;\text{B};4},
|
|
1411
|
+
:label: b_2_of_band__1
|
|
1412
|
+
|
|
1413
|
+
.. math ::
|
|
1414
|
+
c_{\text{B};2}=\begin{cases}
|
|
1415
|
+
u_{x;\text{B};4}u_{y;\text{B};3}
|
|
1416
|
+
-u_{x;\text{B};3}u_{y;\text{B};4},
|
|
1417
|
+
& \text{if }u_{x;\text{B};3}\neq u_{x;\text{B};4},\\
|
|
1418
|
+
-u_{x;\text{B};3}, & \text{otherwise}.
|
|
1419
|
+
\end{cases}
|
|
1420
|
+
:label: c_2_of_band__1
|
|
1421
|
+
|
|
1422
|
+
Parameters
|
|
1423
|
+
----------
|
|
1424
|
+
end_pt_1 : `array_like` (`float`, shape=(``2``,)), optional
|
|
1425
|
+
The first end point of the band,
|
|
1426
|
+
:math:`\left(u_{x;\text{B};1},u_{y;\text{B};1}\right)`.
|
|
1427
|
+
end_pt_2 : `array_like` (`float`, shape=(``2``,)), optional
|
|
1428
|
+
The second end point of the band,
|
|
1429
|
+
:math:`\left(u_{x;\text{B};2},u_{y;\text{B};2}\right)`.
|
|
1430
|
+
width : `float`, optional
|
|
1431
|
+
The width of the band, :math:`W_{\text{B}}`. Must be a positve number.
|
|
1432
|
+
intra_shape_val : `float`, optional
|
|
1433
|
+
The value of the intensity pattern inside the band.
|
|
1434
|
+
skip_validation_and_conversion : `bool`, optional
|
|
1435
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
1436
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
1437
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
1438
|
+
being `dict` objects.
|
|
1439
|
+
|
|
1440
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
1441
|
+
representation of the constructor parameters excluding the parameter
|
|
1442
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
1443
|
+
different constructor parameter name, excluding the name
|
|
1444
|
+
``"skip_validation_and_conversion"``, and
|
|
1445
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
1446
|
+
constructor parameter with the name given by ``key``.
|
|
1447
|
+
|
|
1448
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
1449
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
1450
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
1451
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
1452
|
+
|
|
1453
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
1454
|
+
then ``core_attrs`` is set to
|
|
1455
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
1456
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
1457
|
+
and/or conversions of the `dict` values of
|
|
1458
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
1459
|
+
copies or conversions are made in this case.
|
|
1460
|
+
|
|
1461
|
+
"""
|
|
1462
|
+
ctor_param_names = ("end_pt_1",
|
|
1463
|
+
"end_pt_2",
|
|
1464
|
+
"width",
|
|
1465
|
+
"intra_shape_val")
|
|
1466
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
1467
|
+
"ctor_param_names": ctor_param_names}
|
|
1468
|
+
|
|
1469
|
+
_validation_and_conversion_funcs_ = \
|
|
1470
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
1471
|
+
_pre_serialization_funcs_ = \
|
|
1472
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
1473
|
+
_de_pre_serialization_funcs_ = \
|
|
1474
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
1475
|
+
|
|
1476
|
+
del ctor_param_names, kwargs
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
|
|
1480
|
+
def __init__(self,
|
|
1481
|
+
end_pt_1=\
|
|
1482
|
+
_default_end_pt_1,
|
|
1483
|
+
end_pt_2=\
|
|
1484
|
+
_default_end_pt_2,
|
|
1485
|
+
width=\
|
|
1486
|
+
_default_width,
|
|
1487
|
+
intra_shape_val=\
|
|
1488
|
+
_default_intra_shape_val,
|
|
1489
|
+
skip_validation_and_conversion=\
|
|
1490
|
+
_default_skip_validation_and_conversion):
|
|
1491
|
+
ctor_params = {key: val
|
|
1492
|
+
for key, val in locals().items()
|
|
1493
|
+
if (key not in ("self", "__class__"))}
|
|
1494
|
+
BaseShape.__init__(self, ctor_params)
|
|
1495
|
+
|
|
1496
|
+
self.execute_post_core_attrs_update_actions()
|
|
1497
|
+
|
|
1498
|
+
return None
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
def execute_post_core_attrs_update_actions(self):
|
|
1503
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
1504
|
+
for self_core_attr_name in self_core_attrs:
|
|
1505
|
+
attr_name = "_"+self_core_attr_name
|
|
1506
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
1507
|
+
setattr(self, attr_name, attr)
|
|
1508
|
+
|
|
1509
|
+
u_x_1, u_y_1 = self._end_pt_1
|
|
1510
|
+
u_x_2, u_y_2 = self._end_pt_2
|
|
1511
|
+
|
|
1512
|
+
length = np.sqrt((u_x_2-u_x_1)**2 + (u_y_2-u_y_1)**2)
|
|
1513
|
+
theta = np.arctan2(u_y_2-u_y_1, u_x_2-u_x_1)
|
|
1514
|
+
phi = theta + (np.pi/2)
|
|
1515
|
+
|
|
1516
|
+
u_x_3 = (u_x_1 + (length/2)*np.cos(theta)).item()
|
|
1517
|
+
u_y_3 = (u_y_1 + (length/2)*np.sin(theta)).item()
|
|
1518
|
+
u_x_4 = (u_x_3 + (length/2)*np.cos(phi)).item()
|
|
1519
|
+
u_y_4 = (u_y_3 + (length/2)*np.sin(phi)).item()
|
|
1520
|
+
|
|
1521
|
+
a_1 = u_y_2-u_y_1 if (u_x_1 != u_x_2) else 1
|
|
1522
|
+
b_1 = u_x_1-u_x_2
|
|
1523
|
+
c_1 = (u_x_2*u_y_1-u_x_1*u_y_2) if (u_x_1 != u_x_2) else -u_x_1
|
|
1524
|
+
|
|
1525
|
+
a_2 = u_y_4-u_y_3 if (u_x_3 != u_x_4) else 1
|
|
1526
|
+
b_2 = u_x_3-u_x_4
|
|
1527
|
+
c_2 = (u_x_4*u_y_3-u_x_3*u_y_4) if (u_x_3 != u_x_4) else -u_x_3
|
|
1528
|
+
|
|
1529
|
+
self._a_1 = a_1
|
|
1530
|
+
self._b_1 = b_1
|
|
1531
|
+
self._c_1 = c_1
|
|
1532
|
+
self._denom_of_d_1 = np.sqrt(a_1*a_1 + b_1*b_1).item()
|
|
1533
|
+
|
|
1534
|
+
self._a_2 = a_2
|
|
1535
|
+
self._b_2 = b_2
|
|
1536
|
+
self._c_2 = c_2
|
|
1537
|
+
self._denom_of_d_2 = np.sqrt(a_2*a_2 + b_2*b_2).item()
|
|
1538
|
+
|
|
1539
|
+
self._length = length
|
|
1540
|
+
|
|
1541
|
+
return None
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
def update(self,
|
|
1546
|
+
new_core_attr_subset_candidate,
|
|
1547
|
+
skip_validation_and_conversion=\
|
|
1548
|
+
_default_skip_validation_and_conversion):
|
|
1549
|
+
super().update(new_core_attr_subset_candidate,
|
|
1550
|
+
skip_validation_and_conversion)
|
|
1551
|
+
self.execute_post_core_attrs_update_actions()
|
|
1552
|
+
|
|
1553
|
+
return None
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
def _d_1(self, u_x, u_y):
|
|
1558
|
+
a_1 = self._a_1
|
|
1559
|
+
b_1 = self._b_1
|
|
1560
|
+
c_1 = self._c_1
|
|
1561
|
+
denom_of_d_1 = self._denom_of_d_1
|
|
1562
|
+
|
|
1563
|
+
d_1 = torch.abs(a_1*u_x + b_1*u_y + c_1) / denom_of_d_1
|
|
1564
|
+
|
|
1565
|
+
return d_1
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
|
|
1569
|
+
def _d_2(self, u_x, u_y):
|
|
1570
|
+
a_2 = self._a_2
|
|
1571
|
+
b_2 = self._b_2
|
|
1572
|
+
c_2 = self._c_2
|
|
1573
|
+
denom_of_d_2 = self._denom_of_d_2
|
|
1574
|
+
|
|
1575
|
+
d_2 = torch.abs(a_2*u_x + b_2*u_y + c_2) / denom_of_d_2
|
|
1576
|
+
|
|
1577
|
+
return d_2
|
|
1578
|
+
|
|
1579
|
+
|
|
1580
|
+
|
|
1581
|
+
def _eval(self, u_x, u_y):
|
|
1582
|
+
A = self._intra_shape_val
|
|
1583
|
+
w_over_2 = self._width/2
|
|
1584
|
+
l_over_2 = self._length/2
|
|
1585
|
+
|
|
1586
|
+
d_1 = self._d_1(u_x, u_y)
|
|
1587
|
+
d_2 = self._d_2(u_x, u_y)
|
|
1588
|
+
|
|
1589
|
+
one = torch.tensor(1.0, device=d_1.device)
|
|
1590
|
+
|
|
1591
|
+
result = (A
|
|
1592
|
+
* torch.heaviside(w_over_2 - d_1, one)
|
|
1593
|
+
* torch.heaviside(l_over_2 - d_2, one))
|
|
1594
|
+
|
|
1595
|
+
return result
|
|
1596
|
+
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
def _check_and_convert_amplitude(params):
|
|
1600
|
+
current_func_name = inspect.stack()[0][3]
|
|
1601
|
+
obj_name = current_func_name[19:]
|
|
1602
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1603
|
+
amplitude = czekitout.convert.to_float(**kwargs)
|
|
1604
|
+
|
|
1605
|
+
return amplitude
|
|
1606
|
+
|
|
1607
|
+
|
|
1608
|
+
|
|
1609
|
+
def _pre_serialize_amplitude(amplitude):
|
|
1610
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1611
|
+
serializable_rep = obj_to_pre_serialize
|
|
1612
|
+
|
|
1613
|
+
return serializable_rep
|
|
1614
|
+
|
|
1615
|
+
|
|
1616
|
+
|
|
1617
|
+
def _de_pre_serialize_amplitude(serializable_rep):
|
|
1618
|
+
amplitude = serializable_rep
|
|
1619
|
+
|
|
1620
|
+
return amplitude
|
|
1621
|
+
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
def _check_and_convert_wavelength(params):
|
|
1625
|
+
current_func_name = inspect.stack()[0][3]
|
|
1626
|
+
obj_name = current_func_name[19:]
|
|
1627
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1628
|
+
wavelength = czekitout.convert.to_positive_float(**kwargs)
|
|
1629
|
+
|
|
1630
|
+
return wavelength
|
|
1631
|
+
|
|
1632
|
+
|
|
1633
|
+
|
|
1634
|
+
def _pre_serialize_wavelength(wavelength):
|
|
1635
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1636
|
+
serializable_rep = obj_to_pre_serialize
|
|
1637
|
+
|
|
1638
|
+
return serializable_rep
|
|
1639
|
+
|
|
1640
|
+
|
|
1641
|
+
|
|
1642
|
+
def _de_pre_serialize_wavelength(serializable_rep):
|
|
1643
|
+
wavelength = serializable_rep
|
|
1644
|
+
|
|
1645
|
+
return wavelength
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
|
|
1649
|
+
def _check_and_convert_propagation_direction(params):
|
|
1650
|
+
current_func_name = inspect.stack()[0][3]
|
|
1651
|
+
obj_name = current_func_name[19:]
|
|
1652
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1653
|
+
propagation_direction = czekitout.convert.to_float(**kwargs) % (2*np.pi)
|
|
1654
|
+
|
|
1655
|
+
return propagation_direction
|
|
1656
|
+
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
def _pre_serialize_propagation_direction(propagation_direction):
|
|
1660
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1661
|
+
serializable_rep = obj_to_pre_serialize
|
|
1662
|
+
|
|
1663
|
+
return serializable_rep
|
|
1664
|
+
|
|
1665
|
+
|
|
1666
|
+
|
|
1667
|
+
def _de_pre_serialize_propagation_direction(serializable_rep):
|
|
1668
|
+
propagation_direction = serializable_rep
|
|
1669
|
+
|
|
1670
|
+
return propagation_direction
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
def _check_and_convert_phase(params):
|
|
1675
|
+
current_func_name = inspect.stack()[0][3]
|
|
1676
|
+
obj_name = current_func_name[19:]
|
|
1677
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1678
|
+
phase = czekitout.convert.to_float(**kwargs)
|
|
1679
|
+
|
|
1680
|
+
return phase
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
|
|
1684
|
+
def _pre_serialize_phase(phase):
|
|
1685
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1686
|
+
serializable_rep = obj_to_pre_serialize
|
|
1687
|
+
|
|
1688
|
+
return serializable_rep
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
def _de_pre_serialize_phase(serializable_rep):
|
|
1693
|
+
phase = serializable_rep
|
|
1694
|
+
|
|
1695
|
+
return phase
|
|
1696
|
+
|
|
1697
|
+
|
|
1698
|
+
|
|
1699
|
+
_default_amplitude = 1
|
|
1700
|
+
_default_wavelength = 0.01
|
|
1701
|
+
_default_propagation_direction = 0
|
|
1702
|
+
_default_phase = 0
|
|
1703
|
+
|
|
1704
|
+
|
|
1705
|
+
|
|
1706
|
+
class PlaneWave(BaseShape):
|
|
1707
|
+
r"""The intensity pattern of a plane wave.
|
|
1708
|
+
|
|
1709
|
+
Let :math:`A_{\text{PW}}`, :math:`\lambda_{\text{PW}}`,
|
|
1710
|
+
:math:`\theta_{\text{PW}}`, and :math:`\phi_{\text{PW}}` be the amplitude,
|
|
1711
|
+
the wavelength, the propagation direction, and the phase of the plane wave
|
|
1712
|
+
respectively. The undistorted intensity pattern of the plane wave is given
|
|
1713
|
+
by:
|
|
1714
|
+
|
|
1715
|
+
.. math ::
|
|
1716
|
+
\mathcal{I}_{\text{PW}}\left(u_{x},u_{y}\right)=
|
|
1717
|
+
A_{\text{PW}}\cos\left(u_{x}k_{x;\text{PW}}+u_{y}k_{y;\text{PW}}
|
|
1718
|
+
+\phi_{\text{PW}}\right),
|
|
1719
|
+
:label: intensity_pattern_of_plane_wave__1
|
|
1720
|
+
|
|
1721
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
1722
|
+
coordinates of the undistorted intensity pattern of the plane wave
|
|
1723
|
+
respectively,
|
|
1724
|
+
|
|
1725
|
+
.. math ::
|
|
1726
|
+
k_{x;\text{PW}}=\frac{2\pi}{\lambda_{\text{PW}}}
|
|
1727
|
+
\cos\left(\theta_{\text{PW}}\right),
|
|
1728
|
+
:label: k_x_of_plane_wave__1
|
|
1729
|
+
|
|
1730
|
+
and
|
|
1731
|
+
|
|
1732
|
+
.. math ::
|
|
1733
|
+
k_{y;\text{PW}}=\frac{2\pi}{\lambda_{\text{PW}}}
|
|
1734
|
+
\sin\left(\theta_{\text{PW}}\right).
|
|
1735
|
+
:label: k_y_of_plane_wave__1
|
|
1736
|
+
|
|
1737
|
+
Parameters
|
|
1738
|
+
----------
|
|
1739
|
+
amplitude : `float`, optional
|
|
1740
|
+
The amplitude of the plane wave, :math:`A_{\text{PW}}`.
|
|
1741
|
+
wavelength : `float`, optional
|
|
1742
|
+
The wavelength of the plane wave, :math:`\lambda_{\text{PW}}`. Must be a
|
|
1743
|
+
positve number.
|
|
1744
|
+
propagation_direction : `float`, optional
|
|
1745
|
+
The propagation direction of the plane wave, :math:`\theta_{\text{PW}}`.
|
|
1746
|
+
phase : `float`, optional
|
|
1747
|
+
The phase of the plane wave, :math:`\phi_{\text{PW}}`.
|
|
1748
|
+
skip_validation_and_conversion : `bool`, optional
|
|
1749
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
1750
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
1751
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
1752
|
+
being `dict` objects.
|
|
1753
|
+
|
|
1754
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
1755
|
+
representation of the constructor parameters excluding the parameter
|
|
1756
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
1757
|
+
different constructor parameter name, excluding the name
|
|
1758
|
+
``"skip_validation_and_conversion"``, and
|
|
1759
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
1760
|
+
constructor parameter with the name given by ``key``.
|
|
1761
|
+
|
|
1762
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
1763
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
1764
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
1765
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
1766
|
+
|
|
1767
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
1768
|
+
then ``core_attrs`` is set to
|
|
1769
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
1770
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
1771
|
+
and/or conversions of the `dict` values of
|
|
1772
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
1773
|
+
copies or conversions are made in this case.
|
|
1774
|
+
|
|
1775
|
+
"""
|
|
1776
|
+
ctor_param_names = ("amplitude",
|
|
1777
|
+
"wavelength",
|
|
1778
|
+
"propagation_direction",
|
|
1779
|
+
"phase")
|
|
1780
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
1781
|
+
"ctor_param_names": ctor_param_names}
|
|
1782
|
+
|
|
1783
|
+
_validation_and_conversion_funcs_ = \
|
|
1784
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
1785
|
+
_pre_serialization_funcs_ = \
|
|
1786
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
1787
|
+
_de_pre_serialization_funcs_ = \
|
|
1788
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
1789
|
+
|
|
1790
|
+
del ctor_param_names, kwargs
|
|
1791
|
+
|
|
1792
|
+
|
|
1793
|
+
|
|
1794
|
+
def __init__(self,
|
|
1795
|
+
amplitude=\
|
|
1796
|
+
_default_amplitude,
|
|
1797
|
+
wavelength=\
|
|
1798
|
+
_default_wavelength,
|
|
1799
|
+
propagation_direction=\
|
|
1800
|
+
_default_propagation_direction,
|
|
1801
|
+
phase=\
|
|
1802
|
+
_default_phase,
|
|
1803
|
+
skip_validation_and_conversion=\
|
|
1804
|
+
_default_skip_validation_and_conversion):
|
|
1805
|
+
ctor_params = {key: val
|
|
1806
|
+
for key, val in locals().items()
|
|
1807
|
+
if (key not in ("self", "__class__"))}
|
|
1808
|
+
BaseShape.__init__(self, ctor_params)
|
|
1809
|
+
|
|
1810
|
+
self.execute_post_core_attrs_update_actions()
|
|
1811
|
+
|
|
1812
|
+
return None
|
|
1813
|
+
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
def execute_post_core_attrs_update_actions(self):
|
|
1817
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
1818
|
+
for self_core_attr_name in self_core_attrs:
|
|
1819
|
+
attr_name = "_"+self_core_attr_name
|
|
1820
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
1821
|
+
setattr(self, attr_name, attr)
|
|
1822
|
+
|
|
1823
|
+
L = self._wavelength
|
|
1824
|
+
theta = self._propagation_direction
|
|
1825
|
+
|
|
1826
|
+
self._k_x = (2*np.pi/L)*np.cos(theta).item()
|
|
1827
|
+
self._k_y = (2*np.pi/L)*np.sin(theta).item()
|
|
1828
|
+
|
|
1829
|
+
return None
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
|
|
1833
|
+
def update(self,
|
|
1834
|
+
new_core_attr_subset_candidate,
|
|
1835
|
+
skip_validation_and_conversion=\
|
|
1836
|
+
_default_skip_validation_and_conversion):
|
|
1837
|
+
super().update(new_core_attr_subset_candidate,
|
|
1838
|
+
skip_validation_and_conversion)
|
|
1839
|
+
self.execute_post_core_attrs_update_actions()
|
|
1840
|
+
|
|
1841
|
+
return None
|
|
1842
|
+
|
|
1843
|
+
|
|
1844
|
+
|
|
1845
|
+
def _eval(self, u_x, u_y):
|
|
1846
|
+
A = self._amplitude
|
|
1847
|
+
phi = self._phase
|
|
1848
|
+
k_x = self._k_x
|
|
1849
|
+
k_y = self._k_y
|
|
1850
|
+
|
|
1851
|
+
result = A*torch.cos(u_x*k_x+u_y*k_y + phi)
|
|
1852
|
+
|
|
1853
|
+
return result
|
|
1854
|
+
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
def _check_and_convert_midpoint_angle(params):
|
|
1858
|
+
current_func_name = inspect.stack()[0][3]
|
|
1859
|
+
obj_name = current_func_name[19:]
|
|
1860
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1861
|
+
midpoint_angle = czekitout.convert.to_float(**kwargs) % (2*np.pi)
|
|
1862
|
+
|
|
1863
|
+
return midpoint_angle
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
|
|
1867
|
+
def _pre_serialize_midpoint_angle(midpoint_angle):
|
|
1868
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1869
|
+
serializable_rep = obj_to_pre_serialize
|
|
1870
|
+
|
|
1871
|
+
return serializable_rep
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
|
|
1875
|
+
def _de_pre_serialize_midpoint_angle(serializable_rep):
|
|
1876
|
+
midpoint_angle = serializable_rep
|
|
1877
|
+
|
|
1878
|
+
return midpoint_angle
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
|
|
1882
|
+
def _check_and_convert_subtending_angle(params):
|
|
1883
|
+
current_func_name = inspect.stack()[0][3]
|
|
1884
|
+
obj_name = current_func_name[19:]
|
|
1885
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
1886
|
+
subtending_angle = min(abs(czekitout.convert.to_float(**kwargs)), (2*np.pi))
|
|
1887
|
+
|
|
1888
|
+
return subtending_angle
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
def _pre_serialize_subtending_angle(subtending_angle):
|
|
1893
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1894
|
+
serializable_rep = obj_to_pre_serialize
|
|
1895
|
+
|
|
1896
|
+
return serializable_rep
|
|
1897
|
+
|
|
1898
|
+
|
|
1899
|
+
|
|
1900
|
+
def _de_pre_serialize_subtending_angle(serializable_rep):
|
|
1901
|
+
subtending_angle = serializable_rep
|
|
1902
|
+
|
|
1903
|
+
return subtending_angle
|
|
1904
|
+
|
|
1905
|
+
|
|
1906
|
+
|
|
1907
|
+
def _check_and_convert_radial_range(params):
|
|
1908
|
+
current_func_name = inspect.stack()[0][3]
|
|
1909
|
+
char_idx = 19
|
|
1910
|
+
obj_name = current_func_name[char_idx:]
|
|
1911
|
+
obj = params[obj_name]
|
|
1912
|
+
|
|
1913
|
+
func_alias = czekitout.convert.to_pair_of_positive_floats
|
|
1914
|
+
kwargs = {"obj": obj, "obj_name": obj_name}
|
|
1915
|
+
radial_range = func_alias(**kwargs)
|
|
1916
|
+
|
|
1917
|
+
if radial_range[0] >= radial_range[1]:
|
|
1918
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
1919
|
+
raise ValueError(err_msg)
|
|
1920
|
+
|
|
1921
|
+
return radial_range
|
|
1922
|
+
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
def _pre_serialize_radial_range(radial_range):
|
|
1926
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
1927
|
+
serializable_rep = obj_to_pre_serialize
|
|
1928
|
+
|
|
1929
|
+
return serializable_rep
|
|
1930
|
+
|
|
1931
|
+
|
|
1932
|
+
|
|
1933
|
+
def _de_pre_serialize_radial_range(serializable_rep):
|
|
1934
|
+
radial_range = serializable_rep
|
|
1935
|
+
|
|
1936
|
+
return radial_range
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
|
|
1940
|
+
_default_midpoint_angle = 0
|
|
1941
|
+
_default_subtending_angle = np.pi/4
|
|
1942
|
+
_default_radial_range = (0.10, 0.15)
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
|
|
1946
|
+
class Arc(BaseShape):
|
|
1947
|
+
r"""The intensity pattern of a circular arc.
|
|
1948
|
+
|
|
1949
|
+
Let :math:`\left(u_{x;c;\text{A}},u_{y;c;\text{A}}\right)`,
|
|
1950
|
+
:math:`\theta_{\text{A}}`, :math:`\phi_{\text{A}}`, and
|
|
1951
|
+
:math:`\left(R_{\text{A};1},R_{\text{A};2}\right)` be the circle center, the
|
|
1952
|
+
midpoint angle, the subtending angle, and the radial range of the circular
|
|
1953
|
+
arc respectively. Furthermore, let :math:`A_{\text{A}}` be the value of the
|
|
1954
|
+
intensity pattern inside the arc. The undistorted intensity pattern of the
|
|
1955
|
+
circular arc is given by:
|
|
1956
|
+
|
|
1957
|
+
.. math ::
|
|
1958
|
+
\mathcal{I}_{\text{A}}\left(u_{x},u_{y}\right)&=
|
|
1959
|
+
A_{\text{A}}\\&\quad\mathop{\times}
|
|
1960
|
+
\Theta\left(\left|\frac{\phi_{\text{A}}}{2}\right|
|
|
1961
|
+
-u_{\theta;\text{A}}\right)\\
|
|
1962
|
+
&\quad\mathop{\times}\Theta\left(\left|\frac{\phi_{\text{A}}}{2}\right|
|
|
1963
|
+
+u_{\theta;\text{A}}\right)\\
|
|
1964
|
+
&\quad\mathop{\times}\Theta\left(u_{r;\text{A}}
|
|
1965
|
+
-R_{\text{A};1}\right)\\
|
|
1966
|
+
&\quad\mathop{\times}\Theta\left(R_{\text{A};2}
|
|
1967
|
+
-u_{r;\text{A}}\right),
|
|
1968
|
+
:label: intensity_pattern_of_arc__1
|
|
1969
|
+
|
|
1970
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
1971
|
+
coordinates of the undistorted intensity pattern of the circular arc
|
|
1972
|
+
respectively, :math:`\Theta\left(\cdots\right)` is the Heaviside step
|
|
1973
|
+
function,
|
|
1974
|
+
|
|
1975
|
+
.. math ::
|
|
1976
|
+
u_{r;\text{A}}=\sqrt{\left(u_{x}-u_{x;c;\text{A}}\right)^{2}
|
|
1977
|
+
+\left(u_{y}-u_{y;c;\text{A}}\right)^{2}},
|
|
1978
|
+
:label: u_r_UA__1
|
|
1979
|
+
|
|
1980
|
+
and
|
|
1981
|
+
|
|
1982
|
+
.. math ::
|
|
1983
|
+
u_{\theta;\text{A}}=\left\{ \tan^{-1}\left(\frac{u_{y}
|
|
1984
|
+
-u_{y;c;\text{A}}}{u_{x}-u_{x;c;\text{A}}}\right)
|
|
1985
|
+
-\theta_{\text{A}}\right\} \mod 2\pi.
|
|
1986
|
+
:label: u_theta_UA__1
|
|
1987
|
+
|
|
1988
|
+
Parameters
|
|
1989
|
+
----------
|
|
1990
|
+
center : `array_like` (`float`, shape=(``2``,)), optional
|
|
1991
|
+
The circle center, :math:`\left(u_{x;c;\text{A}},
|
|
1992
|
+
u_{y;c;\text{A}}\right)`.
|
|
1993
|
+
midpoint_angle : `float`, optional
|
|
1994
|
+
The midpoint angle of the circular arc, :math:`\theta_{\text{A}}`.
|
|
1995
|
+
subtending_angle : `float`, optional
|
|
1996
|
+
The subtending angle of the circular arc, :math:`\phi_{\text{A}}`.
|
|
1997
|
+
radial_range : `array_like` (`float`, shape=(2,)), optional
|
|
1998
|
+
The radial range of the circular arc,
|
|
1999
|
+
:math:`\left(R_{\text{A};1},R_{\text{A};2}\right)`, where
|
|
2000
|
+
``radial_range[0]`` and ``radial_range{1]`` are :math:`R_{\text{A};1}`
|
|
2001
|
+
and :math:`R_{\text{A};2}` respectively. ``radial_range`` must satisfy
|
|
2002
|
+
``0<radial_range[0]<radial_range[1]``.
|
|
2003
|
+
intra_shape_val : `float`, optional
|
|
2004
|
+
The value of the intensity pattern inside the circular arc.
|
|
2005
|
+
skip_validation_and_conversion : `bool`, optional
|
|
2006
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
2007
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
2008
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
2009
|
+
being `dict` objects.
|
|
2010
|
+
|
|
2011
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
2012
|
+
representation of the constructor parameters excluding the parameter
|
|
2013
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
2014
|
+
different constructor parameter name, excluding the name
|
|
2015
|
+
``"skip_validation_and_conversion"``, and
|
|
2016
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
2017
|
+
constructor parameter with the name given by ``key``.
|
|
2018
|
+
|
|
2019
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
2020
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
2021
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
2022
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
2023
|
+
|
|
2024
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
2025
|
+
then ``core_attrs`` is set to
|
|
2026
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
2027
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
2028
|
+
and/or conversions of the `dict` values of
|
|
2029
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
2030
|
+
copies or conversions are made in this case.
|
|
2031
|
+
|
|
2032
|
+
"""
|
|
2033
|
+
ctor_param_names = ("center",
|
|
2034
|
+
"midpoint_angle",
|
|
2035
|
+
"subtending_angle",
|
|
2036
|
+
"radial_range",
|
|
2037
|
+
"intra_shape_val")
|
|
2038
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
2039
|
+
"ctor_param_names": ctor_param_names}
|
|
2040
|
+
|
|
2041
|
+
_validation_and_conversion_funcs_ = \
|
|
2042
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
2043
|
+
_pre_serialization_funcs_ = \
|
|
2044
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
2045
|
+
_de_pre_serialization_funcs_ = \
|
|
2046
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
2047
|
+
|
|
2048
|
+
del ctor_param_names, kwargs
|
|
2049
|
+
|
|
2050
|
+
|
|
2051
|
+
|
|
2052
|
+
def __init__(self,
|
|
2053
|
+
center=\
|
|
2054
|
+
_default_center,
|
|
2055
|
+
midpoint_angle=\
|
|
2056
|
+
_default_midpoint_angle,
|
|
2057
|
+
subtending_angle=\
|
|
2058
|
+
_default_subtending_angle,
|
|
2059
|
+
radial_range=\
|
|
2060
|
+
_default_radial_range,
|
|
2061
|
+
intra_shape_val=\
|
|
2062
|
+
_default_intra_shape_val,
|
|
2063
|
+
skip_validation_and_conversion=\
|
|
2064
|
+
_default_skip_validation_and_conversion):
|
|
2065
|
+
ctor_params = {key: val
|
|
2066
|
+
for key, val in locals().items()
|
|
2067
|
+
if (key not in ("self", "__class__"))}
|
|
2068
|
+
BaseShape.__init__(self, ctor_params)
|
|
2069
|
+
|
|
2070
|
+
self.execute_post_core_attrs_update_actions()
|
|
2071
|
+
|
|
2072
|
+
return None
|
|
2073
|
+
|
|
2074
|
+
|
|
2075
|
+
|
|
2076
|
+
def execute_post_core_attrs_update_actions(self):
|
|
2077
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
2078
|
+
for self_core_attr_name in self_core_attrs:
|
|
2079
|
+
attr_name = "_"+self_core_attr_name
|
|
2080
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
2081
|
+
setattr(self, attr_name, attr)
|
|
2082
|
+
|
|
2083
|
+
return None
|
|
2084
|
+
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
def update(self,
|
|
2088
|
+
new_core_attr_subset_candidate,
|
|
2089
|
+
skip_validation_and_conversion=\
|
|
2090
|
+
_default_skip_validation_and_conversion):
|
|
2091
|
+
super().update(new_core_attr_subset_candidate,
|
|
2092
|
+
skip_validation_and_conversion)
|
|
2093
|
+
self.execute_post_core_attrs_update_actions()
|
|
2094
|
+
|
|
2095
|
+
return None
|
|
2096
|
+
|
|
2097
|
+
|
|
2098
|
+
|
|
2099
|
+
def _eval(self, u_x, u_y):
|
|
2100
|
+
u_x_c, u_y_c = self._center
|
|
2101
|
+
theta = self._midpoint_angle
|
|
2102
|
+
phi = self._subtending_angle
|
|
2103
|
+
R_1, R_2 = self._radial_range
|
|
2104
|
+
A = self._intra_shape_val
|
|
2105
|
+
|
|
2106
|
+
delta_u_x = u_x-u_x_c
|
|
2107
|
+
delta_u_y = u_y-u_y_c
|
|
2108
|
+
|
|
2109
|
+
phi_over_2 = phi/2
|
|
2110
|
+
|
|
2111
|
+
u_r = torch.sqrt(delta_u_x*delta_u_x + delta_u_y*delta_u_y)
|
|
2112
|
+
u_theta = ((torch.atan2(delta_u_y, delta_u_x)-theta+phi_over_2)
|
|
2113
|
+
% (2*np.pi))
|
|
2114
|
+
|
|
2115
|
+
one = torch.tensor(1.0, device=u_x.device)
|
|
2116
|
+
|
|
2117
|
+
result = (A
|
|
2118
|
+
* torch.heaviside(phi-u_theta, one)
|
|
2119
|
+
* torch.heaviside(u_r-R_1, one)
|
|
2120
|
+
* torch.heaviside(R_2-u_r, one))
|
|
2121
|
+
|
|
2122
|
+
return result
|
|
2123
|
+
|
|
2124
|
+
|
|
2125
|
+
|
|
2126
|
+
def _check_and_convert_radial_reference_pt(params):
|
|
2127
|
+
current_func_name = inspect.stack()[0][3]
|
|
2128
|
+
obj_name = current_func_name[19:]
|
|
2129
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2130
|
+
radial_reference_pt = czekitout.convert.to_pair_of_floats(**kwargs)
|
|
2131
|
+
|
|
2132
|
+
return radial_reference_pt
|
|
2133
|
+
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
def _pre_serialize_radial_reference_pt(radial_reference_pt):
|
|
2137
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2138
|
+
serializable_rep = obj_to_pre_serialize
|
|
2139
|
+
|
|
2140
|
+
return serializable_rep
|
|
2141
|
+
|
|
2142
|
+
|
|
2143
|
+
|
|
2144
|
+
def _de_pre_serialize_radial_reference_pt(serializable_rep):
|
|
2145
|
+
radial_reference_pt = serializable_rep
|
|
2146
|
+
|
|
2147
|
+
return radial_reference_pt
|
|
2148
|
+
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
def _check_and_convert_radial_amplitudes(params):
|
|
2152
|
+
current_func_name = inspect.stack()[0][3]
|
|
2153
|
+
obj_name = current_func_name[19:]
|
|
2154
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2155
|
+
func_alias = czekitout.convert.to_tuple_of_nonnegative_floats
|
|
2156
|
+
radial_amplitudes = func_alias(**kwargs)
|
|
2157
|
+
|
|
2158
|
+
num_radial_amplitudes = len(radial_amplitudes)
|
|
2159
|
+
if num_radial_amplitudes == 0:
|
|
2160
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
2161
|
+
raise ValueError(err_msg)
|
|
2162
|
+
|
|
2163
|
+
partial_amplitude_sum = sum(radial_amplitudes[1:])
|
|
2164
|
+
if radial_amplitudes[0] <= partial_amplitude_sum:
|
|
2165
|
+
err_msg = globals()[current_func_name+"_err_msg_2"]
|
|
2166
|
+
raise ValueError(err_msg)
|
|
2167
|
+
|
|
2168
|
+
return radial_amplitudes
|
|
2169
|
+
|
|
2170
|
+
|
|
2171
|
+
|
|
2172
|
+
def _pre_serialize_radial_amplitudes(radial_amplitudes):
|
|
2173
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2174
|
+
serializable_rep = obj_to_pre_serialize
|
|
2175
|
+
|
|
2176
|
+
return serializable_rep
|
|
2177
|
+
|
|
2178
|
+
|
|
2179
|
+
|
|
2180
|
+
def _de_pre_serialize_radial_amplitudes(serializable_rep):
|
|
2181
|
+
radial_amplitudes = serializable_rep
|
|
2182
|
+
|
|
2183
|
+
return radial_amplitudes
|
|
2184
|
+
|
|
2185
|
+
|
|
2186
|
+
|
|
2187
|
+
def _check_and_convert_radial_phases(params):
|
|
2188
|
+
current_func_name = inspect.stack()[0][3]
|
|
2189
|
+
obj_name = current_func_name[19:]
|
|
2190
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2191
|
+
radial_phases = czekitout.convert.to_tuple_of_floats(**kwargs)
|
|
2192
|
+
radial_phases = tuple(radial_phase%(2*np.pi)
|
|
2193
|
+
for radial_phase
|
|
2194
|
+
in radial_phases)
|
|
2195
|
+
|
|
2196
|
+
radial_amplitudes = _check_and_convert_radial_amplitudes(params)
|
|
2197
|
+
|
|
2198
|
+
num_radial_phases = len(radial_phases)
|
|
2199
|
+
num_radial_amplitudes = len(radial_amplitudes)
|
|
2200
|
+
|
|
2201
|
+
if num_radial_phases+1 != num_radial_amplitudes:
|
|
2202
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
2203
|
+
raise ValueError(err_msg)
|
|
2204
|
+
|
|
2205
|
+
return radial_phases
|
|
2206
|
+
|
|
2207
|
+
|
|
2208
|
+
|
|
2209
|
+
def _pre_serialize_radial_phases(radial_phases):
|
|
2210
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2211
|
+
serializable_rep = obj_to_pre_serialize
|
|
2212
|
+
|
|
2213
|
+
return serializable_rep
|
|
2214
|
+
|
|
2215
|
+
|
|
2216
|
+
|
|
2217
|
+
def _de_pre_serialize_radial_phases(serializable_rep):
|
|
2218
|
+
radial_phases = serializable_rep
|
|
2219
|
+
|
|
2220
|
+
return radial_phases
|
|
2221
|
+
|
|
2222
|
+
|
|
2223
|
+
|
|
2224
|
+
_default_radial_reference_pt = (0.5, 0.5)
|
|
2225
|
+
_default_radial_amplitudes = (0.1,)
|
|
2226
|
+
_default_radial_phases = tuple()
|
|
2227
|
+
|
|
2228
|
+
|
|
2229
|
+
|
|
2230
|
+
class GenericBlob(BaseShape):
|
|
2231
|
+
r"""The intensity pattern of a generic blob.
|
|
2232
|
+
|
|
2233
|
+
Let :math:`\left(u_{x;c;\text{GB}},u_{y;c;\text{GB}}\right)`,
|
|
2234
|
+
:math:`N_{\text{GB}}`, :math:`\left\{ \phi_{\text{GB};n}\right\}
|
|
2235
|
+
_{n=0}^{N_{\text{GB}}-1}`, and :math:`\left\{ D_{\text{GB};n}\right\}
|
|
2236
|
+
_{n=0}^{N_{\text{GB}}}` be the radial reference point, the number of radial
|
|
2237
|
+
phases, the radial phases, and the radial amplitudes of the generic blob
|
|
2238
|
+
respectively. Furthermore, let :math:`A_{\text{GB}}` be the value of the
|
|
2239
|
+
intensity pattern inside the generic blob. The undistorted intensity pattern
|
|
2240
|
+
of the generic blob is given by:
|
|
2241
|
+
|
|
2242
|
+
.. math ::
|
|
2243
|
+
\mathcal{I}_{\text{GB}}\left(u_{x},u_{y}\right)=
|
|
2244
|
+
A_{\text{GB}}\Theta\left(
|
|
2245
|
+
R_{\text{GB}}\left(u_{\theta;\text{UA}}\right)-u_{r;\text{GB}}\right),
|
|
2246
|
+
:label: intensity_pattern_of_generic_blob__1
|
|
2247
|
+
|
|
2248
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
2249
|
+
coordinates of the undistorted intensity pattern of the generic blob
|
|
2250
|
+
respectively, :math:`\Theta\left(\cdots\right)` is the Heaviside step
|
|
2251
|
+
function,
|
|
2252
|
+
|
|
2253
|
+
.. math ::
|
|
2254
|
+
u_{r;\text{GB}}=\sqrt{\left(u_{x}-u_{x;c;\text{GB}}\right)^{2}
|
|
2255
|
+
+\left(u_{y}-u_{y;c;\text{GB}}\right)^{2}};
|
|
2256
|
+
:label: u_r_UGB__1
|
|
2257
|
+
|
|
2258
|
+
.. math ::
|
|
2259
|
+
u_{\theta;\text{GB}}=
|
|
2260
|
+
\tan^{-1}\left(\frac{u_{y}
|
|
2261
|
+
-u_{y;c;\text{GB}}}{u_{x}-u_{x;c;\text{GB}}}\right);
|
|
2262
|
+
:label: u_theta_UGB__1
|
|
2263
|
+
|
|
2264
|
+
and
|
|
2265
|
+
|
|
2266
|
+
.. math ::
|
|
2267
|
+
R_{\text{GB}}\left(u_{\theta;\text{GB}}\right)&=
|
|
2268
|
+
D_{\text{GB};0}\\&\quad\mathop{+}\min\left(1,N_{\text{GB}}\right)
|
|
2269
|
+
\sum_{n=1}^{N_{\text{GB}}}D_{\text{GB};n}
|
|
2270
|
+
\cos\left(nu_{\theta;\text{GB}}-\phi_{\text{GB};n-1}\right),
|
|
2271
|
+
:label: R_UGB__1
|
|
2272
|
+
|
|
2273
|
+
with
|
|
2274
|
+
|
|
2275
|
+
.. math ::
|
|
2276
|
+
D_{\text{GB};n} \ge 0,
|
|
2277
|
+
\quad\forall n\in\left\{ 1,\ldots,N_{\text{GB}}\right\},
|
|
2278
|
+
:label: D_UGB_n__1
|
|
2279
|
+
|
|
2280
|
+
and
|
|
2281
|
+
|
|
2282
|
+
.. math ::
|
|
2283
|
+
D_{\text{GB};0}>\sum_{n=1}^{N_{\text{GB}}}D_{\text{GB};n}.
|
|
2284
|
+
:label: D_UGB_n__2
|
|
2285
|
+
|
|
2286
|
+
Parameters
|
|
2287
|
+
----------
|
|
2288
|
+
radial_reference_pt : `array_like` (`float`, shape=(``2``,)), optional
|
|
2289
|
+
The radial reference point, :math:`\left(u_{x;c;\text{GB}},
|
|
2290
|
+
u_{y;c;\text{GB}}\right)`.
|
|
2291
|
+
radial_amplitudes : `array_like` (`float`, ndim=1), optional
|
|
2292
|
+
The radial amplitudes,
|
|
2293
|
+
:math:`\left\{ D_{\text{GB};n}\right\} _{n=0}^{N_{\text{GB}}}`.
|
|
2294
|
+
radial_phases : `array_like` (`float`, shape=(``len(radial_amplitudes)-1``,)), optional
|
|
2295
|
+
The radial phases,
|
|
2296
|
+
:math:`\left\{\phi_{\text{GB};n}\right\}_{n=0}^{N_{\text{GB}}-1}`.
|
|
2297
|
+
intra_shape_val : `float`, optional
|
|
2298
|
+
The value of the intensity pattern inside the generic blob.
|
|
2299
|
+
skip_validation_and_conversion : `bool`, optional
|
|
2300
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
2301
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
2302
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
2303
|
+
being `dict` objects.
|
|
2304
|
+
|
|
2305
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
2306
|
+
representation of the constructor parameters excluding the parameter
|
|
2307
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
2308
|
+
different constructor parameter name, excluding the name
|
|
2309
|
+
``"skip_validation_and_conversion"``, and
|
|
2310
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
2311
|
+
constructor parameter with the name given by ``key``.
|
|
2312
|
+
|
|
2313
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
2314
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
2315
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
2316
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
2317
|
+
|
|
2318
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
2319
|
+
then ``core_attrs`` is set to
|
|
2320
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
2321
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
2322
|
+
and/or conversions of the `dict` values of
|
|
2323
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
2324
|
+
copies or conversions are made in this case.
|
|
2325
|
+
|
|
2326
|
+
"""
|
|
2327
|
+
ctor_param_names = ("radial_reference_pt",
|
|
2328
|
+
"radial_amplitudes",
|
|
2329
|
+
"radial_phases",
|
|
2330
|
+
"intra_shape_val")
|
|
2331
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
2332
|
+
"ctor_param_names": ctor_param_names}
|
|
2333
|
+
|
|
2334
|
+
_validation_and_conversion_funcs_ = \
|
|
2335
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
2336
|
+
_pre_serialization_funcs_ = \
|
|
2337
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
2338
|
+
_de_pre_serialization_funcs_ = \
|
|
2339
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
2340
|
+
|
|
2341
|
+
del ctor_param_names, kwargs
|
|
2342
|
+
|
|
2343
|
+
|
|
2344
|
+
|
|
2345
|
+
def __init__(self,
|
|
2346
|
+
radial_reference_pt=\
|
|
2347
|
+
_default_radial_reference_pt,
|
|
2348
|
+
radial_amplitudes=\
|
|
2349
|
+
_default_radial_amplitudes,
|
|
2350
|
+
radial_phases=\
|
|
2351
|
+
_default_radial_phases,
|
|
2352
|
+
intra_shape_val=\
|
|
2353
|
+
_default_intra_shape_val,
|
|
2354
|
+
skip_validation_and_conversion=\
|
|
2355
|
+
_default_skip_validation_and_conversion):
|
|
2356
|
+
ctor_params = {key: val
|
|
2357
|
+
for key, val in locals().items()
|
|
2358
|
+
if (key not in ("self", "__class__"))}
|
|
2359
|
+
BaseShape.__init__(self, ctor_params)
|
|
2360
|
+
|
|
2361
|
+
self.execute_post_core_attrs_update_actions()
|
|
2362
|
+
|
|
2363
|
+
return None
|
|
2364
|
+
|
|
2365
|
+
|
|
2366
|
+
|
|
2367
|
+
def execute_post_core_attrs_update_actions(self):
|
|
2368
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
2369
|
+
for self_core_attr_name in self_core_attrs:
|
|
2370
|
+
attr_name = "_"+self_core_attr_name
|
|
2371
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
2372
|
+
setattr(self, attr_name, attr)
|
|
2373
|
+
|
|
2374
|
+
return None
|
|
2375
|
+
|
|
2376
|
+
|
|
2377
|
+
|
|
2378
|
+
def update(self,
|
|
2379
|
+
new_core_attr_subset_candidate,
|
|
2380
|
+
skip_validation_and_conversion=\
|
|
2381
|
+
_default_skip_validation_and_conversion):
|
|
2382
|
+
super().update(new_core_attr_subset_candidate,
|
|
2383
|
+
skip_validation_and_conversion)
|
|
2384
|
+
self.execute_post_core_attrs_update_actions()
|
|
2385
|
+
|
|
2386
|
+
return None
|
|
2387
|
+
|
|
2388
|
+
|
|
2389
|
+
|
|
2390
|
+
def _eval(self, u_x, u_y):
|
|
2391
|
+
u_x_c, u_y_c = self._radial_reference_pt
|
|
2392
|
+
D = self._radial_amplitudes
|
|
2393
|
+
phi = self._radial_phases
|
|
2394
|
+
A = self._intra_shape_val
|
|
2395
|
+
|
|
2396
|
+
delta_u_x = u_x-u_x_c
|
|
2397
|
+
delta_u_y = u_y-u_y_c
|
|
2398
|
+
|
|
2399
|
+
u_r = torch.sqrt(delta_u_x*delta_u_x + delta_u_y*delta_u_y)
|
|
2400
|
+
u_theta = torch.atan2(delta_u_y, delta_u_x) % (2*np.pi)
|
|
2401
|
+
|
|
2402
|
+
N = len(phi)
|
|
2403
|
+
|
|
2404
|
+
one = torch.tensor(1.0, device=u_x.device)
|
|
2405
|
+
|
|
2406
|
+
R = D[0]*torch.ones_like(u_theta)
|
|
2407
|
+
for n in range(1, N+1):
|
|
2408
|
+
R += D[n]*torch.cos(n*u_theta - phi[n-1])
|
|
2409
|
+
|
|
2410
|
+
result = A * torch.heaviside(R-u_r, one)
|
|
2411
|
+
|
|
2412
|
+
return result
|
|
2413
|
+
|
|
2414
|
+
|
|
2415
|
+
|
|
2416
|
+
def _check_and_convert_principal_quantum_number(params):
|
|
2417
|
+
current_func_name = inspect.stack()[0][3]
|
|
2418
|
+
obj_name = current_func_name[19:]
|
|
2419
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2420
|
+
principal_quantum_number = czekitout.convert.to_positive_int(**kwargs)
|
|
2421
|
+
|
|
2422
|
+
return principal_quantum_number
|
|
2423
|
+
|
|
2424
|
+
|
|
2425
|
+
|
|
2426
|
+
def _pre_serialize_principal_quantum_number(principal_quantum_number):
|
|
2427
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2428
|
+
serializable_rep = obj_to_pre_serialize
|
|
2429
|
+
|
|
2430
|
+
return serializable_rep
|
|
2431
|
+
|
|
2432
|
+
|
|
2433
|
+
|
|
2434
|
+
def _de_pre_serialize_principal_quantum_number(serializable_rep):
|
|
2435
|
+
principal_quantum_number = serializable_rep
|
|
2436
|
+
|
|
2437
|
+
return principal_quantum_number
|
|
2438
|
+
|
|
2439
|
+
|
|
2440
|
+
|
|
2441
|
+
def _check_and_convert_azimuthal_quantum_number(params):
|
|
2442
|
+
current_func_name = inspect.stack()[0][3]
|
|
2443
|
+
obj_name = current_func_name[19:]
|
|
2444
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2445
|
+
azimuthal_quantum_number = czekitout.convert.to_nonnegative_int(**kwargs)
|
|
2446
|
+
|
|
2447
|
+
principal_quantum_number = \
|
|
2448
|
+
_check_and_convert_principal_quantum_number(params)
|
|
2449
|
+
|
|
2450
|
+
if azimuthal_quantum_number >= principal_quantum_number:
|
|
2451
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
2452
|
+
raise ValueError(err_msg)
|
|
2453
|
+
|
|
2454
|
+
return azimuthal_quantum_number
|
|
2455
|
+
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
def _pre_serialize_azimuthal_quantum_number(azimuthal_quantum_number):
|
|
2459
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2460
|
+
serializable_rep = obj_to_pre_serialize
|
|
2461
|
+
|
|
2462
|
+
return serializable_rep
|
|
2463
|
+
|
|
2464
|
+
|
|
2465
|
+
|
|
2466
|
+
def _de_pre_serialize_azimuthal_quantum_number(serializable_rep):
|
|
2467
|
+
azimuthal_quantum_number = serializable_rep
|
|
2468
|
+
|
|
2469
|
+
return azimuthal_quantum_number
|
|
2470
|
+
|
|
2471
|
+
|
|
2472
|
+
|
|
2473
|
+
def _check_and_convert_magnetic_quantum_number(params):
|
|
2474
|
+
current_func_name = inspect.stack()[0][3]
|
|
2475
|
+
obj_name = current_func_name[19:]
|
|
2476
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2477
|
+
magnetic_quantum_number = czekitout.convert.to_int(**kwargs)
|
|
2478
|
+
|
|
2479
|
+
azimuthal_quantum_number = \
|
|
2480
|
+
_check_and_convert_azimuthal_quantum_number(params)
|
|
2481
|
+
|
|
2482
|
+
if abs(magnetic_quantum_number) > azimuthal_quantum_number:
|
|
2483
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
2484
|
+
raise ValueError(err_msg)
|
|
2485
|
+
|
|
2486
|
+
return magnetic_quantum_number
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
|
|
2490
|
+
def _pre_serialize_magnetic_quantum_number(magnetic_quantum_number):
|
|
2491
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2492
|
+
serializable_rep = obj_to_pre_serialize
|
|
2493
|
+
|
|
2494
|
+
return serializable_rep
|
|
2495
|
+
|
|
2496
|
+
|
|
2497
|
+
|
|
2498
|
+
def _de_pre_serialize_magnetic_quantum_number(serializable_rep):
|
|
2499
|
+
magnetic_quantum_number = serializable_rep
|
|
2500
|
+
|
|
2501
|
+
return magnetic_quantum_number
|
|
2502
|
+
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
def _check_and_convert_effective_size(params):
|
|
2506
|
+
current_func_name = inspect.stack()[0][3]
|
|
2507
|
+
obj_name = current_func_name[19:]
|
|
2508
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2509
|
+
effective_size = czekitout.convert.to_positive_float(**kwargs)
|
|
2510
|
+
|
|
2511
|
+
return effective_size
|
|
2512
|
+
|
|
2513
|
+
|
|
2514
|
+
|
|
2515
|
+
def _pre_serialize_effective_size(effective_size):
|
|
2516
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2517
|
+
serializable_rep = obj_to_pre_serialize
|
|
2518
|
+
|
|
2519
|
+
return serializable_rep
|
|
2520
|
+
|
|
2521
|
+
|
|
2522
|
+
|
|
2523
|
+
def _de_pre_serialize_effective_size(serializable_rep):
|
|
2524
|
+
effective_size = serializable_rep
|
|
2525
|
+
|
|
2526
|
+
return effective_size
|
|
2527
|
+
|
|
2528
|
+
|
|
2529
|
+
|
|
2530
|
+
def _check_and_convert_renormalization_factor(params):
|
|
2531
|
+
current_func_name = inspect.stack()[0][3]
|
|
2532
|
+
obj_name = current_func_name[19:]
|
|
2533
|
+
kwargs = {"obj": params[obj_name], "obj_name": obj_name}
|
|
2534
|
+
renormalization_factor = czekitout.convert.to_float(**kwargs)
|
|
2535
|
+
|
|
2536
|
+
return renormalization_factor
|
|
2537
|
+
|
|
2538
|
+
|
|
2539
|
+
|
|
2540
|
+
def _pre_serialize_renormalization_factor(renormalization_factor):
|
|
2541
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2542
|
+
serializable_rep = obj_to_pre_serialize
|
|
2543
|
+
|
|
2544
|
+
return serializable_rep
|
|
2545
|
+
|
|
2546
|
+
|
|
2547
|
+
|
|
2548
|
+
def _de_pre_serialize_renormalization_factor(serializable_rep):
|
|
2549
|
+
renormalization_factor = serializable_rep
|
|
2550
|
+
|
|
2551
|
+
return renormalization_factor
|
|
2552
|
+
|
|
2553
|
+
|
|
2554
|
+
|
|
2555
|
+
_default_principal_quantum_number = 1
|
|
2556
|
+
_default_azimuthal_quantum_number = 0
|
|
2557
|
+
_default_magnetic_quantum_number = 0
|
|
2558
|
+
_default_effective_size = 0.1
|
|
2559
|
+
_default_renormalization_factor = 1.0
|
|
2560
|
+
|
|
2561
|
+
|
|
2562
|
+
|
|
2563
|
+
class Orbital(BaseShape):
|
|
2564
|
+
r"""The intensity pattern of a hydrogen-like atomic orbital.
|
|
2565
|
+
|
|
2566
|
+
Let :math:`\left(u_{x;c;\text{O}},u_{y;c;\text{O}}\right)`,
|
|
2567
|
+
:math:`n_{\text{O}}`, :math:`l_{\text{O}}`, :math:`m_{\text{O}}`,
|
|
2568
|
+
:math:`a_{0;\text{O}}^{*}`, and :math:`\theta_{\text{O}}` be the center, the
|
|
2569
|
+
principal quantum number, the azimuthal quantum number, the magnetic quantum
|
|
2570
|
+
number, the effective size, and the rotation angle of the hydrogen-like
|
|
2571
|
+
atomic orbital respectively. Furthermore, let :math:`A_{\text{O}}` be the
|
|
2572
|
+
renormalization factor of the intensity pattern. The undistorted intensity
|
|
2573
|
+
pattern of the orbital is given by:
|
|
2574
|
+
|
|
2575
|
+
.. math ::
|
|
2576
|
+
\mathcal{I}_{\text{O}}\left(u_{x},u_{y}\right)=
|
|
2577
|
+
A_{\text{O}}\left|\psi_{n_{\text{O}},l_{\text{O}},m_{O}}\left(
|
|
2578
|
+
u_{r;\text{O}},u_{\theta;O},0\right)\right|^{2},
|
|
2579
|
+
:label: intensity_pattern_of_orbital__1
|
|
2580
|
+
|
|
2581
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
2582
|
+
coordinates of the undistorted intensity pattern of the orbital
|
|
2583
|
+
respectively,
|
|
2584
|
+
|
|
2585
|
+
.. math ::
|
|
2586
|
+
u_{r;\text{O}}=\sqrt{\left(u_{x}-u_{x;c;\text{O}}\right)^{2}
|
|
2587
|
+
+\left(u_{y}-u_{y;c;\text{O}}\right)^{2}},
|
|
2588
|
+
:label: u_r_O__1
|
|
2589
|
+
|
|
2590
|
+
.. math ::
|
|
2591
|
+
u_{\theta;\text{O}}=\tan^{-1}\left(\frac{u_{y}-u_{y;c;\text{O}}}{
|
|
2592
|
+
u_{x}-u_{x;c;\text{O}}}\right)-\theta_{\text{O}};
|
|
2593
|
+
:label: u_theta_O__1
|
|
2594
|
+
|
|
2595
|
+
.. math ::
|
|
2596
|
+
\psi_{n_{\text{O}},l_{\text{O}},m_{O}}\left(u_{r;\text{O}},u_{\theta;O},
|
|
2597
|
+
u_{\phi;\text{O}}\right)&=\sqrt{\left\{ \frac{2}{n_{\text{O}}
|
|
2598
|
+
a_{0;\text{O}}^{*}}\right\} ^{3}\frac{\left(
|
|
2599
|
+
n_{\text{O}}-l_{\text{O}}-1\right)!}{2n_{\text{O}}\left(
|
|
2600
|
+
n_{\text{O}}+l_{\text{O}}\right)!}}\\&\quad\mathop{\times}
|
|
2601
|
+
e^{-u_{\rho;\text{O}}/2}u_{\rho;\text{O}}^{l_{\text{O}}}
|
|
2602
|
+
L_{n_{\text{O}}-l_{\text{O}}-1}^{\left(2l_{\text{O}}+1\right)}\left(
|
|
2603
|
+
u_{\rho;\text{O}}\right)\\&\quad\mathop{\times}
|
|
2604
|
+
Y_{l_{\text{O}}}^{m_{\text{O}}}\left(u_{\theta;\text{O}},
|
|
2605
|
+
u_{\phi;\text{O}}\right),
|
|
2606
|
+
:label: psi__1
|
|
2607
|
+
|
|
2608
|
+
with
|
|
2609
|
+
|
|
2610
|
+
.. math ::
|
|
2611
|
+
u_{\rho;\text{O}}=\frac{2u_{r;\text{O}}}{n_{\text{O}}
|
|
2612
|
+
a_{0;\text{O}}^{*}},
|
|
2613
|
+
:label: u_rho_O__1
|
|
2614
|
+
|
|
2615
|
+
:math:`L_{n_{\text{O}}-l_{\text{O}}-1}^{2l_{\text{O}}+1}\left(
|
|
2616
|
+
u_{\rho;\text{O}}\right)` being the generalized Laguerre polynomial of
|
|
2617
|
+
degree :math:`n_{\text{O}}-l_{\text{O}}-1`, and
|
|
2618
|
+
:math:`Y_{l_{\text{O}}}^{m_{\text{O}}}\left(u_{\theta;\text{O}},
|
|
2619
|
+
u_{\phi;\text{O}}\right)` is the spherical harmonic function of degree
|
|
2620
|
+
:math:`l_{\text{O}}` and order :math:`m_{\text{O}}`.
|
|
2621
|
+
|
|
2622
|
+
Parameters
|
|
2623
|
+
----------
|
|
2624
|
+
center : `array_like` (`float`, shape=(``2``,)), optional
|
|
2625
|
+
The center of the hydrogen-like atomic orbital,
|
|
2626
|
+
:math:`\left(u_{x;c;\text{O}}, u_{y;c;\text{O}}\right)`.
|
|
2627
|
+
principal_quantum_number : `int`, optional
|
|
2628
|
+
The principal quantum number of the hydrogen-like atomic orbital,
|
|
2629
|
+
:math:`n_{\text{O}}`. Must be a positve number.
|
|
2630
|
+
azimuthal_quantum_number : `int`, optional
|
|
2631
|
+
The azimuthal quantum number of the hydrogen-like atomic orbital,
|
|
2632
|
+
:math:`l_{\text{O}}`. Must be a nonnegative number satisfying
|
|
2633
|
+
``azimuthal_quantum_number < principal_quantum_number``.
|
|
2634
|
+
magnetic_quantum_number : `int`, optional
|
|
2635
|
+
The magnetic quantum number of the hydrogen-like atomic orbital,
|
|
2636
|
+
:math:`m_{\text{O}}`. Must satisfy ``abs(magnetic_quantum_number) <=
|
|
2637
|
+
azimuthal_quantum_number``.
|
|
2638
|
+
effective_size : `float`, optional
|
|
2639
|
+
The effective size of the hydrogen-like atomic orbital,
|
|
2640
|
+
:math:`a_{0;\text{O}}^{*}`. Must be a positive number.
|
|
2641
|
+
renormalization_factor : `float`, optional
|
|
2642
|
+
The renormalization factor of the hydrogen-like atomic orbital,
|
|
2643
|
+
:math:`A_{\text{O}}`.
|
|
2644
|
+
rotation_angle : `float`, optional
|
|
2645
|
+
The rotation angle of the hydrogen-like atomic orbital,
|
|
2646
|
+
:math:`\theta_{\text{O}}`.
|
|
2647
|
+
skip_validation_and_conversion : `bool`, optional
|
|
2648
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
2649
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
2650
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
2651
|
+
being `dict` objects.
|
|
2652
|
+
|
|
2653
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
2654
|
+
representation of the constructor parameters excluding the parameter
|
|
2655
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
2656
|
+
different constructor parameter name, excluding the name
|
|
2657
|
+
``"skip_validation_and_conversion"``, and
|
|
2658
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
2659
|
+
constructor parameter with the name given by ``key``.
|
|
2660
|
+
|
|
2661
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
2662
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
2663
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
2664
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
2665
|
+
|
|
2666
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
2667
|
+
then ``core_attrs`` is set to
|
|
2668
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
2669
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
2670
|
+
and/or conversions of the `dict` values of
|
|
2671
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
2672
|
+
copies or conversions are made in this case.
|
|
2673
|
+
|
|
2674
|
+
"""
|
|
2675
|
+
ctor_param_names = ("center",
|
|
2676
|
+
"principal_quantum_number",
|
|
2677
|
+
"azimuthal_quantum_number",
|
|
2678
|
+
"magnetic_quantum_number",
|
|
2679
|
+
"effective_size",
|
|
2680
|
+
"renormalization_factor",
|
|
2681
|
+
"rotation_angle")
|
|
2682
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
2683
|
+
"ctor_param_names": ctor_param_names}
|
|
2684
|
+
|
|
2685
|
+
_validation_and_conversion_funcs_ = \
|
|
2686
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
2687
|
+
_pre_serialization_funcs_ = \
|
|
2688
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
2689
|
+
_de_pre_serialization_funcs_ = \
|
|
2690
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
2691
|
+
|
|
2692
|
+
del ctor_param_names, kwargs
|
|
2693
|
+
|
|
2694
|
+
|
|
2695
|
+
|
|
2696
|
+
def __init__(self,
|
|
2697
|
+
center=\
|
|
2698
|
+
_default_center,
|
|
2699
|
+
principal_quantum_number=\
|
|
2700
|
+
_default_principal_quantum_number,
|
|
2701
|
+
azimuthal_quantum_number=\
|
|
2702
|
+
_default_azimuthal_quantum_number,
|
|
2703
|
+
magnetic_quantum_number=\
|
|
2704
|
+
_default_magnetic_quantum_number,
|
|
2705
|
+
effective_size=\
|
|
2706
|
+
_default_effective_size,
|
|
2707
|
+
renormalization_factor=\
|
|
2708
|
+
_default_renormalization_factor,
|
|
2709
|
+
rotation_angle=\
|
|
2710
|
+
_default_rotation_angle,
|
|
2711
|
+
skip_validation_and_conversion=\
|
|
2712
|
+
_default_skip_validation_and_conversion):
|
|
2713
|
+
ctor_params = {key: val
|
|
2714
|
+
for key, val in locals().items()
|
|
2715
|
+
if (key not in ("self", "__class__"))}
|
|
2716
|
+
BaseShape.__init__(self, ctor_params)
|
|
2717
|
+
|
|
2718
|
+
self.execute_post_core_attrs_update_actions()
|
|
2719
|
+
|
|
2720
|
+
return None
|
|
2721
|
+
|
|
2722
|
+
|
|
2723
|
+
|
|
2724
|
+
def execute_post_core_attrs_update_actions(self):
|
|
2725
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
2726
|
+
for self_core_attr_name in self_core_attrs:
|
|
2727
|
+
attr_name = "_"+self_core_attr_name
|
|
2728
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
2729
|
+
setattr(self, attr_name, attr)
|
|
2730
|
+
|
|
2731
|
+
n = self._principal_quantum_number
|
|
2732
|
+
l = self._azimuthal_quantum_number
|
|
2733
|
+
m = self._magnetic_quantum_number
|
|
2734
|
+
a = self._effective_size
|
|
2735
|
+
A = self._renormalization_factor
|
|
2736
|
+
|
|
2737
|
+
self._pre_factor = (A
|
|
2738
|
+
* ((2/(n*a))**3
|
|
2739
|
+
* math.factorial(n-l-1)
|
|
2740
|
+
/ (2*n*math.factorial(n+l)))
|
|
2741
|
+
* ((2*l+1)
|
|
2742
|
+
* math.factorial(l-m)
|
|
2743
|
+
/ math.factorial(l+m)
|
|
2744
|
+
/ (4*np.pi)))
|
|
2745
|
+
|
|
2746
|
+
self._generalized_laguerre_polynomial_coeffs = \
|
|
2747
|
+
self._calc_generalized_laguerre_polynomial_coeffs()
|
|
2748
|
+
|
|
2749
|
+
unformatted_method_name = \
|
|
2750
|
+
"_calc_generalized_laguerre_polynomial_and_u_rho_to_power_of_2l_v{}"
|
|
2751
|
+
if l <= n-l-1:
|
|
2752
|
+
method_name = \
|
|
2753
|
+
unformatted_method_name.format(1)
|
|
2754
|
+
else:
|
|
2755
|
+
method_name = \
|
|
2756
|
+
unformatted_method_name.format(2)
|
|
2757
|
+
method_alias = \
|
|
2758
|
+
getattr(self, method_name)
|
|
2759
|
+
self._calc_generalized_laguerre_polynomial_and_u_rho_to_power_of_2l = \
|
|
2760
|
+
method_alias
|
|
2761
|
+
|
|
2762
|
+
self._associated_legendre_polynomial_coeffs = \
|
|
2763
|
+
self._calc_associated_legendre_polynomial_coeffs()
|
|
2764
|
+
|
|
2765
|
+
return None
|
|
2766
|
+
|
|
2767
|
+
|
|
2768
|
+
|
|
2769
|
+
def _calc_generalized_laguerre_polynomial_coeffs(self):
|
|
2770
|
+
n = self._principal_quantum_number
|
|
2771
|
+
l = self._azimuthal_quantum_number
|
|
2772
|
+
|
|
2773
|
+
polynomial_degree = n-l-1
|
|
2774
|
+
alpha = 2*l+1
|
|
2775
|
+
|
|
2776
|
+
coeff = 1
|
|
2777
|
+
numerator = polynomial_degree+alpha
|
|
2778
|
+
denominator = polynomial_degree
|
|
2779
|
+
for k in range(polynomial_degree):
|
|
2780
|
+
coeff *= (numerator/denominator)
|
|
2781
|
+
numerator -= 1
|
|
2782
|
+
denominator -= 1
|
|
2783
|
+
|
|
2784
|
+
generalized_laguerre_polynomial_coeffs = (coeff,)
|
|
2785
|
+
for i in range(polynomial_degree):
|
|
2786
|
+
i_plus_1 = i+1
|
|
2787
|
+
coeff = -((polynomial_degree-i)
|
|
2788
|
+
/ (i_plus_1 * (alpha+i_plus_1))
|
|
2789
|
+
* generalized_laguerre_polynomial_coeffs[-1])
|
|
2790
|
+
generalized_laguerre_polynomial_coeffs += (coeff,)
|
|
2791
|
+
|
|
2792
|
+
return generalized_laguerre_polynomial_coeffs
|
|
2793
|
+
|
|
2794
|
+
|
|
2795
|
+
|
|
2796
|
+
def _calc_associated_legendre_polynomial_coeffs(self):
|
|
2797
|
+
l = self._azimuthal_quantum_number
|
|
2798
|
+
m = self._magnetic_quantum_number
|
|
2799
|
+
abs_m = abs(m)
|
|
2800
|
+
r = (l-abs_m)//2
|
|
2801
|
+
|
|
2802
|
+
coeff = (-1)**r
|
|
2803
|
+
numerator = 2*l - 2*r
|
|
2804
|
+
denominator = l - r
|
|
2805
|
+
for k in range(0, l-r):
|
|
2806
|
+
coeff *= (numerator/denominator)
|
|
2807
|
+
numerator -= 1
|
|
2808
|
+
denominator -= 1
|
|
2809
|
+
denominator = r
|
|
2810
|
+
for k in range(l-r, l):
|
|
2811
|
+
coeff *= (numerator/denominator)
|
|
2812
|
+
numerator -= 1
|
|
2813
|
+
denominator -= 1
|
|
2814
|
+
denominator = 1
|
|
2815
|
+
for k in range(l, 2*l-2*r):
|
|
2816
|
+
coeff *= (numerator/denominator)
|
|
2817
|
+
numerator -= 1
|
|
2818
|
+
coeff /= 2**l
|
|
2819
|
+
|
|
2820
|
+
associated_legendre_polynomial_coeffs = (coeff,)
|
|
2821
|
+
temp_1 = r
|
|
2822
|
+
temp_2 = l-r+1
|
|
2823
|
+
temp_3 = 2*l-2*r+1
|
|
2824
|
+
temp_4 = l-abs_m-2*r+1
|
|
2825
|
+
for i in range(r):
|
|
2826
|
+
coeff = -((temp_1/temp_2)
|
|
2827
|
+
* (temp_3/temp_4)
|
|
2828
|
+
* ((temp_3+1)/(temp_4+1))
|
|
2829
|
+
* associated_legendre_polynomial_coeffs[-1])
|
|
2830
|
+
associated_legendre_polynomial_coeffs += (coeff,)
|
|
2831
|
+
temp_1 -= 1
|
|
2832
|
+
temp_2 += 1
|
|
2833
|
+
temp_3 += 2
|
|
2834
|
+
temp_4 += 2
|
|
2835
|
+
|
|
2836
|
+
return associated_legendre_polynomial_coeffs
|
|
2837
|
+
|
|
2838
|
+
|
|
2839
|
+
|
|
2840
|
+
def update(self,
|
|
2841
|
+
new_core_attr_subset_candidate,
|
|
2842
|
+
skip_validation_and_conversion=\
|
|
2843
|
+
_default_skip_validation_and_conversion):
|
|
2844
|
+
super().update(new_core_attr_subset_candidate,
|
|
2845
|
+
skip_validation_and_conversion)
|
|
2846
|
+
self.execute_post_core_attrs_update_actions()
|
|
2847
|
+
|
|
2848
|
+
return None
|
|
2849
|
+
|
|
2850
|
+
|
|
2851
|
+
|
|
2852
|
+
def _eval(self, u_x, u_y):
|
|
2853
|
+
u_x_c, u_y_c = self._center
|
|
2854
|
+
n = self._principal_quantum_number
|
|
2855
|
+
l = self._azimuthal_quantum_number
|
|
2856
|
+
a = self._effective_size
|
|
2857
|
+
theta = self._rotation_angle
|
|
2858
|
+
pre_factor = self._pre_factor
|
|
2859
|
+
|
|
2860
|
+
delta_u_x = u_x-u_x_c
|
|
2861
|
+
delta_u_y = u_y-u_y_c
|
|
2862
|
+
|
|
2863
|
+
u_r = torch.sqrt(delta_u_x*delta_u_x + delta_u_y*delta_u_y)
|
|
2864
|
+
u_rho = (2/n/a)*u_r
|
|
2865
|
+
|
|
2866
|
+
u_theta = torch.atan2(delta_u_y, delta_u_x) - theta
|
|
2867
|
+
cos_u_theta = torch.cos(u_theta)
|
|
2868
|
+
|
|
2869
|
+
method_name = ("_calc_generalized_laguerre_polynomial"
|
|
2870
|
+
"_and_u_rho_to_power_of_2l")
|
|
2871
|
+
method_alias = getattr(self, method_name)
|
|
2872
|
+
L, u_rho_to_power_of_2l = method_alias(u_rho)
|
|
2873
|
+
L_sq = L*L
|
|
2874
|
+
|
|
2875
|
+
method_name = "_calc_associated_legendre_polynomial_sq"
|
|
2876
|
+
method_alias = getattr(self, method_name)
|
|
2877
|
+
P_sq = method_alias(cos_u_theta)
|
|
2878
|
+
|
|
2879
|
+
result = (pre_factor
|
|
2880
|
+
* torch.exp(-u_rho)
|
|
2881
|
+
* u_rho_to_power_of_2l
|
|
2882
|
+
* L_sq
|
|
2883
|
+
* P_sq)
|
|
2884
|
+
|
|
2885
|
+
return result
|
|
2886
|
+
|
|
2887
|
+
|
|
2888
|
+
|
|
2889
|
+
def _calc_generalized_laguerre_polynomial_and_u_rho_to_power_of_2l_v1(
|
|
2890
|
+
self, u_rho):
|
|
2891
|
+
coeffs = self._generalized_laguerre_polynomial_coeffs
|
|
2892
|
+
|
|
2893
|
+
n = self._principal_quantum_number
|
|
2894
|
+
l = self._azimuthal_quantum_number
|
|
2895
|
+
polynomial_degree = n-l-1
|
|
2896
|
+
|
|
2897
|
+
power_of_u_rho = torch.ones_like(u_rho)
|
|
2898
|
+
generalized_laguerre_polynomial = coeffs[0]*power_of_u_rho
|
|
2899
|
+
|
|
2900
|
+
for i in range(1, l+1):
|
|
2901
|
+
power_of_u_rho *= u_rho
|
|
2902
|
+
generalized_laguerre_polynomial += coeffs[i]*power_of_u_rho
|
|
2903
|
+
|
|
2904
|
+
u_rho_to_power_of_2l = power_of_u_rho*power_of_u_rho
|
|
2905
|
+
|
|
2906
|
+
for i in range(l+1, polynomial_degree+1):
|
|
2907
|
+
power_of_u_rho *= u_rho
|
|
2908
|
+
generalized_laguerre_polynomial += coeffs[i]*power_of_u_rho
|
|
2909
|
+
|
|
2910
|
+
return generalized_laguerre_polynomial, u_rho_to_power_of_2l
|
|
2911
|
+
|
|
2912
|
+
|
|
2913
|
+
|
|
2914
|
+
def _calc_generalized_laguerre_polynomial_and_u_rho_to_power_of_2l_v2(
|
|
2915
|
+
self, u_rho):
|
|
2916
|
+
coeffs = self._generalized_laguerre_polynomial_coeffs
|
|
2917
|
+
|
|
2918
|
+
n = self._principal_quantum_number
|
|
2919
|
+
l = self._azimuthal_quantum_number
|
|
2920
|
+
polynomial_degree = n-l-1
|
|
2921
|
+
|
|
2922
|
+
power_of_u_rho = torch.ones_like(u_rho)
|
|
2923
|
+
generalized_laguerre_polynomial = coeffs[0]*power_of_u_rho
|
|
2924
|
+
|
|
2925
|
+
for i in range(1, polynomial_degree+1):
|
|
2926
|
+
power_of_u_rho *= u_rho
|
|
2927
|
+
generalized_laguerre_polynomial += coeffs[i]*power_of_u_rho
|
|
2928
|
+
|
|
2929
|
+
for i in range(polynomial_degree+1, l+1):
|
|
2930
|
+
power_of_u_rho *= u_rho
|
|
2931
|
+
|
|
2932
|
+
u_rho_to_power_of_2l = power_of_u_rho*power_of_u_rho
|
|
2933
|
+
|
|
2934
|
+
return generalized_laguerre_polynomial, u_rho_to_power_of_2l
|
|
2935
|
+
|
|
2936
|
+
|
|
2937
|
+
|
|
2938
|
+
def _calc_associated_legendre_polynomial_sq(self, cos_u_theta):
|
|
2939
|
+
coeffs = self._associated_legendre_polynomial_coeffs
|
|
2940
|
+
|
|
2941
|
+
l = self._azimuthal_quantum_number
|
|
2942
|
+
m = self._magnetic_quantum_number
|
|
2943
|
+
abs_m = abs(m)
|
|
2944
|
+
r = (l-abs_m)//2
|
|
2945
|
+
|
|
2946
|
+
x = cos_u_theta
|
|
2947
|
+
x_sq = x*x
|
|
2948
|
+
power_of_x = torch.ones_like(x)
|
|
2949
|
+
for _ in range(l-abs_m-2*r):
|
|
2950
|
+
power_of_x *= x
|
|
2951
|
+
|
|
2952
|
+
y_sq = 1-x_sq
|
|
2953
|
+
|
|
2954
|
+
associated_legendre_polynomial_sq = coeffs[0]*power_of_x
|
|
2955
|
+
for i in range(1, r+1):
|
|
2956
|
+
power_of_x *= x_sq
|
|
2957
|
+
associated_legendre_polynomial_sq += coeffs[i]*power_of_x
|
|
2958
|
+
associated_legendre_polynomial_sq *= associated_legendre_polynomial_sq
|
|
2959
|
+
for i in range(abs_m):
|
|
2960
|
+
associated_legendre_polynomial_sq *= y_sq
|
|
2961
|
+
|
|
2962
|
+
return associated_legendre_polynomial_sq
|
|
2963
|
+
|
|
2964
|
+
|
|
2965
|
+
|
|
2966
|
+
def _check_and_convert_bg_ellipse(params):
|
|
2967
|
+
current_func_name = inspect.stack()[0][3]
|
|
2968
|
+
char_idx = 19
|
|
2969
|
+
obj_name = current_func_name[char_idx:]
|
|
2970
|
+
obj = params[obj_name]
|
|
2971
|
+
|
|
2972
|
+
accepted_types = (Ellipse, Circle, type(None))
|
|
2973
|
+
|
|
2974
|
+
if isinstance(obj, accepted_types[-1]):
|
|
2975
|
+
bg_ellipse = accepted_types[0]()
|
|
2976
|
+
else:
|
|
2977
|
+
kwargs = {"obj": obj,
|
|
2978
|
+
"obj_name": obj_name,
|
|
2979
|
+
"accepted_types": accepted_types}
|
|
2980
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
2981
|
+
bg_ellipse = copy.deepcopy(obj)
|
|
2982
|
+
|
|
2983
|
+
return bg_ellipse
|
|
2984
|
+
|
|
2985
|
+
|
|
2986
|
+
|
|
2987
|
+
def _pre_serialize_bg_ellipse(bg_ellipse):
|
|
2988
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
2989
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
2990
|
+
|
|
2991
|
+
return serializable_rep
|
|
2992
|
+
|
|
2993
|
+
|
|
2994
|
+
|
|
2995
|
+
def _de_pre_serialize_bg_ellipse(serializable_rep):
|
|
2996
|
+
if "radius" in serializable_rep:
|
|
2997
|
+
bg_ellipse = Circle.de_pre_serialize(serializable_rep)
|
|
2998
|
+
else:
|
|
2999
|
+
bg_ellipse = Ellipse.de_pre_serialize(serializable_rep)
|
|
3000
|
+
|
|
3001
|
+
return bg_ellipse
|
|
3002
|
+
|
|
3003
|
+
|
|
3004
|
+
|
|
3005
|
+
def _check_and_convert_fg_ellipse(params):
|
|
3006
|
+
current_func_name = inspect.stack()[0][3]
|
|
3007
|
+
char_idx = 19
|
|
3008
|
+
obj_name = current_func_name[char_idx:]
|
|
3009
|
+
obj = params[obj_name]
|
|
3010
|
+
|
|
3011
|
+
accepted_types = (Ellipse, Circle, type(None))
|
|
3012
|
+
|
|
3013
|
+
if isinstance(obj, accepted_types[-1]):
|
|
3014
|
+
fg_ellipse = accepted_types[0]()
|
|
3015
|
+
else:
|
|
3016
|
+
kwargs = {"obj": obj,
|
|
3017
|
+
"obj_name": obj_name,
|
|
3018
|
+
"accepted_types": accepted_types}
|
|
3019
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
3020
|
+
fg_ellipse = copy.deepcopy(obj)
|
|
3021
|
+
|
|
3022
|
+
return fg_ellipse
|
|
3023
|
+
|
|
3024
|
+
|
|
3025
|
+
|
|
3026
|
+
def _pre_serialize_fg_ellipse(fg_ellipse):
|
|
3027
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
3028
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
3029
|
+
|
|
3030
|
+
return serializable_rep
|
|
3031
|
+
|
|
3032
|
+
|
|
3033
|
+
|
|
3034
|
+
def _de_pre_serialize_fg_ellipse(serializable_rep):
|
|
3035
|
+
if "radius" in serializable_rep:
|
|
3036
|
+
fg_ellipse = Circle.de_pre_serialize(serializable_rep)
|
|
3037
|
+
else:
|
|
3038
|
+
fg_ellipse = Ellipse.de_pre_serialize(serializable_rep)
|
|
3039
|
+
|
|
3040
|
+
return fg_ellipse
|
|
3041
|
+
|
|
3042
|
+
|
|
3043
|
+
|
|
3044
|
+
_default_bg_ellipse = None
|
|
3045
|
+
_default_fg_ellipse = None
|
|
3046
|
+
|
|
3047
|
+
|
|
3048
|
+
|
|
3049
|
+
class Lune(BaseShape):
|
|
3050
|
+
r"""The intensity pattern of a lune.
|
|
3051
|
+
|
|
3052
|
+
Let :math:`\mathcal{I}_{\text{BE}}\left(u_{x},u_{y}\right)` and
|
|
3053
|
+
:math:`\mathcal{I}_{\text{FE}}\left(u_{x},u_{y}\right)` be the intensity
|
|
3054
|
+
patterns of the background and the foreground ellipses respectively, the
|
|
3055
|
+
latter of which is used to mask the former to form the lune. The undistorted
|
|
3056
|
+
intensity pattern of the lune is given by:
|
|
3057
|
+
|
|
3058
|
+
.. math ::
|
|
3059
|
+
\mathcal{I}_{\text{L}}\left(u_{x},u_{y}\right)=\begin{cases}
|
|
3060
|
+
\mathcal{I}_{\text{BE}}\left(u_{x},u_{y}\right),
|
|
3061
|
+
& \text{if }\mathcal{I}_{\text{FE}}\left(u_{x},u_{y}\right)=0,\\
|
|
3062
|
+
0, & \text{otherwise},
|
|
3063
|
+
\end{cases}
|
|
3064
|
+
:label: intensity_pattern_of_lune__1
|
|
3065
|
+
|
|
3066
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
3067
|
+
coordinates of the undistorted intensity pattern of the lune respectively.
|
|
3068
|
+
|
|
3069
|
+
Parameters
|
|
3070
|
+
----------
|
|
3071
|
+
bg_ellipse : :class:`fakecbed.shapes.Circle` | :class:`fakecbed.shapes.Ellipse` | `None`, optional
|
|
3072
|
+
The intensity pattern of the background ellipse,
|
|
3073
|
+
:math:`\mathcal{I}_{\text{BE}}\left(u_{x},u_{y}\right)`. If
|
|
3074
|
+
``bg_ellipse`` is set to ``None``, then the parameter will be reassigned
|
|
3075
|
+
to the value ``fakecbed.shapes.Ellipse()``.
|
|
3076
|
+
fg_ellipse : :class:`fakecbed.shapes.Circle` | :class:`fakecbed.shapes.Ellipse` | `None`, optional
|
|
3077
|
+
The intensity pattern of the foreground ellipse,
|
|
3078
|
+
:math:`\mathcal{I}_{\text{FE}}\left(u_{x},u_{y}\right)`. If
|
|
3079
|
+
``fg_ellipse`` is set to ``None``, then the parameter will be reassigned
|
|
3080
|
+
to the value ``fakecbed.shapes.Ellipse()``.
|
|
3081
|
+
skip_validation_and_conversion : `bool`, optional
|
|
3082
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
3083
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
3084
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
3085
|
+
being `dict` objects.
|
|
3086
|
+
|
|
3087
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
3088
|
+
representation of the constructor parameters excluding the parameter
|
|
3089
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
3090
|
+
different constructor parameter name, excluding the name
|
|
3091
|
+
``"skip_validation_and_conversion"``, and
|
|
3092
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
3093
|
+
constructor parameter with the name given by ``key``.
|
|
3094
|
+
|
|
3095
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
3096
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
3097
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
3098
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
3099
|
+
|
|
3100
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
3101
|
+
then ``core_attrs`` is set to
|
|
3102
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
3103
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
3104
|
+
and/or conversions of the `dict` values of
|
|
3105
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
3106
|
+
copies or conversions are made in this case.
|
|
3107
|
+
|
|
3108
|
+
"""
|
|
3109
|
+
ctor_param_names = ("bg_ellipse",
|
|
3110
|
+
"fg_ellipse")
|
|
3111
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
3112
|
+
"ctor_param_names": ctor_param_names}
|
|
3113
|
+
|
|
3114
|
+
_validation_and_conversion_funcs_ = \
|
|
3115
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
3116
|
+
_pre_serialization_funcs_ = \
|
|
3117
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
3118
|
+
_de_pre_serialization_funcs_ = \
|
|
3119
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
3120
|
+
|
|
3121
|
+
del ctor_param_names, kwargs
|
|
3122
|
+
|
|
3123
|
+
|
|
3124
|
+
|
|
3125
|
+
def __init__(self,
|
|
3126
|
+
bg_ellipse=\
|
|
3127
|
+
_default_bg_ellipse,
|
|
3128
|
+
fg_ellipse=\
|
|
3129
|
+
_default_fg_ellipse,
|
|
3130
|
+
skip_validation_and_conversion=\
|
|
3131
|
+
_default_skip_validation_and_conversion):
|
|
3132
|
+
ctor_params = {key: val
|
|
3133
|
+
for key, val in locals().items()
|
|
3134
|
+
if (key not in ("self", "__class__"))}
|
|
3135
|
+
BaseShape.__init__(self, ctor_params)
|
|
3136
|
+
|
|
3137
|
+
self.execute_post_core_attrs_update_actions()
|
|
3138
|
+
|
|
3139
|
+
return None
|
|
3140
|
+
|
|
3141
|
+
|
|
3142
|
+
|
|
3143
|
+
def execute_post_core_attrs_update_actions(self):
|
|
3144
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
3145
|
+
for self_core_attr_name in self_core_attrs:
|
|
3146
|
+
attr_name = "_"+self_core_attr_name
|
|
3147
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
3148
|
+
setattr(self, attr_name, attr)
|
|
3149
|
+
|
|
3150
|
+
return None
|
|
3151
|
+
|
|
3152
|
+
|
|
3153
|
+
|
|
3154
|
+
def update(self,
|
|
3155
|
+
new_core_attr_subset_candidate,
|
|
3156
|
+
skip_validation_and_conversion=\
|
|
3157
|
+
_default_skip_validation_and_conversion):
|
|
3158
|
+
super().update(new_core_attr_subset_candidate,
|
|
3159
|
+
skip_validation_and_conversion)
|
|
3160
|
+
self.execute_post_core_attrs_update_actions()
|
|
3161
|
+
|
|
3162
|
+
return None
|
|
3163
|
+
|
|
3164
|
+
|
|
3165
|
+
|
|
3166
|
+
def _eval(self, u_x, u_y):
|
|
3167
|
+
bg_ellipse = self._bg_ellipse
|
|
3168
|
+
fg_ellipse = self._fg_ellipse
|
|
3169
|
+
result = bg_ellipse.eval(u_x, u_y) * (fg_ellipse.eval(u_x, u_y) == 0)
|
|
3170
|
+
|
|
3171
|
+
return result
|
|
3172
|
+
|
|
3173
|
+
|
|
3174
|
+
|
|
3175
|
+
def _check_and_convert_support(params):
|
|
3176
|
+
current_func_name = inspect.stack()[0][3]
|
|
3177
|
+
char_idx = 19
|
|
3178
|
+
obj_name = current_func_name[char_idx:]
|
|
3179
|
+
obj = params[obj_name]
|
|
3180
|
+
|
|
3181
|
+
accepted_types = (Circle, Ellipse, Band, Arc, GenericBlob, Lune, type(None))
|
|
3182
|
+
|
|
3183
|
+
if isinstance(obj, accepted_types[-1]):
|
|
3184
|
+
support = accepted_types[0]()
|
|
3185
|
+
else:
|
|
3186
|
+
kwargs = {"obj": obj,
|
|
3187
|
+
"obj_name": obj_name,
|
|
3188
|
+
"accepted_types": accepted_types}
|
|
3189
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
3190
|
+
support = copy.deepcopy(obj)
|
|
3191
|
+
|
|
3192
|
+
return support
|
|
3193
|
+
|
|
3194
|
+
|
|
3195
|
+
|
|
3196
|
+
def _pre_serialize_support(support):
|
|
3197
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
3198
|
+
serializable_rep = obj_to_pre_serialize.pre_serialize()
|
|
3199
|
+
|
|
3200
|
+
return serializable_rep
|
|
3201
|
+
|
|
3202
|
+
|
|
3203
|
+
|
|
3204
|
+
def _de_pre_serialize_support(serializable_rep):
|
|
3205
|
+
if "radius" in serializable_rep:
|
|
3206
|
+
support = Circle.de_pre_serialize(serializable_rep)
|
|
3207
|
+
elif "eccentricity" in serializable_rep:
|
|
3208
|
+
support = Ellipse.de_pre_serialize(serializable_rep)
|
|
3209
|
+
elif "end_pt_1" in serializable_rep:
|
|
3210
|
+
support = Band.de_pre_serialize(serializable_rep)
|
|
3211
|
+
elif "subtending_angle" in serializable_rep:
|
|
3212
|
+
support = Arc.de_pre_serialize(serializable_rep)
|
|
3213
|
+
elif "radial_amplitudes" in serializable_rep:
|
|
3214
|
+
support = GenericBlob.de_pre_serialize(serializable_rep)
|
|
3215
|
+
else:
|
|
3216
|
+
support = Lune.de_pre_serialize(serializable_rep)
|
|
3217
|
+
|
|
3218
|
+
return support
|
|
3219
|
+
|
|
3220
|
+
|
|
3221
|
+
|
|
3222
|
+
def _check_and_convert_intra_support_shapes(params):
|
|
3223
|
+
current_func_name = inspect.stack()[0][3]
|
|
3224
|
+
char_idx = 19
|
|
3225
|
+
obj_name = current_func_name[char_idx:]
|
|
3226
|
+
obj = params[obj_name]
|
|
3227
|
+
|
|
3228
|
+
accepted_types = (Circle,
|
|
3229
|
+
Ellipse,
|
|
3230
|
+
Peak,
|
|
3231
|
+
Band,
|
|
3232
|
+
PlaneWave,
|
|
3233
|
+
Arc,
|
|
3234
|
+
GenericBlob,
|
|
3235
|
+
Orbital,
|
|
3236
|
+
Lune,
|
|
3237
|
+
NonuniformBoundedShape)
|
|
3238
|
+
|
|
3239
|
+
try:
|
|
3240
|
+
for intra_support_shape in obj:
|
|
3241
|
+
kwargs = {"obj": intra_support_shape,
|
|
3242
|
+
"obj_name": "intra_support_shape",
|
|
3243
|
+
"accepted_types": accepted_types}
|
|
3244
|
+
czekitout.check.if_instance_of_any_accepted_types(**kwargs)
|
|
3245
|
+
except:
|
|
3246
|
+
err_msg = globals()[current_func_name+"_err_msg_1"]
|
|
3247
|
+
raise TypeError(err_msg)
|
|
3248
|
+
|
|
3249
|
+
intra_support_shapes = copy.deepcopy(obj)
|
|
3250
|
+
|
|
3251
|
+
return intra_support_shapes
|
|
3252
|
+
|
|
3253
|
+
|
|
3254
|
+
|
|
3255
|
+
def _pre_serialize_intra_support_shapes(intra_support_shapes):
|
|
3256
|
+
obj_to_pre_serialize = random.choice(list(locals().values()))
|
|
3257
|
+
serializable_rep = tuple()
|
|
3258
|
+
for elem in obj_to_pre_serialize:
|
|
3259
|
+
serializable_rep += (elem.pre_serialize(),)
|
|
3260
|
+
|
|
3261
|
+
return serializable_rep
|
|
3262
|
+
|
|
3263
|
+
|
|
3264
|
+
|
|
3265
|
+
def _de_pre_serialize_intra_support_shapes(serializable_rep):
|
|
3266
|
+
intra_support_shapes = tuple()
|
|
3267
|
+
|
|
3268
|
+
for pre_serialized_intra_support_shape in serializable_rep:
|
|
3269
|
+
if "radius" in pre_serialized_intra_support_shape:
|
|
3270
|
+
cls_alias = Circle
|
|
3271
|
+
elif "eccentricity" in pre_serialized_intra_support_shape:
|
|
3272
|
+
cls_alias = Ellipse
|
|
3273
|
+
elif "functional_form" in pre_serialized_intra_support_shape:
|
|
3274
|
+
cls_alias = Peak
|
|
3275
|
+
elif "end_pt_1" in pre_serialized_intra_support_shape:
|
|
3276
|
+
cls_alias = Band
|
|
3277
|
+
elif "propagation_direction" in pre_serialized_intra_support_shape:
|
|
3278
|
+
cls_alias = PlaneWave
|
|
3279
|
+
elif "subtending_angle" in pre_serialized_intra_support_shape:
|
|
3280
|
+
cls_alias = Arc
|
|
3281
|
+
elif "radial_amplitudes" in pre_serialized_intra_support_shape:
|
|
3282
|
+
cls_alias = GenericBlob
|
|
3283
|
+
elif "magnetic_quantum_number" in pre_serialized_intra_support_shape:
|
|
3284
|
+
cls_alias = Orbital
|
|
3285
|
+
elif "bg_ellipse" in pre_serialized_intra_support_shape:
|
|
3286
|
+
cls_alias = Lune
|
|
3287
|
+
else:
|
|
3288
|
+
cls_alias = NonuniformBoundedShape
|
|
3289
|
+
|
|
3290
|
+
intra_support_shape = \
|
|
3291
|
+
cls_alias.de_pre_serialize(pre_serialized_intra_support_shape)
|
|
3292
|
+
intra_support_shapes += \
|
|
3293
|
+
(intra_support_shape,)
|
|
3294
|
+
|
|
3295
|
+
return intra_support_shapes
|
|
3296
|
+
|
|
3297
|
+
|
|
3298
|
+
|
|
3299
|
+
_default_support = None
|
|
3300
|
+
_default_intra_support_shapes = tuple()
|
|
3301
|
+
|
|
3302
|
+
|
|
3303
|
+
|
|
3304
|
+
class NonuniformBoundedShape(BaseShape):
|
|
3305
|
+
r"""The intensity pattern of a nonuniform bounded shape.
|
|
3306
|
+
|
|
3307
|
+
Let :math:`\mathcal{I}_{0;\text{NBS}}\left(u_{x},u_{y}\right)`,
|
|
3308
|
+
:math:`N_{\text{NBS}}`, and :math:`\left\{
|
|
3309
|
+
\mathcal{I}_{k;\text{NBS}}\left(u_{x},u_{y}\right)\right\}
|
|
3310
|
+
_{k=1}^{N_{\text{NBS}}}` be the intensity pattern of the uniform bounded
|
|
3311
|
+
shape supporting the nonuniform bounded shape, the number of intra-support
|
|
3312
|
+
shapes, and the intensity patterns of the intra-support shapes
|
|
3313
|
+
respectively. The undistorted intensity pattern of the nonuniform bounded
|
|
3314
|
+
shape is given by:
|
|
3315
|
+
|
|
3316
|
+
.. math ::
|
|
3317
|
+
\mathcal{I}_{\text{NBS}}\left(u_{x},u_{y}\right)=
|
|
3318
|
+
\mathcal{I}_{0;\text{NBS}}\left(u_{x},u_{y}\right)\left|
|
|
3319
|
+
\sum_{k=1}^{N_{\text{NBS}}}\mathcal{I}_{k;\text{NBS}}\left(u_{x},
|
|
3320
|
+
u_{y}\right)\right|,
|
|
3321
|
+
:label: intensity_pattern_of_nonuniform_bounded_shape__1
|
|
3322
|
+
|
|
3323
|
+
where :math:`u_{x}` and :math:`u_{y}` are fractional horizontal and vertical
|
|
3324
|
+
coordinates of the undistorted intensity pattern of the nonuniform bounded
|
|
3325
|
+
shape respectively.
|
|
3326
|
+
|
|
3327
|
+
Parameters
|
|
3328
|
+
----------
|
|
3329
|
+
support : :class:`fakecbed.shapes.Circle` | :class:`fakecbed.shapes.Ellipse` | :class:`fakecbed.shapes.Band` | :class:`fakecbed.shapes.Arc` | :class:`fakecbed.shapes.GenericBlob` | :class:`fakecbed.shapes.Lune` | `None`, optional
|
|
3330
|
+
The intensity pattern of the uniform bounded shape supporting the
|
|
3331
|
+
nonuniform bounded shape,
|
|
3332
|
+
:math:`\mathcal{I}_{0;\text{NBS}}\left(u_{x},u_{y}\right)`. If
|
|
3333
|
+
``support`` is set to ``None``, then the parameter will be reassigned to
|
|
3334
|
+
the value ``fakecbed.shapes.Circle()``.
|
|
3335
|
+
intra_support_shapes : `array_like` (`any_shape`, ndim=1), optional
|
|
3336
|
+
The intensity patterns of the intra-support shapes, :math:`\left\{
|
|
3337
|
+
\mathcal{I}_{k;\text{NBS}}\left(u_{x},u_{y}\right)\right\}
|
|
3338
|
+
_{k=1}^{N_{\text{NBS}}}`. Note that `any_shape` means any public class
|
|
3339
|
+
defined in the module :mod:`fakecbed.shapes` that is a subclass of
|
|
3340
|
+
:class:`fakecbed.shapes.BaseShape`.
|
|
3341
|
+
skip_validation_and_conversion : `bool`, optional
|
|
3342
|
+
Let ``validation_and_conversion_funcs`` and ``core_attrs`` denote the
|
|
3343
|
+
attributes :attr:`~fancytypes.Checkable.validation_and_conversion_funcs`
|
|
3344
|
+
and :attr:`~fancytypes.Checkable.core_attrs` respectively, both of which
|
|
3345
|
+
being `dict` objects.
|
|
3346
|
+
|
|
3347
|
+
Let ``params_to_be_mapped_to_core_attrs`` denote the `dict`
|
|
3348
|
+
representation of the constructor parameters excluding the parameter
|
|
3349
|
+
``skip_validation_and_conversion``, where each `dict` key ``key`` is a
|
|
3350
|
+
different constructor parameter name, excluding the name
|
|
3351
|
+
``"skip_validation_and_conversion"``, and
|
|
3352
|
+
``params_to_be_mapped_to_core_attrs[key]`` would yield the value of the
|
|
3353
|
+
constructor parameter with the name given by ``key``.
|
|
3354
|
+
|
|
3355
|
+
If ``skip_validation_and_conversion`` is set to ``False``, then for each
|
|
3356
|
+
key ``key`` in ``params_to_be_mapped_to_core_attrs``,
|
|
3357
|
+
``core_attrs[key]`` is set to ``validation_and_conversion_funcs[key]
|
|
3358
|
+
(params_to_be_mapped_to_core_attrs)``.
|
|
3359
|
+
|
|
3360
|
+
Otherwise, if ``skip_validation_and_conversion`` is set to ``True``,
|
|
3361
|
+
then ``core_attrs`` is set to
|
|
3362
|
+
``params_to_be_mapped_to_core_attrs.copy()``. This option is desired
|
|
3363
|
+
primarily when the user wants to avoid potentially expensive deep copies
|
|
3364
|
+
and/or conversions of the `dict` values of
|
|
3365
|
+
``params_to_be_mapped_to_core_attrs``, as it is guaranteed that no
|
|
3366
|
+
copies or conversions are made in this case.
|
|
3367
|
+
|
|
3368
|
+
"""
|
|
3369
|
+
ctor_param_names = ("support",
|
|
3370
|
+
"intra_support_shapes")
|
|
3371
|
+
kwargs = {"namespace_as_dict": globals(),
|
|
3372
|
+
"ctor_param_names": ctor_param_names}
|
|
3373
|
+
|
|
3374
|
+
_validation_and_conversion_funcs_ = \
|
|
3375
|
+
fancytypes.return_validation_and_conversion_funcs(**kwargs)
|
|
3376
|
+
_pre_serialization_funcs_ = \
|
|
3377
|
+
fancytypes.return_pre_serialization_funcs(**kwargs)
|
|
3378
|
+
_de_pre_serialization_funcs_ = \
|
|
3379
|
+
fancytypes.return_de_pre_serialization_funcs(**kwargs)
|
|
3380
|
+
|
|
3381
|
+
del ctor_param_names, kwargs
|
|
3382
|
+
|
|
3383
|
+
|
|
3384
|
+
|
|
3385
|
+
def __init__(self,
|
|
3386
|
+
support=\
|
|
3387
|
+
_default_support,
|
|
3388
|
+
intra_support_shapes=\
|
|
3389
|
+
_default_intra_support_shapes,
|
|
3390
|
+
skip_validation_and_conversion=\
|
|
3391
|
+
_default_skip_validation_and_conversion):
|
|
3392
|
+
ctor_params = {key: val
|
|
3393
|
+
for key, val in locals().items()
|
|
3394
|
+
if (key not in ("self", "__class__"))}
|
|
3395
|
+
BaseShape.__init__(self, ctor_params)
|
|
3396
|
+
|
|
3397
|
+
self.execute_post_core_attrs_update_actions()
|
|
3398
|
+
|
|
3399
|
+
return None
|
|
3400
|
+
|
|
3401
|
+
|
|
3402
|
+
|
|
3403
|
+
def execute_post_core_attrs_update_actions(self):
|
|
3404
|
+
self_core_attrs = self.get_core_attrs(deep_copy=False)
|
|
3405
|
+
for self_core_attr_name in self_core_attrs:
|
|
3406
|
+
attr_name = "_"+self_core_attr_name
|
|
3407
|
+
attr = self_core_attrs[self_core_attr_name]
|
|
3408
|
+
setattr(self, attr_name, attr)
|
|
3409
|
+
|
|
3410
|
+
return None
|
|
3411
|
+
|
|
3412
|
+
|
|
3413
|
+
|
|
3414
|
+
def update(self,
|
|
3415
|
+
new_core_attr_subset_candidate,
|
|
3416
|
+
skip_validation_and_conversion=\
|
|
3417
|
+
_default_skip_validation_and_conversion):
|
|
3418
|
+
super().update(new_core_attr_subset_candidate,
|
|
3419
|
+
skip_validation_and_conversion)
|
|
3420
|
+
self.execute_post_core_attrs_update_actions()
|
|
3421
|
+
|
|
3422
|
+
return None
|
|
3423
|
+
|
|
3424
|
+
|
|
3425
|
+
|
|
3426
|
+
def _eval(self, u_x, u_y):
|
|
3427
|
+
result = (self._eval_without_support(u_x, u_y)
|
|
3428
|
+
* self._eval_without_intra_support_shapes(u_x, u_y))
|
|
3429
|
+
|
|
3430
|
+
return result
|
|
3431
|
+
|
|
3432
|
+
|
|
3433
|
+
|
|
3434
|
+
def _eval_without_intra_support_shapes(self, u_x, u_y):
|
|
3435
|
+
support = self._support
|
|
3436
|
+
result = support._eval(u_x, u_y)
|
|
3437
|
+
|
|
3438
|
+
return result
|
|
3439
|
+
|
|
3440
|
+
|
|
3441
|
+
|
|
3442
|
+
def _eval_without_support(self, u_x, u_y):
|
|
3443
|
+
intra_support_shapes = self._intra_support_shapes
|
|
3444
|
+
|
|
3445
|
+
result = torch.zeros_like(u_x)
|
|
3446
|
+
for intra_support_shape in intra_support_shapes:
|
|
3447
|
+
result += intra_support_shape._eval(u_x, u_y)
|
|
3448
|
+
result = torch.abs(result)
|
|
3449
|
+
|
|
3450
|
+
return result
|
|
3451
|
+
|
|
3452
|
+
|
|
3453
|
+
|
|
3454
|
+
###########################
|
|
3455
|
+
## Define error messages ##
|
|
3456
|
+
###########################
|
|
3457
|
+
|
|
3458
|
+
_check_and_convert_cartesian_coords_err_msg_1 = \
|
|
3459
|
+
("The objects ``{}`` and ``{}`` must be real-valued matrices of the same "
|
|
3460
|
+
"shape.")
|
|
3461
|
+
|
|
3462
|
+
_check_and_convert_real_torch_matrix_err_msg_1 = \
|
|
3463
|
+
("The object ``{}`` must be a real-valued matrix.")
|
|
3464
|
+
|
|
3465
|
+
_base_shape_err_msg_1 = \
|
|
3466
|
+
("Cannot construct instances of the class `fakecbed.shapes.BaseShape`, "
|
|
3467
|
+
"only subclasses of itself defined in the `fakecbed` library.")
|
|
3468
|
+
|
|
3469
|
+
_check_and_convert_eccentricity_err_msg_1 = \
|
|
3470
|
+
("The object ``eccentricity`` must be a nonnegative number less than or "
|
|
3471
|
+
"equal to unity.")
|
|
3472
|
+
|
|
3473
|
+
_check_and_convert_radial_range_err_msg_1 = \
|
|
3474
|
+
("The object ``radial_range`` must be a pair of positive real numbers "
|
|
3475
|
+
"satisfying ``radial_range[0]<radial_range[1]``.")
|
|
3476
|
+
|
|
3477
|
+
_check_and_convert_radial_amplitudes_err_msg_1 = \
|
|
3478
|
+
("The object ``radial_amplitudes`` must be a non-empty sequence of "
|
|
3479
|
+
"nonnegative real numbers.")
|
|
3480
|
+
_check_and_convert_radial_amplitudes_err_msg_2 = \
|
|
3481
|
+
("The object ``radial_amplitudes`` must satisfy ``radial_amplitudes[0] > "
|
|
3482
|
+
"sum(radial_amplitudes[1:])``.")
|
|
3483
|
+
|
|
3484
|
+
_check_and_convert_radial_phases_err_msg_1 = \
|
|
3485
|
+
("The objects ``radial_phases`` and ``radial_amplitudes`` must satisfy "
|
|
3486
|
+
"``len(radial_phases)+1 == len(radial_amplitudes)``.")
|
|
3487
|
+
|
|
3488
|
+
_check_and_convert_azimuthal_quantum_number_err_msg_1 = \
|
|
3489
|
+
("The objects ``azimuthal_quantum_number`` and "
|
|
3490
|
+
"``principal_quantum_number`` must satisfy "
|
|
3491
|
+
"``azimuthal_quantum_number < principal_quantum_number``.")
|
|
3492
|
+
|
|
3493
|
+
_check_and_convert_magnetic_quantum_number_err_msg_1 = \
|
|
3494
|
+
("The objects ``magnetic_quantum_number`` and "
|
|
3495
|
+
"``azimuthal_quantum_number`` must satisfy "
|
|
3496
|
+
"``abs(magnetic_quantum_number) <= azimuthal_quantum_number``.")
|
|
3497
|
+
|
|
3498
|
+
_check_and_convert_intra_support_shapes_err_msg_1 = \
|
|
3499
|
+
("The object ``intra_support_shapes`` must be a sequence of objects of any "
|
|
3500
|
+
"of the following types: ("
|
|
3501
|
+
"`fakecbed.shapes.Circle`, "
|
|
3502
|
+
"`fakecbed.shapes.Ellipse`, "
|
|
3503
|
+
"`fakecbed.shapes.Peak`, "
|
|
3504
|
+
"`fakecbed.shapes.Band`, "
|
|
3505
|
+
"`fakecbed.shapes.PlaneWave`, "
|
|
3506
|
+
"`fakecbed.shapes.Arc`, "
|
|
3507
|
+
"`fakecbed.shapes.GenericBlob`, "
|
|
3508
|
+
"`fakecbed.shapes.Orbital`, "
|
|
3509
|
+
"`fakecbed.shapes.Lune`, "
|
|
3510
|
+
"`fakecbed.shapes.NonuniformBoundedShape`).")
|