digichem-core 6.1.0__py3-none-any.whl → 6.10.3__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.
- digichem/__init__.py +2 -2
- digichem/config/base.py +6 -4
- digichem/data/batoms/batoms-renderer.py +190 -50
- digichem/data/batoms/batoms_renderer.py +500 -0
- digichem/file/base.py +14 -0
- digichem/file/cube.py +185 -16
- digichem/file/types.py +1 -0
- digichem/image/render.py +149 -48
- digichem/image/vmd.py +7 -2
- digichem/input/digichem_input.py +2 -2
- digichem/memory.py +10 -0
- digichem/misc/io.py +95 -1
- digichem/parse/__init__.py +6 -1
- digichem/parse/base.py +85 -54
- digichem/parse/cclib.py +139 -13
- digichem/parse/dump.py +3 -3
- digichem/parse/orca.py +1 -0
- digichem/parse/pyscf.py +35 -0
- digichem/parse/turbomole.py +3 -3
- digichem/parse/util.py +146 -65
- digichem/result/excited_state.py +17 -11
- digichem/result/metadata.py +307 -3
- digichem/result/result.py +3 -0
- digichem/result/spectroscopy.py +42 -0
- digichem/test/conftest.py +5 -0
- digichem/test/mock/cubegen +87172 -0
- digichem/test/mock/formchk +9456 -0
- digichem/test/test_image.py +54 -42
- digichem/test/test_memory.py +33 -0
- digichem/test/test_parsing.py +68 -1
- digichem/test/test_result.py +1 -1
- digichem/test/util.py +4 -1
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/METADATA +4 -3
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/RECORD +37 -32
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/WHEEL +1 -1
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/licenses/COPYING.md +0 -0
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/licenses/LICENSE +0 -0
digichem/result/metadata.py
CHANGED
|
@@ -8,6 +8,7 @@ from deepmerge import conservative_merger
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
import copy
|
|
10
10
|
import warnings
|
|
11
|
+
from scipy import integrate
|
|
11
12
|
|
|
12
13
|
from digichem.misc.time import latest_datetime, total_timedelta, date_to_string,\
|
|
13
14
|
timedelta_to_string
|
|
@@ -147,6 +148,7 @@ class Metadata(Result_object):
|
|
|
147
148
|
auxiliary_files = None,
|
|
148
149
|
history = None,
|
|
149
150
|
date = None,
|
|
151
|
+
insert_date = None,
|
|
150
152
|
duration = None,
|
|
151
153
|
package = None,
|
|
152
154
|
package_version = None,
|
|
@@ -167,6 +169,7 @@ class Metadata(Result_object):
|
|
|
167
169
|
num_cpu = None,
|
|
168
170
|
memory_available = None,
|
|
169
171
|
memory_used = None,
|
|
172
|
+
performance = None,
|
|
170
173
|
|
|
171
174
|
# Deprecated.
|
|
172
175
|
solvent_model = None,
|
|
@@ -181,6 +184,7 @@ class Metadata(Result_object):
|
|
|
181
184
|
:param history: Optional SHA of the calculation from which the coordinates of this calculation were generated.
|
|
182
185
|
:param num_calculations: Optional number of individual calculations this metadata represents.
|
|
183
186
|
:param date: Optional date (datetime object) of this calculation result.
|
|
187
|
+
:param insert_date: Optional date (datetime object) of when this calculation result was stored (normally in a DB).
|
|
184
188
|
:param duration: Optional duration (timedelta object) of this calculation.
|
|
185
189
|
:param package: Optional string identifying the computational chem program that performed the calculation.
|
|
186
190
|
:param package_version: Optional string identifying the version of the computational chem program that performed the calculation.
|
|
@@ -206,6 +210,7 @@ class Metadata(Result_object):
|
|
|
206
210
|
self.auxiliary_files = auxiliary_files if auxiliary_files is not None and len(auxiliary_files) != 0 else {}
|
|
207
211
|
self.history = history
|
|
208
212
|
self.date = date
|
|
213
|
+
self.insert_date = insert_date
|
|
209
214
|
self.duration = duration
|
|
210
215
|
self.package = package
|
|
211
216
|
self.package_version = package_version
|
|
@@ -229,6 +234,7 @@ class Metadata(Result_object):
|
|
|
229
234
|
self.num_cpu = num_cpu
|
|
230
235
|
self.memory_available = memory_available
|
|
231
236
|
self.memory_used = memory_used
|
|
237
|
+
self.performance = performance
|
|
232
238
|
|
|
233
239
|
# Deprecated solvent system.
|
|
234
240
|
if solvent_model is not None:
|
|
@@ -473,6 +479,7 @@ class Metadata(Result_object):
|
|
|
473
479
|
num_cpu = parser.data.metadata.get('num_cpu', None),
|
|
474
480
|
memory_available = memory_available,
|
|
475
481
|
memory_used = memory_used,
|
|
482
|
+
performance = Performance.from_parser(parser) if "performance" in parser.data.metadata else None
|
|
476
483
|
)
|
|
477
484
|
except AttributeError:
|
|
478
485
|
# There is no metadata available, give up.
|
|
@@ -513,6 +520,11 @@ class Metadata(Result_object):
|
|
|
513
520
|
"units": "s",
|
|
514
521
|
"string": date_to_string(self.date) if self.date is not None else None
|
|
515
522
|
}
|
|
523
|
+
attr_dict['insert_date'] = {
|
|
524
|
+
"value": self.insert_date.timestamp() if self.insert_date is not None else None,
|
|
525
|
+
"units": "s",
|
|
526
|
+
"string": date_to_string(self.insert_date) if self.insert_date is not None else None
|
|
527
|
+
}
|
|
516
528
|
attr_dict['duration'] = {
|
|
517
529
|
"value": self.duration.total_seconds() if self.duration is not None else None,
|
|
518
530
|
"units": "s",
|
|
@@ -542,6 +554,8 @@ class Metadata(Result_object):
|
|
|
542
554
|
"units": None
|
|
543
555
|
}
|
|
544
556
|
|
|
557
|
+
attr_dict['performance'] = self.performance.dump(digichem_options) if self.performance else None
|
|
558
|
+
|
|
545
559
|
return attr_dict
|
|
546
560
|
|
|
547
561
|
@classmethod
|
|
@@ -557,9 +571,14 @@ class Metadata(Result_object):
|
|
|
557
571
|
kwargs = copy.deepcopy(data)
|
|
558
572
|
|
|
559
573
|
# For more complex fields, use the data item.
|
|
560
|
-
for attr in ['date', 'duration', 'temperature', "pressure"]:
|
|
561
|
-
|
|
574
|
+
for attr in ['insert_date', 'date', 'duration', 'temperature', "pressure"]:
|
|
575
|
+
if attr in data:
|
|
576
|
+
kwargs[attr] = data[attr]['value']
|
|
577
|
+
|
|
578
|
+
else:
|
|
579
|
+
kwargs[attr] = None
|
|
562
580
|
|
|
581
|
+
kwargs['insert_date'] = datetime.fromtimestamp(kwargs['insert_date']) if kwargs['insert_date'] is not None else None
|
|
563
582
|
kwargs['date'] = datetime.fromtimestamp(kwargs['date']) if kwargs['date'] is not None else None
|
|
564
583
|
kwargs['duration'] = timedelta(seconds = kwargs['duration']) if kwargs['duration'] is not None else None
|
|
565
584
|
|
|
@@ -571,6 +590,8 @@ class Metadata(Result_object):
|
|
|
571
590
|
|
|
572
591
|
else:
|
|
573
592
|
kwargs[attr_name] = None
|
|
593
|
+
|
|
594
|
+
kwargs['performance'] = Performance.from_dump(data['performance'], result_set, options) if "performance" in data and data['performance'] is not None else None
|
|
574
595
|
|
|
575
596
|
return self(**kwargs)
|
|
576
597
|
|
|
@@ -644,4 +665,287 @@ class Merged_metadata(Metadata):
|
|
|
644
665
|
merged_metadata.auxiliary_files = conservative_merger.merge(merged_metadata.auxiliary_files, metadata.auxiliary_files)
|
|
645
666
|
|
|
646
667
|
return merged_metadata
|
|
647
|
-
|
|
668
|
+
|
|
669
|
+
class Performance(Result_object):
|
|
670
|
+
"""
|
|
671
|
+
Performance metrics and profiling data for the calculation.
|
|
672
|
+
"""
|
|
673
|
+
|
|
674
|
+
def __init__(
|
|
675
|
+
self,
|
|
676
|
+
duration = [],
|
|
677
|
+
memory_used = [],
|
|
678
|
+
memory_used_percent = [],
|
|
679
|
+
memory_available = [],
|
|
680
|
+
memory_available_percent = [],
|
|
681
|
+
cpu_used = [],
|
|
682
|
+
output_available = [],
|
|
683
|
+
scratch_used = [],
|
|
684
|
+
scratch_available = [],
|
|
685
|
+
memory_allocated = None,
|
|
686
|
+
cpu_allocated = None,
|
|
687
|
+
):
|
|
688
|
+
self.duration = duration
|
|
689
|
+
self.memory_used = memory_used
|
|
690
|
+
self.memory_used_percent = memory_used_percent
|
|
691
|
+
self.memory_available = memory_available
|
|
692
|
+
self.memory_available_percent = memory_available_percent
|
|
693
|
+
self.cpu_used = cpu_used
|
|
694
|
+
self.output_available = output_available
|
|
695
|
+
self.scratch_used = scratch_used
|
|
696
|
+
self.scratch_available = scratch_available
|
|
697
|
+
|
|
698
|
+
self.memory_allocated = memory_allocated if memory_allocated is not None else self.max_mem
|
|
699
|
+
self.cpu_allocated = cpu_allocated if cpu_allocated is not None else math.ceil(max(cpu_used) / 100)
|
|
700
|
+
|
|
701
|
+
@property
|
|
702
|
+
def output_space(self):
|
|
703
|
+
warnings.warn("output_space is deprecated, use output_available instead", DeprecationWarning)
|
|
704
|
+
return self.output_available
|
|
705
|
+
|
|
706
|
+
@property
|
|
707
|
+
def scratch_space(self):
|
|
708
|
+
warnings.warn("scratch_space is deprecated, use scratch_available instead", DeprecationWarning)
|
|
709
|
+
return self.scratch_available
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
@classmethod
|
|
713
|
+
def from_parser(self, parser):
|
|
714
|
+
"""
|
|
715
|
+
Construct a Performance object from an output file parser.
|
|
716
|
+
|
|
717
|
+
:param parser: Output data parser.
|
|
718
|
+
:return: A populated Performance object.
|
|
719
|
+
"""
|
|
720
|
+
return self(
|
|
721
|
+
duration = parser.data.metadata['performance']['duration'].tolist(),
|
|
722
|
+
memory_used = parser.data.metadata['performance']['memory_used'].tolist(),
|
|
723
|
+
memory_allocated = Memory(parser.data.metadata['memory_available']) if "memory_available" in parser.data.metadata else None,
|
|
724
|
+
memory_used_percent = parser.data.metadata['performance']['memory_used_percent'].tolist(),
|
|
725
|
+
memory_available = parser.data.metadata['performance']['memory_available'].tolist(),
|
|
726
|
+
memory_available_percent = parser.data.metadata['performance']['memory_available_percent'].tolist(),
|
|
727
|
+
cpu_used = parser.data.metadata['performance']['cpu_used'].tolist(),
|
|
728
|
+
cpu_allocated = parser.data.metadata.get('num_cpu', None),
|
|
729
|
+
output_available = parser.data.metadata['performance']['output_available'].tolist(),
|
|
730
|
+
scratch_used = parser.data.metadata['performance']['scratch_used'].tolist() if 'scratch_used' in parser.data.metadata['performance'] else [0] * len(parser.data.metadata['performance']['duration']),
|
|
731
|
+
scratch_available = parser.data.metadata['performance']['scratch_available'].tolist()
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
return self(
|
|
736
|
+
duration = parser.data.metadata['performance'][:, 0].tolist(),
|
|
737
|
+
memory_used = parser.data.metadata['performance'][:, 1].tolist(),
|
|
738
|
+
memory_allocated = Memory(parser.data.metadata['memory_available']) if "memory_available" in parser.data.metadata else None,
|
|
739
|
+
memory_used_percent = parser.data.metadata['performance'][:, 2].tolist(),
|
|
740
|
+
memory_available = parser.data.metadata['performance'][:, 3].tolist(),
|
|
741
|
+
memory_available_percent = parser.data.metadata['performance'][:, 4].tolist(),
|
|
742
|
+
cpu_used = parser.data.metadata['performance'][:, 5].tolist(),
|
|
743
|
+
cpu_allocated = parser.data.metadata.get('num_cpu', None),
|
|
744
|
+
output_space = parser.data.metadata['performance'][:, 6].tolist(),
|
|
745
|
+
scratch_space = parser.data.metadata['performance'][:, 7].tolist()
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
@property
|
|
749
|
+
def max_mem(self):
|
|
750
|
+
"""
|
|
751
|
+
The maximum amount of memory used in the calculation (in bytes)
|
|
752
|
+
"""
|
|
753
|
+
return max(self.memory_used)
|
|
754
|
+
|
|
755
|
+
@property
|
|
756
|
+
def memory_margin(self):
|
|
757
|
+
max_memory = float(self.memory_allocated) if self.memory_allocated is not None else self.max_mem
|
|
758
|
+
|
|
759
|
+
return max_memory - self.max_mem
|
|
760
|
+
|
|
761
|
+
@property
|
|
762
|
+
def memory_efficiency(self):
|
|
763
|
+
"""
|
|
764
|
+
Calculate the memory efficiency of this calculation.
|
|
765
|
+
|
|
766
|
+
:param max_memory: The amount of allocated memory (in bytes), this will be guestimated automatically if not available.
|
|
767
|
+
"""
|
|
768
|
+
# Integrate to find the number of byte seconds used.
|
|
769
|
+
area = integrate.trapezoid(self.memory_used, self.duration)
|
|
770
|
+
|
|
771
|
+
# How much we should/could have used.
|
|
772
|
+
total_area = (self.duration[-1] - self.duration[0]) * float(self.memory_allocated)
|
|
773
|
+
|
|
774
|
+
# Return as %.
|
|
775
|
+
try:
|
|
776
|
+
return area / total_area * 100
|
|
777
|
+
|
|
778
|
+
except Exception:
|
|
779
|
+
return 0
|
|
780
|
+
|
|
781
|
+
@property
|
|
782
|
+
def cpu_efficiency(self):
|
|
783
|
+
"""
|
|
784
|
+
Calculate the CPU efficiency of this calculation.
|
|
785
|
+
|
|
786
|
+
:param max_cpu: The number of allocated CPUs, this will be guestimated automatically if not available.
|
|
787
|
+
"""
|
|
788
|
+
# Integrate to find the number of CPU seconds used.
|
|
789
|
+
area = integrate.trapezoid(self.cpu_used, self.duration)
|
|
790
|
+
|
|
791
|
+
# How much we should/could have used.
|
|
792
|
+
total_area = (self.duration[-1] - self.duration[0]) * self.cpu_allocated * 100
|
|
793
|
+
|
|
794
|
+
# Return as %.
|
|
795
|
+
try:
|
|
796
|
+
return area / total_area * 100
|
|
797
|
+
|
|
798
|
+
except Exception:
|
|
799
|
+
# Div zero
|
|
800
|
+
return 0
|
|
801
|
+
|
|
802
|
+
@classmethod
|
|
803
|
+
def from_dump(self, data, result_set, options):
|
|
804
|
+
"""
|
|
805
|
+
Get an instance of this class from its dumped representation.
|
|
806
|
+
|
|
807
|
+
:param data: The data to parse.
|
|
808
|
+
:param result_set: The partially constructed result set which is being populated.
|
|
809
|
+
"""
|
|
810
|
+
duration = [0.0] * len(data['values'])
|
|
811
|
+
memory_used = [0.0] * len(data['values'])
|
|
812
|
+
memory_allocated = Memory(data['memory_allocated']['value'])
|
|
813
|
+
memory_used_percent = [0.0] * len(data['values'])
|
|
814
|
+
memory_available = [0.0] * len(data['values'])
|
|
815
|
+
memory_available_percent = [0.0] * len(data['values'])
|
|
816
|
+
cpu_used = [0.0] * len(data['values'])
|
|
817
|
+
cpu_allocated = data['cpu_allocated']
|
|
818
|
+
output_available = [0.0] * len(data['values'])
|
|
819
|
+
scratch_used = [0.0] * len(data['values'])
|
|
820
|
+
scratch_available = [0.0] * len(data['values'])
|
|
821
|
+
|
|
822
|
+
for i, value in enumerate(data['values']):
|
|
823
|
+
duration[i] = value['duration']['value']
|
|
824
|
+
memory_used[i] = value['memory_used']['value']
|
|
825
|
+
memory_used_percent[i] = value['memory_used_percent']['value']
|
|
826
|
+
memory_available[i] = value['memory_available']['value']
|
|
827
|
+
memory_available_percent[i] = value['memory_available_percent']['value']
|
|
828
|
+
cpu_used[i] = value['cpu_used']['value']
|
|
829
|
+
output_available[i] = value['output_space']['value']
|
|
830
|
+
if 'scratch_used' in value:
|
|
831
|
+
scratch_used[i] = value['scratch_used']['value']
|
|
832
|
+
scratch_available[i] = value['scratch_space']['value']
|
|
833
|
+
|
|
834
|
+
return self(
|
|
835
|
+
duration = duration,
|
|
836
|
+
memory_used = memory_used,
|
|
837
|
+
memory_allocated = memory_allocated,
|
|
838
|
+
memory_used_percent = memory_used_percent,
|
|
839
|
+
memory_available = memory_available,
|
|
840
|
+
memory_available_percent = memory_available_percent,
|
|
841
|
+
cpu_used = cpu_used,
|
|
842
|
+
cpu_allocated = cpu_allocated,
|
|
843
|
+
output_available = output_available,
|
|
844
|
+
scratch_used = scratch_used,
|
|
845
|
+
scratch_available = scratch_available
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def dump(self, digichem_options):
|
|
850
|
+
"""
|
|
851
|
+
Get a representation of this result object in primitive format.
|
|
852
|
+
"""
|
|
853
|
+
return {
|
|
854
|
+
"cpu_allocated": self.cpu_allocated,
|
|
855
|
+
"cpu_efficiency": {
|
|
856
|
+
"units": "%",
|
|
857
|
+
"value": float(self.cpu_efficiency),
|
|
858
|
+
},
|
|
859
|
+
"memory_allocated": {
|
|
860
|
+
"units": "bytes",
|
|
861
|
+
"value": float(self.memory_allocated)
|
|
862
|
+
},
|
|
863
|
+
"maximum_memory": {
|
|
864
|
+
"units": "bytes",
|
|
865
|
+
"value": self.max_mem,
|
|
866
|
+
},
|
|
867
|
+
"memory_margin": {
|
|
868
|
+
"units": "bytes",
|
|
869
|
+
"value": self.memory_margin
|
|
870
|
+
},
|
|
871
|
+
"memory_efficiency": {
|
|
872
|
+
"units": "%",
|
|
873
|
+
"value": float(self.memory_efficiency)
|
|
874
|
+
},
|
|
875
|
+
"values":[
|
|
876
|
+
{
|
|
877
|
+
'duration': {
|
|
878
|
+
"units": "s",
|
|
879
|
+
"value": self.duration[i]
|
|
880
|
+
},
|
|
881
|
+
'memory_used': {
|
|
882
|
+
"units": "bytes",
|
|
883
|
+
"value": self.memory_used[i]
|
|
884
|
+
},
|
|
885
|
+
'memory_used_percent': {
|
|
886
|
+
"units": "%",
|
|
887
|
+
"value": self.memory_used_percent[i]
|
|
888
|
+
},
|
|
889
|
+
'memory_available': {
|
|
890
|
+
"units": "bytes",
|
|
891
|
+
"value": self.memory_available[i]
|
|
892
|
+
},
|
|
893
|
+
'memory_available_percent': {
|
|
894
|
+
"units": "bytes",
|
|
895
|
+
"value": self.memory_available_percent[i]
|
|
896
|
+
},
|
|
897
|
+
'cpu_used': {
|
|
898
|
+
"units": "%",
|
|
899
|
+
"value": self.cpu_used[i]
|
|
900
|
+
},
|
|
901
|
+
'output_space': {
|
|
902
|
+
"units": "bytes",
|
|
903
|
+
"value": self.output_space[i]
|
|
904
|
+
},
|
|
905
|
+
'scratch_used': {
|
|
906
|
+
"units": "bytes",
|
|
907
|
+
"value": self.scratch_used[i]
|
|
908
|
+
},
|
|
909
|
+
'scratch_space': {
|
|
910
|
+
"units": "bytes",
|
|
911
|
+
"value": self.scratch_space[i]
|
|
912
|
+
}
|
|
913
|
+
} for i in range(len(self.duration))
|
|
914
|
+
]
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return {
|
|
918
|
+
'duration': {
|
|
919
|
+
"units": "s",
|
|
920
|
+
"values": self.duration.tolist()
|
|
921
|
+
},
|
|
922
|
+
'memory_used': {
|
|
923
|
+
"units": "bytes",
|
|
924
|
+
"values": self.memory_used.tolist()
|
|
925
|
+
},
|
|
926
|
+
'memory_used_percent': {
|
|
927
|
+
"units": "%",
|
|
928
|
+
"values": self.memory_used_percent.tolist()
|
|
929
|
+
},
|
|
930
|
+
'memory_available': {
|
|
931
|
+
"units": "bytes",
|
|
932
|
+
"values": self.memory_available.tolist()
|
|
933
|
+
},
|
|
934
|
+
'memory_available_percent': {
|
|
935
|
+
"units": "bytes",
|
|
936
|
+
"values": self.memory_available_percent.tolist()
|
|
937
|
+
},
|
|
938
|
+
'cpu_used': {
|
|
939
|
+
"units": "%",
|
|
940
|
+
"values": self.cpu_used.tolist()
|
|
941
|
+
},
|
|
942
|
+
'output_space': {
|
|
943
|
+
"units": "bytes",
|
|
944
|
+
"values": self.output_space.tolist()
|
|
945
|
+
},
|
|
946
|
+
'scratch_space': {
|
|
947
|
+
"units": "bytes",
|
|
948
|
+
"values": self.scratch_space.tolist()
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
digichem/result/result.py
CHANGED
|
@@ -34,6 +34,9 @@ class Result_set(Result_object):
|
|
|
34
34
|
|
|
35
35
|
self.results = (self,)
|
|
36
36
|
self.emission = attributes.pop('emission', Emissions())
|
|
37
|
+
|
|
38
|
+
# Any ancillary data.
|
|
39
|
+
self._aux = attributes.pop('aux', {})
|
|
37
40
|
|
|
38
41
|
for attr_name, attribute in attributes.items():
|
|
39
42
|
setattr(self, attr_name, attribute)
|
digichem/result/spectroscopy.py
CHANGED
|
@@ -333,6 +333,48 @@ class Absorption_emission_graph(Spectroscopy_graph):
|
|
|
333
333
|
"""
|
|
334
334
|
return ((E * scipy.constants.electron_volt)**2 * f_E) / (scipy.constants.Planck * scipy.constants.c)
|
|
335
335
|
|
|
336
|
+
@classmethod
|
|
337
|
+
def inverse_jacobian(self, E, f_nm):
|
|
338
|
+
"""
|
|
339
|
+
An implementation of the jacobian transform that scales intensity in wavelength units to intensity in energy units.
|
|
340
|
+
|
|
341
|
+
See J. Phys. Chem. Lett. 2014, 5, 20, 3497 for why this is necessary.
|
|
342
|
+
|
|
343
|
+
Note that the jacobian transform will maintain the area under the curve regardless of x units (nm or x).
|
|
344
|
+
Sadly, this has the consequence of mangling the intensity units (it becomes tiny; an oscillator strength of 1 at 3 eV becomes 1.163e-12).
|
|
345
|
+
"""
|
|
346
|
+
# TODO: Might be better to rearrange this to accept nm rather than eV?
|
|
347
|
+
return (
|
|
348
|
+
(f_nm * scipy.constants.Planck * scipy.constants.c) / (E * scipy.constants.electron_volt) **2
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
@classmethod
|
|
352
|
+
def shift_coord(self, coord, delta_eV):
|
|
353
|
+
"""
|
|
354
|
+
Shift a coordinate (in nm) by a given energy value.
|
|
355
|
+
|
|
356
|
+
:param delta_eV: The energy (in eV) to shift by. A positive value will blueshift (higher energy).
|
|
357
|
+
"""
|
|
358
|
+
old_x_nm, old_y_nm = coord
|
|
359
|
+
# Convert x to energy.
|
|
360
|
+
old_x_ev = digichem.result.excited_state.Excited_state.wavelength_to_energy(old_x_nm)
|
|
361
|
+
# Transform y.
|
|
362
|
+
old_y_ev = self.inverse_jacobian(old_x_ev, old_y_nm)
|
|
363
|
+
|
|
364
|
+
# Shift by given amount.
|
|
365
|
+
new_x_ev = old_x_ev + delta_eV
|
|
366
|
+
|
|
367
|
+
# Convert back to nm.
|
|
368
|
+
new_x_nm, new_f_nm = self.energy_to_wavelength((new_x_ev, old_y_ev), True)
|
|
369
|
+
|
|
370
|
+
return (new_x_nm, new_f_nm)
|
|
371
|
+
|
|
372
|
+
def shift(self, delta_eV):
|
|
373
|
+
"""
|
|
374
|
+
"""
|
|
375
|
+
return map(lambda coord: self.shift_coord(coord, delta_eV), self.coordinates)
|
|
376
|
+
|
|
377
|
+
|
|
336
378
|
def plot_gaussian(self):
|
|
337
379
|
"""
|
|
338
380
|
Plot a gaussian distribution around our excited state energies.
|
digichem/test/conftest.py
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import numpy
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
2
4
|
|
|
3
5
|
# Set numpy errors (not sure why this isn't the default...)
|
|
4
6
|
numpy.seterr(invalid = 'raise', divide = 'raise')
|
|
7
|
+
|
|
8
|
+
# Expand path to include mocks.
|
|
9
|
+
os.environ["PATH"] = str(Path(__file__).parent / "mock") + os.pathsep + os.environ["PATH"]
|