dynasor 2.2__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.
- dynasor/__init__.py +28 -0
- dynasor/cli/__init__.py +0 -0
- dynasor/cli/main.py +204 -0
- dynasor/core/__init__.py +0 -0
- dynasor/core/reciprocal.py +50 -0
- dynasor/core/rho_j_q_numba.py +127 -0
- dynasor/core/time_averager.py +44 -0
- dynasor/correlation_functions.py +538 -0
- dynasor/logging_tools.py +46 -0
- dynasor/post_processing/__init__.py +21 -0
- dynasor/post_processing/atomic_weighting.py +147 -0
- dynasor/post_processing/average_runs.py +61 -0
- dynasor/post_processing/electron_scattering_factors.py +170 -0
- dynasor/post_processing/filon.py +148 -0
- dynasor/post_processing/form-factors/compton-parameters-waasmaier-kirfel-1995.json +1 -0
- dynasor/post_processing/form-factors/electron-parameters-ions-peng-1998.json +1 -0
- dynasor/post_processing/form-factors/electron-parameters-kmax2-peng-1996.json +1 -0
- dynasor/post_processing/form-factors/electron-parameters-kmax6-peng-1996.json +1 -0
- dynasor/post_processing/form-factors/neutron_scattering_lengths.json +1 -0
- dynasor/post_processing/form-factors/x-ray-parameters-itcc-2006.json +1 -0
- dynasor/post_processing/form-factors/x-ray-parameters-waasmaier-kirfel-1995.json +1 -0
- dynasor/post_processing/neutron_scattering_lengths.py +137 -0
- dynasor/post_processing/spherical_average.py +225 -0
- dynasor/post_processing/weights.py +72 -0
- dynasor/post_processing/x_ray_form_factors.py +186 -0
- dynasor/qpoints/__init__.py +6 -0
- dynasor/qpoints/lattice.py +136 -0
- dynasor/qpoints/spherical_qpoints.py +171 -0
- dynasor/qpoints/tools.py +247 -0
- dynasor/sample.py +204 -0
- dynasor/tools/__init__.py +0 -0
- dynasor/tools/acfs.py +117 -0
- dynasor/tools/damped_harmonic_oscillator.py +114 -0
- dynasor/tools/structures.py +102 -0
- dynasor/trajectory/__init__.py +4 -0
- dynasor/trajectory/abstract_trajectory_reader.py +38 -0
- dynasor/trajectory/ase_trajectory_reader.py +63 -0
- dynasor/trajectory/atomic_indices.py +37 -0
- dynasor/trajectory/extxyz_trajectory_reader.py +138 -0
- dynasor/trajectory/lammps_trajectory_reader.py +209 -0
- dynasor/trajectory/mdanalysis_trajectory_reader.py +139 -0
- dynasor/trajectory/trajectory.py +300 -0
- dynasor/trajectory/trajectory_frame.py +157 -0
- dynasor/units.py +42 -0
- dynasor-2.2.dist-info/METADATA +64 -0
- dynasor-2.2.dist-info/RECORD +50 -0
- dynasor-2.2.dist-info/WHEEL +5 -0
- dynasor-2.2.dist-info/entry_points.txt +2 -0
- dynasor-2.2.dist-info/licenses/LICENSE +21 -0
- dynasor-2.2.dist-info/top_level.txt +1 -0
dynasor/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
dynasor module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .correlation_functions import (
|
|
8
|
+
compute_dynamic_structure_factors,
|
|
9
|
+
compute_spectral_energy_density,
|
|
10
|
+
compute_static_structure_factors
|
|
11
|
+
)
|
|
12
|
+
from .qpoints import (
|
|
13
|
+
get_spherical_qpoints,
|
|
14
|
+
get_supercell_qpoints_along_path
|
|
15
|
+
)
|
|
16
|
+
from .sample import read_sample_from_npz
|
|
17
|
+
from .trajectory import Trajectory
|
|
18
|
+
|
|
19
|
+
__version__ = '2.2'
|
|
20
|
+
__all__ = [
|
|
21
|
+
'compute_dynamic_structure_factors',
|
|
22
|
+
'compute_spectral_energy_density',
|
|
23
|
+
'compute_static_structure_factors',
|
|
24
|
+
'get_spherical_qpoints',
|
|
25
|
+
'get_supercell_qpoints_along_path',
|
|
26
|
+
'read_sample_from_npz',
|
|
27
|
+
'Trajectory',
|
|
28
|
+
]
|
dynasor/cli/__init__.py
ADDED
|
File without changes
|
dynasor/cli/main.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from dynasor.logging_tools import logger, set_logging_level
|
|
8
|
+
from dynasor.qpoints import get_spherical_qpoints
|
|
9
|
+
from dynasor.trajectory import Trajectory
|
|
10
|
+
from dynasor.correlation_functions import compute_dynamic_structure_factors
|
|
11
|
+
from dynasor.post_processing import get_spherically_averaged_sample_binned
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
description='dynasor is a simple tool for calculating total and partial dynamic structure'
|
|
18
|
+
' factors as well as current correlation functions from molecular dynamics simulations.'
|
|
19
|
+
' The main input consists of a trajectory output from a MD simulation, i.e., a file'
|
|
20
|
+
' containing snapshots of the particle coordinates and optionally velocities that'
|
|
21
|
+
' correspond to consecutive, equally spaced points in (simulation) time.'
|
|
22
|
+
'\n'
|
|
23
|
+
' Dynasor has recently been updated. Some of the old options have new names and'
|
|
24
|
+
' some new options have been added. If your script has stopped working, check the'
|
|
25
|
+
' options with "dynasor --help". In addition, we would like to let you know that'
|
|
26
|
+
' dynasor now has a Python interface, which gives you acces to more'
|
|
27
|
+
' functionality and options.')
|
|
28
|
+
|
|
29
|
+
iogroup = parser.add_argument_group(
|
|
30
|
+
'Input/output options',
|
|
31
|
+
'Options controlling input and output, files and fileformats.')
|
|
32
|
+
iogroup.add_argument(
|
|
33
|
+
'-f', '--trajectory', type=str, metavar='TRAJECTORY_FILE',
|
|
34
|
+
help='Molecular dynamics trajectory file to be analyzed.'
|
|
35
|
+
' Supported formats depends on MDAnalysis. As a fallback, a lammps-trajectory parser'
|
|
36
|
+
' implemented in Python is also available as well as an extended-xyz reader based on ASE')
|
|
37
|
+
iogroup.add_argument(
|
|
38
|
+
'--trajectory-format', type=str, metavar='TRAJECTORY_FORMAT',
|
|
39
|
+
help='Format of trajectory. Choose from: "lammps_internal", "extxyz", or one'
|
|
40
|
+
' of the formats supported by MDAnalysis (except "lammpsdump", which is'
|
|
41
|
+
' called through "lammps_mdanalysis" to avoid ambiguity)')
|
|
42
|
+
iogroup.add_argument(
|
|
43
|
+
'--length-unit', type=str, metavar='LENGTH_UNIT', default='Angstrom',
|
|
44
|
+
help='Length unit of trajectory ("Angstrom", "nm", "pm", "fm"). Necessary for correct '
|
|
45
|
+
'conversion to internal dynasor units if the trajectory file does not contain '
|
|
46
|
+
'unit information.')
|
|
47
|
+
iogroup.add_argument(
|
|
48
|
+
'--time-unit', type=str, metavar='TIME_UNIT', default='fs',
|
|
49
|
+
help='Time unit of trajectory ("fs", "ps", "ns"). Necessary for correct conversion to '
|
|
50
|
+
'internal dynasor units if the trajectory file does not contain unit information.')
|
|
51
|
+
iogroup.add_argument(
|
|
52
|
+
'-n', '--index', type=str, metavar='INDEX_FILE',
|
|
53
|
+
help='Optional index file (think Gromacs NDX-style) for specifying atom types. Atoms are'
|
|
54
|
+
' indexed from 1 up to N (total number of atoms). It is possible to index only a subset of'
|
|
55
|
+
' all atoms, and atoms can be indexed in more than one group. If no index file is provided,'
|
|
56
|
+
' all atoms will be considered identical.')
|
|
57
|
+
iogroup.add_argument(
|
|
58
|
+
'--outfile', type=str, metavar='FILE',
|
|
59
|
+
help='Write output to FILE as a numpy npz file')
|
|
60
|
+
|
|
61
|
+
qspace = parser.add_argument_group(
|
|
62
|
+
'General q-space options',
|
|
63
|
+
'Options controlling general aspects for how q-space should be sampled and collected.')
|
|
64
|
+
qspace.add_argument(
|
|
65
|
+
'--q-sampling', type=str,
|
|
66
|
+
metavar='STYLE', default='isotropic',
|
|
67
|
+
help='Possible values are "isotropic" (default) for sampling isotropic systems'
|
|
68
|
+
' (as liquids), and "line" to sample uniformly along a certain direction in q-space. ')
|
|
69
|
+
defval = 80
|
|
70
|
+
qspace.add_argument(
|
|
71
|
+
'--q-bins',
|
|
72
|
+
metavar='BINS', type=int, default=defval,
|
|
73
|
+
help='Number of "radial" bins to use (between 0 and largest |q|-value) when collecting'
|
|
74
|
+
f' resulting average. Default value is {defval}.')
|
|
75
|
+
|
|
76
|
+
qiso = parser.add_argument_group('Isotropic q-space sampling')
|
|
77
|
+
defval = 20000
|
|
78
|
+
qiso.add_argument(
|
|
79
|
+
'--max-q-points', metavar='QPOINTS', type=int,
|
|
80
|
+
default=defval,
|
|
81
|
+
help='Maximum number of points used to sample q-space. Puts an (approximate) upper'
|
|
82
|
+
f' limit by randomly selecting. points. Default value is {defval}.')
|
|
83
|
+
defval = 60
|
|
84
|
+
qiso.add_argument(
|
|
85
|
+
'--q-max', metavar='QMAX', type=int, default=defval,
|
|
86
|
+
help='Largest q-value to consider in units of "2*pi*Å^-1".'
|
|
87
|
+
' Default value for QMAX is {defval}.')
|
|
88
|
+
|
|
89
|
+
qline = parser.add_argument_group('Line-style q-space sampling')
|
|
90
|
+
qline.add_argument(
|
|
91
|
+
'--q-direction', metavar='QDIRECTION',
|
|
92
|
+
help='Direction along which to sample. QPOINTS points will be evenly placed between'
|
|
93
|
+
' 0,0,0 and QDIRECTION. Given as three comma separated values.')
|
|
94
|
+
defval = 100
|
|
95
|
+
qline.add_argument(
|
|
96
|
+
'--q-points', metavar='QPOINTS', type=int, default=defval,
|
|
97
|
+
help=f'Number of q-points to sample along line. Default: {defval}')
|
|
98
|
+
|
|
99
|
+
tgroup = parser.add_argument_group(
|
|
100
|
+
'Time-related options',
|
|
101
|
+
'Options controlling timestep, length and shape of trajectory frame window, etc.')
|
|
102
|
+
tgroup.add_argument(
|
|
103
|
+
'--time-window', metavar='TIME_WINDOW', type=int,
|
|
104
|
+
help='The length of the trajectory frame window to use for time correlation calculation.'
|
|
105
|
+
' It is expressed in number of frames and determines, among other things, the smallest'
|
|
106
|
+
' frequency that can be resolved. If no TIME_WINDOW is provided, only static (t=0)'
|
|
107
|
+
' correlations will be calculated')
|
|
108
|
+
defval = 100
|
|
109
|
+
tgroup.add_argument(
|
|
110
|
+
'--max-frames', metavar='FRAMES', type=int, default=defval,
|
|
111
|
+
help='Limits the total number of trajectory frames read to FRAMES.'
|
|
112
|
+
f' The default value is {defval}.')
|
|
113
|
+
defval = 1
|
|
114
|
+
tgroup.add_argument(
|
|
115
|
+
'--step', metavar='STEP', type=int, default=defval,
|
|
116
|
+
help='Only use every STEP-th trajectory frame. The default STEP is {defval}, meaning'
|
|
117
|
+
' every frame is processed. STEP affects dt and hence the smallest time resolved.')
|
|
118
|
+
defval = 1
|
|
119
|
+
tgroup.add_argument(
|
|
120
|
+
'--stride', metavar='STRIDE', type=int, default=defval,
|
|
121
|
+
help='STRIDE number of frames between consecutive trajectory windows. This does not affect'
|
|
122
|
+
' dt. If e.g. STRIDE > TIME_CORR_STEPS, some frames will be completely unused.')
|
|
123
|
+
tgroup.add_argument(
|
|
124
|
+
'--dt', metavar='DELTATIME', type=float,
|
|
125
|
+
help='Explicitly sets the time difference between two consecutively processed'
|
|
126
|
+
' trajectory frames to DELTATIME (femtoseconds). ')
|
|
127
|
+
|
|
128
|
+
options = parser.add_argument_group('General processing options')
|
|
129
|
+
options.add_argument(
|
|
130
|
+
'--calculate-incoherent',
|
|
131
|
+
action='store_true', default=False,
|
|
132
|
+
help='Calculate the incoherent part. Default is False.')
|
|
133
|
+
options.add_argument(
|
|
134
|
+
'--calculate-currents',
|
|
135
|
+
action='store_true', default=False,
|
|
136
|
+
help='Calculate the current (velocity) correlations, Default is False.')
|
|
137
|
+
|
|
138
|
+
parser.add_argument(
|
|
139
|
+
'-q', '--quiet', action='count', default=0,
|
|
140
|
+
help='Increase quietness (opposite of verbosity).')
|
|
141
|
+
parser.add_argument(
|
|
142
|
+
'-v', '--verbose', action='count', default=0,
|
|
143
|
+
help='Increase verbosity (opposite of quietness).')
|
|
144
|
+
|
|
145
|
+
args = parser.parse_args()
|
|
146
|
+
|
|
147
|
+
# set log level
|
|
148
|
+
quietness = args.quiet - args.verbose
|
|
149
|
+
if quietness < 0:
|
|
150
|
+
log_level = 'DEBUG'
|
|
151
|
+
elif quietness == 0:
|
|
152
|
+
log_level = 'INFO'
|
|
153
|
+
elif quietness == 1:
|
|
154
|
+
log_level = 'WARN'
|
|
155
|
+
elif quietness == 2:
|
|
156
|
+
log_level = 'ERROR'
|
|
157
|
+
else:
|
|
158
|
+
log_level = 'CRITICAL'
|
|
159
|
+
set_logging_level(log_level)
|
|
160
|
+
|
|
161
|
+
# parse args
|
|
162
|
+
if args.trajectory is None:
|
|
163
|
+
logger.error('A trajectory must be specified. Use option -f')
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
|
|
166
|
+
if not args.outfile:
|
|
167
|
+
logger.error('An output file must be specified. Use option --outfile')
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
|
|
170
|
+
if args.dt is None:
|
|
171
|
+
logger.info('No value set for dt. Setting to 1 fs. Note that this is irrelevant when only '
|
|
172
|
+
'computing static structure factors, i.e., if time_window is not set.')
|
|
173
|
+
args.dt = 1
|
|
174
|
+
|
|
175
|
+
# setup Trajectory
|
|
176
|
+
traj = Trajectory(args.trajectory,
|
|
177
|
+
trajectory_format=args.trajectory_format,
|
|
178
|
+
atomic_indices=args.index,
|
|
179
|
+
length_unit=args.length_unit,
|
|
180
|
+
time_unit=args.time_unit,
|
|
181
|
+
frame_stop=args.max_frames,
|
|
182
|
+
frame_step=args.step,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# setup q-points
|
|
186
|
+
if args.q_sampling == 'line':
|
|
187
|
+
q_dir = np.array(args.q_direction)
|
|
188
|
+
n_qpoints = args.q_points
|
|
189
|
+
q_points = np.array([i * q_dir for i in np.linspace(0, 1, n_qpoints)])
|
|
190
|
+
elif args.q_sampling == 'isotropic':
|
|
191
|
+
q_points = get_spherical_qpoints(traj.cell, args.q_max, args.max_q_points)
|
|
192
|
+
|
|
193
|
+
# run dynasor calculation
|
|
194
|
+
sample = compute_dynamic_structure_factors(
|
|
195
|
+
traj, q_points=q_points,
|
|
196
|
+
dt=args.dt, window_size=args.time_window, window_step=args.stride,
|
|
197
|
+
calculate_currents=args.calculate_currents,
|
|
198
|
+
calculate_incoherent=args.calculate_incoherent,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# save results to file
|
|
202
|
+
if args.q_sampling == 'isotropic':
|
|
203
|
+
sample = get_spherically_averaged_sample_binned(sample, num_q_bins=args.q_bins)
|
|
204
|
+
sample.write_to_npz(args.outfile)
|
dynasor/core/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from dynasor.core.rho_j_q_numba import rho_q as rho_q_numba
|
|
4
|
+
from dynasor.core.rho_j_q_numba import rho_j_q as rho_j_q_numba
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def calc_rho_q(x, q):
|
|
8
|
+
"""Calculate rho(q) of particle coordinates x.
|
|
9
|
+
|
|
10
|
+
Will call external function rho_q to calculate the
|
|
11
|
+
particle density in q-space.
|
|
12
|
+
Particle coordinates and q-space points of interest are
|
|
13
|
+
passed as input via x and q, respectively.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
assert x.shape[1] == 3
|
|
17
|
+
assert q.shape[1] == 3
|
|
18
|
+
|
|
19
|
+
Nx, _ = x.shape
|
|
20
|
+
Nq, _ = q.shape
|
|
21
|
+
|
|
22
|
+
rho_q = np.zeros(Nq, dtype=np.complex128)
|
|
23
|
+
|
|
24
|
+
x = x.copy() # Don't ask why
|
|
25
|
+
rho_q_numba(x, q, rho_q)
|
|
26
|
+
|
|
27
|
+
return rho_q
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def calc_rho_j_q(x, v, q):
|
|
31
|
+
"""As calc_rho_q, but calculate also velocities in q-space
|
|
32
|
+
"""
|
|
33
|
+
assert x.shape == v.shape
|
|
34
|
+
|
|
35
|
+
assert x.shape[1] == 3
|
|
36
|
+
assert v.shape[1] == 3
|
|
37
|
+
assert q.shape[1] == 3
|
|
38
|
+
|
|
39
|
+
Nx, _ = x.shape
|
|
40
|
+
Nq, _ = q.shape
|
|
41
|
+
|
|
42
|
+
rho_q = np.zeros(Nq, dtype=np.complex128)
|
|
43
|
+
j_q = np.zeros((Nq, 3), dtype=np.complex128)
|
|
44
|
+
|
|
45
|
+
x = x.copy()
|
|
46
|
+
v = v.copy()
|
|
47
|
+
|
|
48
|
+
rho_j_q_numba(x, v, q, rho_q, j_q)
|
|
49
|
+
|
|
50
|
+
return rho_q, j_q
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""This module replaces the original c implementation of the reciprocal
|
|
2
|
+
densities and currents in dynasor with numba.
|
|
3
|
+
|
|
4
|
+
Numba is as of 2023 an ongoing project to create a JIT compiler frontend for
|
|
5
|
+
python code using the LLVM project as backend. Due to current limitations and
|
|
6
|
+
quirks of numba the code is not always straightforward. Typically the code
|
|
7
|
+
needs to be refactored in a trial and error process to get the expected
|
|
8
|
+
performance but should in the end be on the level of c.
|
|
9
|
+
|
|
10
|
+
Especially, numba makes very pessimistic assumptions about aliasing but this is
|
|
11
|
+
expected to change in the future. Also, in theory, via the llvm-lite interface
|
|
12
|
+
compilation flags should be passable to LLVM.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
import numba
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# This is often faster than calling np.dot for small arrays
|
|
20
|
+
# Calling this instead of manually inlining it actually incurs a small
|
|
21
|
+
# performance hit (<10%) with current numba (2023). It increases readability
|
|
22
|
+
# though and will probably sort itself out with later numba versions
|
|
23
|
+
@numba.njit(fastmath=True, nogil=True)
|
|
24
|
+
def dot(a, b):
|
|
25
|
+
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# fastmath True makes the summation fast and also speeds up exponentiation
|
|
29
|
+
# nogil releases the python GIL, probably not neccesary here
|
|
30
|
+
@numba.njit(fastmath=True, nogil=True)
|
|
31
|
+
def rho_q_single(x: np.ndarray,
|
|
32
|
+
q: np.ndarray) -> complex:
|
|
33
|
+
"""Calculates the density at a single q-point
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
x
|
|
38
|
+
positions as a (N, 3) array
|
|
39
|
+
q
|
|
40
|
+
single q point as a with shape (3,)
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
rho
|
|
45
|
+
complex density at the specified q-point
|
|
46
|
+
"""
|
|
47
|
+
Nx = len(x)
|
|
48
|
+
|
|
49
|
+
assert x.shape == (Nx, 3)
|
|
50
|
+
assert q.shape == (3,)
|
|
51
|
+
|
|
52
|
+
rho = 0.0j
|
|
53
|
+
for i in range(Nx):
|
|
54
|
+
alpha = dot(x[i], q)
|
|
55
|
+
rho += np.exp(1j * alpha) # very expensive operation
|
|
56
|
+
return rho
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# parallel enables the numba.prange directive
|
|
60
|
+
@numba.njit(fastmath=True, nogil=True, parallel=True)
|
|
61
|
+
def rho_q(x: np.ndarray, q: np.ndarray, rho: np.ndarray):
|
|
62
|
+
"""Calculates the fourier transformed density
|
|
63
|
+
|
|
64
|
+
The parallelization is over q-points. The density is calculated in-place.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
x
|
|
69
|
+
the positions as a float array with shape (``Nx``, 3)
|
|
70
|
+
q
|
|
71
|
+
the q points as a float array with shape (``Nq``, 3)
|
|
72
|
+
rho
|
|
73
|
+
density as a complex array of length ``Nq``
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
Nx = len(x)
|
|
77
|
+
Nq = len(q)
|
|
78
|
+
|
|
79
|
+
assert x.shape == (Nx, 3)
|
|
80
|
+
assert q.shape == (Nq, 3)
|
|
81
|
+
assert rho.shape == (Nq,)
|
|
82
|
+
|
|
83
|
+
# Numba prange is like OMP
|
|
84
|
+
for i in numba.prange(Nq):
|
|
85
|
+
rho[i] = rho_q_single(x, q[i])
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@numba.njit(fastmath=True, parallel=True, nogil=True)
|
|
89
|
+
def rho_j_q(x: np.ndarray, v: np.ndarray, q: np.ndarray,
|
|
90
|
+
rho: np.ndarray, j_q: np.ndarray):
|
|
91
|
+
"""Calculates the fourier transformed density and current.
|
|
92
|
+
|
|
93
|
+
The output is stored in the supplied output arrays ``rho`` and ``j_q``
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
x
|
|
98
|
+
the positions as a float array with shape (``Nx``, 3)
|
|
99
|
+
v
|
|
100
|
+
the velocities as a float array with shape (``Nx``, 3)
|
|
101
|
+
q
|
|
102
|
+
the q points as a float array with shape (``Nq``, 3)
|
|
103
|
+
rho
|
|
104
|
+
density as a complex array of length ``Nq``
|
|
105
|
+
j_q
|
|
106
|
+
current as a complex array with shape (``Nq``, 3)
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
Nx = len(x)
|
|
110
|
+
Nq = len(q)
|
|
111
|
+
|
|
112
|
+
assert x.shape == (Nx, 3)
|
|
113
|
+
assert v.shape == (Nx, 3)
|
|
114
|
+
assert q.shape == (Nq, 3)
|
|
115
|
+
assert rho.shape == (Nq,)
|
|
116
|
+
assert j_q.shape == (Nq, 3)
|
|
117
|
+
|
|
118
|
+
for qi in numba.prange(Nq):
|
|
119
|
+
for xi in range(Nx):
|
|
120
|
+
|
|
121
|
+
alpha = dot(x[xi], q[qi])
|
|
122
|
+
exp_ialpha = np.exp(1.0j * alpha)
|
|
123
|
+
|
|
124
|
+
rho[qi] += exp_ialpha
|
|
125
|
+
|
|
126
|
+
for i in range(3):
|
|
127
|
+
j_q[qi, i] += exp_ialpha * v[xi][i]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TimeAverager:
|
|
5
|
+
"""Naive special purpose averager class used in dynasor to collect and time-average arrays
|
|
6
|
+
obtained from sliding time-window averaging.
|
|
7
|
+
|
|
8
|
+
It assists with keeping track of how many data samples have been added to each slot.
|
|
9
|
+
|
|
10
|
+
It will time-average arrays of shape ``(Nq, time_window)`` where ``Ǹq`` is the
|
|
11
|
+
number of q-points and ``time_window`` is the size of the time window.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
time_window
|
|
16
|
+
size of the time window in which the time-average happens
|
|
17
|
+
array_length
|
|
18
|
+
length of the array to be averaged for each time-lag, i.e. number of q-points
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, time_window: int, array_length: int):
|
|
22
|
+
assert time_window >= 1
|
|
23
|
+
self._time_window = time_window
|
|
24
|
+
self._array_length = array_length
|
|
25
|
+
|
|
26
|
+
self._counts = np.zeros(time_window, dtype=int)
|
|
27
|
+
self._arrays = [np.zeros(array_length) for _ in range(time_window)]
|
|
28
|
+
|
|
29
|
+
def add_sample(self, time_lag: int, sample: np.ndarray):
|
|
30
|
+
assert len(sample) == self._array_length
|
|
31
|
+
self._counts[time_lag] += 1
|
|
32
|
+
self._arrays[time_lag] += sample
|
|
33
|
+
|
|
34
|
+
def get_average_at_timelag(self, time_lag: int):
|
|
35
|
+
if self._counts[time_lag] == 0:
|
|
36
|
+
array = np.full((self._array_length, ), np.nan)
|
|
37
|
+
return array
|
|
38
|
+
return self._arrays[time_lag] / self._counts[time_lag]
|
|
39
|
+
|
|
40
|
+
def get_average_all(self):
|
|
41
|
+
"""
|
|
42
|
+
Returns an averaged array of shape ``(array_length, time_window)``
|
|
43
|
+
"""
|
|
44
|
+
return np.array([self.get_average_at_timelag(t) for t in range(self._time_window)]).T
|