pyshs-optics 4.0.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.
- py_shs/HRS.py +521 -0
- py_shs/SHS.py +666 -0
- py_shs/__init__.py +7 -0
- py_shs/__main__.py +5 -0
- py_shs/cli.py +50 -0
- py_shs/cube_SHS.py +658 -0
- py_shs/cylinder_SHS.py +571 -0
- py_shs/ellipsoid_SHS.py +612 -0
- py_shs/graph_angle.py +60 -0
- py_shs/graph_assembly.py +74 -0
- py_shs/pyshs_gui.py +1911 -0
- py_shs/script_cylinder_random.py +60 -0
- py_shs/script_gauss2PySHS.py +108 -0
- py_shs/script_sphere.py +61 -0
- py_shs/script_sphere_random.py +67 -0
- py_shs/script_spheroid_random.py +80 -0
- py_shs/shs_angles.py +41 -0
- py_shs/shs_grid.py +167 -0
- py_shs/shs_io.py +20 -0
- py_shs/shs_mie.py +714 -0
- py_shs/shs_units.py +76 -0
- py_shs/sphere_SHS.py +579 -0
- pyshs_optics-4.0.2.dist-info/METADATA +457 -0
- pyshs_optics-4.0.2.dist-info/RECORD +28 -0
- pyshs_optics-4.0.2.dist-info/WHEEL +5 -0
- pyshs_optics-4.0.2.dist-info/entry_points.txt +9 -0
- pyshs_optics-4.0.2.dist-info/licenses/LICENSE +21 -0
- pyshs_optics-4.0.2.dist-info/top_level.txt +1 -0
py_shs/HRS.py
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
HRS.py -- Py_SHS : Hyper-Rayleigh Scattering of a single molecule (Python / NumPy)
|
|
5
|
+
|
|
6
|
+
Derived from SHS.py for the special case N = 1, position (0,0,0),
|
|
7
|
+
molecular orientation (phi, theta, psi) = (0,0,0).
|
|
8
|
+
|
|
9
|
+
No orientation file is required: the molecular hyperpolarizability beta
|
|
10
|
+
is used directly. The isotropic orientational average is performed over
|
|
11
|
+
the laboratory-frame Euler angles of the molecule (same grid as SHS).
|
|
12
|
+
|
|
13
|
+
Because r = 0 the phase factor is identically 1, so the coherent sum
|
|
14
|
+
reduces to the single-molecule tensor. Numerically this recovers exactly
|
|
15
|
+
the result of SHS.py run with N = 1 and a one-line orientation file
|
|
16
|
+
"0 0 0 0 0 0".
|
|
17
|
+
|
|
18
|
+
Keywords : polarplot_single | polarplot_integrate | angle_scattering
|
|
19
|
+
Usage : python HRS.py keyword beta_file output_file [--legacy]
|
|
20
|
+
stdin : wavelength (nm), Re(n), Im(n), then the angle(s) of the mode
|
|
21
|
+
(the wavelength and the index are read to keep the same input protocol as the
|
|
22
|
+
other modules; they do not change the default `raw` intensities -- they only
|
|
23
|
+
enter the radiative prefactor (|K|^2/4pi)^2 when PYSHS_UNITS=rad is requested)
|
|
24
|
+
|
|
25
|
+
Options / environment
|
|
26
|
+
---------------------
|
|
27
|
+
--legacy (or PYSHS_LEGACY=1) reproduces two defects of the old C code: the
|
|
28
|
+
normalisation of bv1, bv2, bh1, bh2 (not divided by 8 pi^2) and the
|
|
29
|
+
pairing of the sin*cos cross terms of bh1, bh2. After the isotropic
|
|
30
|
+
orientational average these four coefficients are identically zero (the
|
|
31
|
+
odd terms are the real part of a Hermitian-symmetric contraction that the
|
|
32
|
+
chiral invariants cannot feed), so --legacy only changes rounding noise in
|
|
33
|
+
them and none of av, bv, cv, ah, bh, ch, I2, I4. Kept for completeness;
|
|
34
|
+
see tests/test_core.py (TestLegacyMode) for the exact definitions.
|
|
35
|
+
"""
|
|
36
|
+
import os
|
|
37
|
+
import sys
|
|
38
|
+
import math
|
|
39
|
+
|
|
40
|
+
import numpy as np
|
|
41
|
+
|
|
42
|
+
from py_shs import shs_units
|
|
43
|
+
from py_shs import shs_angles
|
|
44
|
+
from py_shs import shs_io
|
|
45
|
+
|
|
46
|
+
# ---- orientation grid (same values as SHS.py / the C code) ----
|
|
47
|
+
PAS = 10
|
|
48
|
+
N_PHI = 2 * PAS # phi in [0, 2 pi)
|
|
49
|
+
N_THETA = PAS # theta in [0, pi]
|
|
50
|
+
N_PSI = 2 * PAS # psi in [0, 2 pi)
|
|
51
|
+
PI = math.pi
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# --------------------------------------------------------------------------
|
|
55
|
+
# Small helpers (identical to SHS.py)
|
|
56
|
+
# --------------------------------------------------------------------------
|
|
57
|
+
# number format of the intensities: historical '%f' when the multiplier is 1,
|
|
58
|
+
# scientific notation otherwise (values can be ~1e-9 with PYSHS_UNITS=rad)
|
|
59
|
+
FMT = "%f"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TokenReader:
|
|
63
|
+
"""Reads whitespace separated values from stdin like C scanf."""
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.buf = []
|
|
67
|
+
|
|
68
|
+
def next(self):
|
|
69
|
+
while not self.buf:
|
|
70
|
+
line = sys.stdin.readline()
|
|
71
|
+
if line == "":
|
|
72
|
+
raise EOFError("unexpected end of the standard input")
|
|
73
|
+
self.buf = line.split()[::-1]
|
|
74
|
+
return self.buf.pop()
|
|
75
|
+
|
|
76
|
+
def real(self):
|
|
77
|
+
return float(self.next())
|
|
78
|
+
|
|
79
|
+
def integer(self):
|
|
80
|
+
return int(self.next())
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def say(text="", end="\n"):
|
|
84
|
+
print(text, end=end, flush=True)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def euler_matrices(phi, theta, psi):
|
|
88
|
+
"""ZYZ Euler matrices, identical to Txx..Tzz of the C code.
|
|
89
|
+
phi, theta, psi : arrays of shape (n,) -> T : (n, 3, 3)"""
|
|
90
|
+
sp, cp = np.sin(phi), np.cos(phi)
|
|
91
|
+
st, ct = np.sin(theta), np.cos(theta)
|
|
92
|
+
ss, cs = np.sin(psi), np.cos(psi)
|
|
93
|
+
T = np.empty(np.shape(phi) + (3, 3))
|
|
94
|
+
T[..., 0, 0] = -sp * ss + ct * cp * cs
|
|
95
|
+
T[..., 0, 1] = -sp * cs - ct * cp * ss
|
|
96
|
+
T[..., 0, 2] = st * cp
|
|
97
|
+
T[..., 1, 0] = cp * ss + ct * sp * cs
|
|
98
|
+
T[..., 1, 1] = cp * cs - ct * ss * sp
|
|
99
|
+
T[..., 1, 2] = st * sp
|
|
100
|
+
T[..., 2, 0] = -st * cs
|
|
101
|
+
T[..., 2, 1] = st * ss
|
|
102
|
+
T[..., 2, 2] = ct
|
|
103
|
+
return T
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
_JK = ((0, 0), (1, 1), (0, 1))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def rotate_needed(T, S):
|
|
110
|
+
"""L[n,i,q] = sum_a T[n,i,a] * ( sum_bc T[n,j,b] T[n,k,c] S[n,a,b,c] ),
|
|
111
|
+
q <-> (j,k) in ((0,0),(1,1),(0,1))."""
|
|
112
|
+
L = np.empty(S.shape[:1] + (3, 3), dtype=S.dtype)
|
|
113
|
+
for q, (j, k) in enumerate(_JK):
|
|
114
|
+
w = np.einsum("nb,nc,nabc->na", T[:, j, :], T[:, k, :], S)
|
|
115
|
+
L[:, :, q] = np.einsum("nia,na->ni", T, w)
|
|
116
|
+
return L
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def pol_sums(w, xx, yy, xy):
|
|
120
|
+
"""Weighted sums (a, b, c, b1, b2) of the intensity coefficients."""
|
|
121
|
+
xyc = np.conj(xy)
|
|
122
|
+
return np.array([
|
|
123
|
+
np.sum(w * (xx * np.conj(xx)).real),
|
|
124
|
+
np.sum(w * (4.0 * (xy * xyc).real + 2.0 * (xx * np.conj(yy)).real)),
|
|
125
|
+
np.sum(w * (yy * np.conj(yy)).real),
|
|
126
|
+
np.sum(w * 4.0 * (xx * xyc).real),
|
|
127
|
+
np.sum(w * 4.0 * (yy * xyc).real),
|
|
128
|
+
])
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# --------------------------------------------------------------------------
|
|
132
|
+
# Inputs
|
|
133
|
+
# --------------------------------------------------------------------------
|
|
134
|
+
def read_beta(name):
|
|
135
|
+
"""1 header line + 27 lines "name value"."""
|
|
136
|
+
beta = np.zeros((3, 3, 3))
|
|
137
|
+
try:
|
|
138
|
+
with open(name, "rt") as f:
|
|
139
|
+
f.readline()
|
|
140
|
+
for i in range(3):
|
|
141
|
+
for j in range(3):
|
|
142
|
+
for k in range(3):
|
|
143
|
+
beta[i, j, k] = float(f.readline().split()[1])
|
|
144
|
+
except (OSError, IndexError, ValueError):
|
|
145
|
+
return None
|
|
146
|
+
# The intensity formulas use beta_{i,jk} only for (j,k) = (x,x), (y,y), (x,y) and
|
|
147
|
+
# assume beta_{ijk} = beta_{ikj} (intrinsic permutation symmetry). Symmetrise in the
|
|
148
|
+
# last two indices so that every module treats any input tensor the same way.
|
|
149
|
+
asym = np.abs(beta - beta.transpose(0, 2, 1)).max()
|
|
150
|
+
if asym > 1e-12 * max(np.abs(beta).max(), 1e-300):
|
|
151
|
+
say("WARNING: the tensor is not symmetric in its last two indices "
|
|
152
|
+
"(max |beta_ijk - beta_ikj| = %.3e): it has been symmetrised, "
|
|
153
|
+
"beta_ijk = beta_ikj = (beta_ijk + beta_ikj)/2." % asym)
|
|
154
|
+
beta = 0.5 * (beta + beta.transpose(0, 2, 1))
|
|
155
|
+
return beta
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# --------------------------------------------------------------------------
|
|
159
|
+
# Core calculation (HRS = single dipole at origin, identity orientation)
|
|
160
|
+
# --------------------------------------------------------------------------
|
|
161
|
+
def theta_rule(n):
|
|
162
|
+
"""Quadrature nodes/weights for int_0^pi f(theta) sin(theta) dtheta.
|
|
163
|
+
|
|
164
|
+
Default: Gauss-Legendre in cos(theta) (exact for polynomials of degree <= 2n-1,
|
|
165
|
+
weights sum to exactly 2). PYSHS_THETA_RULE=midpoint restores the former midpoint
|
|
166
|
+
rule (weights h*sin(theta), sum slightly different from 2).
|
|
167
|
+
"""
|
|
168
|
+
if os.environ.get("PYSHS_THETA_RULE", "gauss").strip().lower() == "midpoint":
|
|
169
|
+
h = PI / n
|
|
170
|
+
th = (np.arange(n) + 0.5) * h
|
|
171
|
+
return th, h * np.sin(th)
|
|
172
|
+
x, wl = np.polynomial.legendre.leggauss(n)
|
|
173
|
+
return np.arccos(x), wl
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class Orientations:
|
|
178
|
+
"""Orientation table (Euler matrices and weights), computed ONCE."""
|
|
179
|
+
|
|
180
|
+
def __init__(self):
|
|
181
|
+
hx, hz = 2 * PI / N_PHI, 2 * PI / N_PSI
|
|
182
|
+
th_nodes, th_w = theta_rule(N_THETA)
|
|
183
|
+
i, j, k = np.meshgrid(np.arange(N_PHI), np.arange(N_THETA),
|
|
184
|
+
np.arange(N_PSI), indexing="ij")
|
|
185
|
+
i, j, k = i.ravel(), j.ravel(), k.ravel()
|
|
186
|
+
self.T = euler_matrices(0.5 * hx + i * hx, th_nodes[j], 0.5 * hz + k * hz)
|
|
187
|
+
self.w = hx * hz * th_w[j]
|
|
188
|
+
self.n = i.size
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class AngleTable:
|
|
192
|
+
"""Angle dependent constants, computed ONCE."""
|
|
193
|
+
|
|
194
|
+
def __init__(self, theta_s, petitk, grandk):
|
|
195
|
+
theta_s = np.asarray(theta_s, dtype=np.float64)
|
|
196
|
+
self.cs, self.sn = np.cos(theta_s), np.sin(theta_s)
|
|
197
|
+
# q is not needed for HRS (r = 0 => phase = 1), but cs/sn are used
|
|
198
|
+
# in finalize for the H polarisation combination.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def lab_amplitudes(S, ori):
|
|
202
|
+
"""Lab-frame amplitudes A[n, i, q] (q = xx, yy, xy) for every orientation.
|
|
203
|
+
They do not depend on the scattering angle, so they are computed once.
|
|
204
|
+
S is real for HRS (phase = 1; the imaginary half S[:, 27:] is zero)."""
|
|
205
|
+
n = ori.n
|
|
206
|
+
Lr = rotate_needed(ori.T, S[:, :27].reshape(n, 3, 3, 3))
|
|
207
|
+
return Lr + 0j
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def combine(A, w, c, s, legacy):
|
|
211
|
+
"""Polarisation sums and orientational average for one angle (cos c, sin s).
|
|
212
|
+
Returns the (2, 5) array [V; H] of coefficients (a, b, c, b1, b2), unnormalised."""
|
|
213
|
+
V = pol_sums(w, A[:, 0, 0], A[:, 0, 1], A[:, 0, 2])
|
|
214
|
+
Hxx = c * A[:, 1, 0] - s * A[:, 2, 0]
|
|
215
|
+
Hyy = c * A[:, 1, 1] - s * A[:, 2, 1]
|
|
216
|
+
Hxy = c * A[:, 1, 2] - s * A[:, 2, 2]
|
|
217
|
+
H = pol_sums(w, Hxx, Hyy, Hxy)
|
|
218
|
+
if legacy:
|
|
219
|
+
gyx, gzx, gyy, gzy = A[:, 1, 0], A[:, 2, 0], A[:, 1, 1], A[:, 2, 1]
|
|
220
|
+
gyxy, gzxy = A[:, 1, 2], A[:, 2, 2]
|
|
221
|
+
|
|
222
|
+
def r4(u, v):
|
|
223
|
+
return 4.0 * (u * np.conj(v)).real
|
|
224
|
+
|
|
225
|
+
H[3] += np.sum(w * (-r4(Hxx, Hxy)))
|
|
226
|
+
H[3] += np.sum(w * (c * c * r4(gyx, gyxy) + s * s * r4(gzx, gzxy)
|
|
227
|
+
- s * c * (r4(gzx, gzxy) + r4(gyx, gyxy))))
|
|
228
|
+
H[4] += np.sum(w * (-r4(Hyy, Hxy)))
|
|
229
|
+
H[4] += np.sum(w * (c * c * r4(gyy, gyxy) + s * s * r4(gzy, gzxy)
|
|
230
|
+
- s * c * (r4(gzy, gzxy) + r4(gyy, gyxy))))
|
|
231
|
+
return np.vstack((V, H))
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def finalize(S, ori, ang, ag, legacy):
|
|
235
|
+
"""Rotation to the lab frame and orientational average for angle ag
|
|
236
|
+
(kept as one call for the tests; compute() rotates only once)."""
|
|
237
|
+
return combine(lab_amplitudes(S, ori), ori.w, ang.cs[ag], ang.sn[ag], legacy)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def compute(beta, petitk, grandk, theta_s, legacy):
|
|
241
|
+
"""HRS calculation for a single molecule.
|
|
242
|
+
beta is already the molecular tensor (meso = mol for identity orientation).
|
|
243
|
+
Returns acc[nang, 2, 5]."""
|
|
244
|
+
nang = len(theta_s)
|
|
245
|
+
ori = Orientations()
|
|
246
|
+
ang = AngleTable(theta_s, petitk, grandk)
|
|
247
|
+
acc = np.zeros((nang, 2, 5))
|
|
248
|
+
|
|
249
|
+
# Single dipole at origin with identity orientation: the phase factor is 1, so the
|
|
250
|
+
# lab-frame amplitudes are the same for every angle; only the H combination
|
|
251
|
+
# (cos Theta * Y - sin Theta * Z) depends on the angle.
|
|
252
|
+
S = np.zeros((ori.n, 54))
|
|
253
|
+
S[:, :27] = beta.reshape(27) # same tensor for all orientations
|
|
254
|
+
A = lab_amplitudes(S, ori)
|
|
255
|
+
next_print = 0
|
|
256
|
+
for ag in range(nang):
|
|
257
|
+
acc[ag] = combine(A, ori.w, ang.cs[ag], ang.sn[ag], legacy)
|
|
258
|
+
pct = int(100.0 * (ag + 1) / nang)
|
|
259
|
+
if pct >= next_print:
|
|
260
|
+
say("calculation in progress : %f pourcent " % pct)
|
|
261
|
+
next_print = pct + 5
|
|
262
|
+
say("calculation in progress : %f pourcent " % 100.0)
|
|
263
|
+
return acc
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# --------------------------------------------------------------------------
|
|
267
|
+
# Outputs (identical format to SHS.py)
|
|
268
|
+
# --------------------------------------------------------------------------
|
|
269
|
+
def write_header(f, b):
|
|
270
|
+
f.write("********************************************************\n")
|
|
271
|
+
f.write("**** Py_SHS: An open source software ****\n")
|
|
272
|
+
f.write("**** about Second Harmonic Scattering ****\n")
|
|
273
|
+
f.write("**** Dr Pierre-Marie GASSIN and Dr Gaelle GASSIN ****\n")
|
|
274
|
+
f.write("**** ICGM - ENSCM ****\n")
|
|
275
|
+
f.write("**** june 2021 ****\n")
|
|
276
|
+
f.write("********************************************************\n")
|
|
277
|
+
f.write(" \n")
|
|
278
|
+
f.write(" Don't forget to cite this work: \n")
|
|
279
|
+
f.write(" J. Chem. Inf. Model. 2020, 60, 12, pp 5912–5917 \n")
|
|
280
|
+
f.write(" \n")
|
|
281
|
+
f.write("********************************************************\n")
|
|
282
|
+
f.write("********************************************************\n")
|
|
283
|
+
f.write("****** SIMULATION RESULTS *******\n")
|
|
284
|
+
f.write("********************************************************\n")
|
|
285
|
+
f.write("****** SURFACE HYPERPOLARISABILITY TENSOR ******\n")
|
|
286
|
+
f.write("\n")
|
|
287
|
+
f.write(" xxx=%f xxy=%f xxz= %f \n" % (b[0, 0, 0], b[0, 0, 1], b[0, 0, 2]))
|
|
288
|
+
f.write(" xyx=%f xyy=%f xyz= %f \n" % (b[0, 1, 0], b[0, 1, 1], b[0, 1, 2]))
|
|
289
|
+
f.write(" xzx=%f xzy=%f xzz= %f \n" % (b[0, 2, 0], b[0, 2, 1], b[0, 2, 2]))
|
|
290
|
+
f.write("\n")
|
|
291
|
+
f.write(" yxx=%f yxy=%f yxz= %f \n" % (b[1, 0, 0], b[1, 0, 1], b[1, 0, 2]))
|
|
292
|
+
f.write(" yyx=%f yyy=%f yyz= %f \n" % (b[1, 1, 0], b[1, 1, 1], b[1, 1, 2]))
|
|
293
|
+
f.write(" yzx=%f yzy=%f yzz= %f \n" % (b[1, 2, 0], b[1, 2, 1], b[1, 2, 2]))
|
|
294
|
+
f.write("\n")
|
|
295
|
+
f.write(" zxx=%f zxy=%f zxz= %f \n" % (b[2, 0, 0], b[2, 0, 1], b[2, 0, 2]))
|
|
296
|
+
f.write(" zyx=%f zyy=%f zyz= %f \n" % (b[2, 1, 0], b[2, 1, 1], b[2, 1, 2]))
|
|
297
|
+
f.write(" zzx=%f zzy=%f zzz= %f \n" % (b[2, 2, 0], b[2, 2, 1], b[2, 2, 2]))
|
|
298
|
+
f.write("\n")
|
|
299
|
+
f.write("*******************************************************\n")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def report_polar(fout, deg, ndip, V, H):
|
|
303
|
+
"""Final report of polarplot_single / polarplot_integrate."""
|
|
304
|
+
av, bv, cv, bv1, bv2 = V[0], V[1], V[2], V[3], V[4]
|
|
305
|
+
ah, bh, ch, bh1, bh2 = H[0], H[1], H[2], H[3], H[4]
|
|
306
|
+
with np.errstate(all="ignore"):
|
|
307
|
+
I4v = (av - bv + cv) / (3 * av + bv + 3 * cv)
|
|
308
|
+
I2v = 4.0 * (av - cv) / (3 * av + bv + 3 * cv)
|
|
309
|
+
I4h = (ah - bh + ch) / (3 * ah + bh + 3 * ch)
|
|
310
|
+
I2h = 4.0 * (ah - ch) / (3 * ah + bh + 3 * ch)
|
|
311
|
+
|
|
312
|
+
say("")
|
|
313
|
+
say("**********calculation complete ************")
|
|
314
|
+
for name, val in (("av", av), ("bv", bv), ("bv1", bv1), ("bv2", bv2), ("cv", cv),
|
|
315
|
+
("ah", ah), ("bh", bh), ("bh1", bh1), ("bh2", bh2), ("ch", ch)):
|
|
316
|
+
say(("%s = " + FMT) % (name, val))
|
|
317
|
+
say("********************************************")
|
|
318
|
+
say("I2v = %f " % I2v)
|
|
319
|
+
say("I4v = %f " % I4v)
|
|
320
|
+
say("I2h = %f " % I2h)
|
|
321
|
+
say("I4h = %f " % I4h)
|
|
322
|
+
|
|
323
|
+
fout.write(" \n")
|
|
324
|
+
fout.write(" POLAR PLOT - CONFIGURATION AT %f ° \n" % deg)
|
|
325
|
+
fout.write(" \n")
|
|
326
|
+
fout.write("nombre de dipole = %d \n" % ndip)
|
|
327
|
+
fout.write("*************************\n")
|
|
328
|
+
fout.write("I2v=%f I4v=%f \n" % (I2v, I4v))
|
|
329
|
+
fout.write("I2h=%f I4h=%f \n" % (I2h, I4h))
|
|
330
|
+
fout.write("*************************\n")
|
|
331
|
+
fout.write("av=%f bv=%f bv1=%f bv2=%f cv= %f \n".replace("%f", FMT) % (av, bv, bv1, bv2, cv))
|
|
332
|
+
fout.write("ah=%f bh=%f bh1=%f bh2=%f ch= %f \n".replace("%f", FMT) % (ah, bh, bh1, bh2, ch))
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
fp = open(shs_io.path("out_plot"), "w", encoding="utf-8")
|
|
336
|
+
except OSError:
|
|
337
|
+
say("impossible d'ouvrir le fichier pour ecrire gnuplot!", end="")
|
|
338
|
+
return
|
|
339
|
+
with fp:
|
|
340
|
+
fp.write("gamma IV(gamma) IH(gamma)\n")
|
|
341
|
+
g = 2.0 * PI * np.arange(100) / 100
|
|
342
|
+
c, s = np.cos(g), np.sin(g)
|
|
343
|
+
Iv = av * c**4 + bv * c**2 * s**2 + cv * s**4 + bv1 * c**3 * s + bv2 * c * s**3
|
|
344
|
+
Ih = ah * c**4 + bh * c**2 * s**2 + ch * s**4 + bh1 * c**3 * s + bh2 * c * s**3
|
|
345
|
+
for gg, a, b in zip(g, Iv, Ih):
|
|
346
|
+
fp.write(("%f " + FMT + " " + FMT + "\n") % (gg, a, b))
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
# --------------------------------------------------------------------------
|
|
350
|
+
def main(argv):
|
|
351
|
+
legacy = os.environ.get("PYSHS_LEGACY", "") not in ("", "0")
|
|
352
|
+
if "--legacy" in argv:
|
|
353
|
+
legacy = True
|
|
354
|
+
argv = [a for a in argv if a != "--legacy"]
|
|
355
|
+
if legacy:
|
|
356
|
+
print("NOTE: legacy mode (old C quirks) only affects bv1, bv2, bh1, bh2, which are "
|
|
357
|
+
"zero after the isotropic average; av, bv, cv, ah, bh, ch are unchanged.")
|
|
358
|
+
try:
|
|
359
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
360
|
+
except AttributeError:
|
|
361
|
+
pass
|
|
362
|
+
|
|
363
|
+
say("********************************************************")
|
|
364
|
+
say("**** Py_SHS: An open source software ****")
|
|
365
|
+
say("**** about Second Harmonic Scattering ****")
|
|
366
|
+
say("**** Dr Pierre-Marie GASSIN and Dr Gaelle GASSIN ****")
|
|
367
|
+
say("**** ICGM - ENSCM ****")
|
|
368
|
+
say("**** june 2021 ****")
|
|
369
|
+
say("********************************************************")
|
|
370
|
+
say(" ")
|
|
371
|
+
say(" Don't forget to cite this work: ")
|
|
372
|
+
say(" J. Chem. Inf. Model. 2020, 60, 12, pp 5912–5917 ")
|
|
373
|
+
say(" ")
|
|
374
|
+
say("********************************************************")
|
|
375
|
+
say("********************************************************")
|
|
376
|
+
say("******** Enter the input parameters **********")
|
|
377
|
+
say("********************************************************")
|
|
378
|
+
|
|
379
|
+
# HRS: 3 arguments (keyword, beta_file, output_file) — no orientation file
|
|
380
|
+
if len(argv) != 4:
|
|
381
|
+
say("Computation failed: ")
|
|
382
|
+
say("3 arguments expected : the keyword of the calculation, "
|
|
383
|
+
"the beta inputfile and the outputfile. ")
|
|
384
|
+
say("For more details see the documentation ")
|
|
385
|
+
return 1
|
|
386
|
+
|
|
387
|
+
keyword = argv[1]
|
|
388
|
+
mode_single = keyword == "polarplot_single"
|
|
389
|
+
mode_integ = keyword == "polarplot_integrate"
|
|
390
|
+
mode_angle = keyword == "angle_scattering"
|
|
391
|
+
if not (mode_single or mode_integ or mode_angle):
|
|
392
|
+
say("--------------------- ----------------------------------------------------")
|
|
393
|
+
say(" ERROR in the name of the keyword ")
|
|
394
|
+
say(" USE ONLY polarplot_single OR polarplot_integrate OR angle_scattering !! ")
|
|
395
|
+
say(" ! no calculation has been done ! Retry with the correct keyword ")
|
|
396
|
+
say("--------------------------------------------------------------------------")
|
|
397
|
+
return 1
|
|
398
|
+
|
|
399
|
+
rd = TokenReader()
|
|
400
|
+
try:
|
|
401
|
+
# HRS: no "Number of dipoles" — always N = 1
|
|
402
|
+
ndip = 1
|
|
403
|
+
say("HRS mode: single dipole at origin (N = 1)")
|
|
404
|
+
say("Enter the wavelength in nm? ", end="")
|
|
405
|
+
lam = rd.real()
|
|
406
|
+
say("The wavelength is: %f " % lam)
|
|
407
|
+
say("Enter the real part of the refractive index? ", end="")
|
|
408
|
+
nr = rd.real()
|
|
409
|
+
say("Enter the imaginary part of the refractive index? ", end="")
|
|
410
|
+
ni = rd.real()
|
|
411
|
+
except (EOFError, ValueError):
|
|
412
|
+
return 1
|
|
413
|
+
|
|
414
|
+
n = complex(nr, ni)
|
|
415
|
+
say("The refractive index is: %f + I*%f " % (n.real, n.imag))
|
|
416
|
+
say("The input beta file is %s and the output file is %s "
|
|
417
|
+
% (argv[2], argv[3]))
|
|
418
|
+
grandk = 4.0 * PI * n / lam
|
|
419
|
+
petitk = 2.0 * PI * n / lam
|
|
420
|
+
|
|
421
|
+
beta = read_beta(argv[2])
|
|
422
|
+
if beta is None:
|
|
423
|
+
say("Problem to read the input beta file \n!")
|
|
424
|
+
return 1
|
|
425
|
+
|
|
426
|
+
shs_io.set_output(argv[3])
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
fout = open(argv[3], "w", encoding="utf-8")
|
|
430
|
+
except OSError:
|
|
431
|
+
say("problem to open the output file to write results!")
|
|
432
|
+
return 1
|
|
433
|
+
|
|
434
|
+
unit = shs_units.settings(grandk, "raw")
|
|
435
|
+
for _l in unit.lines():
|
|
436
|
+
say(_l)
|
|
437
|
+
write_header(fout, beta)
|
|
438
|
+
shs_units.write_header_lines(fout, unit)
|
|
439
|
+
|
|
440
|
+
# ---------- angles ----------
|
|
441
|
+
t1 = t2 = 0.0
|
|
442
|
+
nang = 1
|
|
443
|
+
try:
|
|
444
|
+
if mode_single:
|
|
445
|
+
say("Enter the scattered angle in degree (0°=transmission) ? ", end="")
|
|
446
|
+
t1 = rd.real()
|
|
447
|
+
say(" ****************************** ")
|
|
448
|
+
say("********** Begin calculation polar plot at %f ° ********" % t1)
|
|
449
|
+
elif mode_integ:
|
|
450
|
+
say("Enter the beginning scattered angle in degree (0°=transmission) ? ", end="")
|
|
451
|
+
t1 = rd.real()
|
|
452
|
+
say(" ****************************** ")
|
|
453
|
+
say("Enter the final scattered angle in degree (0°=transmission) ? ", end="")
|
|
454
|
+
t2 = rd.real()
|
|
455
|
+
say(" ****************************** ")
|
|
456
|
+
say(" Number of integrale points ? ", end="")
|
|
457
|
+
nang = rd.integer()
|
|
458
|
+
say(" ****************************** ")
|
|
459
|
+
if nang < 1:
|
|
460
|
+
return 1
|
|
461
|
+
else:
|
|
462
|
+
fout.write(" \n")
|
|
463
|
+
fout.write("* HRS angular distribution \n")
|
|
464
|
+
fout.write(" \n")
|
|
465
|
+
fout.write("nombre de dipole = %d \n" % ndip)
|
|
466
|
+
fout.write(" ****************OUTPUT****************************************\n")
|
|
467
|
+
fout.write(" grandtheta IPSS-IH(0°) IPPP-IH(90°) ISSS-IV(0°) ISPP-IV(90°) \n")
|
|
468
|
+
say("Enter the number of points between 0 and 180° for the angular HRS analysis ?", end="")
|
|
469
|
+
pas2 = rd.integer()
|
|
470
|
+
if pas2 < 1:
|
|
471
|
+
return 1
|
|
472
|
+
nang = shs_angles.count(pas2)
|
|
473
|
+
say("********** Begin HRS Angular Distribution calculation**************")
|
|
474
|
+
except (EOFError, ValueError):
|
|
475
|
+
return 1
|
|
476
|
+
|
|
477
|
+
ia = np.arange(nang)
|
|
478
|
+
if mode_angle:
|
|
479
|
+
th = shs_angles.scattering_angles(pas2)
|
|
480
|
+
elif mode_integ:
|
|
481
|
+
th = (np.full(nang, t1) if nang == 1
|
|
482
|
+
else t1 + ia * (t2 - t1) / (nang - 1)) * 2.0 * PI / 360.0
|
|
483
|
+
else:
|
|
484
|
+
th = np.full(1, t1 * 2.0 * PI / 360.0)
|
|
485
|
+
|
|
486
|
+
acc = compute(beta, petitk, grandk, th, legacy)
|
|
487
|
+
|
|
488
|
+
norm = unit.multiplier / (8.0 * PI * PI) # units: see shs_units.py
|
|
489
|
+
global FMT
|
|
490
|
+
if unit.multiplier != 1.0:
|
|
491
|
+
FMT = "%.6e"
|
|
492
|
+
if mode_angle:
|
|
493
|
+
with open(shs_io.path("out_plot"), "w", encoding="utf-8") as fgr:
|
|
494
|
+
fgr.write(" grandtheta IH(0°) IH(90°) IV(0°) IV(90°) \n")
|
|
495
|
+
for k in range(nang):
|
|
496
|
+
deg = th[k] * 360.0 / (2.0 * PI)
|
|
497
|
+
IH_0, IH_90 = acc[k, 1, 0] * norm, acc[k, 1, 2] * norm
|
|
498
|
+
IV_0, IV_90 = acc[k, 0, 0] * norm, acc[k, 0, 2] * norm
|
|
499
|
+
say("angle value: %f " % deg)
|
|
500
|
+
say("IH_0=%f IH_90=%f IV_0=%f IV_90=%f ".replace("%f", FMT)
|
|
501
|
+
% (IH_0, IH_90, IV_0, IV_90))
|
|
502
|
+
row = (" %f %f %f %f %f \n".replace("%f", FMT).replace(FMT, "%f", 1)) % (deg, IH_0, IH_90, IV_0, IV_90)
|
|
503
|
+
fout.write(row)
|
|
504
|
+
fgr.write(row)
|
|
505
|
+
say("calculation finished ")
|
|
506
|
+
else:
|
|
507
|
+
tot = acc.sum(axis=0) # (2, 5)
|
|
508
|
+
f = norm / nang
|
|
509
|
+
if legacy:
|
|
510
|
+
tot[:, :3] *= f
|
|
511
|
+
tot[:, 3:] *= unit.multiplier / nang
|
|
512
|
+
else:
|
|
513
|
+
tot *= f
|
|
514
|
+
report_polar(fout, th[nang - 1] * 360.0 / (2.0 * PI), ndip,
|
|
515
|
+
tot[0], tot[1])
|
|
516
|
+
fout.close()
|
|
517
|
+
return 0
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
if __name__ == "__main__":
|
|
521
|
+
sys.exit(main(sys.argv))
|