From 96c23c62d107a207a6eaa84ad952df41d9aab1c8 Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Sat, 22 Mar 2025 00:32:16 +0100 Subject: [PATCH] working preliminary POC --- .gitignore | 4 + isotables/isotopapp/models.py | 18 ++--- isotables/isotopapp/templates/home.html | 35 ++++++--- isotables/isotopapp/templates/result.html | 89 +++++++++++++++++++++-- isotables/isotopapp/views.py | 81 +++++++++++++++++---- 5 files changed, 183 insertions(+), 44 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..843261c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +.pyc +venv +.idea diff --git a/isotables/isotopapp/models.py b/isotables/isotopapp/models.py index 4624681..20a97ab 100644 --- a/isotables/isotopapp/models.py +++ b/isotables/isotopapp/models.py @@ -2,12 +2,12 @@ from django.db import models # Create your models here. class Isotope(models.Model): - n_protons = models.IntegerField() - n_nucleons = models.IntegerField() - stable = models.BooleanField() - symbol = models.CharField(max_length=2) - name = models.CharField(max_length=255) - spin_quantum_number = models.FloatField() - gamma = models.FloatField() # MHz/T - natural_abundance = models.FloatField() - quadrupole_moment = models.FloatField(null=True) + n_protons = models.IntegerField(help_text="Protons in isotope") + n_nucleons = models.IntegerField(help_text="Nucleons in isotope") + stable = models.BooleanField(help_text="Is isotope stable?") + symbol = models.CharField(max_length=2, help_text="Symbol of isotope") + name = models.CharField(max_length=255, help_text="Name of isotope") + spin_quantum_number = models.FloatField(help_text="Spin quantum number") + gamma = models.FloatField(help_text="Gyromagnetic ratio in MHz/T") # MHz/T + natural_abundance = models.FloatField(help_text="Natural abundance") + quadrupole_moment = models.FloatField(null=True, help_text="Quadrupole moment") diff --git a/isotables/isotopapp/templates/home.html b/isotables/isotopapp/templates/home.html index 51a6b2f..5dde4cf 100644 --- a/isotables/isotopapp/templates/home.html +++ b/isotables/isotopapp/templates/home.html @@ -1,18 +1,29 @@
-

Basic Calculator

+

NMR Frequency Calculator

- -
-
- -
-
- - - - - + + + + + + + + + + + + + + + + + + + + + +
Source isotope:
Source frequency in MHz:
Destination isotope:
Frequency range in MHz:
Gradient in T/m: (assuming 5mm sample diameter)
diff --git a/isotables/isotopapp/templates/result.html b/isotables/isotopapp/templates/result.html index df708ca..f599d63 100644 --- a/isotables/isotopapp/templates/result.html +++ b/isotables/isotopapp/templates/result.html @@ -1,15 +1,88 @@ +
- The result is: -

{{ans}}

- + + + + + + + + + + + + + + + + {% for attr in ans %} + + {% for i in attr %} + + {% endfor %} + + {% endfor %} + +
+ Table downloaded from easyspin.org + (2023-07-29 markusro) +
NSymbolNamef0 in MHzSpinNat. ab. in %γ in MHz/T
{{ i }}
+ Go Back -
- + } + + table { + border-collapse: collapse; + border: 2px solid rgb(140 140 140); + font-family: sans-serif; + font-size: 1.2rem; + letter-spacing: 1px; + } + + caption { + caption-side: bottom; + padding: 8px; + font-weight: normal; + font-size: 0.8rem; + text-align: center; + } + + thead, + tfoot { + background-color: rgb(228 240 245); + } + + th, + td { + border: 1px solid rgb(160 160 160); + padding: 10px 4px; + text-align: center; + } + + td:last-of-type { + text-align: center; + } + + tbody > tr:nth-of-type(even) { + background-color: rgb(237 238 242); + } + + tfoot th { + text-align: right; + } + + tfoot td { + font-weight: bold; + } + + diff --git a/isotables/isotopapp/views.py b/isotables/isotopapp/views.py index 7bd9ab7..6aa6f35 100644 --- a/isotables/isotopapp/views.py +++ b/isotables/isotopapp/views.py @@ -1,25 +1,76 @@ -from django.shortcuts import render +from math import pi +from django.shortcuts import render +from django.utils.safestring import mark_safe +import re + + +from isotopapp.models import Isotope # Create your views here. def home(request): return render(request, 'home.html') -def result(request): - num1 = int(request.GET.get('number1')) - num2 = int(request.GET.get('number2')) - - - if request.GET.get('add') == "": - ans = num1 + num2 - - elif request.GET.get('subtract') == "": - ans = num1 - num2 - - elif request.GET.get('multiply') == "": - ans = num1 * num2 +def extract_isotope_parts(isotope_str): + """Extracts the number and element from an isotope string (e.g., '23Na').""" + print(isotope_str) + match = re.match(r"(\d+)([A-Za-z]+)", isotope_str) + if not match: + raise ValueError("Invalid isotope format") + return int(match.group(1)), match.group(2) +def isotope_info(isotope, field): + f_larmor = field*isotope.gamma + if isotope.spin_quantum_number.as_integer_ratio()[1]==1: + spin = int(isotope.spin_quantum_number) else: - ans = num1 / num2 + spin = mark_safe(f"{isotope.spin_quantum_number.as_integer_ratio()[0]}{isotope.spin_quantum_number.as_integer_ratio()[1]}") + return [isotope.n_nucleons, + mark_safe(f"{isotope.n_nucleons}{isotope.symbol}"), + isotope.name.capitalize(), + f"{f_larmor:.3f}", + spin, + f"{isotope.natural_abundance:.1f}", + f"{isotope.gamma*1e6:.5e}"] + +def result(request): + n1, element1 = extract_isotope_parts(request.GET.get('isotope1')) + isotope1 = Isotope.objects.filter(symbol=element1, n_nucleons=n1).get() + + + freq = float(request.GET.get('freq')) + + field_T = freq / isotope1.gamma + + close_isotopes = [] + + if request.GET.get('search') == "": + freq_range = float(request.GET.get('freq_range')) + Isotope.objects.filter() + # calculate the frequency for all isotopes and compile a list of close by isotopes + for isotope in Isotope.objects.all(): + if isotope.gamma == 0: continue + if not isotope.stable: continue + f_Larmor = field_T*isotope.gamma + if abs(f_Larmor - freq) <= freq_range: + close_isotopes.append(isotope_info(isotope, field_T)) + ans = sorted(close_isotopes, key=lambda x: x[3]) + + elif request.GET.get('calculate') == "": + sample_diameter = 5e-3 + gradient = float(request.GET.get('gradient')) + freq_range = sample_diameter/2 * gradient * isotope1.gamma + for isotope in Isotope.objects.all(): + if isotope.gamma == 0: continue + if not isotope.stable: continue + f_Larmor = field_T*isotope.gamma + if abs(f_Larmor - freq) <= freq_range: + close_isotopes.append(isotope_info(isotope, field_T)) + ans = sorted(close_isotopes, key=lambda x: x[3]) + elif request.GET.get('transform') == "": + n2, element2 = extract_isotope_parts(request.GET.get('isotope2')) + isotope2 = Isotope.objects.filter(symbol=element2, n_nucleons=n2).get() + #isotope_info(isotope2, field_T) + ans = [isotope_info(isotope2, field_T)] return render(request, 'result.html', {'ans': ans})