Skip to content

Commit 00f2336

Browse files
authored
Merge pull request #140 from hiddenSymmetries/graph_surface
Surface and MHD classes converted to graph framework
2 parents 8c449c4 + 2138e48 commit 00f2336

File tree

85 files changed

+3495
-2487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+3495
-2487
lines changed

.github/workflows/extensive_ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ jobs:
2727
strategy:
2828
fail-fast: false
2929
matrix:
30-
python-version: [3.7.10, 3.8.10, 3.9.5] # To sync with coveragerc
3130
test-type: [unit, integrated]
31+
packages: [all, vmec, spec, boost, none]
32+
python-version: [3.7.10, 3.8.10, 3.9.5] # To sync with coveragerc
3233
# spec: [true, false]
3334
# vmec: [true, false]
3435
# boost: [true]
35-
packages: [none, boost, vmec, spec, all]
3636
include:
3737
- python-version: 3.9.5
3838
test-type: unit

.github/workflows/wheel.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ jobs:
5151
with:
5252
python-version: ${{ matrix.python }}
5353

54+
- name: Upgrade pip
55+
run: python -m pip install --upgrade pip
56+
5457
- name: Install wheel builder packages
55-
run: python -m pip install --upgrade pip setuptools wheel delocate
58+
run: python -m pip install setuptools wheel delocate
5659

5760
- name: Build and repair wheels
5861
run: |

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ _skbuild/
1111
build/
1212
build-cmake/
1313
dist/
14+
.eggs/
1415
pip-wheel-metadata/
1516
*simsopt.egg-info/
1617
.DS_Store
@@ -39,9 +40,10 @@ wout*
3940
mercier*
4041
jxbout*
4142
parvmecinfo.txt
42-
threed*
43+
*threed*
4344
vmec_f90wrap.py
4445
fort.9
46+
parvmec*.txt
4547

4648
# SPEC specific
4749
spec*.sp
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
boost:
22
- 1.76
33
numpy:
4+
- 1.19
45
- 1.20
56

67
pin_run_as_build:
78
boost: x.x
8-
numpy: x.x
9+
numpy: x.x

docs/source/concepts.rst

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,71 @@ A number of quantities are implemented in :obj:`simsopt.geo.curveobjectives` and
203203

204204
The value of the quantity and its derivative with respect to the curve dofs can be obtained by calling e.g., ``CurveLength.J()`` and ``CurveLength.dJ()``.
205205

206+
.. _surfaces:
207+
206208
Surfaces
207209
~~~~~~~~
208210

209-
The second large class of geometric objects are surfaces.
210-
A :obj:`simsopt.geo.surface.Surface` is modelled as a function :math:`\Gamma:[0, 1] \times [0, 1] \to \mathbb{R}^3` and is evaluated at quadrature points :math:`\{\phi_1, \ldots, \phi_{n_\phi}\}\times\{\theta_1, \ldots, \theta_{n_\theta}\}`.
211-
212-
The usage is similar to that of the :obj:`~simsopt.geo.curve.Curve` class:
211+
The second large class of geometric objects are surfaces. A
212+
:obj:`simsopt.geo.surface.Surface` is modelled as a function
213+
:math:`\Gamma:[0, 1] \times [0, 1] \to \mathbb{R}^3` and is evaluated
214+
at quadrature points :math:`\{\phi_1, \ldots,
215+
\phi_{n_\phi}\}\times\{\theta_1, \ldots, \theta_{n_\theta}\}`. Here,
216+
:math:`\phi` is the toroidal angle and :math:`\theta` is the poloidal
217+
angle. Note that :math:`\phi` and :math:`\theta` go up to 1, not up to
218+
:math:`2 \pi`!
219+
220+
In practice, you almost never use the base
221+
:obj:`~simsopt.geo.surface.Surface` class. Rather, you typically use
222+
one of the subclasses corresponding to a specific parameterization.
223+
Presently, the available subclasses are
224+
:obj:`~simsopt.geo.surfacerzfourier.SurfaceRZFourier`,
225+
:obj:`~simsopt.geo.surfacegarabedian.SurfaceGarabedian`,
226+
:obj:`~simsopt.geo.surfacehenneberg.SurfaceHenneberg`,
227+
:obj:`~simsopt.geo.surfacexyzfourier.SurfaceXYZFourier`,
228+
and
229+
:obj:`~simsopt.geo.surfacexyztensorfourier.SurfaceXYZTensorFourier`.
230+
In many cases you can convert a surface from one type to another by going through
231+
:obj:`~simsopt.geo.surfacerzfourier.SurfaceRZFourier`, as most surface types have
232+
``to_RZFourier()`` and ``from_RZFourier()`` methods.
233+
Note that :obj:`~simsopt.geo.surfacerzfourier.SurfaceRZFourier`
234+
corresponds to the surface parameterization used internally in the VMEC and SPEC codes.
235+
However when using these codes in simsopt, any of the available surface subclasses
236+
can be used to represent the surfaces, and simsopt will automatically handle the conversion
237+
to :obj:`~simsopt.geo.surfacerzfourier.SurfaceRZFourier` when running the code.
238+
239+
The points :math:`\phi_j` and :math:`\theta_j` are used for evaluating
240+
the position vector and its derivatives, for computing integrals, and
241+
for plotting, and there are several available methods to specify these
242+
points. For :math:`\theta_j`, you typically specify a keyword
243+
argument ``ntheta`` to the constructor when instantiating a surface
244+
class. This results in a grid of ``ntheta`` uniformly spaced points
245+
between 0 and 1, with no endpoint at 1. Alternatively, you can specify
246+
a list or array of points to the ``quadpoints_theta`` keyword argument
247+
when instantiating a surface class, specifying the :math:`\theta_j`
248+
directly. If both ``ntheta`` and ``quadpoints_theta`` are specified,
249+
an exception will be raised. For the :math:`\phi` coordinate, you
250+
sometimes want points up to 1 (the full torus), sometimes up to
251+
:math:`1/n_{fp}` (one field period), and sometimes up to :math:`1/(2
252+
n_{fp})` (half a field period). These three cases can be selected by
253+
setting the ``range`` keyword argument of the surface subclasses to
254+
``"full torus"``, ``"field period"``, or ``"half period"``.
255+
Equivalently, you can set ``range`` to the constants
256+
``S.RANGE_FULL_TORUS``, ``S.RANGE_FIELD_PERIOD``, or
257+
``S.RANGE_HALF_PERIOD``, where ``S`` can be
258+
:obj:`simsopt.geo.surface.Surface` or any of its subclasses. For all
259+
of these cases, the ``nphi`` keyword argument can be set to the
260+
desired number of :math:`\phi` grid points. Alternatively, you can
261+
pass a list or array to the ``quadpoints_phi`` keyword argument of the
262+
constructor for any Surface subclass to specify the :math:`\phi_j`
263+
points directly. An exception will be raised if both ``nphi`` and
264+
``quadpoints_phi`` are specified. For more information about these
265+
arguments, see the
266+
:obj:`~simsopt.geo.surfacerzfourier.SurfaceRZFourier` API
267+
documentation.
268+
269+
The methods available to each surface class are similar to those of
270+
the :obj:`~simsopt.geo.curve.Curve` class:
213271

214272
- ``Surface.gamma()``: returns a ``(n_phi, n_theta, 3)`` array containing :math:`\Gamma(\phi_i, \theta_j)` for :math:`i\in\{1, \ldots, n_\phi\}, j\in\{1, \ldots, n_\theta\}`, i.e. returns a list of XYZ coordinates on the surface.
215273
- ``Surface.gammadash1()``: returns a ``(n_phi, n_theta, 3)`` array containing :math:`\partial_\phi \Gamma(\phi_i, \theta_j)` for :math:`i\in\{1, \ldots, n_\phi\}, j\in\{1, \ldots, n_\theta\}`.

docs/source/getting_started.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,16 @@ can be installed for your user only::
9999
If you want to build SIMSOPT locally with the optional dependencies,
100100
you can run
101101

102+
.. code-block::
103+
102104
pip install --user -e .[MPI,SPEC]
103105
104106
However, if you're using a zsh terminal (example: latest Macbook versions),
105107
you'll need to run instead
106108

107-
pip install --user -e .\[MPI,SPEC]\
109+
.. code-block::
110+
111+
pip install --user -e ".[MPI,SPEC]"
108112
109113
110114
From docker container
Lines changed: 6 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#!/usr/bin/env python3
22

3-
from simsopt.geo.graph_surface import SurfaceRZFourier
3+
from simsopt.geo.surfacerzfourier import SurfaceRZFourier
44
from simsopt.objectives.graph_least_squares import LeastSquaresProblem
55
from simsopt.solve.graph_serial import least_squares_serial_solve
66
"""
77
Optimize the minor radius and elongation of an axisymmetric torus to
88
obtain a desired volume and area using the graph method.
99
"""
1010

11+
print("Running 1_Simple/graph_surf_vol_area.py")
12+
print("=======================================")
1113
desired_volume = 0.6
1214
desired_area = 8.0
1315

@@ -20,43 +22,8 @@
2022
# from the space of independent variables by setting their 'fixed'
2123
# property to True.
2224
surf.fix('rc(0,0)')
23-
surf.get_return_fn_names()
2425

2526
# Approach 1
26-
27-
prob1 = LeastSquaresProblem(depends_on=surf,
28-
goals=[desired_area, desired_volume],
29-
weights=[1, 1])
30-
least_squares_serial_solve(prob1)
31-
32-
print("At the optimum using approach 1,")
33-
print(" rc(m=1,n=0) = ", surf.get_rc(1, 0))
34-
print(" zs(m=1,n=0) = ", surf.get_zs(1, 0))
35-
print(" volume = ", surf.volume())
36-
print(" area = ", surf.area())
37-
print(" objective function = ", prob1.objective())
38-
print(" -------------------------\n\n")
39-
40-
41-
# Approach 2
42-
43-
surf2 = SurfaceRZFourier()
44-
surf2.fix('rc(0,0)')
45-
prob2 = LeastSquaresProblem(depends_on=surf2,
46-
opt_return_fns=['area', 'volume'],
47-
goals=[desired_area, desired_volume],
48-
weights=[1, 1])
49-
least_squares_serial_solve(prob2)
50-
print("At the optimum using approach 2,")
51-
print(" rc(m=1,n=0) = ", surf2.get_rc(1, 0))
52-
print(" zs(m=1,n=0) = ", surf2.get_zs(1, 0))
53-
print(" volume = ", surf2.volume())
54-
print(" area = ", surf2.area())
55-
print(" objective function = ", prob2.objective())
56-
print(" -------------------------\n\n")
57-
58-
59-
# Approach 3
6027
surf3 = SurfaceRZFourier()
6128
surf3.fix('rc(0,0)')
6229
prob3 = LeastSquaresProblem(funcs_in=[surf3.area, surf3.volume],
@@ -71,7 +38,7 @@
7138
print(" objective function = ", prob3.objective())
7239
print(" -------------------------\n\n")
7340

74-
# Approach 4
41+
# Approach 2
7542
surf4 = SurfaceRZFourier()
7643
surf4.fix('rc(0,0)')
7744
prob4 = LeastSquaresProblem.from_tuples([(surf4.area, desired_area, 1),
@@ -86,21 +53,6 @@
8653
print(" objective function = ", prob4.objective())
8754
print(" -------------------------\n\n")
8855

89-
# Each target function is then equipped with a shift and weight, to
90-
# become a term in a least-squares objective function
91-
#term1 = (surf.volume, desired_volume, 1)
92-
#term2 = (surf.area, desired_area, 1)
93-
94-
# A list of terms are combined to form a nonlinear-least-squares
95-
# problem.
96-
#prob = LeastSquaresProblem([term1, term2])
97-
98-
# Solve the minimization problem:
99-
#least_squares_serial_solve(prob)
10056

101-
#print("At the optimum,")
102-
#print(" rc(m=1,n=0) = ", surf.get_rc(1, 0))
103-
#print(" zs(m=1,n=0) = ", surf.get_zs(1, 0))
104-
#print(" volume = ", surf.volume())
105-
#print(" area = ", surf.area())
106-
#print(" objective function = ", prob.objective())
57+
print("End of 1_Simple/graph_surf_vol_area.py")
58+
print("=======================================")

examples/1_Simple/just_a_quadratic.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# To print out diagnostic information along the way, uncomment this
1414
# next line:
1515
#logging.basicConfig(level=logging.INFO)
16+
print("Running 1_Simple/just_a_quadratic.py")
17+
print("====================================")
1618

1719
# Define some Target objects that depend on Parameter objects. In the
1820
# future these functions would involve codes like VMEC, but for now we
@@ -44,3 +46,5 @@
4446
print("An optimum was found at x=", iden1.x, ", y=", iden2.x, \
4547
", z=", iden3.x)
4648
print("The minimum value of the objective function is ", prob.objective())
49+
print("End of 1_Simple/just_a_quadratic.py")
50+
print("====================================")

examples/1_Simple/logger_example.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
# Serial logging
1111
initialize_logging(filename='serial.log')
12+
print("Running 1_Simple/logger_example.py")
13+
print("==================================")
1214
for i in range(2):
1315
logging.info("Hello (times %i) from serial job" % (i+1))
1416

@@ -23,3 +25,5 @@
2325
initialize_logging(mpi=True, filename='mpi.log')
2426
for i in range(2):
2527
logging.warning("Hello (times %i) from mpi job" % (i+1))
28+
print("End of 1_Simple/logger_example.py")
29+
print("==================================")

examples/1_Simple/minimize_curve_length.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
Minimize the length of a curve, holding the 0-frequency Fourier mode fixed.
1111
The result should be a circle.
1212
"""
13-
13+
print("Running 1_Simple/minimize_curve_length.py")
14+
print("==============================================")
1415
# Create a curve:
1516
nquadrature = 100
1617
nfourier = 4
@@ -58,3 +59,5 @@
5859
print(' Final curve length: ', obj.J())
5960
print(' Expected final length: ', 2 * np.pi * x0[0])
6061
print(' objective function: ', prob.objective())
62+
print("End of 1_Simple/minimize_curve_length.py")
63+
print("==============================================")

0 commit comments

Comments
 (0)