working preliminary POC
This commit is contained in:
parent
0d55853f03
commit
96c23c62d1
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
__pycache__
|
||||||
|
.pyc
|
||||||
|
venv
|
||||||
|
.idea
|
@ -2,12 +2,12 @@ from django.db import models
|
|||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
class Isotope(models.Model):
|
class Isotope(models.Model):
|
||||||
n_protons = models.IntegerField()
|
n_protons = models.IntegerField(help_text="Protons in isotope")
|
||||||
n_nucleons = models.IntegerField()
|
n_nucleons = models.IntegerField(help_text="Nucleons in isotope")
|
||||||
stable = models.BooleanField()
|
stable = models.BooleanField(help_text="Is isotope stable?")
|
||||||
symbol = models.CharField(max_length=2)
|
symbol = models.CharField(max_length=2, help_text="Symbol of isotope")
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255, help_text="Name of isotope")
|
||||||
spin_quantum_number = models.FloatField()
|
spin_quantum_number = models.FloatField(help_text="Spin quantum number")
|
||||||
gamma = models.FloatField() # MHz/T
|
gamma = models.FloatField(help_text="Gyromagnetic ratio in MHz/T") # MHz/T
|
||||||
natural_abundance = models.FloatField()
|
natural_abundance = models.FloatField(help_text="Natural abundance")
|
||||||
quadrupole_moment = models.FloatField(null=True)
|
quadrupole_moment = models.FloatField(null=True, help_text="Quadrupole moment")
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
<div class="center">
|
<div class="center">
|
||||||
<h1>Basic Calculator</h1>
|
<h1>NMR Frequency Calculator</h1>
|
||||||
|
|
||||||
<form action="result">
|
<form action="result">
|
||||||
<input type="number" name="number1" placeholder="Enter first number">
|
<table>
|
||||||
<br>
|
<tr>
|
||||||
<br>
|
<td>Source isotope:</td>
|
||||||
<input type="number" name="number2" placeholder="Enter second number">
|
<td> <input type="text" name="isotope1" placeholder="Current isotope"></td>
|
||||||
<br>
|
</tr>
|
||||||
<br>
|
<tr>
|
||||||
<button type="submit" name="add">Add</button>
|
<td>Source frequency in MHz: </td><td><input type="number" name="freq" step="any" placeholder="Larmor frequency in MHz"></td>
|
||||||
<button type="submit" name="subtract">Subtract</button>
|
</tr>
|
||||||
<button type="submit" name="multiply">Multiply</button>
|
<tr>
|
||||||
<button type="submit" name="divide">Divide</button>
|
<td>Destination isotope: <td><input type="text" name="isotope2" placeholder="Other isotope"></td>
|
||||||
|
<td><button type="submit" name="transform">Search</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Frequency range in MHz: <td><input type="number" name="freq_range" step="any" placeholder="Range in MHz"></td>
|
||||||
|
<td><button type="submit" name="search">Search</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Gradient in T/m:</td> <td><input type="number" name="gradient" step="any" placeholder="Gradient in T/m"></td>
|
||||||
|
<td><button type="submit" name="calculate">Search</button></td>
|
||||||
|
<td>(assuming 5mm sample diameter)</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,8 +1,36 @@
|
|||||||
|
|
||||||
<div class="center">
|
<div class="center">
|
||||||
The result is:
|
<table>
|
||||||
<h1>{{ans}}</h1>
|
<caption>
|
||||||
|
Table downloaded from <a href="https://easyspin.org/documentation/isotopetable.html">easyspin.org</a>
|
||||||
|
(2023-07-29 markusro)
|
||||||
|
</caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">N</th>
|
||||||
|
<th scope="col">Symbol</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">f<sub>0</sub> in MHz</th>
|
||||||
|
<th scope="col">Spin</th>
|
||||||
|
<th scope="col">Nat. ab. in %</th>
|
||||||
|
<th scope="col">γ in MHz/T</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for attr in ans %}
|
||||||
|
<tr>
|
||||||
|
{% for i in attr %}
|
||||||
|
<td>{{ i }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<a href="{% url 'home' %}">Go Back</a>
|
<a href="{% url 'home' %}">Go Back</a>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
.center {
|
.center {
|
||||||
@ -11,5 +39,50 @@
|
|||||||
border: 3px solid #a5addb;
|
border: 3px solid #a5addb;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -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.
|
# Create your views here.
|
||||||
|
|
||||||
def home(request):
|
def home(request):
|
||||||
return render(request, 'home.html')
|
return render(request, 'home.html')
|
||||||
|
|
||||||
def result(request):
|
def extract_isotope_parts(isotope_str):
|
||||||
num1 = int(request.GET.get('number1'))
|
"""Extracts the number and element from an isotope string (e.g., '23Na')."""
|
||||||
num2 = int(request.GET.get('number2'))
|
print(isotope_str)
|
||||||
|
match = re.match(r"(\d+)([A-Za-z]+)", isotope_str)
|
||||||
|
if not match:
|
||||||
if request.GET.get('add') == "":
|
raise ValueError("Invalid isotope format")
|
||||||
ans = num1 + num2
|
return int(match.group(1)), match.group(2)
|
||||||
|
|
||||||
elif request.GET.get('subtract') == "":
|
|
||||||
ans = num1 - num2
|
|
||||||
|
|
||||||
elif request.GET.get('multiply') == "":
|
|
||||||
ans = num1 * num2
|
|
||||||
|
|
||||||
|
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:
|
else:
|
||||||
ans = num1 / num2
|
spin = mark_safe(f"<sup>{isotope.spin_quantum_number.as_integer_ratio()[0]}</sup>⁄<sub>{isotope.spin_quantum_number.as_integer_ratio()[1]}</sub>")
|
||||||
|
return [isotope.n_nucleons,
|
||||||
|
mark_safe(f"<sup>{isotope.n_nucleons}</sup>{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})
|
return render(request, 'result.html', {'ans': ans})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user