Skip to content

Commit

Permalink
final improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
dehe1011 committed Feb 7, 2025
1 parent 712c66e commit 99adaee
Show file tree
Hide file tree
Showing 16 changed files with 3,114 additions and 88 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ include MANIFEST.in
include requirements/*.txt

# Include files in the package's root directory
include qDNA/visualization/*.mplstyle
include qDNA/*.mplstyle
include qDNA/defaults.yaml

# Include all .py files
Expand Down
25 changes: 11 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,24 @@ classifiers = [
authors = [
{ name = "Dennis Herb", email = "[email protected]" },
]
dynamic = ["version"]
dynamic = ["version", "dependencies", "optional-dependencies"]

dependencies = [
"pandas",
"qutip",
"pyyaml",
"matplotlib",
"tqdm",
"customtkinter",
"openpyxl",
]
[tool.setuptools]
packages = ["qDNA", "qDNA.gui"]

[tool.setuptools.dynamic]
version = {attr = "qDNA.__version__"}
dependencies = {file = "requirements/requirements.txt"}

[tool.setuptools.dynamic.optional-dependencies]
dev = { file = "requirements/dev-requirements.txt" }
docs = { file = "requirements/doc-requirements.txt" }

[project.urls]
documentation = "https://quantumdna.readthedocs.io/en/latest/"
repository = "https://github.com/dehe1011/QuantumDNA"
tutorials = "https://github.com/dehe1011/QuantumDNA-notbooks"

# Optional dependencies for documentation
[project.optional-dependencies]
doc = ["sphinx>=3.0", "sphinx_rtd_theme", "sphinxcontrib-bibtex", "numpydoc"]

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
2 changes: 1 addition & 1 deletion qDNA/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.1.9"
__version__ = "0.1.10"

import pathlib
import os
Expand Down
2,986 changes: 2,986 additions & 0 deletions qDNA/data/logs/qDNA.log

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions qDNA/data/raw/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@
"Mantela2021",
"Simserides2024",
"Herb2024",
"1BNA",
"1BNA1",
"wildtype_ATGAC",
"modified_ATGAC"
"1BNA"
],
"SPECTRAL_DENSITIES": [
"debye",
Expand Down
5 changes: 5 additions & 0 deletions qDNA/dna_seq.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import re
from itertools import product

from . import TB_MODELS_PROPS
from qDNA.tools import DNA_BASES, TB_MODELS_PROPS

__all__ = ["DNA_Seq", "create_upper_strands"]
Expand Down Expand Up @@ -78,6 +79,10 @@ def __init__(
self.lower_strand = lower_strand
self.methylated = methylated
self.tb_model_name = tb_model_name
TB_MODELS = list(TB_MODELS_PROPS.keys())
assert (
self.tb_model_name in TB_MODELS
), f"tb_model_name must be a predefined model {TB_MODELS}"

# Get the properties of the tight-binding model
self.tb_model_props = TB_MODELS_PROPS[self.tb_model_name]
Expand Down
1 change: 1 addition & 0 deletions qDNA/gui/pdb_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def __init__(self, master, configs):

self.tb_model_combobox = ctk.CTkComboBox(self, values=self.configs["TB_MODELS"])
self.tb_model_combobox.grid(row=3, column=1, padx=10, pady=10)
self.tb_model_combobox.set("ELM")

self.notes_label = ctk.CTkLabel(self, text="Notes:")
self.notes_label.grid(row=4, column=1, padx=10, pady=0)
Expand Down
53 changes: 42 additions & 11 deletions qDNA/gui/plot_options_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,35 +55,66 @@ def __init__(self, master, **kwargs):
)

def _calc_lifetime(self):
assert (
self.kwargs["description"] == "2P"
), "2P description is required for the calculation."
assert (
self.kwargs["relaxation"] == True
), "Groundstate is required for the calculation."

lifetime = calc_lifetime(**self.kwargs)
if isinstance(lifetime, str):
print("---------------------------")
print(f"Exciton Lifetime: {lifetime}")
print(f"Exciton Lifetime: {lifetime}" "\n-------------------------------")
else:
print("---------------------------")
print(f"Exciton Lifetime: {lifetime} fs")
print(
f"Exciton Lifetime: {lifetime} fs" "\n-------------------------------"
)

def _calc_dipole(self):
assert (
self.kwargs["description"] == "2P"
), "2P description is required for the calculation."
assert (
self.kwargs["relaxation"] == True
), "Groundstate is required for the calculation."

dipole = calc_dipole(**self.kwargs)
print("---------------------------")
print(f"Charge Separation: {dipole} A")
print(f"Charge Separation: {dipole} A" "\n-------------------------------")

def _calc_dipole_moment(self):
assert (
self.kwargs["description"] == "2P"
), "2P description is required for the calculation."
assert (
self.kwargs["relaxation"] == True
), "Groundstate is required for the calculation."

dipole_moment = calc_dipole_moment(**self.kwargs)
print("---------------------------")
print(f"Dipole Moment: {dipole_moment} D")
print(f"Dipole Moment: {dipole_moment} D" "\n-------------------------------")

def _calc_exciton_transfer(self):
assert (
self.kwargs["description"] == "2P"
), "2P description is required for the calculation."
assert (
self.kwargs["relaxation"] == True
), "Groundstate is required for the calculation."
assert (
"exciton" in self.kwargs["particles"]
), "Exciton must be selected in particles."

avg_pop_upper_strand, avg_pop_lower_strand = calc_exciton_transfer(
**self.kwargs
)
avg_pop_upper_strand, avg_pop_lower_strand = (
avg_pop_upper_strand["exciton"],
avg_pop_lower_strand["exciton"],
)
print("---------------------------")
print(f"Average Exciton Population (upper strand): {avg_pop_upper_strand}")
print(f"Average Exciton Population (lower strand): {avg_pop_lower_strand}")
print(
f"Average Exciton Population (upper strand): {avg_pop_upper_strand}"
+ f"\nAverage Exciton Population (lower strand): {avg_pop_lower_strand}"
+ "\n-------------------------------"
)


# --------------------------------------------------
Expand Down
12 changes: 12 additions & 0 deletions qDNA/gui/qdna_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ def get_init_kwargs(self):
"lower_strand": self.lower_strand,
"tb_model_name": self.tb_model_name,
}

if len(self.upper_strand) >= 8:
print(
"Info: This is a long sequence. The calculation may take some time."
+ "\n-------------------------------"
)
if self.tb_model_name in ["FWM", "FLM", "FELM", "FC"]:
print(
"Info: Most predefined TB parametrizations (sources) are not available for this model."
+ "\n-------------------------------"
)

self.kwargs.update(self.init_kwargs)

# initialize dna_seq, tb_model and tb_basis
Expand Down
50 changes: 43 additions & 7 deletions qDNA/gui/scrollable_console_frame.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
# pylint: skip-file

import sys
from tkinter.scrolledtext import ScrolledText

import customtkinter as ctk
from tkinter.scrolledtext import ScrolledText
import re

# ------------------------------------------------------------

import sys
import customtkinter as ctk
from tkinter.scrolledtext import ScrolledText


class RedirectText:
def __init__(self, text_widget):
self.output = text_widget

def write(self, string):
"""Handles both stdout and stderr redirection."""
self.output.configure(state="normal")
self.output.insert(ctk.END, string)
self.output.insert(ctk.END, string.strip() + "\n") # Ensures new line output
self.output.see(ctk.END)
self.output.configure(state="disabled")

def flush(self):
pass
pass # Required for compatibility with sys.stdout and sys.stderr


class ScrollableConsoleFrame(ctk.CTkFrame):
Expand All @@ -28,14 +33,45 @@ def __init__(self, master=None, **kwargs):

# Create a ScrolledText widget for the console output
self.console_output = ScrolledText(
self, wrap="word", state="disabled", height=10, width=40
self, wrap="word", state="disabled", height=10, width=35
)
self.console_output.grid(row=0, column=0, sticky="nsew", padx=(10, 0), pady=10)

scrollbar = ctk.CTkScrollbar(self, command=self.console_output.yview)
scrollbar.grid(row=0, column=1, sticky="nsew", padx=(0, 10), pady=10)
scrollbar.grid(row=0, column=1, sticky="ns", padx=(0, 10), pady=10)
self.console_output["yscrollcommand"] = scrollbar.set

# Redirect stdout to the ScrolledText widget
# Redirect both stdout and stderr to the ScrolledText widget
self.redirected_output = RedirectText(self.console_output)
sys.stdout = self.redirected_output
sys.stderr = self.redirected_output

# Override sys.excepthook to filter AssertionError traceback
sys.excepthook = self.handle_exceptions

# Override Tkinter's error handling to capture GUI-related assertions
self.master.report_callback_exception = self.handle_tkinter_exceptions

def handle_exceptions(self, exc_type, exc_value, exc_traceback):
"""Custom exception handler that filters out assertion tracebacks."""
if issubclass(exc_type, AssertionError):
sys.stderr.write(str(exc_value) + "\n") # Only print the assertion message
else:
import traceback

sys.stderr.write(
"".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
) # Keep full error

def handle_tkinter_exceptions(self, exc_type, exc_value, exc_traceback):
"""Handles Tkinter's internal exceptions to prevent unwanted traceback."""
if issubclass(exc_type, AssertionError):
sys.stderr.write(
"Warning: " + str(exc_value) + "\n-------------------------------"
) # Print only assertion message
else:
import traceback

sys.stderr.write(
"".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
) # Full traceback
7 changes: 7 additions & 0 deletions qDNA/lcao/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ def __init__(self, xyz_identifier, xyz_data):
self.atom_identifiers = [
f"{atom}_{atom_idx}" for atom_idx, atom in enumerate(self.atoms)
] # e.g. C_0, H_1, O_2
for atom_id in self.atom_identifiers:
assert atom_id[0] in [
"H",
"C",
"N",
"O",
], "Your File contains atoms other than ['H', 'C', 'N', 'O']. Maybe you forgot to remove the sugar-phosphate backbone?"
self.atom_coordinates = self._get_atom_coordinates()
self.atom_distance_matrix = self._get_atom_distance_matrix()
self.atom_bond_matrix = (
Expand Down
2 changes: 1 addition & 1 deletion qDNA/lcao/save_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def find_xyz_files(directory):
return xyz_files


def load_xyz(filename, directory=os.path.join(DATA_DIR, "geometries")):
def load_xyz(filename, directory=os.getcwd()):
"""
Load atomic coordinates from an XYZ file.
Expand Down
3 changes: 3 additions & 0 deletions qDNA/visualization/plot_pop.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ def plot_pops_heatmap(me_solver, vmax_list=[1, 1, 1], heatmap_type="seaborn"):
1, num_particles, figsize=(6.4 * num_particles, 4.8), sharey=False
)

if len(me_solver.tb_ham.particles) == 1:
ax = [ax]

pop_dict = me_solver.get_pop()
cmaps = ["Blues", "Reds", "Greys"] # grey scale for exciton
cmaps = ["Blues", "Reds", "Greens"] # green scale for exciton
Expand Down
8 changes: 0 additions & 8 deletions requirements/old_requirements.txt

This file was deleted.

Binary file removed requirements/requirements_long.txt
Binary file not shown.
41 changes: 0 additions & 41 deletions setup.py

This file was deleted.

0 comments on commit 99adaee

Please sign in to comment.