wavepacket 0.4.0__tar.gz → 0.4.1__tar.gz

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.
Files changed (107) hide show
  1. {wavepacket-0.4.0 → wavepacket-0.4.1}/CHANGELOG.rst +22 -0
  2. {wavepacket-0.4.0 → wavepacket-0.4.1}/LICENSE +1 -1
  3. {wavepacket-0.4.0 → wavepacket-0.4.1}/PKG-INFO +1 -1
  4. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/conf.py +1 -1
  5. {wavepacket-0.4.0 → wavepacket-0.4.1}/pyproject.toml +1 -1
  6. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/__init__.py +1 -1
  7. wavepacket-0.4.1/src/wavepacket/logging.py +54 -0
  8. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/plot/plot_1d.py +1 -1
  9. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/special/pulse_shapes.py +1 -1
  10. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/state_utilities.py +3 -1
  11. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/special/test_pulse_shapes.py +2 -2
  12. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/test_state_utilities.py +8 -2
  13. wavepacket-0.4.1/test_acceptance/readme +7 -0
  14. wavepacket-0.4.1/test_acceptance/test_1d_absorbing_boundaries.txt +75 -0
  15. wavepacket-0.4.1/test_acceptance/test_2d_harmonic_oscillator.txt +65 -0
  16. wavepacket-0.4.1/test_acceptance/test_laser_pulse_with_density_operator.txt +161 -0
  17. wavepacket-0.4.1/test_acceptance/test_relaxation.txt +37 -0
  18. wavepacket-0.4.1/test_acceptance/test_tise.txt +49 -0
  19. wavepacket-0.4.0/src/wavepacket/logging.py +0 -39
  20. {wavepacket-0.4.0 → wavepacket-0.4.1}/README.rst +0 -0
  21. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/Makefile +0 -0
  22. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/advanced/ode_solvers.md +0 -0
  23. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/advanced/pendular_states.md +0 -0
  24. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/advanced/plane_wave_grid.md +0 -0
  25. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/advanced/polynomial_solvers.md +0 -0
  26. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/advanced/thermal_states.md +0 -0
  27. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/architecture.rst +0 -0
  28. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/index.rst +0 -0
  29. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/license.rst +0 -0
  30. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/license.txt +0 -0
  31. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/make.bat +0 -0
  32. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/representations.rst +0 -0
  33. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/requirements.txt +0 -0
  34. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/tutorials/chebychev_solvers.md +0 -0
  35. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/tutorials/eigenstates.md +0 -0
  36. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/tutorials/plotting.md +0 -0
  37. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/tutorials/relaxation.md +0 -0
  38. {wavepacket-0.4.0 → wavepacket-0.4.1}/doc/tutorials/schroedinger_cat.md +0 -0
  39. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/builder/__init__.py +0 -0
  40. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/builder/density.py +0 -0
  41. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/builder/wave_function.py +0 -0
  42. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/exceptions.py +0 -0
  43. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/expression/__init__.py +0 -0
  44. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/expression/expressionbase.py +0 -0
  45. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/expression/liouvillian.py +0 -0
  46. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/expression/schroedingerequation.py +0 -0
  47. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/__init__.py +0 -0
  48. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/_utils.py +0 -0
  49. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/dofbase.py +0 -0
  50. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/grid.py +0 -0
  51. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/planewavedof.py +0 -0
  52. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/spherical_harmonics_dof.py +0 -0
  53. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/grid/state.py +0 -0
  54. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/__init__.py +0 -0
  55. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/_clipping.py +0 -0
  56. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/fbroperators.py +0 -0
  57. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/misc_operators.py +0 -0
  58. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/operatorbase.py +0 -0
  59. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/potentials.py +0 -0
  60. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator/time_dependent_operators.py +0 -0
  61. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/operator_utils.py +0 -0
  62. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/plot/__init__.py +0 -0
  63. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/plot/_utilities.py +0 -0
  64. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/plot/plot_2d.py +0 -0
  65. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/py.typed +0 -0
  66. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/solver/__init__.py +0 -0
  67. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/solver/chebychev.py +0 -0
  68. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/solver/odesolver.py +0 -0
  69. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/solver/solverbase.py +0 -0
  70. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/special/__init__.py +0 -0
  71. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/special/generators.py +0 -0
  72. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/testing/__init__.py +0 -0
  73. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/testing/assertions.py +0 -0
  74. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/testing/dummydof.py +0 -0
  75. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/testing/dummyoperator.py +0 -0
  76. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/testing/random.py +0 -0
  77. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/typing/__init__.py +0 -0
  78. {wavepacket-0.4.0 → wavepacket-0.4.1}/src/wavepacket/typing/data_types.py +0 -0
  79. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/builder/test_density.py +0 -0
  80. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/builder/test_product_wave_function.py +0 -0
  81. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/builder/test_wave_function.py +0 -0
  82. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/conftest.py +0 -0
  83. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/expression/test_expression_sum.py +0 -0
  84. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/expression/test_liouvillian.py +0 -0
  85. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/expression/test_schroedingerequation.py +0 -0
  86. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_dofbase.py +0 -0
  87. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_grid.py +0 -0
  88. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_planewavedof_basics.py +0 -0
  89. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_planewavedof_transformations.py +0 -0
  90. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_sphericalharmonicsdof_basics.py +0 -0
  91. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_sphericalharmonicsdof_transformations.py +0 -0
  92. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/grid/test_state.py +0 -0
  93. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_clipping.py +0 -0
  94. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_fbroperators.py +0 -0
  95. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_misc_operators.py +0 -0
  96. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_operator_arithmetic.py +0 -0
  97. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_operatorbase.py +0 -0
  98. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_planewavefbroperator.py +0 -0
  99. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_potential1d.py +0 -0
  100. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/operator/test_time_dependent_operators.py +0 -0
  101. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/solver/test_chebychev.py +0 -0
  102. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/solver/test_odesolver.py +0 -0
  103. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/solver/test_solverbase.py +0 -0
  104. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/special/test_generators.py +0 -0
  105. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/test_base_exports.py +0 -0
  106. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/test_logging.py +0 -0
  107. {wavepacket-0.4.0 → wavepacket-0.4.1}/test/test_operator_utilities.py +0 -0
@@ -2,6 +2,28 @@
2
2
  ChangeLog
3
3
  =========
4
4
 
5
+ 0.4.1
6
+ -----
7
+
8
+ Minor changes:
9
+
10
+ - (#56) Added acceptance / regression tests, also for CI
11
+
12
+ Bug fixes:
13
+
14
+ - The SinSquare function had an incorrect implementation without the "square" part
15
+
16
+ - population() could return complex values for density operators.
17
+ The imaginary part was negligible, but the type was still wrong.
18
+ The return values are also no longer (negligible) negative values.
19
+
20
+ - log() calculated <x> only for the first degree of freedom,
21
+ and reported it for all other degrees of freedom as well.
22
+ Also, fixed a typo in the output.
23
+
24
+ - Stacked1DPlot threw an exception if you exceeded the number of plots.
25
+ It now overwrites the last plot as it is documented.
26
+
5
27
  0.4
6
28
  ---
7
29
 
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024-2025 Ulf Lorenz
3
+ Copyright (c) 2024-2026 Ulf Lorenz
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavepacket
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: A package for solving Schrödinger and Liouville von Neumann equations.
5
5
  Keywords: wave packet,quantum simulation,quantum system,DVR,Schroedinger equation,Liouville von Neumann equation,open systems,wave function,density operator,Liouvillian
6
6
  Author: Ulf Lorenz
@@ -9,7 +9,7 @@
9
9
  project = "Wavepacket"
10
10
  copyright = "2024-%Y, Ulf Lorenz"
11
11
  author = "Ulf Lorenz"
12
- release = "0.4"
12
+ release = "0.4.1"
13
13
 
14
14
  # -- General configuration ---------------------------------------------------
15
15
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -98,7 +98,7 @@ env_list = ["py311", "py312", "py313", "py314", "py311-min", "py312-min", "py313
98
98
  description = "Run unit tests under {python_base}; Standard environments like py313 implicitly inherit this."
99
99
  set_env = { "PIP_ONLY_BINARY" = ":all:" }
100
100
  dependency_groups = ["test"]
101
- commands = [["pytest", "test"]]
101
+ commands = [["pytest", "test", "test_acceptance"]]
102
102
 
103
103
  [tool.tox.env.py311-min]
104
104
  dependency_groups = ["minimum_dependencies", "test"]
@@ -2,7 +2,7 @@
2
2
  A package for solving Schrödinger and Liouville von Neumann equations.
3
3
  """
4
4
 
5
- __version__ = "0.4.0"
5
+ __version__ = "0.4.1"
6
6
 
7
7
  __all__ = [
8
8
  "__version__",
@@ -0,0 +1,54 @@
1
+ import math
2
+
3
+ import wavepacket as wp
4
+
5
+
6
+ def _truncate(value: float, truncation: float | None) -> float:
7
+ if truncation is None or abs(value) >= truncation:
8
+ return value
9
+ else:
10
+ return 0.0
11
+
12
+
13
+ def log(
14
+ t: float, state: wp.grid.State, precision: int = 6, truncate: float | None = None
15
+ ) -> None:
16
+ """
17
+ Prints some data about the state for inspection.
18
+
19
+ The idea is that you call this function during every solver step and get
20
+ a log with the most important values about the propagation, for example
21
+ the state trace (if it deviates from one, this may be caused by poor convergence).
22
+
23
+ Parameters
24
+ ----------
25
+ t : float
26
+ The time at which you log.
27
+ state : wp.grid.State
28
+ The state to log.
29
+ precision : int, default=6
30
+ How many decimal places should be printed.
31
+ truncate: float | None, default=None
32
+ If set, set all calculated values smaller than this boundary to zero.
33
+ The main use case is for tests; values that are approximately zero can
34
+ differ in their actual value based on numpy version etc. This causes
35
+ regression tests to fail for uninteresting reasons.
36
+ """
37
+ print(
38
+ f"\n-----------------------------------------------\n"
39
+ f"t = {float(t):.{precision}}, trace = {wp.trace(state):.{precision}}\n"
40
+ )
41
+
42
+ normalized_state = wp.normalize(state)
43
+ for index, dof in enumerate(state.grid.dofs):
44
+ x = wp.operator.Potential1D(state.grid, index, lambda dvr_grid: dvr_grid)
45
+
46
+ x_val = wp.expectation_value(x, normalized_state).real
47
+ x2_val = wp.expectation_value(x * x, normalized_state).real
48
+
49
+ x_avg = _truncate(x_val, truncate)
50
+ dx = _truncate(math.sqrt(x2_val - x_avg**2), truncate)
51
+
52
+ # In exotic cases, the error dx**2 can become negative, so we trade
53
+ # correctness for robustness here by taking its absolute value.
54
+ print(f"<x_{index}> = {x_avg:.{precision}} +/- {dx:.{precision}}")
@@ -227,7 +227,7 @@ class StackedPlot1D(BasePlot1D):
227
227
 
228
228
  def plot(self, t: float, state: wp.grid.State) -> plt.Axes:
229
229
  axes: plt.Axes = self._axes.flat[self._index]
230
- self._index = min(self._index + 1, self._axes.size)
230
+ self._index = min(self._index + 1, self._axes.size - 1)
231
231
 
232
232
  super()._plot(axes, t, state)
233
233
 
@@ -36,7 +36,7 @@ class SinSquare:
36
36
  def __call__(self, t: float) -> float:
37
37
  dt = abs(t - self._t0)
38
38
  if dt < self._half_width:
39
- return math.cos(self._scale * dt)
39
+ return math.cos(self._scale * dt) ** 2
40
40
  else:
41
41
  return 0.0
42
42
 
@@ -324,6 +324,8 @@ def population(state: wp.grid.State, target: wp.grid.State) -> float:
324
324
  matrix_form = np.reshape(state.data, [state.grid.size, state.grid.size])
325
325
  flat_target = np.ravel(target.data)
326
326
  left_summation = np.tensordot(np.conj(flat_target), matrix_form, axes=(0, 0))
327
- return np.tensordot(left_summation, flat_target, axes=(0, 0)).sum() / target_trace
327
+ return np.abs(
328
+ np.tensordot(left_summation, flat_target, axes=(0, 0)).sum() / target_trace
329
+ )
328
330
  else:
329
331
  raise wp.BadStateError("Input is not a valid state.")
@@ -18,9 +18,9 @@ def test_sin_square_values():
18
18
  functor = wp.special.SinSquare(5.0, 2.0)
19
19
 
20
20
  assert functor(2.9) == 0
21
- assert_allclose(functor(4), math.sqrt(0.5), rtol=0, atol=1e-12)
21
+ assert_allclose(functor(4), 0.5, rtol=0, atol=1e-12)
22
22
  assert functor(5) == 1
23
- assert_allclose(functor(6), math.sqrt(0.5), rtol=0, atol=1e-12)
23
+ assert_allclose(functor(6), 0.5, rtol=0, atol=1e-12)
24
24
  assert functor(7.1) == 0
25
25
 
26
26
 
@@ -52,6 +52,7 @@ def test_wave_function_dvr_density():
52
52
  dx = 1 / 3.0 * 2 / 5.0
53
53
  expected = np.abs(psi.data**2) / dx
54
54
  assert_allclose(result, expected, atol=1e-14, rtol=0)
55
+ assert_allclose(np.imag(result), 0, atol=0, rtol=0)
55
56
 
56
57
 
57
58
  def test_density_operator_dvr_density(grid_2d):
@@ -62,6 +63,7 @@ def test_density_operator_dvr_density(grid_2d):
62
63
  density_from_rho = wp.dvr_density(rho)
63
64
 
64
65
  assert_allclose(density_from_rho, density_from_psi, atol=1e-14, rtol=0)
66
+ assert_allclose(np.imag(density_from_rho), 0, atol=0, rtol=0)
65
67
 
66
68
 
67
69
  def test_wave_function_fbr_density():
@@ -76,6 +78,7 @@ def test_wave_function_fbr_density():
76
78
  expected = np.zeros(grid.shape)
77
79
  expected[5 - 2, 7 - 5] = 4
78
80
  assert_allclose(expected, density, rtol=0, atol=1e-12)
81
+ assert_allclose(0, np.imag(density), rtol=0, atol=0)
79
82
 
80
83
 
81
84
  def test_density_operator_fbr_density(grid_2d):
@@ -86,6 +89,7 @@ def test_density_operator_fbr_density(grid_2d):
86
89
  density_from_rho = wp.fbr_density(rho)
87
90
 
88
91
  assert_allclose(density_from_psi, density_from_rho, rtol=0, atol=1e-12)
92
+ assert_allclose(0, np.imag(density_from_rho), rtol=0, atol=0)
89
93
 
90
94
 
91
95
  def test_reduced_dvr_and_fbr_density(grid_2d):
@@ -136,6 +140,7 @@ def test_trace():
136
140
  rho = wp.builder.pure_density(psi)
137
141
  result_rho = wp.trace(rho)
138
142
  assert_allclose(result_rho, result, atol=1e-12, rtol=0)
143
+ assert_allclose(result_rho.imag, 0, atol=0, rtol=0)
139
144
 
140
145
 
141
146
  def test_normalize(grid_1d):
@@ -236,7 +241,7 @@ def test_population(grid_2d):
236
241
  a, target = (states[0], states[1])
237
242
 
238
243
  # create a state that triggers a complex scalar product to check for abs()
239
- psi = 2 * a + 0.7 * target * np.sqrt(1j)
244
+ psi = 2j * a + 0.7 * target * np.sqrt(1j)
240
245
  rho = wp.builder.pure_density(psi)
241
246
  expected = 0.7**2
242
247
 
@@ -245,13 +250,14 @@ def test_population(grid_2d):
245
250
 
246
251
  rho_projection = wp.population(rho, target)
247
252
  assert_allclose(rho_projection, expected, rtol=0, atol=1e-12)
253
+ assert_allclose(rho_projection.imag, 0, rtol=0, atol=0)
248
254
 
249
255
 
250
256
  def test_normalize_population_target(grid_2d):
251
257
  target = wp.testing.random_state(grid_2d, 1)
252
258
  target /= math.sqrt(wp.trace(target))
253
259
 
254
- psi = 0.7 * target * np.sqrt(1j)
260
+ psi = 0.7j * target * np.sqrt(1j)
255
261
  rho = wp.builder.pure_density(psi)
256
262
  expected = 0.7**2
257
263
 
@@ -0,0 +1,7 @@
1
+ The tests here verify the correctness on a higher level than the unit tests in test/.
2
+ Thy are meant to catch issues in code that is difficult to unit-test (e.g., logging output),
3
+ and to catch invalid unit tests.
4
+ Because the tests are full-fledged simulations and sometimes do unusual things,
5
+ they can also serve as additional examples, but that is not the primary focus here.
6
+
7
+ Because results are not meant to change at all, most if not all tests are regression tests.
@@ -0,0 +1,75 @@
1
+ >>> import numpy as np
2
+ >>> import wavepacket as wp
3
+ >>>
4
+ >>> dof = wp.grid.PlaneWaveDof(-10, 20, 96)
5
+ >>> grid = wp.grid.Grid(dof)
6
+ >>>
7
+ >>> def absorbing_boundary(x):
8
+ ... potential = -10j * (x - 12.0) ** 2 / (20 - 12.0) ** 2
9
+ ... potential[x < 12] = 0
10
+ ... return potential
11
+ >>>
12
+ >>> kinetic = wp.operator.CartesianKineticEnergy(grid, 0, mass=1)
13
+ >>> absorber = wp.operator.Potential1D(grid, 0, absorbing_boundary)
14
+ >>> equation = wp.expression.SchroedingerEquation(kinetic + absorber)
15
+ >>>
16
+ >>> psi0 = wp.builder.product_wave_function(grid, wp.special.Gaussian(-5, 2, rms=1))
17
+ >>> solver = wp.solver.OdeSolver(equation, 1)
18
+ >>>
19
+ >>> for t, psi in solver.propagate(psi0, 0.0, 10):
20
+ ... wp.log(t, psi, precision=4, truncate=1e-10)
21
+ <BLANKLINE>
22
+ -----------------------------------------------
23
+ t = 0.0, trace = 1.0
24
+ <BLANKLINE>
25
+ <x_0> = -5.0 +/- 0.7071
26
+ <BLANKLINE>
27
+ -----------------------------------------------
28
+ t = 1.0, trace = 1.0
29
+ <BLANKLINE>
30
+ <x_0> = -3.0 +/- 1.0
31
+ <BLANKLINE>
32
+ -----------------------------------------------
33
+ t = 2.0, trace = 1.0
34
+ <BLANKLINE>
35
+ <x_0> = -1.0 +/- 1.581
36
+ <BLANKLINE>
37
+ -----------------------------------------------
38
+ t = 3.0, trace = 1.0
39
+ <BLANKLINE>
40
+ <x_0> = 1.0 +/- 2.236
41
+ <BLANKLINE>
42
+ -----------------------------------------------
43
+ t = 4.0, trace = 0.9999
44
+ <BLANKLINE>
45
+ <x_0> = 2.999 +/- 2.914
46
+ <BLANKLINE>
47
+ -----------------------------------------------
48
+ t = 5.0, trace = 0.996
49
+ <BLANKLINE>
50
+ <x_0> = 4.959 +/- 3.552
51
+ <BLANKLINE>
52
+ -----------------------------------------------
53
+ t = 6.0, trace = 0.9615
54
+ <BLANKLINE>
55
+ <x_0> = 6.637 +/- 3.959
56
+ <BLANKLINE>
57
+ -----------------------------------------------
58
+ t = 7.0, trace = 0.8678
59
+ <BLANKLINE>
60
+ <x_0> = 7.791 +/- 4.111
61
+ <BLANKLINE>
62
+ -----------------------------------------------
63
+ t = 8.0, trace = 0.7319
64
+ <BLANKLINE>
65
+ <x_0> = 8.47 +/- 4.154
66
+ <BLANKLINE>
67
+ -----------------------------------------------
68
+ t = 9.0, trace = 0.5905
69
+ <BLANKLINE>
70
+ <x_0> = 8.83 +/- 4.18
71
+ <BLANKLINE>
72
+ -----------------------------------------------
73
+ t = 10.0, trace = 0.4666
74
+ <BLANKLINE>
75
+ <x_0> = 8.994 +/- 4.221
@@ -0,0 +1,65 @@
1
+ >>> import math
2
+ >>> import wavepacket as wp
3
+ >>>
4
+ >>> dof = wp.grid.PlaneWaveDof(-10, 10, 96)
5
+ >>> grid = wp.grid.Grid([dof, dof])
6
+ >>>
7
+ >>> kin_args = {"mass": 1, "cutoff": 40}
8
+ >>> pot_args = {"generator": lambda x: 0.5 * x**2, "cutoff": 40}
9
+ >>> T_0 = wp.operator.CartesianKineticEnergy(grid, 0, **kin_args)
10
+ >>> T_1 = wp.operator.CartesianKineticEnergy(grid, 1, **kin_args)
11
+ >>> V_0 = wp.operator.Potential1D(grid, 0, **pot_args)
12
+ >>> V_1 = wp.operator.Potential1D(grid, 1, **pot_args)
13
+ >>> hamiltonian = T_0 + T_1 + V_0 + V_1
14
+ >>> equation = wp.expression.SchroedingerEquation(hamiltonian)
15
+ >>>
16
+ >>> psi0 = wp.builder.product_wave_function(
17
+ ... grid, [wp.special.Gaussian(-5, 0, rms=1), wp.special.Gaussian(0, 5, rms=1)]
18
+ ... )
19
+ >>> solver = wp.solver.ChebychevSolver(equation, math.pi / 3, (0, 160))
20
+ >>>
21
+ >>> for t, psi in solver.propagate(psi0, 0.0, 6):
22
+ ... wp.log(t, psi, precision=4, truncate=1e-10)
23
+ <BLANKLINE>
24
+ -----------------------------------------------
25
+ t = 0.0, trace = 1.0
26
+ <BLANKLINE>
27
+ <x_0> = -5.0 +/- 0.7071
28
+ <x_1> = 0.0 +/- 0.7071
29
+ <BLANKLINE>
30
+ -----------------------------------------------
31
+ t = 1.047, trace = 1.0
32
+ <BLANKLINE>
33
+ <x_0> = -2.5 +/- 0.7071
34
+ <x_1> = 4.33 +/- 0.7071
35
+ <BLANKLINE>
36
+ -----------------------------------------------
37
+ t = 2.094, trace = 1.0
38
+ <BLANKLINE>
39
+ <x_0> = 2.5 +/- 0.7071
40
+ <x_1> = 4.33 +/- 0.7071
41
+ <BLANKLINE>
42
+ -----------------------------------------------
43
+ t = 3.142, trace = 1.0
44
+ <BLANKLINE>
45
+ <x_0> = 5.0 +/- 0.7071
46
+ <x_1> = 1.077e-07 +/- 0.7071
47
+ <BLANKLINE>
48
+ -----------------------------------------------
49
+ t = 4.189, trace = 1.0
50
+ <BLANKLINE>
51
+ <x_0> = 2.5 +/- 0.7071
52
+ <x_1> = -4.33 +/- 0.7071
53
+ <BLANKLINE>
54
+ -----------------------------------------------
55
+ t = 5.236, trace = 1.0
56
+ <BLANKLINE>
57
+ <x_0> = -2.5 +/- 0.7071
58
+ <x_1> = -4.33 +/- 0.7071
59
+ <BLANKLINE>
60
+ -----------------------------------------------
61
+ t = 6.283, trace = 1.0
62
+ <BLANKLINE>
63
+ <x_0> = -5.0 +/- 0.7071
64
+ <x_1> = -2.187e-07 +/- 0.7071
65
+
@@ -0,0 +1,161 @@
1
+ Note:
2
+
3
+ * It is not required to use a density operator here; in fact, doing so
4
+ slows down the calculation by a factor of ~10; however, we have no
5
+ other complex test of density operators right now.
6
+ * For a proper calculation, the time evolution should have 20 steps,
7
+ which is the length of the laser pulse. We truncate because
8
+ using a density operator is rather expensive.
9
+
10
+ >>> import numpy as np
11
+ >>> import wavepacket as wp
12
+ >>>
13
+ >>> unit_amu = 1e-3 / 9.10938215E-31 / 6.0221408E23
14
+ >>> unit_angstroem = 1.88973
15
+ >>> unit_debye = 1e10 / 1.60217662E-19 * 1e-21 / 299792458
16
+ >>> unit_mv_per_cm = 0.000194469
17
+ >>> unit_fs = 41.3414
18
+ >>> unit_inv_cm = 4.55634e-06
19
+ >>>
20
+ >>> def morse_potential(x):
21
+ ... return 0.1994 * (1 - np.exp(-1.189 * (x - 1.821))) ** 2
22
+ >>>
23
+ >>> def mecke_dipole(x):
24
+ ... q0 = 7.85 * unit_debye
25
+ ... r0 = 0.6 * unit_angstroem
26
+ ... return q0 * x * np.exp(-x / r0)
27
+ >>>
28
+ >>> mass_H1 = 1.0078250 * unit_amu
29
+ >>> mass_O16 = 15.994915 * unit_amu
30
+ >>> mu = mass_H1 * mass_O16 / (mass_H1 + mass_O16)
31
+ >>>
32
+ >>> dof = wp.grid.PlaneWaveDof(0.7, 8, 64)
33
+ >>> grid = wp.grid.Grid(dof)
34
+ >>>
35
+ >>> kinetic = wp.operator.CartesianKineticEnergy(grid, 0, mass=mu, cutoff=0.3)
36
+ >>> potential = wp.operator.Potential1D(grid, 0, morse_potential, cutoff=0.3)
37
+ >>> dipole = wp.operator.Potential1D(grid, 0, mecke_dipole)
38
+ >>> laser = wp.operator.LaserField(grid,
39
+ ... max_field=328.5 * unit_mv_per_cm,
40
+ ... shape=wp.special.SinSquare(500 * unit_fs, 500 * unit_fs),
41
+ ... omega=3424.19 * unit_inv_cm)
42
+ >>> equation = wp.expression.CommutatorLiouvillian(kinetic + potential - dipole * laser)
43
+ >>>
44
+ >>> bound_states = [state for energy, state in wp.diagonalize(kinetic + potential) if energy < 0.1994]
45
+ >>>
46
+ >>> psi_0 = wp.builder.product_wave_function(grid, lambda x: bound_states[0].data)
47
+ >>> rho_0 = wp.builder.pure_density(psi_0)
48
+ >>>
49
+ >>> solver = wp.solver.OdeSolver(equation, 50.0 * unit_fs)
50
+ >>> populations = np.zeros((9, 8))
51
+ >>> time_index = 0
52
+ >>> for t, rho in solver.propagate(rho_0, 0.0, 8):
53
+ ... for state_index in range(8):
54
+ ... populations[time_index, state_index] = wp.population(rho, bound_states[state_index])
55
+ ... time_index += 1
56
+ >>>
57
+ >>> populations[np.abs(populations) < 1e-10] = 0
58
+ >>>
59
+ >>> for i in range(7):
60
+ ... print(f"\n\nbound state {i}\n\n")
61
+ ... for t in range(9):
62
+ ... print(f"{t*50*41.341:.4}: {populations[t, i]:.4}")
63
+ <BLANKLINE>
64
+ <BLANKLINE>
65
+ bound state 0
66
+ <BLANKLINE>
67
+ <BLANKLINE>
68
+ 0.0: 1.0
69
+ 2.067e+03: 0.9999
70
+ 4.134e+03: 0.9978
71
+ 6.201e+03: 0.9894
72
+ 8.268e+03: 0.9662
73
+ 1.034e+04: 0.9366
74
+ 1.24e+04: 0.8892
75
+ 1.447e+04: 0.803
76
+ 1.654e+04: 0.705
77
+ <BLANKLINE>
78
+ <BLANKLINE>
79
+ bound state 1
80
+ <BLANKLINE>
81
+ <BLANKLINE>
82
+ 0.0: 0.0
83
+ 2.067e+03: 0.0001345
84
+ 4.134e+03: 0.002225
85
+ 6.201e+03: 0.01055
86
+ 8.268e+03: 0.03287
87
+ 1.034e+04: 0.05944
88
+ 1.24e+04: 0.09693
89
+ 1.447e+04: 0.1512
90
+ 1.654e+04: 0.1706
91
+ <BLANKLINE>
92
+ <BLANKLINE>
93
+ bound state 2
94
+ <BLANKLINE>
95
+ <BLANKLINE>
96
+ 0.0: 0.0
97
+ 2.067e+03: 3.286e-08
98
+ 4.134e+03: 5.724e-06
99
+ 6.201e+03: 9.384e-05
100
+ 8.268e+03: 0.0009288
101
+ 1.034e+04: 0.003546
102
+ 1.24e+04: 0.01048
103
+ 1.447e+04: 0.02547
104
+ 1.654e+04: 0.03777
105
+ <BLANKLINE>
106
+ <BLANKLINE>
107
+ bound state 3
108
+ <BLANKLINE>
109
+ <BLANKLINE>
110
+ 0.0: 0.0
111
+ 2.067e+03: 0.0
112
+ 4.134e+03: 2.451e-08
113
+ 6.201e+03: 1.135e-06
114
+ 8.268e+03: 4.049e-05
115
+ 1.034e+04: 0.0003052
116
+ 1.24e+04: 0.001663
117
+ 1.447e+04: 0.006138
118
+ 1.654e+04: 0.02057
119
+ <BLANKLINE>
120
+ <BLANKLINE>
121
+ bound state 4
122
+ <BLANKLINE>
123
+ <BLANKLINE>
124
+ 0.0: 0.0
125
+ 2.067e+03: 0.0
126
+ 4.134e+03: 1.793e-10
127
+ 6.201e+03: 3.112e-08
128
+ 8.268e+03: 4.711e-06
129
+ 1.034e+04: 4.706e-05
130
+ 1.24e+04: 0.0007506
131
+ 1.447e+04: 0.006458
132
+ 1.654e+04: 0.03295
133
+ <BLANKLINE>
134
+ <BLANKLINE>
135
+ bound state 5
136
+ <BLANKLINE>
137
+ <BLANKLINE>
138
+ 0.0: 0.0
139
+ 2.067e+03: 0.0
140
+ 4.134e+03: 0.0
141
+ 6.201e+03: 2.164e-09
142
+ 8.268e+03: 1.098e-06
143
+ 1.034e+04: 4.586e-05
144
+ 1.24e+04: 0.0005787
145
+ 1.447e+04: 0.003726
146
+ 1.654e+04: 0.01069
147
+ <BLANKLINE>
148
+ <BLANKLINE>
149
+ bound state 6
150
+ <BLANKLINE>
151
+ <BLANKLINE>
152
+ 0.0: 0.0
153
+ 2.067e+03: 0.0
154
+ 4.134e+03: 0.0
155
+ 6.201e+03: 0.0
156
+ 8.268e+03: 7.184e-08
157
+ 1.034e+04: 1.186e-05
158
+ 1.24e+04: 0.0003607
159
+ 1.447e+04: 0.003443
160
+ 1.654e+04: 0.01842
161
+
@@ -0,0 +1,37 @@
1
+ >>> import numpy as np
2
+ >>> import wavepacket as wp
3
+ >>>
4
+ >>> dof = wp.grid.PlaneWaveDof(0.7, 8, 96)
5
+ >>> grid = wp.grid.Grid(dof)
6
+ >>>
7
+ >>> def morse_potential(x):
8
+ ... return 0.1994 * (1 - np.exp(-1.189 * (x - 1.821))) ** 2
9
+ >>>
10
+ >>> kinetic = wp.operator.CartesianKineticEnergy(grid, 0, mass=1728.539, cutoff=0.3)
11
+ >>> potential = wp.operator.Potential1D(grid, 0, morse_potential, cutoff=0.3)
12
+ >>> hamiltonian = kinetic + potential
13
+ >>>
14
+ >>> bound_states = [state for energy, state in wp.diagonalize(kinetic + potential) if energy < 0.1994]
15
+ >>>
16
+ >>> solver = wp.solver.RelaxationSolver(hamiltonian, 20, (0, 0.6))
17
+ >>> psi0 = wp.builder.product_wave_function(grid, wp.special.Gaussian(2, 0, fwhm=0.5))
18
+ >>>
19
+ >>> relaxed_states = []
20
+ >>> for i in range(5):
21
+ ... psi = psi0
22
+ ... for step in range(20):
23
+ ... psi = solver.step(psi, 0.0)
24
+ ... # Trick: the last element is orthogonalized w.r.t. all other elements.
25
+ ... # That way, we can orthonormalize psi against all already found states.
26
+ ... psi = wp.orthonormalize(relaxed_states + [psi])[-1]
27
+ ... relaxed_states.append(psi)
28
+ >>>
29
+ >>> for i in range(5):
30
+ ... energy_bound = wp.expectation_value(hamiltonian, bound_states[i]).real
31
+ ... energy_relaxed = wp.expectation_value(hamiltonian, relaxed_states[i]).real
32
+ ... print(f"State {i}: E_bound = {energy_bound:.6} E_relax = {energy_relaxed:.6}")
33
+ State 0: E_bound = 0.00892781 E_relax = 0.00892781
34
+ State 1: E_bound = 0.02617 E_relax = 0.02617
35
+ State 2: E_bound = 0.0425944 E_relax = 0.0425944
36
+ State 3: E_bound = 0.0582008 E_relax = 0.0582009
37
+ State 4: E_bound = 0.0729895 E_relax = 0.0729896
@@ -0,0 +1,49 @@
1
+ >>> import numpy as np
2
+ >>> import wavepacket as wp
3
+ >>>
4
+ >>> dof = wp.grid.PlaneWaveDof(-7, 7, 96)
5
+ >>> grid = wp.grid.Grid(dof)
6
+ >>>
7
+ >>> def razavy_potential(x):
8
+ ... cosh_val = np.cosh(x)
9
+ ... return -0.7 * cosh_val + 0.01 * cosh_val ** 2
10
+ >>>
11
+ >>> kinetic = wp.operator.CartesianKineticEnergy(grid, 0, mass=0.5)
12
+ >>> potential = wp.operator.Potential1D(grid, 0, razavy_potential)
13
+ >>> hamiltonian = kinetic + potential
14
+ >>>
15
+ >>> states = [psi for _, psi in wp.diagonalize(hamiltonian)]
16
+ >>>
17
+ >>> for index, psi in enumerate(states[:30]):
18
+ ... print(f"{index}: {wp.expectation_value(hamiltonian, psi).real:.4}")
19
+ 0: -9.002
20
+ 1: -9.002
21
+ 2: -4.012
22
+ 3: -4.011
23
+ 4: -1.16
24
+ 5: -0.9569
25
+ 6: 0.2135
26
+ 7: 1.374
27
+ 8: 2.813
28
+ 9: 4.463
29
+ 10: 6.305
30
+ 11: 8.324
31
+ 12: 10.51
32
+ 13: 12.86
33
+ 14: 15.37
34
+ 15: 18.03
35
+ 16: 20.84
36
+ 17: 23.79
37
+ 18: 26.88
38
+ 19: 30.11
39
+ 20: 33.48
40
+ 21: 36.99
41
+ 22: 40.62
42
+ 23: 44.39
43
+ 24: 48.28
44
+ 25: 52.3
45
+ 26: 56.45
46
+ 27: 60.72
47
+ 28: 65.11
48
+ 29: 69.62
49
+
@@ -1,39 +0,0 @@
1
- import math
2
-
3
- import wavepacket as wp
4
-
5
-
6
- def log(t: float, state: wp.grid.State, precision: int = 6) -> None:
7
- """
8
- Prints some data about the state for inspection.
9
-
10
- The idea is that you call this function during every solver step and get
11
- a log with the most important values about the propagation, for example
12
- the state trace (if it deviates from one, this may be caused by poor convergence).
13
-
14
- Parameters
15
- ----------
16
- t : float
17
- The time at which you log.
18
- state : wp.grid.State
19
- The state to log.
20
- precision : int, default=6
21
- How many decimal places should be printed.
22
- """
23
- print(
24
- f"\n-----------------------------------------------\n"
25
- f"t = {float(t):.{precision}}, trace = {wp.trace(state):.{precision}}\n"
26
- )
27
-
28
- normalized_state = wp.normalize(state)
29
- for index, dof in enumerate(state.grid.dofs):
30
- x = wp.operator.Potential1D(state.grid, 0, lambda dvr_grid: dvr_grid)
31
-
32
- x_avg = wp.expectation_value(x, normalized_state).real
33
- x2_avg = wp.expectation_value(x * x, normalized_state).real
34
-
35
- # In exotic cases, the error dx**2 can become negative, so we trade
36
- # correctness for robustness here by taking its absolute value.
37
- print(
38
- f"<x_{index}> = {x_avg:.{precision}} =/- {math.sqrt(abs(x2_avg - x_avg ** 2)):.{precision}}"
39
- )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes