Skip to content

Commit

Permalink
Revert to min/max convention for stopping criteria.
Browse files Browse the repository at this point in the history
  • Loading branch information
ejpaul committed Dec 23, 2023
1 parent 16a9640 commit 2087a79
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 73 deletions.
90 changes: 64 additions & 26 deletions src/simsopt/field/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
logger = logging.getLogger(__name__)

__all__ = ['SurfaceClassifier', 'LevelsetStoppingCriterion',
'ToroidalFluxStoppingCriterion','RStoppingCriterion','ZStoppingCriterion',
'MinToroidalFluxStoppingCriterion','MaxToroidalFluxStoppingCriterion',
'MinRStoppingCriterion','MinZStoppingCriterion',
'MaxRStoppingCriterion','MaxZStoppingCriterion',
'IterationStoppingCriterion', 'ToroidalTransitStoppingCriterion',
'compute_fieldlines', 'compute_resonances',
'compute_poloidal_transits', 'compute_toroidal_transits',
Expand Down Expand Up @@ -738,29 +740,41 @@ def __init__(self, classifier):
else:
sopp.LevelsetStoppingCriterion.__init__(self, classifier)

class ToroidalFluxStoppingCriterion(sopp.ToroidalFluxStoppingCriterion):
class MinToroidalFluxStoppingCriterion(sopp.MinToroidalFluxStoppingCriterion):
"""
Stop the iteration once a particle falls above or below a critical value of
Stop the iteration once a particle falls below a critical value of
``s``, the normalized toroidal flux. This :class:`StoppingCriterion` is
important to use when tracing particles in flux coordinates. For example,
the poloidal angle becomes ill-defined at the magnetic axis. This should
only be used when tracing trajectories in a flux coordinate system (i.e.,
:class:`trace_particles_boozer`).
important to use when tracing particles in flux coordinates, as the poloidal
angle becomes ill-defined at the magnetic axis. This should only be used
when tracing trajectories in a flux coordinate system (i.e., :class:`trace_particles_boozer`).
Usage:
.. code-block::
stopping_criteria=[ToroidalFluxStopingCriterion(crit_s,min_bool)]
stopping_criteria=[MinToroidalFluxStopingCriterion(s)]
where ``crit_s`` is the value of the critical normalized toroidal flux and
``min_bool'' is a boolean indicating whether to stop when
``s'' is less than the critical value (``true'') or
greater than the critical value (``false'').
where ``s`` is the value of the minimum normalized toroidal flux.
"""
pass


class MaxToroidalFluxStoppingCriterion(sopp.MaxToroidalFluxStoppingCriterion):
"""
Stop the iteration once a particle falls above a critical value of
``s``, the normalized toroidal flux. This should only be used when tracing
trajectories in a flux coordinate system (i.e., :class:`trace_particles_boozer`).
Usage:
.. code-block::
stopping_criteria=[MaxToroidalFluxStopingCriterion(s)]
where ``s`` is the value of the maximum normalized toroidal flux.
"""
pass

class ToroidalTransitStoppingCriterion(sopp.ToroidalTransitStoppingCriterion):
"""
Stop the iteration once the maximum number of toroidal transits is reached.
Expand All @@ -783,39 +797,63 @@ class IterationStoppingCriterion(sopp.IterationStoppingCriterion):
"""
pass

class RStoppingCriterion(sopp.RStoppingCriterion):
class MinRStoppingCriterion(sopp.MinRStoppingCriterion):
"""
Stop the iteration once a particle falls below a critical value of
``R``, the radial cylindrical coordinate.
Usage:
.. code-block::
stopping_criteria=[MinRStopingCriterion(crit_r)]
where ``crit_r`` is the value of the critical coordinate.
"""
pass

class MinZStoppingCriterion(sopp.MinZStoppingCriterion):
"""
Stop the iteration once a particle falls below a critical value of
``Z``, the cylindrical vertical coordinate.
Usage:
.. code-block::
stopping_criteria=[MinZStopingCriterion(crit_z)]
where ``crit_z`` is the value of the critical coordinate.
"""
pass

class MaxRStoppingCriterion(sopp.MaxRStoppingCriterion):
"""
Stop the iteration once a particle falls above or below a critical value of
Stop the iteration once a particle goes above a critical value of
``R``, the radial cylindrical coordinate.
Usage:
.. code-block::
stopping_criteria=[RStopingCriterion(crit_r,min_bool)]
stopping_criteria=[MaxRStopingCriterion(crit_r,min_bool)]
where ``crit_r`` is the value of the critical coordinate and
``min_bool'' is a boolean indicating whether to stop when
``R'' is less than the critical value (``true'') or
greater than the critical value (``false'').
where ``crit_r`` is the value of the critical coordinate.
"""
pass

class ZStoppingCriterion(sopp.ZStoppingCriterion):
class MaxZStoppingCriterion(sopp.MaxZStoppingCriterion):
"""
Stop the iteration once a particle falls above or below a critical value of
Stop the iteration once a particle gove above or below a critical value of
``Z``, the cylindrical vertical coordinate.
Usage:
.. code-block::
stopping_criteria=[ZStopingCriterion(crit_z,min_bool)]
stopping_criteria=[MaxZStopingCriterion(crit_z,min_bool)]
where ``crit_z`` is the value of the critical coordinate and
``min_bool'' is a boolean indicating whether to stop when
``Z'' is less than the critical value (``true'') or
greater than the critical value (``false'').
where ``crit_z`` is the value of the critical coordinate.
"""
pass

Expand Down
18 changes: 12 additions & 6 deletions src/simsoptpp/python_tracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ void init_tracing(py::module_ &m){
py::class_<StoppingCriterion, shared_ptr<StoppingCriterion>>(m, "StoppingCriterion");
py::class_<IterationStoppingCriterion, shared_ptr<IterationStoppingCriterion>, StoppingCriterion>(m, "IterationStoppingCriterion")
.def(py::init<int>());
py::class_<RStoppingCriterion, shared_ptr<RStoppingCriterion>, StoppingCriterion>(m, "RStoppingCriterion")
.def(py::init<double,bool>());
py::class_<ZStoppingCriterion, shared_ptr<ZStoppingCriterion>, StoppingCriterion>(m, "ZStoppingCriterion")
.def(py::init<double,bool>());
py::class_<ToroidalFluxStoppingCriterion, shared_ptr<ToroidalFluxStoppingCriterion>, StoppingCriterion>(m, "ToroidalFluxStoppingCriterion")
.def(py::init<double,bool>());
py::class_<MinRStoppingCriterion, shared_ptr<MinRStoppingCriterion>, StoppingCriterion>(m, "MinRStoppingCriterion")
.def(py::init<double>());
py::class_<MinZStoppingCriterion, shared_ptr<MinZStoppingCriterion>, StoppingCriterion>(m, "MinZStoppingCriterion")
.def(py::init<double>());
py::class_<MaxRStoppingCriterion, shared_ptr<MaxRStoppingCriterion>, StoppingCriterion>(m, "MaxRStoppingCriterion")
.def(py::init<double>());
py::class_<MaxZStoppingCriterion, shared_ptr<MaxZStoppingCriterion>, StoppingCriterion>(m, "MaxZStoppingCriterion")
.def(py::init<double>());
py::class_<MaxToroidalFluxStoppingCriterion, shared_ptr<MaxToroidalFluxStoppingCriterion>, StoppingCriterion>(m, "MaxToroidalFluxStoppingCriterion")
.def(py::init<double>());
py::class_<MinToroidalFluxStoppingCriterion, shared_ptr<MinToroidalFluxStoppingCriterion>, StoppingCriterion>(m, "MinToroidalFluxStoppingCriterion")
.def(py::init<double>());
py::class_<ToroidalTransitStoppingCriterion, shared_ptr<ToroidalTransitStoppingCriterion>, StoppingCriterion>(m, "ToroidalTransitStoppingCriterion")
.def(py::init<int,bool>());
py::class_<LevelsetStoppingCriterion<PyTensor>, shared_ptr<LevelsetStoppingCriterion<PyTensor>>, StoppingCriterion>(m, "LevelsetStoppingCriterion")
Expand Down
68 changes: 40 additions & 28 deletions src/simsoptpp/tracing.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,51 +44,63 @@ class ToroidalTransitStoppingCriterion : public StoppingCriterion {
};
};

class ToroidalFluxStoppingCriterion : public StoppingCriterion{
class MaxToroidalFluxStoppingCriterion : public StoppingCriterion{
private:
double crit_s;
bool min_bool;
double max_s;
public:
ToroidalFluxStoppingCriterion(double crit_s, bool min_bool) : crit_s(crit_s), min_bool(min_bool) {};
MaxToroidalFluxStoppingCriterion(double max_s) : max_s(max_s) {};
bool operator()(int iter, double t, double s, double theta, double zeta) override {
if (min_bool) {
return s<=crit_s;
} else {
return s>=crit_s;
}

return s>=max_s;
};
};

class MinToroidalFluxStoppingCriterion : public StoppingCriterion{
private:
double min_s;
public:
MinToroidalFluxStoppingCriterion(double min_s) : min_s(min_s) {};
bool operator()(int iter, double t, double s, double theta, double zeta) override {
return s<=min_s;
};
};

class ZStoppingCriterion : public StoppingCriterion{
class MinZStoppingCriterion : public StoppingCriterion{
private:
double crit_z;
bool min_bool;
public:
ZStoppingCriterion(double crit_z, bool min_bool) : crit_z(crit_z), min_bool(min_bool) {};
MinZStoppingCriterion(double crit_z) : crit_z(crit_z) {};
bool operator()(int iter, double t, double x, double y, double z) override {
if (min_bool) {
return z<=crit_z;
} else {
return z>=crit_z;
}

return z<=crit_z;
};
};

class RStoppingCriterion : public StoppingCriterion{
class MaxZStoppingCriterion : public StoppingCriterion{
private:
double crit_z;
public:
MaxZStoppingCriterion(double crit_z) : crit_z(crit_z) {};
bool operator()(int iter, double t, double x, double y, double z) override {
return z>=crit_z;
};
};

class MinRStoppingCriterion : public StoppingCriterion{
private:
double crit_r;
bool min_bool;
public:
RStoppingCriterion(double crit_r, bool min_bool) : crit_r(crit_r), min_bool(min_bool) {};
MinRStoppingCriterion(double crit_r) : crit_r(crit_r) {};
bool operator()(int iter, double t, double x, double y, double z) override {
if (min_bool) {
return std::sqrt(x*x+y*y)<=crit_r;
} else {
return std::sqrt(x*x+y*y)>=crit_r;
}

return std::sqrt(x*x+y*y)<=crit_r;
};
};

class MaxRStoppingCriterion : public StoppingCriterion{
private:
double crit_r;
public:
MaxRStoppingCriterion(double crit_r) : crit_r(crit_r) {};
bool operator()(int iter, double t, double x, double y, double z) override {
return std::sqrt(x*x+y*y)>=crit_r;
};
};

Expand Down
11 changes: 6 additions & 5 deletions tests/field/test_fieldline.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import numpy as np

from simsopt.field.magneticfieldclasses import ToroidalField, PoloidalField, InterpolatedField, UniformInterpolationRule
from simsopt.field.tracing import compute_fieldlines, particles_to_vtk, plot_poincare_data, RStoppingCriterion, ZStoppingCriterion
from simsopt.field.tracing import compute_fieldlines, particles_to_vtk, plot_poincare_data, \
MinRStoppingCriterion, MinZStoppingCriterion, MaxRStoppingCriterion, MaxZStoppingCriterion
from simsopt.field.biotsavart import BiotSavart
from simsopt.configs.zoo import get_ncsx_data
from simsopt.field.coil import coils_via_symmetries, Coil, Current
Expand Down Expand Up @@ -145,26 +146,26 @@ def test_poincare_caryhanson(self):
# Check that R/Z is less than/greater than the maximum/minimum value.
Rmax = 1
res_tys, res_phi_hits = compute_fieldlines(
bs, [Rmax-0.02], [1], tmax=2000, stopping_criteria=[RStoppingCriterion(Rmax,False)])
bs, [Rmax-0.02], [1], tmax=2000, stopping_criteria=[MaxRStoppingCriterion(Rmax)])
assert res_phi_hits[0][0,1] == -1
assert np.all(np.sqrt(res_tys[0][:, 1]**2 + res_tys[0][:, 2]**2) < Rmax)

Rmin = 0.3
res_tys, res_phi_hits = compute_fieldlines(
bs, [Rmin+0.02], [0.3], tmax=500, stopping_criteria=[RStoppingCriterion(Rmin,True)])
bs, [Rmin+0.02], [0.3], tmax=500, stopping_criteria=[MinRStoppingCriterion(Rmin)])
assert res_phi_hits[0][0,1] == -1
assert np.all(np.sqrt(res_tys[0][:, 1]**2 + res_tys[0][:, 2]**2) > Rmin)

Zmin = -0.1
res_tys, res_phi_hits = compute_fieldlines(
bs, [0.97], [Zmin+0.02], tmax=2000, stopping_criteria=[ZStoppingCriterion(Zmin,True)]
bs, [0.97], [Zmin+0.02], tmax=2000, stopping_criteria=[MinZStoppingCriterion(Zmin)]
)
assert res_phi_hits[0][0,1] == -1
assert np.all(res_tys[0][:, 3] > Zmin)

Zmax = 0.5
res_tys, res_phi_hits = compute_fieldlines(
bs, [0.5], [Zmax-0.02], tmax=2000, stopping_criteria=[ZStoppingCriterion(Zmax,False)]
bs, [0.5], [Zmax-0.02], tmax=2000, stopping_criteria=[MaxZStoppingCriterion(Zmax)]
)
assert res_phi_hits[0][0,1] == -1
assert np.all(res_tys[0][:, 3] < Zmax)
Expand Down
16 changes: 8 additions & 8 deletions tests/field/test_particle.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from simsopt.field.tracing import trace_particles_starting_on_curve, SurfaceClassifier, \
particles_to_vtk, LevelsetStoppingCriterion, compute_gc_radius, gc_to_fullorbit_initial_guesses, \
IterationStoppingCriterion, trace_particles_starting_on_surface, trace_particles_boozer, \
ToroidalFluxStoppingCriterion, ToroidalTransitStoppingCriterion, \
MinToroidalFluxStoppingCriterion, MaxToroidalFluxStoppingCriterion, ToroidalTransitStoppingCriterion, \
compute_poloidal_transits, compute_toroidal_transits, trace_particles, compute_resonances
from simsopt.geo.surfacerzfourier import SurfaceRZFourier
from simsopt.field.boozermagneticfield import BoozerAnalytic
Expand Down Expand Up @@ -451,7 +451,7 @@ def test_energy_momentum_conservation_boozer(self):

gc_tys, gc_phi_hits = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[], mode='gc_vac',
stopping_criteria=[ToroidalFluxStoppingCriterion(.01,True), ToroidalFluxStoppingCriterion(0.99,False), ToroidalTransitStoppingCriterion(100, True)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(.01), MaxToroidalFluxStoppingCriterion(0.99), ToroidalTransitStoppingCriterion(100, True)],
tol=1e-12)

# pick 100 random points on each trace, and ensure that
Expand Down Expand Up @@ -515,7 +515,7 @@ def test_energy_momentum_conservation_boozer(self):

gc_tys, gc_phi_hits = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[], mode='gc_noK',
stopping_criteria=[ToroidalFluxStoppingCriterion(.01,True), ToroidalFluxStoppingCriterion(0.99,False), ToroidalTransitStoppingCriterion(100, True)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(.01), MaxToroidalFluxStoppingCriterion(0.99), ToroidalTransitStoppingCriterion(100, True)],
tol=1e-12)

max_energy_gc_error = np.array([])
Expand Down Expand Up @@ -567,7 +567,7 @@ def test_energy_momentum_conservation_boozer(self):

gc_tys, gc_phi_hits = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[], mode='gc',
stopping_criteria=[ToroidalFluxStoppingCriterion(.01,True), ToroidalFluxStoppingCriterion(0.99,False), ToroidalTransitStoppingCriterion(100, True)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(.01), MaxToroidalFluxStoppingCriterion(0.99), ToroidalTransitStoppingCriterion(100, True)],
tol=1e-12)

max_energy_gc_error = np.array([])
Expand Down Expand Up @@ -607,7 +607,7 @@ def test_energy_momentum_conservation_boozer(self):
# Now trace with forget_exact_path = False. Check that gc_phi_hits is the same
gc_tys, gc_phi_hits_2 = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[], mode='gc_noK',
stopping_criteria=[ToroidalFluxStoppingCriterion(.01,True), ToroidalFluxStoppingCriterion(0.99,False), ToroidalTransitStoppingCriterion(100, True)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(.01), MaxToroidalFluxStoppingCriterion(0.99), ToroidalTransitStoppingCriterion(100, True)],
tol=1e-12, forget_exact_path=True)

for i in range(len(gc_phi_hits_2)):
Expand Down Expand Up @@ -652,7 +652,7 @@ def test_compute_poloidal_toroidal_transits(self):

gc_tys, gc_phi_hits = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[], mode='gc_vac',
stopping_criteria=[ToroidalFluxStoppingCriterion(.01,True), ToroidalFluxStoppingCriterion(0.99,False), ToroidalTransitStoppingCriterion(1, True)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(.01), MaxToroidalFluxStoppingCriterion(0.99), ToroidalTransitStoppingCriterion(1, True)],
tol=1e-12)

mpol = compute_poloidal_transits(gc_tys)
Expand Down Expand Up @@ -726,7 +726,7 @@ def test_toroidal_flux_stopping_criterion(self):

gc_tys, gc_phi_hits = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[], mode='gc_vac',
stopping_criteria=[ToroidalFluxStoppingCriterion(0.4,True), ToroidalFluxStoppingCriterion(0.6,False)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(0.4), MaxToroidalFluxStoppingCriterion(0.6)],
tol=1e-12)

for i in range(Nparticles):
Expand Down Expand Up @@ -768,7 +768,7 @@ def test_compute_resonances(self):

gc_tys, gc_phi_hits = trace_particles_boozer(bsh, stz_inits, vpar_inits,
tmax=tmax, mass=m, charge=q, Ekin=Ekin, zetas=[0], mode='gc_vac',
stopping_criteria=[ToroidalFluxStoppingCriterion(0.01,True), ToroidalFluxStoppingCriterion(0.99,False), ToroidalTransitStoppingCriterion(100, True)],
stopping_criteria=[MinToroidalFluxStoppingCriterion(0.01), MaxToroidalFluxStoppingCriterion(0.99), ToroidalTransitStoppingCriterion(100, True)],
tol=1e-8)

resonances = compute_resonances(gc_tys, gc_phi_hits, delta=0.01)
Expand Down

0 comments on commit 2087a79

Please sign in to comment.