Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
f2d4df2
[#1166] Type hinting, docstr and better default for d3cross, d2cross
dvezinet Jan 6, 2026
a2bceb5
[#1166] Cleanup
dvezinet Jan 7, 2026
4d4c715
[#1166] _xray_thin_target_integrated.py updated to load d2cross direc…
dvezinet Jan 8, 2026
4153bd6
[#1166] operational for basics
dvezinet Jan 8, 2026
c6c3eca
[#1166] operational for basics
dvezinet Jan 8, 2026
da00886
[#1166] minor cleanup
dvezinet Jan 9, 2026
86e2d48
[#1166] d2cross loop on E_e0, E_ph and theta_ph => too slow
dvezinet Jan 9, 2026
c1b3b1b
[#1166] d2cross now looping over just the largest dimension (no angle)
dvezinet Jan 9, 2026
75c8262
[#1166] d3cross trying minor optimization
dvezinet Jan 9, 2026
9fa3adc
[#1166] plot() of d2cross and d2cross filtered now take custom x/y sc…
dvezinet Jan 9, 2026
e854e79
[#1166] default version now at top of file
dvezinet Jan 9, 2026
8cbc7d9
[#1166] Better d2cross saving
dvezinet Jan 9, 2026
da504ff
[#1166] Almost there loading d2cross
dvezinet Jan 9, 2026
ea1c4dd
[#1166] plot_xray_thin_d2cross_ei_anisotropy(d2cross=pfe, dcases=str)…
dvezinet Jan 12, 2026
ae96121
[#1166] plot_xray_thin_d2cross_ei_anisotropy() polished
dvezinet Jan 12, 2026
c5ac714
[#1166] uniformized axes names + cascading dcases
dvezinet Jan 12, 2026
fd6e415
[#1166] Started integrand
dvezinet Jan 12, 2026
761734d
[#1166] dranges and dtrans operational
dvezinet Jan 14, 2026
ddf44e0
[#1166] Revized cases
dvezinet Jan 16, 2026
e826448
[#1166] Better legend
dvezinet Jan 20, 2026
5ced55f
[#1166] Better safeguards against wrong input for distributions
dvezinet Jan 20, 2026
7856f32
[#1166] Better docstr
dvezinet Jan 21, 2026
68057e0
[#1166] get_d2cross_phi(d2cross=pfe) operational
dvezinet Jan 21, 2026
4529883
[#1166] Debugged move_diagnostic_3d()
dvezinet Feb 4, 2026
d72df4b
[#1166] get_dist1d_E() implemented (from 2d)
dvezinet Feb 12, 2026
a1beeea
[#1166] get_xray_thin_integ_dist(ddist=dict) implemented
dvezinet Feb 12, 2026
59b2ba7
[#1166] get_d2cross_phi(d2cross_phi=dict or str) implemented
dvezinet Feb 12, 2026
3f9eb8b
[#1166] plot_xray_thin_integ_dist_filter_anisotropy() - advancing dem…
dvezinet Feb 12, 2026
53d4708
[#1166] Debugging emiss and resp nan
dvezinet Feb 12, 2026
f5ec3b7
[#1166] d3cross now more robust vs nan
dvezinet Feb 19, 2026
7938598
[#1166] Added pbs jobs for d2cross
dvezinet Feb 19, 2026
f1d8d31
[#1166] Better d2cross file naming
dvezinet Feb 19, 2026
f5d6a77
[#1166] Updated file name
dvezinet Feb 19, 2026
89ac242
[#1166] Adjust ~/projects/tofu/tofu/physics_tools/electrons/emission …
dvezinet Feb 19, 2026
be47ca1
[#1166] Debug PBS
dvezinet Feb 19, 2026
6b193f4
[#1166] cleanup
dvezinet Feb 19, 2026
8d84ddd
[#1166] cleanup 2
dvezinet Feb 19, 2026
f417656
[#1166] Debug PBS
dvezinet Feb 19, 2026
5d64a51
[#1166] PBS operational
dvezinet Feb 19, 2026
a3a11cc
[#1166] Try d2cross 9x10x11 EH + add timedelta print to pbs job output
dvezinet Feb 19, 2026
f721da6
[#1166] Debug version in _pbs_d2cross.py
dvezinet Feb 19, 2026
536e348
[#1166] added version to save file name for d2cross_phi + added pbs f…
dvezinet Feb 19, 2026
129fcbb
[#1166] pbd d2cross_phi larger (91x200x201 EH)
dvezinet Feb 19, 2026
d0c7b90
[#1166] Removed python 3.9 from tests
dvezinet Feb 19, 2026
6a601d9
[#1166] updated python and spectrally versions
dvezinet Feb 19, 2026
e63c40f
[#1166] nan-check on responsivity
dvezinet Feb 20, 2026
e6c2f62
[#1166] nan-check on d3cross
dvezinet Feb 20, 2026
dad2b9e
[#1166] finalized emiss plotting for _xray_thin_target_integrated_dis…
dvezinet Feb 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-complete-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
os: [ubuntu-latest]
# macOS-latest: issue with latex + meson fails build
# windows-latest: issue with TcL install error + meson fails to build
python-version: ['3.9', '3.10', '3.11']
python-version: ['3.10', '3.11']
exclude:
- python-version: ['3.11']
os: windows-latest
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ os = import('fs')
# ------------------------

min_numpy_version = '1.26.4' # keep in sync with pyproject.toml
min_python_version = '3.9' # keep in sync with pyproject.toml
min_python_version = '3.10' # keep in sync with pyproject.toml

python_version = py.language_version()
if python_version.version_compare(f'<@min_python_version@')
Expand Down
8 changes: 2 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ maintainers = [
keywords = [
"tomography", "fusion", "synthetic diagnostic", "diagnostic design",
]
requires-python = ">=3.9"
requires-python = ">=3.10,<3.12"
dependencies = [
"numpy<1.25", # for astropy compatibility vs deprecated np.product
# "PySide2 ; platform_system != 'Windows'",
Expand All @@ -58,10 +58,6 @@ classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Natural Language :: English",
Expand All @@ -81,7 +77,7 @@ tofu = "scripts.main:main"
[project.optional-dependencies]
full = [
"Polygon3",
'spectrally>=0.0.9',
'spectrally>=0.0.10',
'pytest', # For testing
'pytest-xvfb', # For GUI testing
'pytest-cov', # For coverage
Expand Down
7 changes: 3 additions & 4 deletions tofu/data/_class08_move3d_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,15 @@ def _add_asis(
if isinstance(kk, tuple):
for ii, ki in enumerate(kk):
k0 = ki.split('_')[0]
if dgeom0.get(k0) is not None:
if isinstance(dgeom0.get(k0), tuple):
dgeom[ki] = coll.ddata[dgeom0[k0][ii]]['data']

elif dgeom0.get(kk) is not None:

if isinstance(dgeom0[kk], str):
dgeom[kk] = coll.ddata[kk]['data']

else:
if dgeom0.get(kk) is not None:
dgeom[kk] = dgeom0[kk]
elif not isinstance(dgeom0[kk], tuple):
dgeom[kk] = dgeom0[kk]

return
23 changes: 12 additions & 11 deletions tofu/data/_class08_move_rotate3d_by.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,26 +268,27 @@ def _rotate_camera(
# unit vects
lk_vect = ['nin', 'e0', 'e1']
for kk in lk_vect:
if dgeom0.get(kk) is not None:
dgeom[kk] = np.r_[_rotate_pts(

if isinstance(dgeom0.get(kk), tuple):
kx, ky, kz = f"{kk}_x", f"{kk}_y", f"{kk}_z"
dgeom[kx], dgeom[ky], dgeom[kz] = _rotate_pts(
axis_pt,
axis_vect,
angle,
*dgeom0[kk],
coll.ddata[dgeom0[kk][0]]['data'],
coll.ddata[dgeom0[kk][1]]['data'],
coll.ddata[dgeom0[kk][2]]['data'],
isvect=True,
)]
)

if dgeom0.get(f"{kk}_x") is not None:
kx, ky, kz = f"{kk}_x", f"{kk}_y", f"{kk}_z"
dgeom[kx], dgeom[ky], dgeom[kz] = _rotate_pts(
elif dgeom0.get(kk) is not None:
dgeom[kk] = np.r_[_rotate_pts(
axis_pt,
axis_vect,
angle,
dgeom0[kx],
dgeom0[ky],
dgeom0[kz],
*dgeom0[kk],
isvect=True,
)
)]

# ----------------
# add as-is
Expand Down
6 changes: 3 additions & 3 deletions tofu/data/_class08_move_translate3d_by.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ def _translate_camera(
# asis
lk_asis = [
'nin', 'e0', 'e1',
'nin_x', 'nin_y', 'nin_z',
'e0_x', 'e0_y', 'e0_z',
'e1_x', 'e1_y', 'e1_z',
('nin_x', 'nin_y', 'nin_z'),
('e0_x', 'e0_y', 'e0_z'),
('e1_x', 'e1_y', 'e1_z'),
('outline_x0', 'outline_x1'),
]

Expand Down
1 change: 1 addition & 0 deletions tofu/physics_tools/electrons/distribution/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ._runaway_growth import get_RE_critical_dreicer_electric_fields
from ._runaway_growth import get_RE_growth_source_terms
from ._distribution import main as get_distribution
from ._distribution import get_dist1d_E
from ._distribution_plot import main as plot_distribution
from ._distribution_study import study_RE_vs_Maxwellian_distribution
193 changes: 190 additions & 3 deletions tofu/physics_tools/electrons/distribution/_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import numpy as np
import scipy.integrate as scpinteg
import scipy.constants as scpct
import scipy.stats as scpstats
import astropy.units as asunits
import datastock as ds


from . import _distribution_check as _check
Expand Down Expand Up @@ -161,13 +163,25 @@ def main(
inan = np.isnan(ddist['dist'][kdist]['dist']['data'])
ddist['dist'][kdist]['dist']['data'][inan] = 0.

# neg => error
ineg = ddist['dist'][kdist]['dist']['data'] < 0.
if np.any(ineg):
msg = (
"Electron dist has negative values!\n"
f"\t- dist = '{kdist}'\n"
f"\t- version = '{dfunc[kdist]['version']}'\n"
f"\t- module = {dfunc[kdist]['func'].__module__}\n"
f"\t- func = {dfunc[kdist]['func'].__name__}\n"
)
raise Exception(msg)

# scale
ne_re = _scale(
din=din,
ddist=ddist,
kdist=kdist,
dcoords=dcoords,
version=version,
version=dfunc[kdist]['version'],
)

# --------------
Expand All @@ -185,7 +199,7 @@ def main(
ddist=ddist,
kdist=kdist,
dcoords=dcoords,
version=version,
version=dfunc[kdist]['version'],
)

# store
Expand Down Expand Up @@ -355,7 +369,10 @@ def _get_velocity_par(ddist, kdist):
energy_kinetic_eV=ddist['coords']['x0']['data'],
)['velocity_ms']
units = v_par_ms['units']
v_par_ms = v_par_ms['data']
# v_par_ms = v_par_ms['data']

# assume 0 drift velocity => average v_par = 0
v_par_ms = np.zeros(v_par_ms['data'].shape)

else:
raise NotImplementedError(kcoords)
Expand All @@ -372,6 +389,175 @@ def _get_velocity_par(ddist, kdist):
return velocity_par


# #####################################################
# #####################################################
# Get Energy
# #####################################################


def get_dist1d_E(ddist, kdist, nbins=None):

# -----------
# check inputs
# -----------

nbins = int(ds._generic_check._check_var(
nbins, 'nbins',
types=(float, int),
default=ddist['coords']['x0']['data'].size,
sign='>3',
))

# -----------
# prepare
# -----------

kcoords = tuple([
ddist['coords'][kk]['key'] for kk in ['x0', 'x1']
if ddist['coords'].get(kk) is not None
])
shape = ddist['dist'][kdist]['dist']['data'].shape

# -----------
# (E, theta)
# -----------

if kcoords == ('E_eV', 'theta'):

assert ddist['coords']['x0']['units'] == 'eV'
E = ddist['coords']['x0']['data']

dist1d = scpinteg.trapezoid(
ddist['dist'][kdist]['dist']['data'],
x=ddist['coords']['x1']['data'],
axis=-1,
)
units = (
asunits.Unit(ddist['dist'][kdist]['dist']['units'])
* asunits.Unit(ddist['coords']['x1']['units'])
)

# -----------
# (E, pitch)
# -----------

elif kcoords == ('E_eV', 'pitch'):

assert ddist['coords']['x0']['units'] == 'eV'
E = ddist['coords']['x0']['data']

dist1d = scpinteg.trapezoid(
ddist['dist'][kdist]['dist']['data'],
x=ddist['coords']['x1']['data'],
axis=-1,
)
units = (
asunits.Unit(ddist['dist'][kdist]['dist']['units'])
* asunits.Unit(ddist['coords']['x1']['units'])
)

# -----------
# (p_par, p_perp)
# -----------

elif kcoords == ('p_par_norm', 'p_perp_norm'):

sli = (None,)*(len(shape)-2) + (slice(None),)*2
pnorm = np.sqrt(
ddist['coords']['x0']['data'][:, None]**2
+ ddist['coords']['x1']['data'][None, :]**2
)[sli]
pnorm = np.broadcast_to(pnorm, shape)

# abs(velocity)
energy = _convert.convert_momentum_velocity_energy(
momentum_normalized=pnorm,
)['energy_kinetic_eV']

# nbins
E = np.linspace(np.min(energy)-1e-14, np.max(energy)+1e-14, nbins + 1)
dE = E[1] - E[0]

# binning
dist1d = scpstats.binned_statictics(
energy,
ddist['dist'][kdist]['dist']['data'],
bins=E,
statistic='sum',
).statistic / dE
E = 0.5*(E[1:] + E[:-1])

units = (
asunits.Unit(ddist['dist'][kdist]['dist']['units'])
/ asunits.unit('eV')
)

# -----------
# (v_par, v_perp)
# -----------

elif kcoords == ('v_par_norm', 'v_perp_norm'):

sli = (None,)*(len(shape)-2) + (slice(None),)*2
vv = np.sqrt(
ddist['coords']['x0']['data'][:, None]**2
+ ddist['coords']['x1']['data'][None, :]**2
)[sli]
vv = np.broadcast_to(vv, shape)

# abs(velocity)
energy = _convert.convert_momentum_velocity_energy(
velocity_ms=vv,
)['energy_kinetic_eV']

# nbins
E = np.linspace(np.min(energy)-1e-14, np.max(energy)+1e-14, nbins + 1)
dE = E[1] - E[0]

# binning
dist1d = scpstats.binned_statictics(
energy,
ddist['dist'][kdist]['dist']['data'],
bins=E,
statistic='sum',
).statistic / dE
E = 0.5*(E[1:] + E[:-1])

units = (
asunits.Unit(ddist['dist'][kdist]['dist']['units'])
/ asunits.unit('eV')
)

# -----------
# (E,)
# -----------

elif kcoords == ('E_eV',):

E = ddist['coords']['x0']['data']
dist1d = ddist['dist'][kdist]['dist']['data']
units = ddist['dist'][kdist]['dist']['units']

else:
raise NotImplementedError(kcoords)

# ---------------
# abs() => v_par
# ---------------

ddist1d = {
'E': {
'data': E,
'units': 'eV',
},
'dist': {
'data': dist1d,
'units': units,
},
}

return ddist1d

# #####################################################
# #####################################################
# Integrate numerically
Expand Down Expand Up @@ -401,6 +587,7 @@ def _integrate(
)
ne = ddist['dist'][kdist]['dist']['data']
x0 = dcoords['x0']['data']

else:
current = scpinteg.trapezoid(
scpct.e
Expand Down
Loading
Loading