yu-mcal 0.1.6__py3-none-any.whl → 0.2.1__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.
mcal/mcal.py CHANGED
@@ -6,6 +6,9 @@ from pathlib import Path
6
6
  from time import time
7
7
  from typing import Dict, List, Literal, Optional, Tuple, Union
8
8
 
9
+ import matplotlib
10
+ matplotlib.use('Agg')
11
+ import matplotlib.pyplot as plt
9
12
  import numpy as np
10
13
  from numpy.typing import NDArray
11
14
  from tcal import Tcal
@@ -33,40 +36,41 @@ def main():
33
36
  --------
34
37
  Basic usage:
35
38
  - Calculate p-type mobility for xxx crystal\n
36
- $ python hop_mcal.py xxx.cif p
39
+ $ mcal xxx.cif p
37
40
 
38
41
  - Calculate n-type mobility for xxx crystal\n
39
- $ python hop_mcal.py xxx.cif n
42
+ $ mcal xxx.cif n
40
43
 
41
44
  With resource options:
42
45
  - Use 8 CPUs and 16GB memory\n
43
- $ python hop_mcal.py xxx.cif p -c 8 -m 16
46
+ $ mcal xxx.cif p -c 8 -m 16
44
47
 
45
48
  - Use different calculation method (default is B3LYP/6-31G(d,p))\n
46
- $ python hop_mcal.py xxx.cif p -M "B3LYP/6-311G(d,p)"
49
+ $ mcal xxx.cif p -M "B3LYP/6-311G(d,p)"
47
50
 
48
51
  High-precision calculation:
49
52
  - Calculate all transfer integrals without speedup using moment of inertia and distance between centers of weight\n
50
- $ python hop_mcal.py xxx.cif p --fullcal
51
-
52
- - Expand calculation range to 3x3x3 supercell\n
53
- $ python hop_mcal.py xxx.cif p --cellsize 1
53
+ $ mcal xxx.cif p --fullcal
54
54
 
55
55
  - Expand calculation range to 5x5x5 supercell to widen transfer integral calculation range\n
56
- $ python hop_mcal.py xxx.cif p --cellsize 2
56
+ $ mcal xxx.cif p --cellsize 2
57
57
 
58
58
  Resume and save results:
59
59
  - Resume from existing calculations\n
60
- $ python hop_mcal.py xxx.cif p --resume
60
+ $ mcal xxx.cif p --resume
61
61
 
62
62
  - Save results to pickle file\n
63
- $ python hop_mcal.py xxx.cif p --pickle
63
+ $ mcal xxx.cif p --pickle
64
64
 
65
65
  - Read results from existing pickle file\n
66
- $ python hop_mcal.py xxx_result.pkl p -rp
66
+ $ mcal xxx_result.pkl p -rp
67
67
 
68
68
  - Read results from existing log files without running Gaussian\n
69
- $ python hop_mcal.py xxx.cif p -r
69
+ $ mcal xxx.cif p -r
70
+
71
+ Plot mobility tensor in 2D plane:
72
+ - Plot mobility tensor in 2D plane (Examples: ab, ac, ba, bc, ca, cb (default is ab))\n
73
+ $ python hop_mcal.py xxx.cif p --plot-plane ab
70
74
 
71
75
  Compare calculation methods:
72
76
  - Compare results using kinetic Monte Carlo and ODE methods\n
@@ -119,6 +123,13 @@ def main():
119
123
  help='use Ordinary Differential Equation method to calculate diffusion coefficient',
120
124
  action='store_true',
121
125
  )
126
+ parser.add_argument(
127
+ '--plot-plane',
128
+ help='plot mobility tensor in 2D plane (Examples: ab, ac, ba, bc, ca, cb (default is ab))',
129
+ type=str,
130
+ default=None,
131
+ choices=['ab', 'ac', 'ba', 'bc', 'ca', 'cb'],
132
+ )
122
133
  parser.add_argument(
123
134
  '--resume',
124
135
  help='resume calculation',
@@ -140,11 +151,11 @@ def main():
140
151
  cif_path_without_ext = f'{directory}/{filename}'
141
152
 
142
153
  print('----------------------------------------')
143
- print(' mcal 0.1.6 (2026/01/29) by Matsui Lab. ')
154
+ print(' mcal 0.2.1 (2026/01/29) by Matsui Lab. ')
144
155
  print('----------------------------------------')
145
156
 
146
157
  if args.read_pickle:
147
- read_pickle(args.file)
158
+ read_pickle(args.file, args.plot_plane)
148
159
  exit()
149
160
 
150
161
  print(f'\nCalculate as {args.osc_type}-type organic semiconductor.')
@@ -352,6 +363,14 @@ def main():
352
363
  'mobility_vector': vector
353
364
  }, f)
354
365
 
366
+ if args.plot_plane:
367
+ plot_mobility_2d(
368
+ Path(f'{cif_path_without_ext}_result.pkl'),
369
+ mu,
370
+ cif_reader.lattice,
371
+ args.plot_plane
372
+ )
373
+
355
374
  Tcal.print_timestamp()
356
375
  end_time = time()
357
376
  elapsed_time = end_time - start_time
@@ -720,6 +739,82 @@ def create_ti_gjf(
720
739
  gjf_maker.export_gjf(file_name=gjf_basename, save_dir=save_dir)
721
740
 
722
741
 
742
+ def plot_mobility_2d(
743
+ save_path: Path,
744
+ mobility_tensor: NDArray[np.float64],
745
+ lattice: NDArray[np.float64],
746
+ plane: Literal['ab', 'ac', 'ba', 'bc', 'ca', 'cb'] = 'ab'
747
+ ) -> None:
748
+ """Plot mobility tensor in 2D plane.
749
+
750
+ Parameters
751
+ ----------
752
+ save_path : Path
753
+ Path to save the plot
754
+ mobility_tensor : NDArray[np.float64]
755
+ Mobility tensor
756
+ lattice : NDArray[np.float64]
757
+ Lattice vectors [Å]
758
+ plane : Literal['ab', 'ac', 'ba', 'bc', 'ca', 'cb']
759
+ Plane to plot, by default 'ab'
760
+ """
761
+ print(f"Plot mobility in {plane} plane.")
762
+ angle_list = np.arange(0, 360, 1)
763
+ mobility_values = []
764
+
765
+ a_vec = lattice[0]
766
+ b_vec = lattice[1]
767
+ c_vec = lattice[2]
768
+ if plane == 'ab':
769
+ v1, v2, = a_vec, b_vec
770
+ elif plane == 'ba':
771
+ v1, v2, = b_vec, a_vec
772
+ elif plane == 'bc':
773
+ v1, v2, = b_vec, c_vec
774
+ elif plane == 'cb':
775
+ v1, v2, = c_vec, b_vec
776
+ elif plane == 'ac':
777
+ v1, v2, = a_vec, c_vec
778
+ elif plane == 'ca':
779
+ v1, v2 = c_vec, a_vec
780
+
781
+ # Angle between the two specified crystal axes
782
+ second_axis_angle = np.rad2deg(np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))))
783
+ print('Crystal axis directions in the plotted plane:')
784
+ print(f'{plane[0]}-axis: 0.0 deg')
785
+ print(f'{plane[1]}-axis: {second_axis_angle:.1f} deg')
786
+ print()
787
+
788
+ # Gram–Schmidt orthonormalization
789
+ e1 = v1 / np.linalg.norm(v1)
790
+ e2 = v2 - np.dot(v2, e1) * e1
791
+ e2 = e2 / np.linalg.norm(e2)
792
+
793
+ for angle in angle_list:
794
+ phi = np.deg2rad(angle)
795
+ direction = np.cos(phi) * e1 + np.sin(phi) * e2
796
+ mobility_value = direction @ mobility_tensor @ direction
797
+ mobility_values.append(mobility_value)
798
+
799
+ plt.rcParams['font.size'] = 12
800
+ width_cm, height_cm = 20, 8
801
+ width_inch, height_inch = width_cm / 2.54, height_cm / 2.54
802
+
803
+ fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}, tight_layout=True, figsize=(width_inch, height_inch))
804
+ ax.set_theta_zero_location('E')
805
+ ax.grid(True, linestyle='--', linewidth=0.5)
806
+ ax.plot(np.deg2rad(angle_list), mobility_values, linewidth=2)
807
+
808
+ ax.set_rlim(bottom=0)
809
+ ax.set_xticks(np.arange(0, 2*np.pi, np.pi/6))
810
+ ax.tick_params(axis="x", pad=5)
811
+ ax.set_ylabel(R'Mobility [$\mathrm{cm}^2 \mathrm{V}^{-1} \mathrm{s}^{-1}$]')
812
+ ax.yaxis.set_label_coords(-0.2, 0.5)
813
+ ax.set_rlabel_position(90)
814
+ plt.savefig(save_path.parent / f"{save_path.stem}_{plane}.png", dpi=300, bbox_inches='tight')
815
+ plt.close()
816
+
817
+
723
818
  def print_mobility(value: NDArray[np.float64], vector: NDArray[np.float64], sim_type: Literal['MC', 'ODE'] = ''):
724
819
  """Print mobility and mobility vector
725
820
 
@@ -811,14 +906,24 @@ def print_transfer_integral(osc_type: Literal['p', 'n'], transfer: float):
811
906
  print(f'{mol_orb[osc_type]}: {transfer:12.6g} eV\n')
812
907
 
813
908
 
814
- def read_pickle(file_name: str):
909
+ def read_pickle(
910
+ file_name: str,
911
+ plot_plane: Optional[Literal['ab', 'ac', 'ba', 'bc', 'ca', 'cb']] = None
912
+ ) -> None:
913
+ """Read pickle file and plot mobility tensor in 2D plane.
914
+
915
+ Parameters
916
+ ----------
917
+ file_name : str
918
+ Path to the pickle file
919
+ plot_plane : Optional[Literal['ab', 'ac', 'ba', 'bc', 'ca', 'cb']]
920
+ Plane to plot, by default None
921
+ """
815
922
  print(f'\nInput File Name: {file_name}')
816
923
 
817
924
  with open(file_name, 'rb') as f:
818
925
  results = pickle.load(f)
819
926
 
820
- # print(results)
821
-
822
927
  print(f'\nCalculate as {results["osc_type"]}-type organic semiconductor.')
823
928
 
824
929
  print_reorg_energy(results['osc_type'], results['reorganization'])
@@ -834,6 +939,14 @@ def read_pickle(file_name: str):
834
939
 
835
940
  print_mobility(results['mobility_value'], results['mobility_vector'])
836
941
 
942
+ if plot_plane:
943
+ plot_mobility_2d(
944
+ Path(file_name).with_suffix(''),
945
+ results['mobility_tensor'],
946
+ results['lattice'],
947
+ plot_plane,
948
+ )
949
+
837
950
 
838
951
  class OSCTypeError(Exception):
839
952
  """Exception for semiconductor type"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yu-mcal
3
- Version: 0.1.6
3
+ Version: 0.2.1
4
4
  Summary: Program for the calculation of mobility tensor for organic semiconductor crystals
5
5
  Author: Koki Ozawa
6
6
  Author-email: Hiroyuki Matsui <h-matsui@yz.yamagata-u.ac.jp>
@@ -40,6 +40,7 @@ Classifier: Topic :: Scientific/Engineering
40
40
  Classifier: Topic :: Scientific/Engineering :: Chemistry
41
41
  Classifier: Topic :: Scientific/Engineering :: Physics
42
42
  Requires-Python: >=3.9
43
+ Requires-Dist: matplotlib>=3.9.4
43
44
  Requires-Dist: numpy>=2.0.2
44
45
  Requires-Dist: pandas>=2.3.3
45
46
  Requires-Dist: yu-tcal==3.1.0
@@ -57,6 +58,7 @@ Description-Content-Type: text/markdown
57
58
  * Python 3.9 or newer
58
59
  * NumPy
59
60
  * Pandas
61
+ * Matplotlib
60
62
  * yu-tcal==3.1.0
61
63
  * Gaussian 09 or 16
62
64
 
@@ -157,6 +159,14 @@ Specify the number of unit cells to expand in each direction around the central
157
159
  Save calculation results to a pickle file.
158
160
  - **Example**: `mcal xxx.cif p -p`
159
161
 
162
+ #### `--plot-plane <plane>`
163
+ Plot mobility tensor as a 2D polar plot on specified crystallographic plane.
164
+ - **Available planes**: `ab`, `ac`, `ba`, `bc`, `ca`, `cb`
165
+ - **Default**: None (no plot generated)
166
+ - **Examples**:
167
+ - `mcal xxx.cif p --plot-plane ab` (plot on ab-plane)
168
+ - `mcal xxx.cif p --plot-plane bc` (plot on bc-plane)
169
+
160
170
  ### Diffusion Coefficient Calculation Methods
161
171
 
162
172
  #### `--mc`
@@ -1,5 +1,5 @@
1
1
  mcal/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- mcal/mcal.py,sha256=LRKAcLKG8Uv0yI16zZW0GmOEiRkbUHuVWVYOrHZR014,29280
2
+ mcal/mcal.py,sha256=1XkovOZTkSlBlyOuWjZ85BTU2VvU30gU5Zf0hgfZk_o,32816
3
3
  mcal/calculations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcal/calculations/hopping_mobility_model.py,sha256=eD9doesa1yVDQxzBYW0N41OyjikZb77S69I_mIMbL2g,13180
5
5
  mcal/calculations/rcal.py,sha256=CH3iV18KTM8xU7M7zKR3e1m67GbJLH8zi0j50TsUXLE,13500
@@ -8,8 +8,8 @@ mcal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  mcal/utils/cif_reader.py,sha256=QAV6xXMd3nfKLpJqBncA6fbGJ5kgYKDeKIx-sK_4P_M,24532
9
9
  mcal/utils/gaus_log_reader.py,sha256=nNIgBae9hRUgpmNF7eIC5LOENSo6NQmuckMM4A3HAa8,3159
10
10
  mcal/utils/gjf_maker.py,sha256=Kkh_gNcifFfhTikZar6SzoNJ7AyEBiCBXJTQkHxHX-0,8193
11
- yu_mcal-0.1.6.dist-info/METADATA,sha256=6qhMLEDBBrwAKLXWz7hlohj-2JI26xxrX2kuf0AF8gE,7890
12
- yu_mcal-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- yu_mcal-0.1.6.dist-info/entry_points.txt,sha256=_0xZR3t9qvFSd9L6Iot03NixVLxXioEY19L6w3Fs1Ew,40
14
- yu_mcal-0.1.6.dist-info/licenses/LICENSE,sha256=JP8vm7gYE73jLgnMFTOLNo_RnH88RrB4Goyh7H_muto,1072
15
- yu_mcal-0.1.6.dist-info/RECORD,,
11
+ yu_mcal-0.2.1.dist-info/METADATA,sha256=OtLCHoL8h1mqApWdTlB9-34BWqey1Spgcp8N-0m6YPA,8270
12
+ yu_mcal-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ yu_mcal-0.2.1.dist-info/entry_points.txt,sha256=_0xZR3t9qvFSd9L6Iot03NixVLxXioEY19L6w3Fs1Ew,40
14
+ yu_mcal-0.2.1.dist-info/licenses/LICENSE,sha256=JP8vm7gYE73jLgnMFTOLNo_RnH88RrB4Goyh7H_muto,1072
15
+ yu_mcal-0.2.1.dist-info/RECORD,,