diffpy.utils 0.6.0rc0__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.
diffpy/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # diffpy by DANSE Diffraction group
5
+ # Simon J. L. Billinge
6
+ # (c) 2010 The Trustees of Columbia University
7
+ # in the City of New York. All rights reserved.
8
+ # (c) 2024 The Trustees of Columbia University in the City of New York.
9
+ # All rights reserved.
10
+ #
11
+ # File coded by: Billinge Group members and community contributors.
12
+ #
13
+ # See GitHub contributions for a more detailed list of contributors.
14
+ # https://github.com/diffpy/diffpy.utils/graphs/contributors
15
+ #
16
+ # See LICENSE.rst for license information.
17
+ #
18
+ ##############################################################################
19
+ """diffpy - tools for structure analysis by diffraction.
20
+
21
+ Blank namespace package.
22
+ """
23
+
24
+
25
+ from pkgutil import extend_path
26
+
27
+ __path__ = extend_path(__path__, __name__)
28
+
29
+ # End of file
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # (c) 2024 The Trustees of Columbia University in the City of New York.
5
+ # All rights reserved.
6
+ #
7
+ # File coded by: Billinge Group members and community contributors.
8
+ #
9
+ # See GitHub contributions for a more detailed list of contributors.
10
+ # https://github.com/diffpy/diffpy.utils/graphs/contributors
11
+ #
12
+ # See LICENSE.rst for license information.
13
+ #
14
+ ##############################################################################
15
+ """Shared utilities for diffpy packages."""
16
+
17
+ # package version
18
+ from diffpy.utils.version import __version__
19
+
20
+ # silence the pyflakes syntax checker
21
+ assert __version__ or True
@@ -0,0 +1,590 @@
1
+ import datetime
2
+ import uuid
3
+ import warnings
4
+ from copy import deepcopy
5
+
6
+ import numpy as np
7
+
8
+ from diffpy.utils.tools import get_package_info
9
+ from diffpy.utils.transforms import d_to_q, d_to_tth, q_to_d, q_to_tth, tth_to_d, tth_to_q
10
+
11
+ QQUANTITIES = ["q"]
12
+ ANGLEQUANTITIES = ["angle", "tth", "twotheta", "2theta"]
13
+ DQUANTITIES = ["d", "dspace"]
14
+ XQUANTITIES = ANGLEQUANTITIES + DQUANTITIES + QQUANTITIES
15
+ XUNITS = ["degrees", "radians", "rad", "deg", "inv_angs", "inv_nm", "nm-1", "A-1"]
16
+
17
+ x_values_not_equal_emsg = (
18
+ "The two objects have different values in x arrays (my_do.all_arrays[:, [1, 2, 3]]). "
19
+ "Please ensure the x values of the two objects are identical by re-instantiating "
20
+ "the DiffractionObject with the correct x value inputs."
21
+ )
22
+
23
+ invalid_add_type_emsg = (
24
+ "You may only add a DiffractionObject with another DiffractionObject or a scalar value. "
25
+ "Please rerun by adding another DiffractionObject instance or a scalar value. "
26
+ "e.g., my_do_1 + my_do_2 or my_do + 10 or 10 + my_do"
27
+ )
28
+
29
+
30
+ def _xtype_wmsg(xtype):
31
+ return (
32
+ f"I don't know how to handle the xtype, '{xtype}'. "
33
+ f"Please rerun specifying an xtype from {*XQUANTITIES, }"
34
+ )
35
+
36
+
37
+ def _setter_wmsg(attribute):
38
+ return (
39
+ f"Direct modification of attribute '{attribute}' is not allowed. "
40
+ f"Please use 'input_data' to modify '{attribute}'.",
41
+ )
42
+
43
+
44
+ class DiffractionObject:
45
+ """Class for storing and manipulating diffraction data.
46
+
47
+ DiffractionObject stores data produced from X-ray, neutron,
48
+ and electron scattering experiments. The object can transform
49
+ between different scattering quantities such as q (scattering vector),
50
+ 2θ (two-theta angle), and d (interplanar spacing), and perform various
51
+ operations like scaling, addition, subtraction, and comparison for equality
52
+ between diffraction objects.
53
+
54
+ Attributes
55
+ ----------
56
+ scat_quantity : str
57
+ The type of scattering experiment (e.g., "x-ray", "neutron"). Default is an empty string "".
58
+ wavelength : float
59
+ The wavelength of the incoming beam, specified in angstroms (Å). Default is none.
60
+ name: str
61
+ The name or label for the scattering data. Default is an empty string "".
62
+ qmin : float
63
+ The minimum q value.
64
+ qmax : float
65
+ The maximum q value.
66
+ tthmin : float
67
+ The minimum two-theta value.
68
+ tthmax : float
69
+ The maximum two-theta value.
70
+ dmin : float
71
+ The minimum d-spacing value.
72
+ dmax : float
73
+ The maximum d-spacing value.
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ xarray,
79
+ yarray,
80
+ xtype,
81
+ wavelength=None,
82
+ scat_quantity="",
83
+ name="",
84
+ metadata={},
85
+ ):
86
+ """Initialize a DiffractionObject instance.
87
+
88
+ Parameters
89
+ ----------
90
+ xarray : ndarray
91
+ The independent variable array containing "q", "tth", or "d" values.
92
+ yarray : ndarray
93
+ The dependent variable array corresponding to intensity values.
94
+ xtype : str
95
+ The type of the independent variable in `xarray`. Must be one of {*XQUANTITIES}.
96
+ wavelength : float, optional, default is None.
97
+ The wavelength of the incoming beam, specified in angstroms (Å)
98
+ scat_quantity : str, optional, default is an empty string "".
99
+ The type of scattering experiment (e.g., "x-ray", "neutron").
100
+ name : str, optional, default is an empty string "".
101
+ The name or label for the scattering data.
102
+ metadata : dict, optional, default is an empty dictionary {}
103
+ The additional metadata associated with the diffraction object.
104
+
105
+ Examples
106
+ --------
107
+ Create a DiffractionObject for X-ray scattering data:
108
+ >>> import numpy as np
109
+ >>> from diffpy.utils.diffraction_objects import DiffractionObject
110
+ ...
111
+ >>> x = np.array([0.12, 0.24, 0.31, 0.4]) # independent variable (e.g., q)
112
+ >>> y = np.array([10, 20, 40, 60]) # intensity values
113
+ >>> metadata = {
114
+ ... "sample": "rock salt from the beach",
115
+ ... "composition": "NaCl",
116
+ ... "temperature": "300 K,",
117
+ ... "experimenters": "Phill, Sally"
118
+ ... }
119
+ >>> do = DiffractionObject(
120
+ ... xarray=x,
121
+ ... yarray=y,
122
+ ... xtype="q",
123
+ ... wavelength=1.54,
124
+ ... scat_quantity="x-ray",
125
+ ... name="beach_rock_salt_1",
126
+ ... metadata=metadata
127
+ ... )
128
+ >>> print(do.metadata)
129
+ """
130
+
131
+ self._uuid = uuid.uuid4()
132
+ self._input_data(xarray, yarray, xtype, wavelength, scat_quantity, name, metadata)
133
+
134
+ def _input_data(self, xarray, yarray, xtype, wavelength, scat_quantity, name, metadata):
135
+ if xtype not in XQUANTITIES:
136
+ raise ValueError(_xtype_wmsg(xtype))
137
+ if len(xarray) != len(yarray):
138
+ raise ValueError(
139
+ "'xarray' and 'yarray' are different lengths. They must "
140
+ "correspond to each other and have the same length. "
141
+ "Please re-initialize 'DiffractionObject'"
142
+ "with valid 'xarray' and 'yarray's"
143
+ )
144
+ self.scat_quantity = scat_quantity
145
+ self.wavelength = wavelength
146
+ self.metadata = metadata
147
+ self.name = name
148
+ self._input_xtype = xtype
149
+ self._set_arrays(xarray, yarray, xtype)
150
+ self._set_min_max_xarray()
151
+
152
+ def __eq__(self, other):
153
+ if not isinstance(other, DiffractionObject):
154
+ return NotImplemented
155
+ self_attributes = [key for key in self.__dict__ if not key.startswith("_")]
156
+ other_attributes = [key for key in other.__dict__ if not key.startswith("_")]
157
+ if not sorted(self_attributes) == sorted(other_attributes):
158
+ return False
159
+ for key in self_attributes:
160
+ value = getattr(self, key)
161
+ other_value = getattr(other, key)
162
+ if isinstance(value, float):
163
+ if (
164
+ not (value is None and other_value is None)
165
+ and (value is None)
166
+ or (other_value is None)
167
+ or not np.isclose(value, other_value, rtol=1e-5)
168
+ ):
169
+ return False
170
+ elif isinstance(value, list) and all(isinstance(i, np.ndarray) for i in value):
171
+ if not all(np.allclose(i, j, rtol=1e-5) for i, j in zip(value, other_value)):
172
+ return False
173
+ else:
174
+ if value != other_value:
175
+ return False
176
+ return True
177
+
178
+ def __add__(self, other):
179
+ """Add a scalar value or another DiffractionObject to the yarray of the
180
+ DiffractionObject.
181
+
182
+ Parameters
183
+ ----------
184
+ other : DiffractionObject, int, or float
185
+ The item to be added. If `other` is a scalar value, this value will be added to each element of the
186
+ yarray of this DiffractionObject instance. If `other` is another DiffractionObject, the yarrays of the
187
+ two DiffractionObjects will be combined element-wise. The result is a new DiffractionObject instance,
188
+ representing the addition and using the xarray from the left-hand side DiffractionObject.
189
+
190
+ Returns
191
+ -------
192
+ DiffractionObject
193
+ The new DiffractionObject instance with modified yarray values. This instance is a deep copy of the
194
+ original with the additions applied.
195
+
196
+ Raises
197
+ ------
198
+ ValueError
199
+ Raised when the xarrays of two DiffractionObject instances are not equal.
200
+ TypeError
201
+ Raised when `other` is not an instance of DiffractionObject, int, or float.
202
+
203
+ Examples
204
+ --------
205
+ Add a scalar value to the yarray of a DiffractionObject instance:
206
+ >>> new_do = my_do + 10.1
207
+ >>> new_do = 10.1 + my_do
208
+
209
+ Combine the yarrays of two DiffractionObject instances:
210
+ >>> new_do = my_do_1 + my_do_2
211
+ """
212
+
213
+ self._check_operation_compatibility(other)
214
+ summed_do = deepcopy(self)
215
+ if isinstance(other, (int, float)):
216
+ summed_do._all_arrays[:, 0] += other
217
+ if isinstance(other, DiffractionObject):
218
+ summed_do._all_arrays[:, 0] += other.all_arrays[:, 0]
219
+ return summed_do
220
+
221
+ __radd__ = __add__
222
+
223
+ def __sub__(self, other):
224
+ """Subtract scalar value or another DiffractionObject to the yarray of
225
+ the DiffractionObject.
226
+
227
+ This method behaves similarly to the `__add__` method, but performs subtraction instead of addition.
228
+ For details on parameters, returns, and exceptions, refer to the documentation for `__add__`.
229
+
230
+ Examples
231
+ --------
232
+ Subtract a scalar value from the yarray of a DiffractionObject instance:
233
+ >>> new_do = my_do - 10.1
234
+
235
+ Subtract the yarrays of two DiffractionObject instances:
236
+ >>> new_do = my_do_1 - my_do_2
237
+ """
238
+
239
+ self._check_operation_compatibility(other)
240
+ subtracted_do = deepcopy(self)
241
+ if isinstance(other, (int, float)):
242
+ subtracted_do._all_arrays[:, 0] -= other
243
+ if isinstance(other, DiffractionObject):
244
+ subtracted_do._all_arrays[:, 0] -= other.all_arrays[:, 0]
245
+ return subtracted_do
246
+
247
+ __rsub__ = __sub__
248
+
249
+ def __mul__(self, other):
250
+ """Multiply a scalar value or another DiffractionObject with the yarray
251
+ of this DiffractionObject.
252
+
253
+ This method behaves similarly to the `__add__` method, but performs multiplication instead of addition.
254
+ For details on parameters, returns, and exceptions, refer to the documentation for `__add__`.
255
+
256
+ Examples
257
+ --------
258
+ Multiply a scalar value with the yarray of a DiffractionObject instance:
259
+ >>> new_do = my_do * 3.5
260
+
261
+ Multiply the yarrays of two DiffractionObject instances:
262
+ >>> new_do = my_do_1 * my_do_2
263
+ """
264
+
265
+ self._check_operation_compatibility(other)
266
+ multiplied_do = deepcopy(self)
267
+ if isinstance(other, (int, float)):
268
+ multiplied_do._all_arrays[:, 0] *= other
269
+ if isinstance(other, DiffractionObject):
270
+ multiplied_do._all_arrays[:, 0] *= other.all_arrays[:, 0]
271
+ return multiplied_do
272
+
273
+ __rmul__ = __mul__
274
+
275
+ def __truediv__(self, other):
276
+ """Divide the yarray of this DiffractionObject by a scalar value or
277
+ another DiffractionObject.
278
+
279
+ This method behaves similarly to the `__add__` method, but performs division instead of addition.
280
+ For details on parameters, returns, and exceptions, refer to the documentation for `__add__`.
281
+
282
+ Examples
283
+ --------
284
+ Divide the yarray of a DiffractionObject instance by a scalar value:
285
+ >>> new_do = my_do / 2.0
286
+
287
+ Divide the yarrays of two DiffractionObject instances:
288
+ >>> new_do = my_do_1 / my_do_2
289
+ """
290
+ self._check_operation_compatibility(other)
291
+ divided_do = deepcopy(self)
292
+ if isinstance(other, (int, float)):
293
+ divided_do._all_arrays[:, 0] /= other
294
+ if isinstance(other, DiffractionObject):
295
+ divided_do._all_arrays[:, 0] /= other.all_arrays[:, 0]
296
+ return divided_do
297
+
298
+ __rtruediv__ = __truediv__
299
+
300
+ def _check_operation_compatibility(self, other):
301
+ if not isinstance(other, (DiffractionObject, int, float)):
302
+ raise TypeError(invalid_add_type_emsg)
303
+ if isinstance(other, DiffractionObject):
304
+ if self.all_arrays.shape != other.all_arrays.shape:
305
+ raise ValueError(x_values_not_equal_emsg)
306
+ if not np.allclose(self.all_arrays[:, [1, 2, 3]], other.all_arrays[:, [1, 2, 3]]):
307
+ raise ValueError(x_values_not_equal_emsg)
308
+
309
+ @property
310
+ def all_arrays(self):
311
+ """The 2D array containing `xarray` and `yarray` values.
312
+
313
+ Returns
314
+ -------
315
+ ndarray
316
+ The shape (len(data), 4) 2D array with columns containing the `yarray` (intensity)
317
+ and the `xarray` values in q, tth, and d.
318
+
319
+ Examples
320
+ --------
321
+ To access specific arrays individually, use these slices:
322
+
323
+ >>> my_do.all_arrays[:, 0] # yarray
324
+ >>> my_do.all_arrays[:, 1] # xarray in q
325
+ >>> my_do.all_arrays[:, 2] # xarray in tth
326
+ >>> my_do.all_arrays[:, 3] # xarray in d
327
+ """
328
+ return self._all_arrays
329
+
330
+ @all_arrays.setter
331
+ def all_arrays(self, _):
332
+ raise AttributeError(_setter_wmsg("all_arrays"))
333
+
334
+ @property
335
+ def input_xtype(self):
336
+ """The type of the independent variable in `xarray`.
337
+
338
+ Returns
339
+ -------
340
+ input_xtype : str
341
+ The type of `xarray`, which must be one of {*XQUANTITIES}.
342
+ """
343
+ return self._input_xtype
344
+
345
+ @input_xtype.setter
346
+ def input_xtype(self, _):
347
+ raise AttributeError(_setter_wmsg("input_xtype"))
348
+
349
+ @property
350
+ def uuid(self):
351
+ """The unique identifier for the DiffractionObject instance.
352
+
353
+ Returns
354
+ -------
355
+ uuid : UUID
356
+ The unique identifier of the DiffractionObject instance.
357
+ """
358
+ return self._uuid
359
+
360
+ @uuid.setter
361
+ def uuid(self, _):
362
+ raise AttributeError(_setter_wmsg("uuid"))
363
+
364
+ def get_array_index(self, xtype, xvalue):
365
+ """Return the index of the closest value in the array associated with
366
+ the specified xtype and the value provided.
367
+
368
+ Parameters
369
+ ----------
370
+ xtype : str
371
+ The type of the independent variable in `xarray`. Must be one of {*XQUANTITIES}.
372
+ xvalue : float
373
+ The value of the xtype to find the closest index for.
374
+
375
+ Returns
376
+ -------
377
+ index : int
378
+ The index of the closest value in the array associated with the specified xtype and the value provided.
379
+ """
380
+
381
+ xtype = self._input_xtype
382
+ xarray = self.on_xtype(xtype)[0]
383
+ if len(xarray) == 0:
384
+ raise ValueError(f"The '{xtype}' array is empty. Please ensure it is initialized.")
385
+ index = (np.abs(xarray - xvalue)).argmin()
386
+ return index
387
+
388
+ def _set_arrays(self, xarray, yarray, xtype):
389
+ self._all_arrays = np.empty(shape=(len(xarray), 4))
390
+ self._all_arrays[:, 0] = yarray
391
+ if xtype.lower() in QQUANTITIES:
392
+ self._all_arrays[:, 1] = xarray
393
+ self._all_arrays[:, 2] = q_to_tth(xarray, self.wavelength)
394
+ self._all_arrays[:, 3] = q_to_d(xarray)
395
+ elif xtype.lower() in ANGLEQUANTITIES:
396
+ self._all_arrays[:, 2] = xarray
397
+ self._all_arrays[:, 1] = tth_to_q(xarray, self.wavelength)
398
+ self._all_arrays[:, 3] = tth_to_d(xarray, self.wavelength)
399
+ elif xtype.lower() in DQUANTITIES:
400
+ self._all_arrays[:, 3] = xarray
401
+ self._all_arrays[:, 1] = d_to_q(xarray)
402
+ self._all_arrays[:, 2] = d_to_tth(xarray, self.wavelength)
403
+
404
+ def _set_min_max_xarray(self):
405
+ self.qmin = np.nanmin(self._all_arrays[:, 1], initial=np.inf)
406
+ self.qmax = np.nanmax(self._all_arrays[:, 1], initial=0.0)
407
+ self.tthmin = np.nanmin(self._all_arrays[:, 2], initial=np.inf)
408
+ self.tthmax = np.nanmax(self._all_arrays[:, 2], initial=0.0)
409
+ self.dmin = np.nanmin(self._all_arrays[:, 3], initial=np.inf)
410
+ self.dmax = np.nanmax(self._all_arrays[:, 3], initial=0.0)
411
+
412
+ def _get_original_array(self):
413
+ if self._input_xtype in QQUANTITIES:
414
+ return self.on_q(), "q"
415
+ elif self._input_xtype in ANGLEQUANTITIES:
416
+ return self.on_tth(), "tth"
417
+ elif self._input_xtype in DQUANTITIES:
418
+ return self.on_d(), "d"
419
+
420
+ def on_q(self):
421
+ """Return the tuple of two 1D numpy arrays containing q and y data.
422
+
423
+ Returns
424
+ -------
425
+ (q-array, y-array) : tuple of ndarray
426
+ The tuple containing two 1D numpy arrays with q and y data
427
+ """
428
+ return [self.all_arrays[:, 1], self.all_arrays[:, 0]]
429
+
430
+ def on_tth(self):
431
+ """Return the tuple of two 1D numpy arrays containing tth and y data.
432
+
433
+ Returns
434
+ -------
435
+ (tth-array, y-array) : tuple of ndarray
436
+ The tuple containing two 1D numpy arrays with tth and y data
437
+ """
438
+ return [self.all_arrays[:, 2], self.all_arrays[:, 0]]
439
+
440
+ def on_d(self):
441
+ """Return the tuple of two 1D numpy arrays containing d and y data.
442
+
443
+ Returns
444
+ -------
445
+ (d-array, y-array) : tuple of ndarray
446
+ The tuple containing two 1D numpy arrays with d and y data
447
+ """
448
+ return [self.all_arrays[:, 3], self.all_arrays[:, 0]]
449
+
450
+ def scale_to(self, target_diff_object, q=None, tth=None, d=None, offset=None):
451
+ """Return a new diffraction object which is the current object but
452
+ rescaled in y to the target.
453
+
454
+ By default, if `q`, `tth`, or `d` are not provided, scaling is based on the max intensity from each object.
455
+ Otherwise, y-value in the target at the closest specified x-value will be used as the factor to scale to.
456
+ The entire array is scaled by this factor so that one object places on top of the other at that point.
457
+ If multiple values of `q`, `tth`, or `d` are provided, an error will be raised.
458
+
459
+ Parameters
460
+ ----------
461
+ target_diff_object: DiffractionObject
462
+ The diffraction object you want to scale the current one onto.
463
+
464
+ q, tth, d : float, optional, default is None
465
+ The value of the x-array where you want the curves to line up vertically.
466
+ Specify a value on one of the allowed grids, q, tth, or d), e.g., q=10.
467
+
468
+ offset : float, optional, default is None
469
+ The offset to add to the scaled y-values.
470
+
471
+ Returns
472
+ -------
473
+ scaled_do : DiffractionObject
474
+ The rescaled DiffractionObject as a new object.
475
+ """
476
+ if offset is None:
477
+ offset = 0
478
+ scaled_do = self.copy()
479
+ count = sum([q is not None, tth is not None, d is not None])
480
+ if count > 1:
481
+ raise ValueError(
482
+ "You must specify none or exactly one of 'q', 'tth', or 'd'. "
483
+ "Please provide either none or one value."
484
+ )
485
+
486
+ if count == 0:
487
+ q_target_max = max(target_diff_object.on_q()[1])
488
+ q_self_max = max(self.on_q()[1])
489
+ scaled_do._all_arrays[:, 0] = scaled_do._all_arrays[:, 0] * q_target_max / q_self_max + offset
490
+ return scaled_do
491
+
492
+ xtype = "q" if q is not None else "tth" if tth is not None else "d"
493
+ data = self.on_xtype(xtype)
494
+ target = target_diff_object.on_xtype(xtype)
495
+
496
+ xvalue = q if xtype == "q" else tth if xtype == "tth" else d
497
+
498
+ xindex_data = (np.abs(data[0] - xvalue)).argmin()
499
+ xindex_target = (np.abs(target[0] - xvalue)).argmin()
500
+ scaled_do._all_arrays[:, 0] = data[1] * target[1][xindex_target] / data[1][xindex_data] + offset
501
+ return scaled_do
502
+
503
+ def on_xtype(self, xtype):
504
+ """Return a tuple of two 1D numpy arrays containing x and y data.
505
+
506
+ Parameters
507
+ ----------
508
+ xtype : str
509
+ The type of quantity for the independent variable chosen from {*XQUANTITIES, }
510
+
511
+ Raises
512
+ ------
513
+ ValueError
514
+ Raised when the specified xtype is not among {*XQUANTITIES, }
515
+
516
+ Returns
517
+ -------
518
+ (xarray, yarray) : tuple of ndarray
519
+ The tuple containing two 1D numpy arrays with x and y data for the specified xtype.
520
+ """
521
+ if xtype.lower() in ANGLEQUANTITIES:
522
+ return self.on_tth()
523
+ elif xtype.lower() in QQUANTITIES:
524
+ return self.on_q()
525
+ elif xtype.lower() in DQUANTITIES:
526
+ return self.on_d()
527
+ else:
528
+ raise ValueError(_xtype_wmsg(xtype))
529
+
530
+ def dump(self, filepath, xtype=None):
531
+ """Dump the xarray and yarray of the diffraction object to a two-column
532
+ file, with the associated information included in the header.
533
+
534
+ Parameters
535
+ ----------
536
+ filepath : str
537
+ The filepath where the diffraction object will be dumped
538
+ xtype : str, optional, default is q
539
+ The type of quantity for the independent variable chosen from {*XQUANTITIES, }
540
+
541
+ Examples
542
+ --------
543
+ To save a diffraction object to a file named "diffraction_data.chi" in the current directory
544
+ with the independent variable 'q':
545
+
546
+ >>> file = "diffraction_data.chi"
547
+ >>> do.dump(file, xtype="q")
548
+
549
+ To save the diffraction data to a file in a subfolder `output`:
550
+
551
+ >>> file = "./output/diffraction_data.chi"
552
+ >>> do.dump(file, xtype="q")
553
+
554
+ To save the diffraction data with a different independent variable, such as 'tth':
555
+
556
+ >>> file = "diffraction_data_tth.chi"
557
+ >>> do.dump(file, xtype="tth")
558
+ """
559
+ if xtype is None:
560
+ xtype = "q"
561
+ if xtype in QQUANTITIES:
562
+ data_to_save = np.column_stack((self.on_q()[0], self.on_q()[1]))
563
+ elif xtype in ANGLEQUANTITIES:
564
+ data_to_save = np.column_stack((self.on_tth()[0], self.on_tth()[1]))
565
+ elif xtype in DQUANTITIES:
566
+ data_to_save = np.column_stack((self.on_d()[0], self.on_d()[1]))
567
+ else:
568
+ warnings.warn(_xtype_wmsg(xtype))
569
+ self.metadata.update(get_package_info("diffpy.utils", metadata=self.metadata))
570
+ self.metadata["creation_time"] = datetime.datetime.now()
571
+
572
+ with open(filepath, "w") as f:
573
+ f.write(
574
+ f"[DiffractionObject]\nname = {self.name}\nwavelength = {self.wavelength}\n"
575
+ f"scat_quantity = {self.scat_quantity}\n"
576
+ )
577
+ for key, value in self.metadata.items():
578
+ f.write(f"{key} = {value}\n")
579
+ f.write("\n#### start data\n")
580
+ np.savetxt(f, data_to_save, delimiter=" ")
581
+
582
+ def copy(self):
583
+ """Create a deep copy of the DiffractionObject instance.
584
+
585
+ Returns
586
+ -------
587
+ DiffractionObject
588
+ The new instance of DiffractionObject, which is a deep copy of the current instance.
589
+ """
590
+ return deepcopy(self)
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # diffpy.utils by DANSE Diffraction group
5
+ # Simon J. L. Billinge
6
+ # (c) 2010 The Trustees of Columbia University
7
+ # in the City of New York. All rights reserved.
8
+ #
9
+ # File coded by: Chris Farrow
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+ """Various utilities related to data parsing and manipulation."""