From 56631c1ff28ac53c002a3b98461b3ef1bcc02e32 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 28 Mar 2022 06:36:37 -0400 Subject: [PATCH 01/95] graph prefix removed from module names and imports updated --- docs/source/example_islands.rst | 4 +- docs/source/example_quasisymmetry.rst | 12 +- docs/source/example_vmec_only.rst | 8 +- docs/source/optimizable.rst | 194 +++++++++--------- docs/source/overview.rst | 4 +- docs/source/simsopt._core.rst | 4 +- docs/source/simsopt.objectives.rst | 8 +- docs/source/simsopt.solve.rst | 8 +- examples/1_Simple/just_a_quadratic.py | 6 +- examples/1_Simple/minimize_curve_length.py | 4 +- ...raph_surf_vol_area.py => surf_vol_area.py} | 10 +- .../2_Intermediate/QH_fixed_resolution.py | 4 +- .../QH_fixed_resolution_boozer.py | 4 +- examples/2_Intermediate/QSC.py | 2 +- .../eliminate_magnetic_islands.py | 4 +- .../2_Intermediate/resolution_increase.py | 4 +- .../resolution_increase_boozer.py | 4 +- .../optimize_qs_and_islands_simultaneously.py | 4 +- ...ircularCrossSection_varyAxis_targetIota.py | 4 +- ...arCrossSection_varyAxis_targetIota_spec.py | 4 +- ...ircularCrossSection_varyR0_targetVolume.py | 4 +- ...arCrossSection_varyR0_targetVolume_spec.py | 4 +- ...ion_varyAxis_targetIotaAndQuasisymmetry.py | 4 +- .../2DOF_specOnly_targetIotaAndVolume.py | 4 +- .../2DOF_vmecAndSpec.py | 4 +- .../2DOF_vmecOnly_targetIotaAndVolume.py | 4 +- examples/stellarator_benchmarks/7dof.py | 4 +- src/simsopt/_core/__init__.py | 2 +- src/simsopt/_core/derivative.py | 4 +- src/simsopt/_core/finite_difference.py | 2 +- .../{graph_optimizable.py => optimizable.py} | 0 src/simsopt/field/coil.py | 2 +- src/simsopt/field/magneticfield.py | 2 +- src/simsopt/geo/curve.py | 2 +- src/simsopt/geo/curveobjectives.py | 2 +- src/simsopt/geo/surface.py | 2 +- src/simsopt/geo/surfaceobjectives.py | 2 +- src/simsopt/geo/surfacerzfourier.py | 2 +- src/simsopt/mhd/bootstrap.py | 4 +- src/simsopt/mhd/profiles.py | 2 +- src/simsopt/mhd/spec.py | 2 +- src/simsopt/mhd/vmec.py | 2 +- src/simsopt/mhd/vmec_diagnostics.py | 2 +- src/simsopt/objectives/__init__.py | 2 +- src/simsopt/objectives/fluxobjective.py | 2 +- .../{graph_functions.py => functions.py} | 4 +- ...raph_least_squares.py => least_squares.py} | 4 +- src/simsopt/objectives/utilities.py | 2 +- src/simsopt/solve/__init__.py | 2 +- src/simsopt/solve/{graph_mpi.py => mpi.py} | 4 +- .../solve/{graph_serial.py => serial.py} | 4 +- tests/core/test_derivative.py | 2 +- .../core/{test_graph_dofs.py => test_dofs.py} | 4 +- tests/core/test_finite_difference.py | 2 +- tests/core/test_integrated.py | 6 +- ...aph_optimizable.py => test_optimizable.py} | 4 +- tests/geo/test_curve_optimizable.py | 4 +- tests/mhd/test_boozer.py | 2 +- tests/mhd/test_integrated_vmec_mpi.py | 4 +- tests/mhd/test_spec.py | 4 +- tests/mhd/test_vmec.py | 6 +- tests/mhd/test_vmec_diagnostics.py | 2 +- ...least_squares.py => test_least_squares.py} | 4 +- ...least_squares.py => test_least_squares.py} | 8 +- .../solve/{test_graph_mpi.py => test_mpi.py} | 8 +- tests/util/test_dev.py | 2 +- 66 files changed, 222 insertions(+), 222 deletions(-) rename examples/1_Simple/{graph_surf_vol_area.py => surf_vol_area.py} (85%) rename src/simsopt/_core/{graph_optimizable.py => optimizable.py} (100%) rename src/simsopt/objectives/{graph_functions.py => functions.py} (99%) rename src/simsopt/objectives/{graph_least_squares.py => least_squares.py} (99%) rename src/simsopt/solve/{graph_mpi.py => mpi.py} (98%) rename src/simsopt/solve/{graph_serial.py => serial.py} (98%) rename tests/core/{test_graph_dofs.py => test_dofs.py} (99%) rename tests/core/{test_graph_optimizable.py => test_optimizable.py} (99%) rename tests/objectives/{test_graph_least_squares.py => test_least_squares.py} (95%) rename tests/solve/{test_graph_least_squares.py => test_least_squares.py} (89%) rename tests/solve/{test_graph_mpi.py => test_mpi.py} (94%) diff --git a/docs/source/example_islands.rst b/docs/source/example_islands.rst index ba5565bf8..9c548a0e2 100644 --- a/docs/source/example_islands.rst +++ b/docs/source/example_islands.rst @@ -28,8 +28,8 @@ is to import some items we will need:: import numpy as np from simsopt.mhd.spec import Spec, Residue - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_serial import least_squares_serial_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.serial import least_squares_serial_solve We then create a Spec object based on the input file:: diff --git a/docs/source/example_quasisymmetry.rst b/docs/source/example_quasisymmetry.rst index 01aeb0e56..79df22571 100644 --- a/docs/source/example_quasisymmetry.rst +++ b/docs/source/example_quasisymmetry.rst @@ -43,8 +43,8 @@ imports of the classes and functions we will need:: from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve For this problem we will want MPI for parallelized finite difference gradients. As explained below, this particular problem has 24 @@ -237,8 +237,8 @@ As usual, we begin with the necessary imports:: from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve We again split the pool of MPI processes into worker groups. Here, for simplicity, we make each process its own worker group, by omitting the @@ -347,8 +347,8 @@ In this case, the imports needed are:: from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.boozer import Boozer, Quasisymmetry - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve We again split the pool of MPI processes into worker groups and initialize a ``Vmec`` object as in the previous example:: diff --git a/docs/source/example_vmec_only.rst b/docs/source/example_vmec_only.rst index 21ff2f5db..18454c5ed 100644 --- a/docs/source/example_vmec_only.rst +++ b/docs/source/example_vmec_only.rst @@ -60,8 +60,8 @@ For simplicity, MPI parallelization will not be used for now. To start, we must import several classes:: from simsopt.mhd import Spec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_serial import least_squares_serial_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.serial import least_squares_serial_solve Then we create the equilibrium object, starting from an input file:: @@ -148,8 +148,8 @@ The complete example is then as follows:: from simsopt.util.mpi import MpiPartition from simsopt.mhd import Vmec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve # In the next line, we can adjust how many groups the pool of MPI # processes is split into. diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 17f0601c6..93d952a9c 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -5,20 +5,20 @@ The Optimizable class --------------------- A basic tool for defining optimization problems in simsopt is the -class :obj:`~simsopt._core.graph_optimizable.Optimizable`. Many +class :obj:`~simsopt._core.optimizable.Optimizable`. Many classes in simsopt are subclasses of this class. This parent class provides several functions. First, it allows for the parameters of an object to be either fixed or varied in an optimization, for a useful name string to be associated with each such degree of freedom, and for box constraints on each parameter to be set. Second, the -:obj:`~simsopt._core.graph_optimizable.Optimizable` class manages +:obj:`~simsopt._core.optimizable.Optimizable` class manages dependencies between objects. For example, if an MHD equilibrium depends on a :obj:`~simsopt.geo.surface.Surface` object representing the boundary, the equilibrium object will know it needs to recompute the equilibrium if the :obj:`~simsopt.geo.surface.Surface` changes. Third, when a set of objects with dependencies is combined into an objective function, the -:obj:`~simsopt._core.graph_optimizable.Optimizable` class +:obj:`~simsopt._core.optimizable.Optimizable` class automatically combines the non-fixed degrees of freedom into a global state vector, which can be passed to numerical optimization algorithms. @@ -27,7 +27,7 @@ Users can create their own optimizable objects in two ways. One method is to create a standard python function, and apply the :obj:`simsopt.make_optimizable()` function to it, as explained below. Or, you can directly subclass -:obj:`simsopt._core.graph_optimizable.Optimizable`. +:obj:`simsopt._core.optimizable.Optimizable`. Optimizable degrees of freedom @@ -60,7 +60,7 @@ parameter :math:`\theta`. By choosing ``order=1``, only mode numbers 0 and 1 are included. Each dof has a string name, which can be queried using the -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names` +:obj:`~simsopt._core.optimizable.Optimizable.local_dof_names` property:: >>> c.local_dof_names @@ -70,7 +70,7 @@ property:: Evidently there are nine dofs in this case. For each, the number in parentheses is the mode number :math:`m`. The values of the dofs can be read or written to using the -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` property:: +:obj:`~simsopt._core.optimizable.Optimizable.x` property:: >>> c.x @@ -86,14 +86,14 @@ be read or written to using the -2.0 Although you can use indices to retrieve selected elements of -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`, as in the last +:obj:`~simsopt._core.optimizable.Optimizable.x`, as in the last line, you *cannot* assign values to individual elements of -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`, i.e. ``c.x[2] = +:obj:`~simsopt._core.optimizable.Optimizable.x`, i.e. ``c.x[2] = 7.0`` will not work -- you can only assign an entire array to -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`. You can get or +:obj:`~simsopt._core.optimizable.Optimizable.x`. You can get or set individual dofs using their index or string name with the -:obj:`~simsopt._core.graph_optimizable.Optimizable.get()` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.set()` methods:: +:obj:`~simsopt._core.optimizable.Optimizable.get()` and +:obj:`~simsopt._core.optimizable.Optimizable.set()` methods:: >>> c.get(5) @@ -120,11 +120,11 @@ surface, fixing the high-mode-number modes of a surface, or fixing the current in a coil. All dofs in our :obj:`~simsopt.geo.curvexyzfourier.CurveXYZFourier` object are free by default. We can fix a dof using the -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()` method. +:obj:`~simsopt._core.optimizable.Optimizable.fix()` method. When a dof is fixed, it is excluded from the state vector -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`, but you can +:obj:`~simsopt._core.optimizable.Optimizable.x`, but you can still access its value either by name, or with the -:obj:`~simsopt._core.graph_optimizable.Optimizable.full_x` property +:obj:`~simsopt._core.optimizable.Optimizable.full_x` property (which gives both the free and fixed dofs):: >>> c.fix('xc(0)') @@ -141,10 +141,10 @@ still access its value either by name, or with the 1.0 To check which dofs are free, you can use the -:obj:`~simsopt._core.graph_optimizable.Optimizable.dofs_free_status` +:obj:`~simsopt._core.optimizable.Optimizable.dofs_free_status` property. The status of individual dofs can also be checked using -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_fixed` or -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_free`, specify +:obj:`~simsopt._core.optimizable.Optimizable.is_fixed` or +:obj:`~simsopt._core.optimizable.Optimizable.is_free`, specify the dof either using its index or string name :: >>> c.dofs_free_status @@ -164,11 +164,11 @@ the dof either using its index or string name :: False In addition to -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()`, you can +:obj:`~simsopt._core.optimizable.Optimizable.fix()`, you can also manipulate the fixed/free status of dofs using the functions -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix_all()`, and -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix_all()`:: +:obj:`~simsopt._core.optimizable.Optimizable.unfix()`, +:obj:`~simsopt._core.optimizable.Optimizable.fix_all()`, and +:obj:`~simsopt._core.optimizable.Optimizable.unfix_all()`:: >>> c.fix_all() >>> c.x @@ -194,9 +194,9 @@ Dependencies A collection of optimizable objects with dependencies is represented in simsopt as a directed acyclic graph (DAG): each vertex in the graph is an instance of an -:obj:`~simsopt._core.graph_optimizable.Optimizable` object, and the +:obj:`~simsopt._core.optimizable.Optimizable` object, and the direction of each edge indicates dependency. An -:obj:`~simsopt._core.graph_optimizable.Optimizable` object can depend +:obj:`~simsopt._core.optimizable.Optimizable` object can depend on the dofs of other objects, which are called its parents. The orignal object is considered a child of the parent objects. An object's "ancestors" are the an object's parents, their parents, and @@ -210,11 +210,11 @@ directly by an object, and another that applies to the dofs of an object together with its ancestors. The version that applies just to the dofs directly owned by an object has a name beginning ``local_``. For example, analogous to the properties -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names`, which +:obj:`~simsopt._core.optimizable.Optimizable.x` and +:obj:`~simsopt._core.optimizable.Optimizable.dof_names`, which include all ancestor dofs, there are also properties -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names`. +:obj:`~simsopt._core.optimizable.Optimizable.local_x` and +:obj:`~simsopt._core.optimizable.Optimizable.local_dof_names`. To demonstrate these features, we can consider the following small collection of objects: a :obj:`simsopt.field.coil.Coil`, which is a pairing of a :obj:`simsopt.field.coil.Current` with a @@ -265,14 +265,14 @@ properties include the dofs of both of its parents:: 'CurveXYZFourier1:zc(1)'] Note that the names returned by -:obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names` have the +:obj:`~simsopt._core.optimizable.Optimizable.dof_names` have the name of the object and a colon prepended, to distinguish which instance owns the dof. This unique name for each object instance can be accessed by -:obj:`~simsopt._core.graph_optimizable.Optimizable.name`. For the ``current`` and ``curve`` objects, +:obj:`~simsopt._core.optimizable.Optimizable.name`. For the ``current`` and ``curve`` objects, since they have no ancestors, their -:obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names` are the same, except +:obj:`~simsopt._core.optimizable.Optimizable.dof_names` and +:obj:`~simsopt._core.optimizable.Optimizable.local_dof_names` are the same, except that the non-``local_`` versions have the object name prepended:: >>> curve.local_dof_names @@ -293,13 +293,13 @@ that the non-``local_`` versions have the object name prepended:: ['Current1:x0'] -The :obj:`~simsopt._core.graph_optimizable.Optimizable.x` property +The :obj:`~simsopt._core.optimizable.Optimizable.x` property discussed in the previous section includes dofs from ancestors. The related property -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` applies +:obj:`~simsopt._core.optimizable.Optimizable.local_x` applies only to the dofs directly owned by an object. When the dofs of a parent are changed, the -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` property of +:obj:`~simsopt._core.optimizable.Optimizable.x` property of child objects is automatically updated:: >>> curve.x = [1.7, -0.2, 0.1, -1.1, 0.7, 0.3, 1.3, -0.6, 0.5] @@ -329,23 +329,23 @@ child objects is automatically updated:: array([], dtype=float64) Above, you can see that -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` +:obj:`~simsopt._core.optimizable.Optimizable.x` and +:obj:`~simsopt._core.optimizable.Optimizable.local_x` give the same results for ``curve`` and ``current`` since these objects have no ancestors. For ``coil``, -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` +:obj:`~simsopt._core.optimizable.Optimizable.local_x` returns an empty array because ``coil`` does not own any dofs itself, while -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` +:obj:`~simsopt._core.optimizable.Optimizable.x` is a concatenation of the dofs of its ancestors. The functions -:obj:`~simsopt._core.graph_optimizable.Optimizable.get()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.set()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_fixed()`, and -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_free()` refer +:obj:`~simsopt._core.optimizable.Optimizable.get()`, +:obj:`~simsopt._core.optimizable.Optimizable.set()`, +:obj:`~simsopt._core.optimizable.Optimizable.fix()`, +:obj:`~simsopt._core.optimizable.Optimizable.unfix()`, +:obj:`~simsopt._core.optimizable.Optimizable.is_fixed()`, and +:obj:`~simsopt._core.optimizable.Optimizable.is_free()` refer only to dofs directly owned by an object. If an integer index is supplied to these functions it must be the local index, and if a string name is supplied to these functions, it does not have the @@ -356,7 +356,7 @@ object name and colon prepended. So for instance, When some dofs are fixed in parent objects, these dofs are automatically removed from the global state vector -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` of a child +:obj:`~simsopt._core.optimizable.Optimizable.x` of a child object:: >>> curve.fix_all() @@ -369,19 +369,19 @@ object:: ['Current1:x0', 'CurveXYZFourier1:zc(0)'] -Thus, the :obj:`~simsopt._core.graph_optimizable.Optimizable.x` +Thus, the :obj:`~simsopt._core.optimizable.Optimizable.x` property of a child object is convenient to use as the state vector for numerical optimization packages, as it automatically combines the selected degrees of freedom that you wish to vary from all objects that are involved in the optimization problem. If you wish to get or set the state vector *including* the fixed dofs, you can use the -properties :obj:`~simsopt._core.graph_optimizable.Optimizable.full_x` +properties :obj:`~simsopt._core.optimizable.Optimizable.full_x` (which includes ancestors) or -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_x` +:obj:`~simsopt._core.optimizable.Optimizable.local_full_x` (which does not). The corresponding string labels including the fixed dofs can be accessed using -:obj:`~simsopt._core.graph_optimizable.Optimizable.full_dof_names` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_dof_names`:: +:obj:`~simsopt._core.optimizable.Optimizable.full_dof_names` and +:obj:`~simsopt._core.optimizable.Optimizable.local_full_dof_names`:: >>> coil.full_x @@ -409,7 +409,7 @@ Function reference ------------------ The following tables provide a reference for many of the properties -and functions of :obj:`~simsopt._core.graph_optimizable.Optimizable` +and functions of :obj:`~simsopt._core.optimizable.Optimizable` objects. Many come in a set of 2x2 variants: .. list-table:: State vector @@ -421,11 +421,11 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_x` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.full_x` + - :obj:`~simsopt._core.optimizable.Optimizable.local_full_x` + - :obj:`~simsopt._core.optimizable.Optimizable.full_x` * - Free only - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.x` + - :obj:`~simsopt._core.optimizable.Optimizable.local_x` + - :obj:`~simsopt._core.optimizable.Optimizable.x` .. list-table:: Number of elements in the state vector :widths: 20 20 20 @@ -436,11 +436,11 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_dof_size` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.full_dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.local_full_dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.full_dof_size` * - Free only - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_size` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.local_dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.dof_size` .. list-table:: String names :widths: 20 20 20 @@ -451,11 +451,11 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_dof_names` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.full_dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.local_full_dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.full_dof_names` * - Free only - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.local_dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.dof_names` .. list-table:: Whether dofs are free :widths: 20 20 20 @@ -466,8 +466,8 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_dofs_free_status` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.dofs_free_status` + - :obj:`~simsopt._core.optimizable.Optimizable.local_dofs_free_status` + - :obj:`~simsopt._core.optimizable.Optimizable.dofs_free_status` * - Free only - N/A - N/A @@ -475,12 +475,12 @@ objects. Many come in a set of 2x2 variants: Other attributes: ``name``, ``parents``, ``ancestors`` Other functions: -:obj:`~simsopt._core.graph_optimizable.Optimizable.get()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.set()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_fixed()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_free()`. +:obj:`~simsopt._core.optimizable.Optimizable.get()`, +:obj:`~simsopt._core.optimizable.Optimizable.set()`, +:obj:`~simsopt._core.optimizable.Optimizable.fix()`, +:obj:`~simsopt._core.optimizable.Optimizable.unfix()`, +:obj:`~simsopt._core.optimizable.Optimizable.is_fixed()`, +:obj:`~simsopt._core.optimizable.Optimizable.is_free()`. Caching @@ -493,14 +493,14 @@ However if any dofs change, either dofs owned locally or by an ancestor object, this computation needs to be re-run. Many Optimizable objects in simsopt therefore implement caching: results are saved, until the cache is cleared due to changes in dofs. The -:obj:`~simsopt._core.graph_optimizable.Optimizable` base class +:obj:`~simsopt._core.optimizable.Optimizable` base class provides a function -:obj:`~simsopt._core.graph_optimizable.Optimizable.recompute_bell()` +:obj:`~simsopt._core.optimizable.Optimizable.recompute_bell()` to assist with caching. This function is called automatically whenever dofs of an object or any of its ancestors change. Subclasses of -:obj:`~simsopt._core.graph_optimizable.Optimizable` can overload the +:obj:`~simsopt._core.optimizable.Optimizable` can overload the default (empty) -:obj:`~simsopt._core.graph_optimizable.Optimizable.recompute_bell()` +:obj:`~simsopt._core.optimizable.Optimizable.recompute_bell()` function to manage their cache in a customized way. @@ -509,15 +509,15 @@ Specifying least-squares objective functions A common use case is to minimize a nonlinear least-squares objective function, which consists of a sum of several terms. In this case the -:obj:`simsopt.objectives.graph_least_squares.LeastSquaresProblem` +:obj:`simsopt.objectives.least_squares.LeastSquaresProblem` class can be used. Suppose we want to solve a least-squares optimization problem in which an -:obj:`~simsopt._core.graph_optimizable.Optimizable` object ``obj`` has +:obj:`~simsopt._core.optimizable.Optimizable` object ``obj`` has some dofs to be optimized. If ``obj`` has a function ``func()``, we can define the objective function ``weight * ((obj.func() - goal) ** 2)`` as follows:: - from simsopt.objectives.graph_least_squares import LeastSquaresProblem + from simsopt.objectives.least_squares import LeastSquaresProblem prob = LeastSquaresProblem.from_tuples([(obj.func, goal, weight)]) Note that the problem was defined using a 3-element tuple of the form @@ -537,19 +537,19 @@ The corresponding objective funtion is then ``weight1 * 2)``. The list of tuples can include any mixture of terms defined by scalar functions and by 1D numpy array-valued functions. Note that the function handles that are specified should be members of an -:obj:`~simsopt._core.graph_optimizable.Optimizable` object. As -:obj:`~simsopt.objectives.graph_least_squares.LeastSquaresProblem` is -a subclass of :obj:`~simsopt._core.graph_optimizable.Optimizable`, the +:obj:`~simsopt._core.optimizable.Optimizable` object. As +:obj:`~simsopt.objectives.least_squares.LeastSquaresProblem` is +a subclass of :obj:`~simsopt._core.optimizable.Optimizable`, the free dofs of all the objects that go into the objective function are available in the global state vector ``prob.x``. The overall scalar objective function is available from -:func:`simsopt.objectives.graph_least_squares.LeastSquaresProblem.objective`. +:func:`simsopt.objectives.least_squares.LeastSquaresProblem.objective`. The vector of residuals before scaling by the ``weight`` factors ``obj.func() - goal`` is available from -:func:`simsopt.objectives.graph_least_squares.LeastSquaresProblem.unweighted_residuals`. +:func:`simsopt.objectives.least_squares.LeastSquaresProblem.unweighted_residuals`. The vector of residuals after scaling by the ``weight`` factors, ``sqrt(weight) * (obj.func() - goal)``, is available from -:func:`simsopt.objectives.graph_least_squares.LeastSquaresProblem.residuals`. +:func:`simsopt.objectives.least_squares.LeastSquaresProblem.residuals`. Least-squares problems can also be defined in an alternative way:: @@ -570,16 +570,16 @@ Custom objective functions and optimizable objects You may wish to use a custom objective function. The recommended approach for this is to use -:func:`simsopt._core.graph_optimizable.make_optimizable()`, which can +:func:`simsopt._core.optimizable.make_optimizable()`, which can be imported from the top-level ``simsopt`` module. In this approach, you first define a standard python function which takes as arguments -any :obj:`~simsopt._core.graph_optimizable.Optimizable` objects that +any :obj:`~simsopt._core.optimizable.Optimizable` objects that the function depends on. This function can return a float or 1D numpy array. You then apply -:func:`~simsopt._core.graph_optimizable.make_optimizable()` to the +:func:`~simsopt._core.optimizable.make_optimizable()` to the function handle, including the parent objects as additional arguments. The newly created -:obj:`~simsopt._core.graph_optimizable.Optimizable` object will have a +:obj:`~simsopt._core.optimizable.Optimizable` object will have a function ``.J()`` that returns the function you created. For instance, suppose we wish to minimize the objective function @@ -589,7 +589,7 @@ can be accomplished as follows:: from simsopt import make_optimizable from simsopt.mhd.vmec import Vmec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem + from simsopt.objectives.least_squares import LeastSquaresProblem def myfunc(v): v.run() # Ensure VMEC has run with the latest dofs. @@ -600,22 +600,22 @@ can be accomplished as follows:: prob = LeastSquaresProblem.from_tuples([(myopt.J, 0.1, 1)]) In this example, the new -:obj:`~simsopt._core.graph_optimizable.Optimizable` object did not own +:obj:`~simsopt._core.optimizable.Optimizable` object did not own any dofs. However the -:func:`~simsopt._core.graph_optimizable.make_optimizable()` can also -create :obj:`~simsopt._core.graph_optimizable.Optimizable` objects +:func:`~simsopt._core.optimizable.make_optimizable()` can also +create :obj:`~simsopt._core.optimizable.Optimizable` objects with their own dofs and other parameters. For this syntax, see the API documentation for -:func:`~simsopt._core.graph_optimizable.make_optimizable()`. +:func:`~simsopt._core.optimizable.make_optimizable()`. An alternative to using -:func:`~simsopt._core.graph_optimizable.make_optimizable()` is to +:func:`~simsopt._core.optimizable.make_optimizable()` is to write your own subclass of -:obj:`~simsopt._core.graph_optimizable.Optimizable`. In this +:obj:`~simsopt._core.optimizable.Optimizable`. In this approach, the above example looks as follows:: - from simsopt._core.graph_optimizable import Optimizable + from simsopt._core.optimizable import Optimizable from simsopt.mhd.vmec import Vmec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem + from simsopt.objectives.least_squares import LeastSquaresProblem class Myopt(Optimizable): def __init__(self, v): diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 553bb8f43..332ea2f8e 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -82,8 +82,8 @@ More details about setting degrees of freedom and defining objective functions can be found on the :doc:`optimizable` page. For the solution step, two functions are provided presently, -:meth:`simsopt.solve.graph_serial.least_squares_serial_solve` and -:meth:`simsopt.solve.graph_mpi.least_squares_mpi_solve`. The first +:meth:`simsopt.solve.serial.least_squares_serial_solve` and +:meth:`simsopt.solve.mpi.least_squares_mpi_solve`. The first is simpler, while the second allows MPI-parallelized finite differences to be used in the optimization. diff --git a/docs/source/simsopt._core.rst b/docs/source/simsopt._core.rst index 951430c23..124ea4bee 100644 --- a/docs/source/simsopt._core.rst +++ b/docs/source/simsopt._core.rst @@ -22,10 +22,10 @@ simsopt.\_core.finite_difference module :show-inheritance: :private-members: -simsopt.\_core.graph\_optimizable module +simsopt.\_core.optimizable module ---------------------------------------- -.. automodule:: simsopt._core.graph_optimizable +.. automodule:: simsopt._core.optimizable :members: :undoc-members: :show-inheritance: diff --git a/docs/source/simsopt.objectives.rst b/docs/source/simsopt.objectives.rst index 48e6cc17e..cfc385ccb 100644 --- a/docs/source/simsopt.objectives.rst +++ b/docs/source/simsopt.objectives.rst @@ -12,18 +12,18 @@ simsopt.objectives.fluxobjective module :undoc-members: :show-inheritance: -simsopt.objectives.graph\_functions module +simsopt.objectives.functions module ------------------------------------------ -.. automodule:: simsopt.objectives.graph_functions +.. automodule:: simsopt.objectives.functions :members: :undoc-members: :show-inheritance: -simsopt.objectives.graph\_least\_squares module +simsopt.objectives.least\_squares module ----------------------------------------------- -.. automodule:: simsopt.objectives.graph_least_squares +.. automodule:: simsopt.objectives.least_squares :members: :undoc-members: :show-inheritance: diff --git a/docs/source/simsopt.solve.rst b/docs/source/simsopt.solve.rst index 703df85ad..4cf6d38c4 100644 --- a/docs/source/simsopt.solve.rst +++ b/docs/source/simsopt.solve.rst @@ -4,18 +4,18 @@ simsopt.solve package Submodules ---------- -simsopt.solve.graph\_mpi module +simsopt.solve.mpi module ------------------------------- -.. automodule:: simsopt.solve.graph_mpi +.. automodule:: simsopt.solve.mpi :members: :undoc-members: :show-inheritance: -simsopt.solve.graph\_serial module +simsopt.solve.serial module ---------------------------------- -.. automodule:: simsopt.solve.graph_serial +.. automodule:: simsopt.solve.serial :members: :undoc-members: :show-inheritance: diff --git a/examples/1_Simple/just_a_quadratic.py b/examples/1_Simple/just_a_quadratic.py index 3da5b2a27..339d09e56 100755 --- a/examples/1_Simple/just_a_quadratic.py +++ b/examples/1_Simple/just_a_quadratic.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 import logging -from simsopt.objectives.graph_functions import Identity -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.functions import Identity +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ Minimize f(x,y,z) = ((x-1)/1)^2 + ((y-2)/2)^2 + ((z-3)/3)^2. diff --git a/examples/1_Simple/minimize_curve_length.py b/examples/1_Simple/minimize_curve_length.py index 483c5d823..971696eec 100755 --- a/examples/1_Simple/minimize_curve_length.py +++ b/examples/1_Simple/minimize_curve_length.py @@ -3,8 +3,8 @@ import numpy as np from simsopt.geo.curverzfourier import CurveRZFourier from simsopt.geo.curveobjectives import CurveLength -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ Minimize the length of a curve, holding the 0-frequency Fourier mode fixed. diff --git a/examples/1_Simple/graph_surf_vol_area.py b/examples/1_Simple/surf_vol_area.py similarity index 85% rename from examples/1_Simple/graph_surf_vol_area.py rename to examples/1_Simple/surf_vol_area.py index dd86d9f0d..484154550 100755 --- a/examples/1_Simple/graph_surf_vol_area.py +++ b/examples/1_Simple/surf_vol_area.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 from simsopt.geo.surfacerzfourier import SurfaceRZFourier -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ Optimize the minor radius and elongation of an axisymmetric torus to -obtain a desired volume and area using the graph method. +obtain a desired volume and area. """ -print("Running 1_Simple/graph_surf_vol_area.py") +print("Running 1_Simple/surf_vol_area.py") print("=======================================") desired_volume = 0.6 desired_area = 8.0 @@ -54,5 +54,5 @@ print(" -------------------------\n\n") -print("End of 1_Simple/graph_surf_vol_area.py") +print("End of 1_Simple/surf_vol_area.py") print("=======================================") diff --git a/examples/2_Intermediate/QH_fixed_resolution.py b/examples/2_Intermediate/QH_fixed_resolution.py index 354615417..ef818bcd8 100755 --- a/examples/2_Intermediate/QH_fixed_resolution.py +++ b/examples/2_Intermediate/QH_fixed_resolution.py @@ -5,8 +5,8 @@ from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ Optimize a VMEC equilibrium for quasi-helical symmetry (M=1, N=1) diff --git a/examples/2_Intermediate/QH_fixed_resolution_boozer.py b/examples/2_Intermediate/QH_fixed_resolution_boozer.py index cbbf56d75..11dede2f4 100755 --- a/examples/2_Intermediate/QH_fixed_resolution_boozer.py +++ b/examples/2_Intermediate/QH_fixed_resolution_boozer.py @@ -3,8 +3,8 @@ import os from simsopt.util.mpi import MpiPartition from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ Optimize for quasi-helical symmetry (M=1, N=1) at a given radius. diff --git a/examples/2_Intermediate/QSC.py b/examples/2_Intermediate/QSC.py index 8cc9f467c..9e0ecd130 100755 --- a/examples/2_Intermediate/QSC.py +++ b/examples/2_Intermediate/QSC.py @@ -4,7 +4,7 @@ import numpy as np from qsc import Qsc -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable from simsopt import LeastSquaresProblem, least_squares_serial_solve """ diff --git a/examples/2_Intermediate/eliminate_magnetic_islands.py b/examples/2_Intermediate/eliminate_magnetic_islands.py index 5999ed3f0..87690395a 100755 --- a/examples/2_Intermediate/eliminate_magnetic_islands.py +++ b/examples/2_Intermediate/eliminate_magnetic_islands.py @@ -4,8 +4,8 @@ import numpy as np from simsopt.util.mpi import MpiPartition, log from simsopt.mhd.spec import Spec, Residue -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve import os """ diff --git a/examples/2_Intermediate/resolution_increase.py b/examples/2_Intermediate/resolution_increase.py index 70f978467..c08befc09 100755 --- a/examples/2_Intermediate/resolution_increase.py +++ b/examples/2_Intermediate/resolution_increase.py @@ -5,8 +5,8 @@ from simsopt.util.mpi import MpiPartition, log from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ This example shows how scripting can be used to increase the size diff --git a/examples/2_Intermediate/resolution_increase_boozer.py b/examples/2_Intermediate/resolution_increase_boozer.py index e5fc2aac3..297344e07 100755 --- a/examples/2_Intermediate/resolution_increase_boozer.py +++ b/examples/2_Intermediate/resolution_increase_boozer.py @@ -3,8 +3,8 @@ import os from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ This example shows how scripting can be used to increase the size diff --git a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py index 809e2083c..aa8415654 100755 --- a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py +++ b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py @@ -6,8 +6,8 @@ from simsopt.util.mpi import log, MpiPartition from simsopt.mhd import Vmec, Spec, Boozer, Quasisymmetry from simsopt.mhd.spec import Residue -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ In this example, we simultaneously optimize for quasisymmetry and diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py index 78e5a735a..4ea51df9f 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py @@ -5,9 +5,9 @@ import numpy as np from simsopt.mhd import Vmec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.util.mpi import MpiPartition, log -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.geo.surfacegarabedian import SurfaceGarabedian """ diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py index bad577bdb..09c50e97f 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py @@ -6,8 +6,8 @@ from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Spec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.geo.surfacegarabedian import SurfaceGarabedian """ diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py index 20ed699bf..3050c87a7 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py @@ -3,10 +3,10 @@ import numpy as np from mpi4py import MPI -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.mhd import Vmec from simsopt.util.mpi import MpiPartition, log -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.solve.mpi import least_squares_mpi_solve """ This script implements the "1DOF_circularCrossSection_varyR0_targetVolume" diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py index dbfa57446..1dab40b07 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py @@ -5,8 +5,8 @@ from simsopt.mhd import Spec from simsopt.geo.surfacerzfourier import SurfaceRZFourier -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ This script implements the "1DOF_circularCrossSection_varyR0_targetVolume" diff --git a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py index 986d1d5c7..ce934ec8a 100755 --- a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py +++ b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py @@ -1,8 +1,8 @@ #!/usr/bin/env python from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve import os """ diff --git a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py index 5fc6e7a3d..3937161f3 100755 --- a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py @@ -4,8 +4,8 @@ import numpy as np from simsopt.mhd import Spec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve import os """ diff --git a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py index 447305046..6483c399d 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py +++ b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py @@ -5,8 +5,8 @@ from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Vmec, Spec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve import os """ diff --git a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py index dd1d4aa5d..9ba544b93 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py @@ -3,9 +3,9 @@ import numpy as np from simsopt.mhd import Vmec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.util.mpi import MpiPartition, log -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.solve.mpi import least_squares_mpi_solve import os """ diff --git a/examples/stellarator_benchmarks/7dof.py b/examples/stellarator_benchmarks/7dof.py index 5c4046f1c..cad0e9904 100755 --- a/examples/stellarator_benchmarks/7dof.py +++ b/examples/stellarator_benchmarks/7dof.py @@ -3,8 +3,8 @@ import os from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.geo.surfacegarabedian import SurfaceGarabedian """ diff --git a/src/simsopt/_core/__init__.py b/src/simsopt/_core/__init__.py index afe2f5a6b..583c418af 100644 --- a/src/simsopt/_core/__init__.py +++ b/src/simsopt/_core/__init__.py @@ -1 +1 @@ -from .graph_optimizable import Optimizable, make_optimizable +from .optimizable import Optimizable, make_optimizable diff --git a/src/simsopt/_core/derivative.py b/src/simsopt/_core/derivative.py index 64300c16d..a8c82396d 100644 --- a/src/simsopt/_core/derivative.py +++ b/src/simsopt/_core/derivative.py @@ -13,7 +13,7 @@ def __init__(self, d): super().__init__(None, d) def __missing__(self, key): - from .graph_optimizable import Optimizable # Import here to avoid circular import + from .optimizable import Optimizable # Import here to avoid circular import assert isinstance(key, Optimizable) self[key] = value = np.zeros((key.local_full_dof_size, )) return value @@ -162,7 +162,7 @@ def __call__(self, optim): Args: optim: An Optimizable object """ - from .graph_optimizable import Optimizable # Import here to avoid circular import + from .optimizable import Optimizable # Import here to avoid circular import assert isinstance(optim, Optimizable) deps = optim.ancestors + [optim] derivs = [] diff --git a/src/simsopt/_core/finite_difference.py b/src/simsopt/_core/finite_difference.py index b886bf221..bd7503838 100644 --- a/src/simsopt/_core/finite_difference.py +++ b/src/simsopt/_core/finite_difference.py @@ -27,7 +27,7 @@ from ..util.types import RealArray from ..util.dev import SimsoptRequires -from .graph_optimizable import Optimizable +from .optimizable import Optimizable from .util import finite_difference_steps logger = logging.getLogger(__name__) diff --git a/src/simsopt/_core/graph_optimizable.py b/src/simsopt/_core/optimizable.py similarity index 100% rename from src/simsopt/_core/graph_optimizable.py rename to src/simsopt/_core/optimizable.py diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 1684de283..b8f5157bb 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -1,4 +1,4 @@ -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable from simsopt._core.derivative import Derivative from simsopt.geo.curvexyzfourier import CurveXYZFourier from simsopt.geo.curve import RotatedCurve diff --git a/src/simsopt/field/magneticfield.py b/src/simsopt/field/magneticfield.py index 50b4ff594..fa2756178 100644 --- a/src/simsopt/field/magneticfield.py +++ b/src/simsopt/field/magneticfield.py @@ -1,7 +1,7 @@ import numpy as np import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.derivative import Derivative diff --git a/src/simsopt/geo/curve.py b/src/simsopt/geo/curve.py index 56432a234..2ff3f488f 100644 --- a/src/simsopt/geo/curve.py +++ b/src/simsopt/geo/curve.py @@ -7,7 +7,7 @@ from monty.dev import requires import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from simsopt._core.derivative import Derivative from .plot import fix_matplotlib_3d diff --git a/src/simsopt/geo/curveobjectives.py b/src/simsopt/geo/curveobjectives.py index 40dc6fea7..7b11e8822 100644 --- a/src/simsopt/geo/curveobjectives.py +++ b/src/simsopt/geo/curveobjectives.py @@ -3,7 +3,7 @@ import jax.numpy as jnp from .jit import jit -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.derivative import derivative_dec diff --git a/src/simsopt/geo/surface.py b/src/simsopt/geo/surface.py index a29469fca..784167714 100644 --- a/src/simsopt/geo/surface.py +++ b/src/simsopt/geo/surface.py @@ -9,7 +9,7 @@ gridToVTK = None import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.dev import SimsoptRequires from .plot import fix_matplotlib_3d diff --git a/src/simsopt/geo/surfaceobjectives.py b/src/simsopt/geo/surfaceobjectives.py index c80c29e58..8883ff08d 100644 --- a/src/simsopt/geo/surfaceobjectives.py +++ b/src/simsopt/geo/surfaceobjectives.py @@ -4,7 +4,7 @@ from nptyping import NDArray, Float import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from simsopt.geo.surface import Surface diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index 6b1bb103d..f693b6449 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -7,7 +7,7 @@ import simsoptpp as sopp from .surface import Surface -from .._core.graph_optimizable import DOFs, Optimizable +from .._core.optimizable import DOFs, Optimizable from .._core.util import nested_lists_to_array logger = logging.getLogger(__name__) diff --git a/src/simsopt/mhd/bootstrap.py b/src/simsopt/mhd/bootstrap.py index 6579223e7..71572268e 100644 --- a/src/simsopt/mhd/bootstrap.py +++ b/src/simsopt/mhd/bootstrap.py @@ -11,7 +11,7 @@ from scipy.interpolate import RectBivariateSpline, interp1d from scipy.optimize import minimize, Bounds from scipy.integrate import quad -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import Struct from ..util.constants import ELEMENTARY_CHARGE from .profiles import Profile, ProfilePolynomial @@ -677,7 +677,7 @@ def residuals(self): representing the objective function as a nonlinear least-squares problem. This is the function handle to use with a - :obj:`~simsopt.objectives.graph_least_squares.LeastSquaresProblem`. + :obj:`~simsopt.objectives.least_squares.LeastSquaresProblem`. Specifically, this function returns diff --git a/src/simsopt/mhd/profiles.py b/src/simsopt/mhd/profiles.py index 8b8f662b7..bbb87d1ed 100644 --- a/src/simsopt/mhd/profiles.py +++ b/src/simsopt/mhd/profiles.py @@ -11,7 +11,7 @@ import numpy as np import numpy.polynomial.polynomial as poly from scipy.interpolate import InterpolatedUnivariateSpline -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable logger = logging.getLogger(__name__) diff --git a/src/simsopt/mhd/spec.py b/src/simsopt/mhd/spec.py index bde1757a6..d10cfc621 100644 --- a/src/simsopt/mhd/spec.py +++ b/src/simsopt/mhd/spec.py @@ -39,7 +39,7 @@ pyoculus = None logger.debug(str(e)) -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import ObjectiveFailure from ..geo.surfacerzfourier import SurfaceRZFourier if MPI is not None: diff --git a/src/simsopt/mhd/vmec.py b/src/simsopt/mhd/vmec.py index a8e1a2717..c7a02473e 100644 --- a/src/simsopt/mhd/vmec.py +++ b/src/simsopt/mhd/vmec.py @@ -29,7 +29,7 @@ vmec = None logger.debug(str(e)) -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import Struct, ObjectiveFailure from ..geo.surfacerzfourier import SurfaceRZFourier diff --git a/src/simsopt/mhd/vmec_diagnostics.py b/src/simsopt/mhd/vmec_diagnostics.py index 8d237916d..df5f1a570 100644 --- a/src/simsopt/mhd/vmec_diagnostics.py +++ b/src/simsopt/mhd/vmec_diagnostics.py @@ -15,7 +15,7 @@ from .vmec import Vmec from .._core.util import Struct -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.types import RealArray from ..geo.surfaceobjectives import parameter_derivatives diff --git a/src/simsopt/objectives/__init__.py b/src/simsopt/objectives/__init__.py index 09afd1e26..7a5d26052 100644 --- a/src/simsopt/objectives/__init__.py +++ b/src/simsopt/objectives/__init__.py @@ -1,3 +1,3 @@ -from .graph_least_squares import LeastSquaresProblem +from .least_squares import LeastSquaresProblem __all__ = ['LeastSquaresTerm', 'LeastSquaresProblem'] diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index f6c85e1ae..ae004cfe9 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -1,4 +1,4 @@ -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable from .._core.derivative import derivative_dec import numpy as np diff --git a/src/simsopt/objectives/graph_functions.py b/src/simsopt/objectives/functions.py similarity index 99% rename from src/simsopt/objectives/graph_functions.py rename to src/simsopt/objectives/functions.py index 231500bd1..6ffcaf516 100644 --- a/src/simsopt/objectives/graph_functions.py +++ b/src/simsopt/objectives/functions.py @@ -13,14 +13,14 @@ import numpy as np -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.types import RealArray class Identity(Optimizable): """ Represents a term in an objective function which is just - the identity. It has one degree of freedom. Conforms to the experimental + the identity. It has one degree of freedom. Conforms to the graph based Optimizable framework. The output of the method `f` is equal to this degree of freedom. diff --git a/src/simsopt/objectives/graph_least_squares.py b/src/simsopt/objectives/least_squares.py similarity index 99% rename from src/simsopt/objectives/graph_least_squares.py rename to src/simsopt/objectives/least_squares.py index a0aeb0119..0cd7988ff 100644 --- a/src/simsopt/objectives/graph_least_squares.py +++ b/src/simsopt/objectives/least_squares.py @@ -17,7 +17,7 @@ import numpy as np -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import ObjectiveFailure from ..util.types import RealArray, IntArray, BoolArray @@ -29,7 +29,7 @@ class LeastSquaresProblem(Optimizable): """ - Represents a nonlinear-least-squares problem implemented using the new + Represents a nonlinear-least-squares problem implemented using the graph based optimization framework. A LeastSquaresProblem instance has 3 basic attributes: a set of functions (`f_in`), target values for each of the functions (`goal`), and weights. The residual diff --git a/src/simsopt/objectives/utilities.py b/src/simsopt/objectives/utilities.py index 347501e8b..49fa535d0 100644 --- a/src/simsopt/objectives/utilities.py +++ b/src/simsopt/objectives/utilities.py @@ -1,6 +1,6 @@ import numpy as np -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.derivative import Derivative, derivative_dec diff --git a/src/simsopt/solve/__init__.py b/src/simsopt/solve/__init__.py index 5c9ac7c3a..0ab707b8d 100644 --- a/src/simsopt/solve/__init__.py +++ b/src/simsopt/solve/__init__.py @@ -1,3 +1,3 @@ -from .graph_serial import least_squares_serial_solve, serial_solve +from .serial import least_squares_serial_solve, serial_solve __all__ = ['least_squares_serial_solve', 'serial_solve'] diff --git a/src/simsopt/solve/graph_mpi.py b/src/simsopt/solve/mpi.py similarity index 98% rename from src/simsopt/solve/graph_mpi.py rename to src/simsopt/solve/mpi.py index a17033a80..ddfd4f2c1 100644 --- a/src/simsopt/solve/graph_mpi.py +++ b/src/simsopt/solve/mpi.py @@ -21,12 +21,12 @@ except ImportError as err: MPI = None -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.mpi import MpiPartition from ..util.types import RealArray from .._core.util import finite_difference_steps from .._core.finite_difference import MPIFiniteDifference -from ..objectives.graph_least_squares import LeastSquaresProblem +from ..objectives.least_squares import LeastSquaresProblem logger = logging.getLogger(__name__) diff --git a/src/simsopt/solve/graph_serial.py b/src/simsopt/solve/serial.py similarity index 98% rename from src/simsopt/solve/graph_serial.py rename to src/simsopt/solve/serial.py index ec27f5e4f..91eed488b 100644 --- a/src/simsopt/solve/graph_serial.py +++ b/src/simsopt/solve/serial.py @@ -16,8 +16,8 @@ import numpy as np from scipy.optimize import least_squares, minimize -from ..objectives.graph_least_squares import LeastSquaresProblem -from .._core.graph_optimizable import Optimizable +from ..objectives.least_squares import LeastSquaresProblem +from .._core.optimizable import Optimizable from .._core.finite_difference import FiniteDifference diff --git a/tests/core/test_derivative.py b/tests/core/test_derivative.py index 430906d1c..47e1011bf 100644 --- a/tests/core/test_derivative.py +++ b/tests/core/test_derivative.py @@ -1,6 +1,6 @@ import unittest import numpy as np -from simsopt._core.graph_optimizable import Optimizable, ScaledOptimizable, OptimizableSum +from simsopt._core.optimizable import Optimizable, ScaledOptimizable, OptimizableSum from simsopt._core.derivative import Derivative, derivative_dec diff --git a/tests/core/test_graph_dofs.py b/tests/core/test_dofs.py similarity index 99% rename from tests/core/test_graph_dofs.py rename to tests/core/test_dofs.py index 4c5528d7a..9b6966eb4 100755 --- a/tests/core/test_graph_dofs.py +++ b/tests/core/test_dofs.py @@ -1,8 +1,8 @@ import unittest import numpy as np -from simsopt._core.graph_optimizable import DOFs -from simsopt.objectives.graph_functions import Identity, Adder, Rosenbrock +from simsopt._core.optimizable import DOFs +from simsopt.objectives.functions import Identity, Adder, Rosenbrock from simsopt._core.util import DofLengthMismatchError diff --git a/tests/core/test_finite_difference.py b/tests/core/test_finite_difference.py index d13bf3508..d7c284b81 100755 --- a/tests/core/test_finite_difference.py +++ b/tests/core/test_finite_difference.py @@ -7,7 +7,7 @@ except: MPI = None -from simsopt._core.graph_optimizable import Optimizable, make_optimizable +from simsopt._core.optimizable import Optimizable, make_optimizable from simsopt._core.finite_difference import FiniteDifference if MPI is not None: from simsopt.util.mpi import MpiPartition diff --git a/tests/core/test_integrated.py b/tests/core/test_integrated.py index 406581b3b..0606bbaff 100755 --- a/tests/core/test_integrated.py +++ b/tests/core/test_integrated.py @@ -10,11 +10,11 @@ from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.geo.surfacegarabedian import SurfaceGarabedian -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve if MPI is not None: from simsopt.util.mpi import MpiPartition - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve def mpi_solve_1group(prob, **kwargs): diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_optimizable.py similarity index 99% rename from tests/core/test_graph_optimizable.py rename to tests/core/test_optimizable.py index 855cbdbee..d40f99039 100755 --- a/tests/core/test_graph_optimizable.py +++ b/tests/core/test_optimizable.py @@ -3,8 +3,8 @@ import numpy as np -from simsopt._core.graph_optimizable import Optimizable, make_optimizable -from simsopt.objectives.graph_functions import Identity, Rosenbrock, TestObject2 +from simsopt._core.optimizable import Optimizable, make_optimizable +from simsopt.objectives.functions import Identity, Rosenbrock, TestObject2 class Adder(Optimizable): diff --git a/tests/geo/test_curve_optimizable.py b/tests/geo/test_curve_optimizable.py index cd357f566..cf2a9e716 100644 --- a/tests/geo/test_curve_optimizable.py +++ b/tests/geo/test_curve_optimizable.py @@ -6,8 +6,8 @@ from simsopt.geo.curve import RotatedCurve from simsopt.geo import parameters from simsopt.geo.curveobjectives import CurveLength -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve parameters['jit'] = False diff --git a/tests/mhd/test_boozer.py b/tests/mhd/test_boozer.py index 42b1afa07..4a21841c7 100755 --- a/tests/mhd/test_boozer.py +++ b/tests/mhd/test_boozer.py @@ -18,7 +18,7 @@ except ImportError as e: MPI = None -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable if MPI is not None: from simsopt.mhd.boozer import Boozer, Quasisymmetry # , booz_xform_found from simsopt.mhd.vmec import Vmec # , vmec_found diff --git a/tests/mhd/test_integrated_vmec_mpi.py b/tests/mhd/test_integrated_vmec_mpi.py index 4e699ca38..3fc4b0b1e 100755 --- a/tests/mhd/test_integrated_vmec_mpi.py +++ b/tests/mhd/test_integrated_vmec_mpi.py @@ -11,11 +11,11 @@ except ImportError: vmec = None -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem if MPI is not None: from simsopt.mhd.vmec import Vmec - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.util.mpi import MpiPartition #logging.basicConfig(level=logging.DEBUG) diff --git a/tests/mhd/test_spec.py b/tests/mhd/test_spec.py index 7c2bc11ec..312a9c71e 100755 --- a/tests/mhd/test_spec.py +++ b/tests/mhd/test_spec.py @@ -26,9 +26,9 @@ if (MPI is not None) and spec_found: from simsopt.mhd.spec import Spec, Residue -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.geo.surfacegarabedian import SurfaceGarabedian -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.solve.serial import least_squares_serial_solve from . import TEST_DIR logger = logging.getLogger(__name__) diff --git a/tests/mhd/test_vmec.py b/tests/mhd/test_vmec.py index 53c1dbc64..547bd89ee 100755 --- a/tests/mhd/test_vmec.py +++ b/tests/mhd/test_vmec.py @@ -14,11 +14,11 @@ except ImportError: vmec_found = False -from simsopt._core.graph_optimizable import make_optimizable -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt._core.optimizable import make_optimizable +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.mhd.profiles import ProfilePolynomial, ProfileSpline, ProfileScaled, ProfilePressure -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.solve.serial import least_squares_serial_solve from simsopt.util.constants import ELEMENTARY_CHARGE from simsopt.mhd.vmec import Vmec diff --git a/tests/mhd/test_vmec_diagnostics.py b/tests/mhd/test_vmec_diagnostics.py index 61884aa1d..a4c1462ef 100755 --- a/tests/mhd/test_vmec_diagnostics.py +++ b/tests/mhd/test_vmec_diagnostics.py @@ -6,7 +6,7 @@ from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual, \ B_cartesian, IotaTargetMetric, IotaWeighted, WellWeighted, \ vmec_fieldlines -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem try: import vmec diff --git a/tests/objectives/test_graph_least_squares.py b/tests/objectives/test_least_squares.py similarity index 95% rename from tests/objectives/test_graph_least_squares.py rename to tests/objectives/test_least_squares.py index 62e46f77c..a67d7f535 100755 --- a/tests/objectives/test_graph_least_squares.py +++ b/tests/objectives/test_least_squares.py @@ -1,9 +1,9 @@ import unittest import logging import numpy as np -from simsopt.objectives.graph_functions import Identity, Rosenbrock +from simsopt.objectives.functions import Identity, Rosenbrock #from simsopt.core.optimizable import Target -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem #logging.basicConfig(level=logging.DEBUG) diff --git a/tests/solve/test_graph_least_squares.py b/tests/solve/test_least_squares.py similarity index 89% rename from tests/solve/test_graph_least_squares.py rename to tests/solve/test_least_squares.py index b7ce355da..36cdba6f5 100755 --- a/tests/solve/test_graph_least_squares.py +++ b/tests/solve/test_least_squares.py @@ -6,12 +6,12 @@ except ImportError: MPI = None -from simsopt.objectives.graph_functions import Identity, Rosenbrock -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve, serial_solve +from simsopt.objectives.functions import Identity, Rosenbrock +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve, serial_solve if MPI is not None: from simsopt.util.mpi import MpiPartition - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve def mpi_solve_1group(prob, **kwargs): diff --git a/tests/solve/test_graph_mpi.py b/tests/solve/test_mpi.py similarity index 94% rename from tests/solve/test_graph_mpi.py rename to tests/solve/test_mpi.py index 1d74d0b7d..734d65ae3 100755 --- a/tests/solve/test_graph_mpi.py +++ b/tests/solve/test_mpi.py @@ -7,12 +7,12 @@ except: MPI = None -from simsopt._core.graph_optimizable import Optimizable -from simsopt.objectives.graph_functions import Beale -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt._core.optimizable import Optimizable +from simsopt.objectives.functions import Beale +from simsopt.objectives.least_squares import LeastSquaresProblem if MPI is not None: from simsopt.util.mpi import MpiPartition - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve #logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) diff --git a/tests/util/test_dev.py b/tests/util/test_dev.py index f1f3db475..11303a1ff 100644 --- a/tests/util/test_dev.py +++ b/tests/util/test_dev.py @@ -6,7 +6,7 @@ np = None from simsopt.util.dev import SimsoptRequires, deprecated -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable @SimsoptRequires(np is not None, "numpy is not installed.") From 8f4c7973ab44b2845d72ed9102e1b29c05dfb6b9 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 28 Mar 2022 07:25:15 -0400 Subject: [PATCH 02/95] Fix the import after removing graph prefix --- src/simsopt/mhd/boozer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/mhd/boozer.py b/src/simsopt/mhd/boozer.py index 8d082060c..9afeee6ed 100644 --- a/src/simsopt/mhd/boozer.py +++ b/src/simsopt/mhd/boozer.py @@ -28,7 +28,7 @@ from .vmec import Vmec -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable class Boozer(Optimizable): From a418eb3694ff7b037a8cf4422808569f30f225ec Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 28 Mar 2022 08:37:20 -0400 Subject: [PATCH 03/95] Update the example name in the script --- examples/run_serial_examples | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/run_serial_examples b/examples/run_serial_examples index 4812f275b..9dbb3447d 100755 --- a/examples/run_serial_examples +++ b/examples/run_serial_examples @@ -5,7 +5,7 @@ set -ex ./1_Simple/just_a_quadratic.py -./1_Simple/graph_surf_vol_area.py +./1_Simple/surf_vol_area.py ./1_Simple/minimize_curve_length.py ./1_Simple/tracing_fieldline.py ./1_Simple/tracing_particle.py From 9a6c6b2a0d300821e53ccc8dcd2bfe94e85fd217 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 16:55:50 -0400 Subject: [PATCH 04/95] Get rid of print --- src/simsopt/_core/optimizable.py | 95 +++++++++++++++++++++++++---- src/simsopt/objectives/functions.py | 42 ++++++++++++- tests/core/test_optimizable.py | 54 ++++++++++++++++ 3 files changed, 177 insertions(+), 14 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 161f28515..d695bcfba 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -19,8 +19,10 @@ MutableSequence as MutSeq, List from functools import lru_cache import logging +import json import numpy as np +from monty.json import MSONable, MontyEncoder, MontyDecoder from ..util.dev import SimsoptRequires from ..util.types import RealArray, StrArray, BoolArray, Key @@ -109,8 +111,8 @@ def __init__(self, == len(names)) self._x = x self._free = free - self._lb = lower_bounds - self._ub = upper_bounds + self._lower_bounds = lower_bounds + self._upper_bounds = upper_bounds self._names = list(names) def __len__(self): @@ -301,7 +303,11 @@ def lower_bounds(self) -> RealArray: Returns: Lower bounds of the DOFs """ - return self._lb[self._free] + return self._lower_bounds[self._free] + + @property + def full_lower_bounds(self) -> RealArray: + return self._lower_bounds @lower_bounds.setter def lower_bounds(self, lower_bounds: RealArray) -> None: @@ -314,7 +320,7 @@ def lower_bounds(self, lower_bounds: RealArray) -> None: # and to prevent broadcasting of a single DOF if self.reduced_len != len(lower_bounds): raise DofLengthMismatchError(len(lower_bounds), self.reduced_len) - self._lb[self._free] = np.asarray(lower_bounds, dtype=np.double) + self._lower_bounds[self._free] = np.asarray(lower_bounds, dtype=np.double) @property def upper_bounds(self) -> RealArray: @@ -323,7 +329,11 @@ def upper_bounds(self) -> RealArray: Returns: Upper bounds of the DOFs """ - return self._ub[self._free] + return self._upper_bounds[self._free] + + @property + def full_upper_bounds(self) -> RealArray: + return self._upper_bounds @upper_bounds.setter def upper_bounds(self, upper_bounds: RealArray) -> None: @@ -336,7 +346,7 @@ def upper_bounds(self, upper_bounds: RealArray) -> None: # and to prevent broadcasting of a single DOF if self.reduced_len != len(upper_bounds): raise DofLengthMismatchError(len(upper_bounds), self.reduced_len) - self._ub[self._free] = np.asarray(upper_bounds, dtype=np.double) + self._upper_bounds[self._free] = np.asarray(upper_bounds, dtype=np.double) @property def bounds(self) -> Tuple[RealArray, RealArray]: @@ -347,6 +357,10 @@ def bounds(self) -> Tuple[RealArray, RealArray]: """ return (self.lower_bounds, self.upper_bounds) + @property + def full_bounds(self) -> Tuple[RealArray, RealArray]: + return (self.full_lower_bounds, self.full_upper_bounds) + def update_lower_bound(self, key: Key, val: Real) -> None: """ Updates the lower bound of the specified DOF to the given value @@ -357,7 +371,7 @@ def update_lower_bound(self, key: Key, val: Real) -> None: """ if isinstance(key, str): key = self._names.index(key) - self._lb[key] = val + self._lower_bounds[key] = val def update_upper_bound(self, key: Key, val: Real) -> None: """ @@ -369,7 +383,7 @@ def update_upper_bound(self, key: Key, val: Real) -> None: """ if isinstance(key, str): key = self._names.index(key) - self._ub[key] = val + self._upper_bounds[key] = val def update_bounds(self, key: Key, val: Tuple[Real, Real]) -> None: """ @@ -381,8 +395,8 @@ def update_bounds(self, key: Key, val: Tuple[Real, Real]) -> None: """ if isinstance(key, str): key = self._names.index(key) - self._lb[key] = val[0] - self._ub[key] = val[1] + self._lower_bounds[key] = val[0] + self._upper_bounds[key] = val[1] @property def names(self): @@ -405,7 +419,7 @@ def full_names(self): return self._names -class Optimizable(ABC_Callable, Hashable, metaclass=OptimizableMeta): +class Optimizable(ABC_Callable, Hashable, MSONable, metaclass=OptimizableMeta): """ Experimental callable ABC that provides lego-like optimizable objects that can be used to partition the optimization problem into a graph. @@ -1047,6 +1061,10 @@ def local_lower_bounds(self) -> RealArray: """ return self._dofs.lower_bounds + @property + def local_full_lower_bounds(self) -> RealArray: + return self._dofs.full_lower_bounds + @property def upper_bounds(self) -> RealArray: """ @@ -1064,6 +1082,10 @@ def local_upper_bounds(self) -> RealArray: """ return self._dofs.upper_bounds + @property + def local_full_upper_bounds(self) -> RealArray: + return self._dofs.full_upper_bounds + @local_upper_bounds.setter def local_upper_bounds(self, lub: RealArray) -> None: self._dofs.upper_bounds = lub @@ -1228,6 +1250,33 @@ def traversal(root): nx.draw_networkx(G, pos=pos, arrows=True, **options) plt.show() + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + if len(self.local_full_x): + d["x0"] = list(self.local_full_x) + d["names"] = self.local_full_dof_names + d["fixed"] = list(np.logical_not(self.local_dofs_free_status)) + d["lower_bounds"] = list(self.local_full_lower_bounds) + d["upper_bounds"] = list(self.local_full_upper_bounds) + d["external_dof_setter"] = self.local_dof_setter + if self.parents: + d["depends_on"] = [] + for parent in self.parents: + d["depends_on"].append(parent.as_dict()) + + return d + + @classmethod + def from_dict(cls, d): + parents_dict = d.pop("depends_on") if "depends_on" in d else None + if parents_dict: + parents = [] + for pdict in parents_dict: + parents.append(json.load(pdict, cls=MontyDecoder)) + return cls(depends_on=parents, **d) + def make_optimizable(func, *args, dof_indicators=None, **kwargs): """ @@ -1384,6 +1433,18 @@ def dJ(self): # Next line uses __rmul__ function for the Derivative class return self.factor * self.opt.dJ(partials=True) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["factor"] = self.factor + d["opt"] = self.opt + return d + + @classmethod + def from_dict(cls, d): + return cls(d["factor"], d["opt"]) + class OptimizableSum(Optimizable): """ @@ -1409,3 +1470,15 @@ def J(self): def dJ(self): # Next line uses __add__ function for the Derivative class return sum(opt.dJ(partials=True) for opt in self.opts) + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["opts"] = self.opts + return d + + @classmethod + def from_dict(cls, d): + return cls(d["opts"]) + diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index 6ffcaf516..2c54fdb62 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -57,6 +57,20 @@ def dJ(self, x: RealArray = None): return_fn_map = {'f': f} + def as_dict(self) -> dict: + d = super().as_dict() + del d["x0"] + del d["names"] + del d["fixed"] + d["x"] = self.local_full_x[0] + d["dof_name"] = self.local_full_dof_names[0] + d["dof_fixed"] = np.logical_not(self.local_dofs_free_status)[0] + return d + + @classmethod + def from_dict(cls, d): + return cls(d["x"], d["dof_name"], d["dof_fixed"]) + class Adder(Optimizable): """ @@ -72,10 +86,9 @@ class Adder(Optimizable): dof_names: Identifiers for the DOFs """ - def __init__(self, n=3, x0=None, dof_names=None): + def __init__(self, n=3, **kwargs): self.n = n - x = x0 if x0 is not None else np.zeros(n) - super().__init__(x, names=dof_names) + super().__init__(**kwargs) def sum(self): """ @@ -93,6 +106,17 @@ def df(self): """ return self.dJ() + def as_dict(self) -> dict: + d = super().as_dict() + print('dict is ', d) + d["n"] = self.n + return d + + @classmethod + def from_dict(cls, d): + n = d.pop("n") + return cls(n=n, **d) + return_fn_map = {'sum': sum} @@ -173,6 +197,18 @@ def dterms(self): """ return np.array([[1.0, 0.0], [2 * self.local_full_x['x'] / self._sqrtb, -1.0 / self._sqrtb]]) + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["b"] = self._sqrtb * self._sqrtb + d["x"] = self.get("x") + d["y"] = self.get("y") + + @classmethod + def from_dict(cls, d): + return cls(d["b"], d["x"], d["y"]) class TestObject1(Optimizable): diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index d40f99039..90d0aa9a6 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -1,10 +1,14 @@ import unittest import re +import json import numpy as np +from monty.json import MontyDecoder, MontyEncoder +from monty.serialization import loadfn, dumpfn from simsopt._core.optimizable import Optimizable, make_optimizable from simsopt.objectives.functions import Identity, Rosenbrock, TestObject2 +from simsopt.objectives.functions import Adder as FAdder class Adder(Optimizable): @@ -23,6 +27,21 @@ def sum(self): return_fn_map = {'sum': sum} + def as_dict(self) -> dict: + d = super().as_dict() + d["dof_names"] = d["names"] + d["dof_fixed"] = d["fixed"] + del d["names"] + del d["fixed"] + d["n"] = self.n + return d + + @classmethod + def from_dict(cls, d): + return cls(d["n"], + d.get("x0", None), + d.get("dof_names", None), + d.get("dof_fixed", None)) class OptClassWithParents(Optimizable): def __init__(self, val, depends_on=None): @@ -36,6 +55,17 @@ def f(self): return_fn_map = {'f': f} + def as_dict(self) -> dict: + d = super().as_dict() + del d["x0"] + del d["names"] + d['val'] = self.local_full_x[0] + return d + + @classmethod + def from_dict(cls, d: dict): + return cls(d["val"], d["depends_on"]) + class N_No(Optimizable): """This class defines a minimal object that can be optimized. It has @@ -1034,5 +1064,29 @@ def test_arb_func_dofs_opts(self): self.assertAlmostEqual(opt.J(), 9.0) +class TestOptimizableSerialize(unittest.TestCase): + """ + Test the serialization of the Optimizable class based on as_dict and + from_dict methods using various sub-classes + """ + def test_serialize(self): + adder = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder, cls=MontyEncoder) + print(s) + + def test_deserialize(self): + adder_orig = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder_orig, cls=MontyEncoder) + adder = json.loads(s, cls=MontyDecoder) + print(adder.name) + print(adder.n) + print(adder.full_x) + print(adder.dofs_free_status) + print(adder.local_full_dof_names) + + + if __name__ == "__main__": unittest.main() From 146d538ae01656b0d0e6e02f538bf420cc84c75b Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 16:58:36 -0400 Subject: [PATCH 05/95] Serialization working for optimizable objects without parents --- tests/core/test_dofs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_dofs.py b/tests/core/test_dofs.py index 9b6966eb4..4ee8995cb 100755 --- a/tests/core/test_dofs.py +++ b/tests/core/test_dofs.py @@ -10,7 +10,7 @@ class DOFsTests(unittest.TestCase): def setUp(self): self.identity_dofs = Identity(x=1, dof_name='x')._dofs - self.adder_dofs = Adder(3, x0=[2, 3, 4], dof_names=["x", "y", "z"])._dofs + self.adder_dofs = Adder(3, x0=[2, 3, 4], names=["x", "y", "z"])._dofs self.rosenbrock_dofs = Rosenbrock()._dofs def tearDown(self) -> None: From f4a2d71e41c58b7199c2c11c27bfd426f9ece387 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 28 Mar 2022 06:36:37 -0400 Subject: [PATCH 06/95] graph prefix removed from module names and imports updated --- docs/source/example_islands.rst | 4 +- docs/source/example_quasisymmetry.rst | 12 +- docs/source/example_vmec_only.rst | 8 +- docs/source/optimizable.rst | 194 +++++++++--------- docs/source/overview.rst | 4 +- docs/source/simsopt._core.rst | 4 +- docs/source/simsopt.objectives.rst | 8 +- docs/source/simsopt.solve.rst | 8 +- examples/1_Simple/just_a_quadratic.py | 6 +- examples/1_Simple/minimize_curve_length.py | 4 +- ...raph_surf_vol_area.py => surf_vol_area.py} | 10 +- .../2_Intermediate/QH_fixed_resolution.py | 4 +- .../QH_fixed_resolution_boozer.py | 4 +- examples/2_Intermediate/QSC.py | 2 +- .../eliminate_magnetic_islands.py | 4 +- .../2_Intermediate/resolution_increase.py | 4 +- .../resolution_increase_boozer.py | 4 +- .../optimize_qs_and_islands_simultaneously.py | 4 +- ...ircularCrossSection_varyAxis_targetIota.py | 4 +- ...arCrossSection_varyAxis_targetIota_spec.py | 4 +- ...ircularCrossSection_varyR0_targetVolume.py | 4 +- ...arCrossSection_varyR0_targetVolume_spec.py | 4 +- ...ion_varyAxis_targetIotaAndQuasisymmetry.py | 4 +- .../2DOF_specOnly_targetIotaAndVolume.py | 4 +- .../2DOF_vmecAndSpec.py | 4 +- .../2DOF_vmecOnly_targetIotaAndVolume.py | 4 +- examples/stellarator_benchmarks/7dof.py | 4 +- src/simsopt/_core/__init__.py | 2 +- src/simsopt/_core/derivative.py | 4 +- src/simsopt/_core/finite_difference.py | 2 +- .../{graph_optimizable.py => optimizable.py} | 0 src/simsopt/field/coil.py | 2 +- src/simsopt/field/magneticfield.py | 2 +- src/simsopt/geo/curve.py | 2 +- src/simsopt/geo/curveobjectives.py | 2 +- src/simsopt/geo/surface.py | 2 +- src/simsopt/geo/surfaceobjectives.py | 2 +- src/simsopt/geo/surfacerzfourier.py | 2 +- src/simsopt/mhd/bootstrap.py | 4 +- src/simsopt/mhd/profiles.py | 2 +- src/simsopt/mhd/spec.py | 2 +- src/simsopt/mhd/vmec.py | 2 +- src/simsopt/mhd/vmec_diagnostics.py | 2 +- src/simsopt/objectives/__init__.py | 2 +- src/simsopt/objectives/fluxobjective.py | 2 +- .../{graph_functions.py => functions.py} | 4 +- ...raph_least_squares.py => least_squares.py} | 4 +- src/simsopt/objectives/utilities.py | 2 +- src/simsopt/solve/__init__.py | 2 +- src/simsopt/solve/{graph_mpi.py => mpi.py} | 4 +- .../solve/{graph_serial.py => serial.py} | 4 +- tests/core/test_derivative.py | 2 +- .../core/{test_graph_dofs.py => test_dofs.py} | 4 +- tests/core/test_finite_difference.py | 2 +- tests/core/test_integrated.py | 6 +- ...aph_optimizable.py => test_optimizable.py} | 4 +- tests/geo/test_curve_optimizable.py | 4 +- tests/mhd/test_boozer.py | 2 +- tests/mhd/test_integrated_vmec_mpi.py | 4 +- tests/mhd/test_spec.py | 4 +- tests/mhd/test_vmec.py | 6 +- tests/mhd/test_vmec_diagnostics.py | 2 +- ...least_squares.py => test_least_squares.py} | 4 +- ...least_squares.py => test_least_squares.py} | 8 +- .../solve/{test_graph_mpi.py => test_mpi.py} | 8 +- tests/util/test_dev.py | 2 +- 66 files changed, 222 insertions(+), 222 deletions(-) rename examples/1_Simple/{graph_surf_vol_area.py => surf_vol_area.py} (85%) rename src/simsopt/_core/{graph_optimizable.py => optimizable.py} (100%) rename src/simsopt/objectives/{graph_functions.py => functions.py} (99%) rename src/simsopt/objectives/{graph_least_squares.py => least_squares.py} (99%) rename src/simsopt/solve/{graph_mpi.py => mpi.py} (98%) rename src/simsopt/solve/{graph_serial.py => serial.py} (98%) rename tests/core/{test_graph_dofs.py => test_dofs.py} (99%) rename tests/core/{test_graph_optimizable.py => test_optimizable.py} (99%) rename tests/objectives/{test_graph_least_squares.py => test_least_squares.py} (95%) rename tests/solve/{test_graph_least_squares.py => test_least_squares.py} (89%) rename tests/solve/{test_graph_mpi.py => test_mpi.py} (94%) diff --git a/docs/source/example_islands.rst b/docs/source/example_islands.rst index ba5565bf8..9c548a0e2 100644 --- a/docs/source/example_islands.rst +++ b/docs/source/example_islands.rst @@ -28,8 +28,8 @@ is to import some items we will need:: import numpy as np from simsopt.mhd.spec import Spec, Residue - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_serial import least_squares_serial_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.serial import least_squares_serial_solve We then create a Spec object based on the input file:: diff --git a/docs/source/example_quasisymmetry.rst b/docs/source/example_quasisymmetry.rst index 01aeb0e56..79df22571 100644 --- a/docs/source/example_quasisymmetry.rst +++ b/docs/source/example_quasisymmetry.rst @@ -43,8 +43,8 @@ imports of the classes and functions we will need:: from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve For this problem we will want MPI for parallelized finite difference gradients. As explained below, this particular problem has 24 @@ -237,8 +237,8 @@ As usual, we begin with the necessary imports:: from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve We again split the pool of MPI processes into worker groups. Here, for simplicity, we make each process its own worker group, by omitting the @@ -347,8 +347,8 @@ In this case, the imports needed are:: from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.boozer import Boozer, Quasisymmetry - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve We again split the pool of MPI processes into worker groups and initialize a ``Vmec`` object as in the previous example:: diff --git a/docs/source/example_vmec_only.rst b/docs/source/example_vmec_only.rst index 21ff2f5db..18454c5ed 100644 --- a/docs/source/example_vmec_only.rst +++ b/docs/source/example_vmec_only.rst @@ -60,8 +60,8 @@ For simplicity, MPI parallelization will not be used for now. To start, we must import several classes:: from simsopt.mhd import Spec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_serial import least_squares_serial_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.serial import least_squares_serial_solve Then we create the equilibrium object, starting from an input file:: @@ -148,8 +148,8 @@ The complete example is then as follows:: from simsopt.util.mpi import MpiPartition from simsopt.mhd import Vmec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.objectives.least_squares import LeastSquaresProblem + from simsopt.solve.mpi import least_squares_mpi_solve # In the next line, we can adjust how many groups the pool of MPI # processes is split into. diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 17f0601c6..93d952a9c 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -5,20 +5,20 @@ The Optimizable class --------------------- A basic tool for defining optimization problems in simsopt is the -class :obj:`~simsopt._core.graph_optimizable.Optimizable`. Many +class :obj:`~simsopt._core.optimizable.Optimizable`. Many classes in simsopt are subclasses of this class. This parent class provides several functions. First, it allows for the parameters of an object to be either fixed or varied in an optimization, for a useful name string to be associated with each such degree of freedom, and for box constraints on each parameter to be set. Second, the -:obj:`~simsopt._core.graph_optimizable.Optimizable` class manages +:obj:`~simsopt._core.optimizable.Optimizable` class manages dependencies between objects. For example, if an MHD equilibrium depends on a :obj:`~simsopt.geo.surface.Surface` object representing the boundary, the equilibrium object will know it needs to recompute the equilibrium if the :obj:`~simsopt.geo.surface.Surface` changes. Third, when a set of objects with dependencies is combined into an objective function, the -:obj:`~simsopt._core.graph_optimizable.Optimizable` class +:obj:`~simsopt._core.optimizable.Optimizable` class automatically combines the non-fixed degrees of freedom into a global state vector, which can be passed to numerical optimization algorithms. @@ -27,7 +27,7 @@ Users can create their own optimizable objects in two ways. One method is to create a standard python function, and apply the :obj:`simsopt.make_optimizable()` function to it, as explained below. Or, you can directly subclass -:obj:`simsopt._core.graph_optimizable.Optimizable`. +:obj:`simsopt._core.optimizable.Optimizable`. Optimizable degrees of freedom @@ -60,7 +60,7 @@ parameter :math:`\theta`. By choosing ``order=1``, only mode numbers 0 and 1 are included. Each dof has a string name, which can be queried using the -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names` +:obj:`~simsopt._core.optimizable.Optimizable.local_dof_names` property:: >>> c.local_dof_names @@ -70,7 +70,7 @@ property:: Evidently there are nine dofs in this case. For each, the number in parentheses is the mode number :math:`m`. The values of the dofs can be read or written to using the -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` property:: +:obj:`~simsopt._core.optimizable.Optimizable.x` property:: >>> c.x @@ -86,14 +86,14 @@ be read or written to using the -2.0 Although you can use indices to retrieve selected elements of -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`, as in the last +:obj:`~simsopt._core.optimizable.Optimizable.x`, as in the last line, you *cannot* assign values to individual elements of -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`, i.e. ``c.x[2] = +:obj:`~simsopt._core.optimizable.Optimizable.x`, i.e. ``c.x[2] = 7.0`` will not work -- you can only assign an entire array to -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`. You can get or +:obj:`~simsopt._core.optimizable.Optimizable.x`. You can get or set individual dofs using their index or string name with the -:obj:`~simsopt._core.graph_optimizable.Optimizable.get()` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.set()` methods:: +:obj:`~simsopt._core.optimizable.Optimizable.get()` and +:obj:`~simsopt._core.optimizable.Optimizable.set()` methods:: >>> c.get(5) @@ -120,11 +120,11 @@ surface, fixing the high-mode-number modes of a surface, or fixing the current in a coil. All dofs in our :obj:`~simsopt.geo.curvexyzfourier.CurveXYZFourier` object are free by default. We can fix a dof using the -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()` method. +:obj:`~simsopt._core.optimizable.Optimizable.fix()` method. When a dof is fixed, it is excluded from the state vector -:obj:`~simsopt._core.graph_optimizable.Optimizable.x`, but you can +:obj:`~simsopt._core.optimizable.Optimizable.x`, but you can still access its value either by name, or with the -:obj:`~simsopt._core.graph_optimizable.Optimizable.full_x` property +:obj:`~simsopt._core.optimizable.Optimizable.full_x` property (which gives both the free and fixed dofs):: >>> c.fix('xc(0)') @@ -141,10 +141,10 @@ still access its value either by name, or with the 1.0 To check which dofs are free, you can use the -:obj:`~simsopt._core.graph_optimizable.Optimizable.dofs_free_status` +:obj:`~simsopt._core.optimizable.Optimizable.dofs_free_status` property. The status of individual dofs can also be checked using -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_fixed` or -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_free`, specify +:obj:`~simsopt._core.optimizable.Optimizable.is_fixed` or +:obj:`~simsopt._core.optimizable.Optimizable.is_free`, specify the dof either using its index or string name :: >>> c.dofs_free_status @@ -164,11 +164,11 @@ the dof either using its index or string name :: False In addition to -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()`, you can +:obj:`~simsopt._core.optimizable.Optimizable.fix()`, you can also manipulate the fixed/free status of dofs using the functions -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix_all()`, and -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix_all()`:: +:obj:`~simsopt._core.optimizable.Optimizable.unfix()`, +:obj:`~simsopt._core.optimizable.Optimizable.fix_all()`, and +:obj:`~simsopt._core.optimizable.Optimizable.unfix_all()`:: >>> c.fix_all() >>> c.x @@ -194,9 +194,9 @@ Dependencies A collection of optimizable objects with dependencies is represented in simsopt as a directed acyclic graph (DAG): each vertex in the graph is an instance of an -:obj:`~simsopt._core.graph_optimizable.Optimizable` object, and the +:obj:`~simsopt._core.optimizable.Optimizable` object, and the direction of each edge indicates dependency. An -:obj:`~simsopt._core.graph_optimizable.Optimizable` object can depend +:obj:`~simsopt._core.optimizable.Optimizable` object can depend on the dofs of other objects, which are called its parents. The orignal object is considered a child of the parent objects. An object's "ancestors" are the an object's parents, their parents, and @@ -210,11 +210,11 @@ directly by an object, and another that applies to the dofs of an object together with its ancestors. The version that applies just to the dofs directly owned by an object has a name beginning ``local_``. For example, analogous to the properties -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names`, which +:obj:`~simsopt._core.optimizable.Optimizable.x` and +:obj:`~simsopt._core.optimizable.Optimizable.dof_names`, which include all ancestor dofs, there are also properties -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names`. +:obj:`~simsopt._core.optimizable.Optimizable.local_x` and +:obj:`~simsopt._core.optimizable.Optimizable.local_dof_names`. To demonstrate these features, we can consider the following small collection of objects: a :obj:`simsopt.field.coil.Coil`, which is a pairing of a :obj:`simsopt.field.coil.Current` with a @@ -265,14 +265,14 @@ properties include the dofs of both of its parents:: 'CurveXYZFourier1:zc(1)'] Note that the names returned by -:obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names` have the +:obj:`~simsopt._core.optimizable.Optimizable.dof_names` have the name of the object and a colon prepended, to distinguish which instance owns the dof. This unique name for each object instance can be accessed by -:obj:`~simsopt._core.graph_optimizable.Optimizable.name`. For the ``current`` and ``curve`` objects, +:obj:`~simsopt._core.optimizable.Optimizable.name`. For the ``current`` and ``curve`` objects, since they have no ancestors, their -:obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names` are the same, except +:obj:`~simsopt._core.optimizable.Optimizable.dof_names` and +:obj:`~simsopt._core.optimizable.Optimizable.local_dof_names` are the same, except that the non-``local_`` versions have the object name prepended:: >>> curve.local_dof_names @@ -293,13 +293,13 @@ that the non-``local_`` versions have the object name prepended:: ['Current1:x0'] -The :obj:`~simsopt._core.graph_optimizable.Optimizable.x` property +The :obj:`~simsopt._core.optimizable.Optimizable.x` property discussed in the previous section includes dofs from ancestors. The related property -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` applies +:obj:`~simsopt._core.optimizable.Optimizable.local_x` applies only to the dofs directly owned by an object. When the dofs of a parent are changed, the -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` property of +:obj:`~simsopt._core.optimizable.Optimizable.x` property of child objects is automatically updated:: >>> curve.x = [1.7, -0.2, 0.1, -1.1, 0.7, 0.3, 1.3, -0.6, 0.5] @@ -329,23 +329,23 @@ child objects is automatically updated:: array([], dtype=float64) Above, you can see that -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` +:obj:`~simsopt._core.optimizable.Optimizable.x` and +:obj:`~simsopt._core.optimizable.Optimizable.local_x` give the same results for ``curve`` and ``current`` since these objects have no ancestors. For ``coil``, -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` +:obj:`~simsopt._core.optimizable.Optimizable.local_x` returns an empty array because ``coil`` does not own any dofs itself, while -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` +:obj:`~simsopt._core.optimizable.Optimizable.x` is a concatenation of the dofs of its ancestors. The functions -:obj:`~simsopt._core.graph_optimizable.Optimizable.get()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.set()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_fixed()`, and -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_free()` refer +:obj:`~simsopt._core.optimizable.Optimizable.get()`, +:obj:`~simsopt._core.optimizable.Optimizable.set()`, +:obj:`~simsopt._core.optimizable.Optimizable.fix()`, +:obj:`~simsopt._core.optimizable.Optimizable.unfix()`, +:obj:`~simsopt._core.optimizable.Optimizable.is_fixed()`, and +:obj:`~simsopt._core.optimizable.Optimizable.is_free()` refer only to dofs directly owned by an object. If an integer index is supplied to these functions it must be the local index, and if a string name is supplied to these functions, it does not have the @@ -356,7 +356,7 @@ object name and colon prepended. So for instance, When some dofs are fixed in parent objects, these dofs are automatically removed from the global state vector -:obj:`~simsopt._core.graph_optimizable.Optimizable.x` of a child +:obj:`~simsopt._core.optimizable.Optimizable.x` of a child object:: >>> curve.fix_all() @@ -369,19 +369,19 @@ object:: ['Current1:x0', 'CurveXYZFourier1:zc(0)'] -Thus, the :obj:`~simsopt._core.graph_optimizable.Optimizable.x` +Thus, the :obj:`~simsopt._core.optimizable.Optimizable.x` property of a child object is convenient to use as the state vector for numerical optimization packages, as it automatically combines the selected degrees of freedom that you wish to vary from all objects that are involved in the optimization problem. If you wish to get or set the state vector *including* the fixed dofs, you can use the -properties :obj:`~simsopt._core.graph_optimizable.Optimizable.full_x` +properties :obj:`~simsopt._core.optimizable.Optimizable.full_x` (which includes ancestors) or -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_x` +:obj:`~simsopt._core.optimizable.Optimizable.local_full_x` (which does not). The corresponding string labels including the fixed dofs can be accessed using -:obj:`~simsopt._core.graph_optimizable.Optimizable.full_dof_names` and -:obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_dof_names`:: +:obj:`~simsopt._core.optimizable.Optimizable.full_dof_names` and +:obj:`~simsopt._core.optimizable.Optimizable.local_full_dof_names`:: >>> coil.full_x @@ -409,7 +409,7 @@ Function reference ------------------ The following tables provide a reference for many of the properties -and functions of :obj:`~simsopt._core.graph_optimizable.Optimizable` +and functions of :obj:`~simsopt._core.optimizable.Optimizable` objects. Many come in a set of 2x2 variants: .. list-table:: State vector @@ -421,11 +421,11 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_x` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.full_x` + - :obj:`~simsopt._core.optimizable.Optimizable.local_full_x` + - :obj:`~simsopt._core.optimizable.Optimizable.full_x` * - Free only - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_x` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.x` + - :obj:`~simsopt._core.optimizable.Optimizable.local_x` + - :obj:`~simsopt._core.optimizable.Optimizable.x` .. list-table:: Number of elements in the state vector :widths: 20 20 20 @@ -436,11 +436,11 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_dof_size` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.full_dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.local_full_dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.full_dof_size` * - Free only - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_size` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.local_dof_size` + - :obj:`~simsopt._core.optimizable.Optimizable.dof_size` .. list-table:: String names :widths: 20 20 20 @@ -451,11 +451,11 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_full_dof_names` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.full_dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.local_full_dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.full_dof_names` * - Free only - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_dof_names` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.local_dof_names` + - :obj:`~simsopt._core.optimizable.Optimizable.dof_names` .. list-table:: Whether dofs are free :widths: 20 20 20 @@ -466,8 +466,8 @@ objects. Many come in a set of 2x2 variants: - Excluding ancestors - Including ancestors * - Both fixed and free - - :obj:`~simsopt._core.graph_optimizable.Optimizable.local_dofs_free_status` - - :obj:`~simsopt._core.graph_optimizable.Optimizable.dofs_free_status` + - :obj:`~simsopt._core.optimizable.Optimizable.local_dofs_free_status` + - :obj:`~simsopt._core.optimizable.Optimizable.dofs_free_status` * - Free only - N/A - N/A @@ -475,12 +475,12 @@ objects. Many come in a set of 2x2 variants: Other attributes: ``name``, ``parents``, ``ancestors`` Other functions: -:obj:`~simsopt._core.graph_optimizable.Optimizable.get()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.set()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.fix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_fixed()`, -:obj:`~simsopt._core.graph_optimizable.Optimizable.is_free()`. +:obj:`~simsopt._core.optimizable.Optimizable.get()`, +:obj:`~simsopt._core.optimizable.Optimizable.set()`, +:obj:`~simsopt._core.optimizable.Optimizable.fix()`, +:obj:`~simsopt._core.optimizable.Optimizable.unfix()`, +:obj:`~simsopt._core.optimizable.Optimizable.is_fixed()`, +:obj:`~simsopt._core.optimizable.Optimizable.is_free()`. Caching @@ -493,14 +493,14 @@ However if any dofs change, either dofs owned locally or by an ancestor object, this computation needs to be re-run. Many Optimizable objects in simsopt therefore implement caching: results are saved, until the cache is cleared due to changes in dofs. The -:obj:`~simsopt._core.graph_optimizable.Optimizable` base class +:obj:`~simsopt._core.optimizable.Optimizable` base class provides a function -:obj:`~simsopt._core.graph_optimizable.Optimizable.recompute_bell()` +:obj:`~simsopt._core.optimizable.Optimizable.recompute_bell()` to assist with caching. This function is called automatically whenever dofs of an object or any of its ancestors change. Subclasses of -:obj:`~simsopt._core.graph_optimizable.Optimizable` can overload the +:obj:`~simsopt._core.optimizable.Optimizable` can overload the default (empty) -:obj:`~simsopt._core.graph_optimizable.Optimizable.recompute_bell()` +:obj:`~simsopt._core.optimizable.Optimizable.recompute_bell()` function to manage their cache in a customized way. @@ -509,15 +509,15 @@ Specifying least-squares objective functions A common use case is to minimize a nonlinear least-squares objective function, which consists of a sum of several terms. In this case the -:obj:`simsopt.objectives.graph_least_squares.LeastSquaresProblem` +:obj:`simsopt.objectives.least_squares.LeastSquaresProblem` class can be used. Suppose we want to solve a least-squares optimization problem in which an -:obj:`~simsopt._core.graph_optimizable.Optimizable` object ``obj`` has +:obj:`~simsopt._core.optimizable.Optimizable` object ``obj`` has some dofs to be optimized. If ``obj`` has a function ``func()``, we can define the objective function ``weight * ((obj.func() - goal) ** 2)`` as follows:: - from simsopt.objectives.graph_least_squares import LeastSquaresProblem + from simsopt.objectives.least_squares import LeastSquaresProblem prob = LeastSquaresProblem.from_tuples([(obj.func, goal, weight)]) Note that the problem was defined using a 3-element tuple of the form @@ -537,19 +537,19 @@ The corresponding objective funtion is then ``weight1 * 2)``. The list of tuples can include any mixture of terms defined by scalar functions and by 1D numpy array-valued functions. Note that the function handles that are specified should be members of an -:obj:`~simsopt._core.graph_optimizable.Optimizable` object. As -:obj:`~simsopt.objectives.graph_least_squares.LeastSquaresProblem` is -a subclass of :obj:`~simsopt._core.graph_optimizable.Optimizable`, the +:obj:`~simsopt._core.optimizable.Optimizable` object. As +:obj:`~simsopt.objectives.least_squares.LeastSquaresProblem` is +a subclass of :obj:`~simsopt._core.optimizable.Optimizable`, the free dofs of all the objects that go into the objective function are available in the global state vector ``prob.x``. The overall scalar objective function is available from -:func:`simsopt.objectives.graph_least_squares.LeastSquaresProblem.objective`. +:func:`simsopt.objectives.least_squares.LeastSquaresProblem.objective`. The vector of residuals before scaling by the ``weight`` factors ``obj.func() - goal`` is available from -:func:`simsopt.objectives.graph_least_squares.LeastSquaresProblem.unweighted_residuals`. +:func:`simsopt.objectives.least_squares.LeastSquaresProblem.unweighted_residuals`. The vector of residuals after scaling by the ``weight`` factors, ``sqrt(weight) * (obj.func() - goal)``, is available from -:func:`simsopt.objectives.graph_least_squares.LeastSquaresProblem.residuals`. +:func:`simsopt.objectives.least_squares.LeastSquaresProblem.residuals`. Least-squares problems can also be defined in an alternative way:: @@ -570,16 +570,16 @@ Custom objective functions and optimizable objects You may wish to use a custom objective function. The recommended approach for this is to use -:func:`simsopt._core.graph_optimizable.make_optimizable()`, which can +:func:`simsopt._core.optimizable.make_optimizable()`, which can be imported from the top-level ``simsopt`` module. In this approach, you first define a standard python function which takes as arguments -any :obj:`~simsopt._core.graph_optimizable.Optimizable` objects that +any :obj:`~simsopt._core.optimizable.Optimizable` objects that the function depends on. This function can return a float or 1D numpy array. You then apply -:func:`~simsopt._core.graph_optimizable.make_optimizable()` to the +:func:`~simsopt._core.optimizable.make_optimizable()` to the function handle, including the parent objects as additional arguments. The newly created -:obj:`~simsopt._core.graph_optimizable.Optimizable` object will have a +:obj:`~simsopt._core.optimizable.Optimizable` object will have a function ``.J()`` that returns the function you created. For instance, suppose we wish to minimize the objective function @@ -589,7 +589,7 @@ can be accomplished as follows:: from simsopt import make_optimizable from simsopt.mhd.vmec import Vmec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem + from simsopt.objectives.least_squares import LeastSquaresProblem def myfunc(v): v.run() # Ensure VMEC has run with the latest dofs. @@ -600,22 +600,22 @@ can be accomplished as follows:: prob = LeastSquaresProblem.from_tuples([(myopt.J, 0.1, 1)]) In this example, the new -:obj:`~simsopt._core.graph_optimizable.Optimizable` object did not own +:obj:`~simsopt._core.optimizable.Optimizable` object did not own any dofs. However the -:func:`~simsopt._core.graph_optimizable.make_optimizable()` can also -create :obj:`~simsopt._core.graph_optimizable.Optimizable` objects +:func:`~simsopt._core.optimizable.make_optimizable()` can also +create :obj:`~simsopt._core.optimizable.Optimizable` objects with their own dofs and other parameters. For this syntax, see the API documentation for -:func:`~simsopt._core.graph_optimizable.make_optimizable()`. +:func:`~simsopt._core.optimizable.make_optimizable()`. An alternative to using -:func:`~simsopt._core.graph_optimizable.make_optimizable()` is to +:func:`~simsopt._core.optimizable.make_optimizable()` is to write your own subclass of -:obj:`~simsopt._core.graph_optimizable.Optimizable`. In this +:obj:`~simsopt._core.optimizable.Optimizable`. In this approach, the above example looks as follows:: - from simsopt._core.graph_optimizable import Optimizable + from simsopt._core.optimizable import Optimizable from simsopt.mhd.vmec import Vmec - from simsopt.objectives.graph_least_squares import LeastSquaresProblem + from simsopt.objectives.least_squares import LeastSquaresProblem class Myopt(Optimizable): def __init__(self, v): diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 553bb8f43..332ea2f8e 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -82,8 +82,8 @@ More details about setting degrees of freedom and defining objective functions can be found on the :doc:`optimizable` page. For the solution step, two functions are provided presently, -:meth:`simsopt.solve.graph_serial.least_squares_serial_solve` and -:meth:`simsopt.solve.graph_mpi.least_squares_mpi_solve`. The first +:meth:`simsopt.solve.serial.least_squares_serial_solve` and +:meth:`simsopt.solve.mpi.least_squares_mpi_solve`. The first is simpler, while the second allows MPI-parallelized finite differences to be used in the optimization. diff --git a/docs/source/simsopt._core.rst b/docs/source/simsopt._core.rst index 951430c23..124ea4bee 100644 --- a/docs/source/simsopt._core.rst +++ b/docs/source/simsopt._core.rst @@ -22,10 +22,10 @@ simsopt.\_core.finite_difference module :show-inheritance: :private-members: -simsopt.\_core.graph\_optimizable module +simsopt.\_core.optimizable module ---------------------------------------- -.. automodule:: simsopt._core.graph_optimizable +.. automodule:: simsopt._core.optimizable :members: :undoc-members: :show-inheritance: diff --git a/docs/source/simsopt.objectives.rst b/docs/source/simsopt.objectives.rst index 48e6cc17e..cfc385ccb 100644 --- a/docs/source/simsopt.objectives.rst +++ b/docs/source/simsopt.objectives.rst @@ -12,18 +12,18 @@ simsopt.objectives.fluxobjective module :undoc-members: :show-inheritance: -simsopt.objectives.graph\_functions module +simsopt.objectives.functions module ------------------------------------------ -.. automodule:: simsopt.objectives.graph_functions +.. automodule:: simsopt.objectives.functions :members: :undoc-members: :show-inheritance: -simsopt.objectives.graph\_least\_squares module +simsopt.objectives.least\_squares module ----------------------------------------------- -.. automodule:: simsopt.objectives.graph_least_squares +.. automodule:: simsopt.objectives.least_squares :members: :undoc-members: :show-inheritance: diff --git a/docs/source/simsopt.solve.rst b/docs/source/simsopt.solve.rst index 703df85ad..4cf6d38c4 100644 --- a/docs/source/simsopt.solve.rst +++ b/docs/source/simsopt.solve.rst @@ -4,18 +4,18 @@ simsopt.solve package Submodules ---------- -simsopt.solve.graph\_mpi module +simsopt.solve.mpi module ------------------------------- -.. automodule:: simsopt.solve.graph_mpi +.. automodule:: simsopt.solve.mpi :members: :undoc-members: :show-inheritance: -simsopt.solve.graph\_serial module +simsopt.solve.serial module ---------------------------------- -.. automodule:: simsopt.solve.graph_serial +.. automodule:: simsopt.solve.serial :members: :undoc-members: :show-inheritance: diff --git a/examples/1_Simple/just_a_quadratic.py b/examples/1_Simple/just_a_quadratic.py index 3da5b2a27..339d09e56 100755 --- a/examples/1_Simple/just_a_quadratic.py +++ b/examples/1_Simple/just_a_quadratic.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 import logging -from simsopt.objectives.graph_functions import Identity -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.functions import Identity +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ Minimize f(x,y,z) = ((x-1)/1)^2 + ((y-2)/2)^2 + ((z-3)/3)^2. diff --git a/examples/1_Simple/minimize_curve_length.py b/examples/1_Simple/minimize_curve_length.py index 483c5d823..971696eec 100755 --- a/examples/1_Simple/minimize_curve_length.py +++ b/examples/1_Simple/minimize_curve_length.py @@ -3,8 +3,8 @@ import numpy as np from simsopt.geo.curverzfourier import CurveRZFourier from simsopt.geo.curveobjectives import CurveLength -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ Minimize the length of a curve, holding the 0-frequency Fourier mode fixed. diff --git a/examples/1_Simple/graph_surf_vol_area.py b/examples/1_Simple/surf_vol_area.py similarity index 85% rename from examples/1_Simple/graph_surf_vol_area.py rename to examples/1_Simple/surf_vol_area.py index dd86d9f0d..484154550 100755 --- a/examples/1_Simple/graph_surf_vol_area.py +++ b/examples/1_Simple/surf_vol_area.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 from simsopt.geo.surfacerzfourier import SurfaceRZFourier -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ Optimize the minor radius and elongation of an axisymmetric torus to -obtain a desired volume and area using the graph method. +obtain a desired volume and area. """ -print("Running 1_Simple/graph_surf_vol_area.py") +print("Running 1_Simple/surf_vol_area.py") print("=======================================") desired_volume = 0.6 desired_area = 8.0 @@ -54,5 +54,5 @@ print(" -------------------------\n\n") -print("End of 1_Simple/graph_surf_vol_area.py") +print("End of 1_Simple/surf_vol_area.py") print("=======================================") diff --git a/examples/2_Intermediate/QH_fixed_resolution.py b/examples/2_Intermediate/QH_fixed_resolution.py index 354615417..ef818bcd8 100755 --- a/examples/2_Intermediate/QH_fixed_resolution.py +++ b/examples/2_Intermediate/QH_fixed_resolution.py @@ -5,8 +5,8 @@ from simsopt.util.mpi import MpiPartition from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ Optimize a VMEC equilibrium for quasi-helical symmetry (M=1, N=1) diff --git a/examples/2_Intermediate/QH_fixed_resolution_boozer.py b/examples/2_Intermediate/QH_fixed_resolution_boozer.py index cbbf56d75..11dede2f4 100755 --- a/examples/2_Intermediate/QH_fixed_resolution_boozer.py +++ b/examples/2_Intermediate/QH_fixed_resolution_boozer.py @@ -3,8 +3,8 @@ import os from simsopt.util.mpi import MpiPartition from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ Optimize for quasi-helical symmetry (M=1, N=1) at a given radius. diff --git a/examples/2_Intermediate/QSC.py b/examples/2_Intermediate/QSC.py index 8cc9f467c..9e0ecd130 100755 --- a/examples/2_Intermediate/QSC.py +++ b/examples/2_Intermediate/QSC.py @@ -4,7 +4,7 @@ import numpy as np from qsc import Qsc -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable from simsopt import LeastSquaresProblem, least_squares_serial_solve """ diff --git a/examples/2_Intermediate/eliminate_magnetic_islands.py b/examples/2_Intermediate/eliminate_magnetic_islands.py index 5999ed3f0..87690395a 100755 --- a/examples/2_Intermediate/eliminate_magnetic_islands.py +++ b/examples/2_Intermediate/eliminate_magnetic_islands.py @@ -4,8 +4,8 @@ import numpy as np from simsopt.util.mpi import MpiPartition, log from simsopt.mhd.spec import Spec, Residue -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve import os """ diff --git a/examples/2_Intermediate/resolution_increase.py b/examples/2_Intermediate/resolution_increase.py index 70f978467..c08befc09 100755 --- a/examples/2_Intermediate/resolution_increase.py +++ b/examples/2_Intermediate/resolution_increase.py @@ -5,8 +5,8 @@ from simsopt.util.mpi import MpiPartition, log from simsopt.mhd.vmec import Vmec from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ This example shows how scripting can be used to increase the size diff --git a/examples/2_Intermediate/resolution_increase_boozer.py b/examples/2_Intermediate/resolution_increase_boozer.py index e5fc2aac3..297344e07 100755 --- a/examples/2_Intermediate/resolution_increase_boozer.py +++ b/examples/2_Intermediate/resolution_increase_boozer.py @@ -3,8 +3,8 @@ import os from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ This example shows how scripting can be used to increase the size diff --git a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py index 809e2083c..aa8415654 100755 --- a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py +++ b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py @@ -6,8 +6,8 @@ from simsopt.util.mpi import log, MpiPartition from simsopt.mhd import Vmec, Spec, Boozer, Quasisymmetry from simsopt.mhd.spec import Residue -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve """ In this example, we simultaneously optimize for quasisymmetry and diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py index 78e5a735a..4ea51df9f 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py @@ -5,9 +5,9 @@ import numpy as np from simsopt.mhd import Vmec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.util.mpi import MpiPartition, log -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.geo.surfacegarabedian import SurfaceGarabedian """ diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py index bad577bdb..09c50e97f 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py @@ -6,8 +6,8 @@ from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Spec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.geo.surfacegarabedian import SurfaceGarabedian """ diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py index 20ed699bf..3050c87a7 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py @@ -3,10 +3,10 @@ import numpy as np from mpi4py import MPI -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.mhd import Vmec from simsopt.util.mpi import MpiPartition, log -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.solve.mpi import least_squares_mpi_solve """ This script implements the "1DOF_circularCrossSection_varyR0_targetVolume" diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py index dbfa57446..1dab40b07 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py @@ -5,8 +5,8 @@ from simsopt.mhd import Spec from simsopt.geo.surfacerzfourier import SurfaceRZFourier -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve """ This script implements the "1DOF_circularCrossSection_varyR0_targetVolume" diff --git a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py index 986d1d5c7..ce934ec8a 100755 --- a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py +++ b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py @@ -1,8 +1,8 @@ #!/usr/bin/env python from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve import os """ diff --git a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py index 5fc6e7a3d..3937161f3 100755 --- a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py @@ -4,8 +4,8 @@ import numpy as np from simsopt.mhd import Spec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve import os """ diff --git a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py index 447305046..6483c399d 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py +++ b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py @@ -5,8 +5,8 @@ from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Vmec, Spec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve import os """ diff --git a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py index dd1d4aa5d..9ba544b93 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py @@ -3,9 +3,9 @@ import numpy as np from simsopt.mhd import Vmec -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.util.mpi import MpiPartition, log -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.solve.mpi import least_squares_mpi_solve import os """ diff --git a/examples/stellarator_benchmarks/7dof.py b/examples/stellarator_benchmarks/7dof.py index 5c4046f1c..cad0e9904 100755 --- a/examples/stellarator_benchmarks/7dof.py +++ b/examples/stellarator_benchmarks/7dof.py @@ -3,8 +3,8 @@ import os from simsopt.util.mpi import MpiPartition, log from simsopt.mhd import Vmec, Boozer, Quasisymmetry -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_mpi import least_squares_mpi_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.geo.surfacegarabedian import SurfaceGarabedian """ diff --git a/src/simsopt/_core/__init__.py b/src/simsopt/_core/__init__.py index afe2f5a6b..583c418af 100644 --- a/src/simsopt/_core/__init__.py +++ b/src/simsopt/_core/__init__.py @@ -1 +1 @@ -from .graph_optimizable import Optimizable, make_optimizable +from .optimizable import Optimizable, make_optimizable diff --git a/src/simsopt/_core/derivative.py b/src/simsopt/_core/derivative.py index 64300c16d..a8c82396d 100644 --- a/src/simsopt/_core/derivative.py +++ b/src/simsopt/_core/derivative.py @@ -13,7 +13,7 @@ def __init__(self, d): super().__init__(None, d) def __missing__(self, key): - from .graph_optimizable import Optimizable # Import here to avoid circular import + from .optimizable import Optimizable # Import here to avoid circular import assert isinstance(key, Optimizable) self[key] = value = np.zeros((key.local_full_dof_size, )) return value @@ -162,7 +162,7 @@ def __call__(self, optim): Args: optim: An Optimizable object """ - from .graph_optimizable import Optimizable # Import here to avoid circular import + from .optimizable import Optimizable # Import here to avoid circular import assert isinstance(optim, Optimizable) deps = optim.ancestors + [optim] derivs = [] diff --git a/src/simsopt/_core/finite_difference.py b/src/simsopt/_core/finite_difference.py index b886bf221..bd7503838 100644 --- a/src/simsopt/_core/finite_difference.py +++ b/src/simsopt/_core/finite_difference.py @@ -27,7 +27,7 @@ from ..util.types import RealArray from ..util.dev import SimsoptRequires -from .graph_optimizable import Optimizable +from .optimizable import Optimizable from .util import finite_difference_steps logger = logging.getLogger(__name__) diff --git a/src/simsopt/_core/graph_optimizable.py b/src/simsopt/_core/optimizable.py similarity index 100% rename from src/simsopt/_core/graph_optimizable.py rename to src/simsopt/_core/optimizable.py diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 1684de283..b8f5157bb 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -1,4 +1,4 @@ -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable from simsopt._core.derivative import Derivative from simsopt.geo.curvexyzfourier import CurveXYZFourier from simsopt.geo.curve import RotatedCurve diff --git a/src/simsopt/field/magneticfield.py b/src/simsopt/field/magneticfield.py index 50b4ff594..fa2756178 100644 --- a/src/simsopt/field/magneticfield.py +++ b/src/simsopt/field/magneticfield.py @@ -1,7 +1,7 @@ import numpy as np import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.derivative import Derivative diff --git a/src/simsopt/geo/curve.py b/src/simsopt/geo/curve.py index 56432a234..2ff3f488f 100644 --- a/src/simsopt/geo/curve.py +++ b/src/simsopt/geo/curve.py @@ -7,7 +7,7 @@ from monty.dev import requires import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from simsopt._core.derivative import Derivative from .plot import fix_matplotlib_3d diff --git a/src/simsopt/geo/curveobjectives.py b/src/simsopt/geo/curveobjectives.py index 40dc6fea7..7b11e8822 100644 --- a/src/simsopt/geo/curveobjectives.py +++ b/src/simsopt/geo/curveobjectives.py @@ -3,7 +3,7 @@ import jax.numpy as jnp from .jit import jit -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.derivative import derivative_dec diff --git a/src/simsopt/geo/surface.py b/src/simsopt/geo/surface.py index a29469fca..784167714 100644 --- a/src/simsopt/geo/surface.py +++ b/src/simsopt/geo/surface.py @@ -9,7 +9,7 @@ gridToVTK = None import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.dev import SimsoptRequires from .plot import fix_matplotlib_3d diff --git a/src/simsopt/geo/surfaceobjectives.py b/src/simsopt/geo/surfaceobjectives.py index f94ec44ea..7df429dca 100644 --- a/src/simsopt/geo/surfaceobjectives.py +++ b/src/simsopt/geo/surfaceobjectives.py @@ -4,7 +4,7 @@ from nptyping import NDArray, Float import simsoptpp as sopp -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from simsopt.geo.surface import Surface diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index 6b1bb103d..f693b6449 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -7,7 +7,7 @@ import simsoptpp as sopp from .surface import Surface -from .._core.graph_optimizable import DOFs, Optimizable +from .._core.optimizable import DOFs, Optimizable from .._core.util import nested_lists_to_array logger = logging.getLogger(__name__) diff --git a/src/simsopt/mhd/bootstrap.py b/src/simsopt/mhd/bootstrap.py index 6579223e7..71572268e 100644 --- a/src/simsopt/mhd/bootstrap.py +++ b/src/simsopt/mhd/bootstrap.py @@ -11,7 +11,7 @@ from scipy.interpolate import RectBivariateSpline, interp1d from scipy.optimize import minimize, Bounds from scipy.integrate import quad -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import Struct from ..util.constants import ELEMENTARY_CHARGE from .profiles import Profile, ProfilePolynomial @@ -677,7 +677,7 @@ def residuals(self): representing the objective function as a nonlinear least-squares problem. This is the function handle to use with a - :obj:`~simsopt.objectives.graph_least_squares.LeastSquaresProblem`. + :obj:`~simsopt.objectives.least_squares.LeastSquaresProblem`. Specifically, this function returns diff --git a/src/simsopt/mhd/profiles.py b/src/simsopt/mhd/profiles.py index 8b8f662b7..bbb87d1ed 100644 --- a/src/simsopt/mhd/profiles.py +++ b/src/simsopt/mhd/profiles.py @@ -11,7 +11,7 @@ import numpy as np import numpy.polynomial.polynomial as poly from scipy.interpolate import InterpolatedUnivariateSpline -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable logger = logging.getLogger(__name__) diff --git a/src/simsopt/mhd/spec.py b/src/simsopt/mhd/spec.py index bde1757a6..d10cfc621 100644 --- a/src/simsopt/mhd/spec.py +++ b/src/simsopt/mhd/spec.py @@ -39,7 +39,7 @@ pyoculus = None logger.debug(str(e)) -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import ObjectiveFailure from ..geo.surfacerzfourier import SurfaceRZFourier if MPI is not None: diff --git a/src/simsopt/mhd/vmec.py b/src/simsopt/mhd/vmec.py index a8e1a2717..c7a02473e 100644 --- a/src/simsopt/mhd/vmec.py +++ b/src/simsopt/mhd/vmec.py @@ -29,7 +29,7 @@ vmec = None logger.debug(str(e)) -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import Struct, ObjectiveFailure from ..geo.surfacerzfourier import SurfaceRZFourier diff --git a/src/simsopt/mhd/vmec_diagnostics.py b/src/simsopt/mhd/vmec_diagnostics.py index 8d237916d..df5f1a570 100644 --- a/src/simsopt/mhd/vmec_diagnostics.py +++ b/src/simsopt/mhd/vmec_diagnostics.py @@ -15,7 +15,7 @@ from .vmec import Vmec from .._core.util import Struct -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.types import RealArray from ..geo.surfaceobjectives import parameter_derivatives diff --git a/src/simsopt/objectives/__init__.py b/src/simsopt/objectives/__init__.py index 09afd1e26..7a5d26052 100644 --- a/src/simsopt/objectives/__init__.py +++ b/src/simsopt/objectives/__init__.py @@ -1,3 +1,3 @@ -from .graph_least_squares import LeastSquaresProblem +from .least_squares import LeastSquaresProblem __all__ = ['LeastSquaresTerm', 'LeastSquaresProblem'] diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index f6c85e1ae..ae004cfe9 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -1,4 +1,4 @@ -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable from .._core.derivative import derivative_dec import numpy as np diff --git a/src/simsopt/objectives/graph_functions.py b/src/simsopt/objectives/functions.py similarity index 99% rename from src/simsopt/objectives/graph_functions.py rename to src/simsopt/objectives/functions.py index 231500bd1..6ffcaf516 100644 --- a/src/simsopt/objectives/graph_functions.py +++ b/src/simsopt/objectives/functions.py @@ -13,14 +13,14 @@ import numpy as np -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.types import RealArray class Identity(Optimizable): """ Represents a term in an objective function which is just - the identity. It has one degree of freedom. Conforms to the experimental + the identity. It has one degree of freedom. Conforms to the graph based Optimizable framework. The output of the method `f` is equal to this degree of freedom. diff --git a/src/simsopt/objectives/graph_least_squares.py b/src/simsopt/objectives/least_squares.py similarity index 99% rename from src/simsopt/objectives/graph_least_squares.py rename to src/simsopt/objectives/least_squares.py index a0aeb0119..0cd7988ff 100644 --- a/src/simsopt/objectives/graph_least_squares.py +++ b/src/simsopt/objectives/least_squares.py @@ -17,7 +17,7 @@ import numpy as np -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.util import ObjectiveFailure from ..util.types import RealArray, IntArray, BoolArray @@ -29,7 +29,7 @@ class LeastSquaresProblem(Optimizable): """ - Represents a nonlinear-least-squares problem implemented using the new + Represents a nonlinear-least-squares problem implemented using the graph based optimization framework. A LeastSquaresProblem instance has 3 basic attributes: a set of functions (`f_in`), target values for each of the functions (`goal`), and weights. The residual diff --git a/src/simsopt/objectives/utilities.py b/src/simsopt/objectives/utilities.py index 347501e8b..49fa535d0 100644 --- a/src/simsopt/objectives/utilities.py +++ b/src/simsopt/objectives/utilities.py @@ -1,6 +1,6 @@ import numpy as np -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from .._core.derivative import Derivative, derivative_dec diff --git a/src/simsopt/solve/__init__.py b/src/simsopt/solve/__init__.py index 5c9ac7c3a..0ab707b8d 100644 --- a/src/simsopt/solve/__init__.py +++ b/src/simsopt/solve/__init__.py @@ -1,3 +1,3 @@ -from .graph_serial import least_squares_serial_solve, serial_solve +from .serial import least_squares_serial_solve, serial_solve __all__ = ['least_squares_serial_solve', 'serial_solve'] diff --git a/src/simsopt/solve/graph_mpi.py b/src/simsopt/solve/mpi.py similarity index 98% rename from src/simsopt/solve/graph_mpi.py rename to src/simsopt/solve/mpi.py index a17033a80..ddfd4f2c1 100644 --- a/src/simsopt/solve/graph_mpi.py +++ b/src/simsopt/solve/mpi.py @@ -21,12 +21,12 @@ except ImportError as err: MPI = None -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable from ..util.mpi import MpiPartition from ..util.types import RealArray from .._core.util import finite_difference_steps from .._core.finite_difference import MPIFiniteDifference -from ..objectives.graph_least_squares import LeastSquaresProblem +from ..objectives.least_squares import LeastSquaresProblem logger = logging.getLogger(__name__) diff --git a/src/simsopt/solve/graph_serial.py b/src/simsopt/solve/serial.py similarity index 98% rename from src/simsopt/solve/graph_serial.py rename to src/simsopt/solve/serial.py index ec27f5e4f..91eed488b 100644 --- a/src/simsopt/solve/graph_serial.py +++ b/src/simsopt/solve/serial.py @@ -16,8 +16,8 @@ import numpy as np from scipy.optimize import least_squares, minimize -from ..objectives.graph_least_squares import LeastSquaresProblem -from .._core.graph_optimizable import Optimizable +from ..objectives.least_squares import LeastSquaresProblem +from .._core.optimizable import Optimizable from .._core.finite_difference import FiniteDifference diff --git a/tests/core/test_derivative.py b/tests/core/test_derivative.py index 430906d1c..47e1011bf 100644 --- a/tests/core/test_derivative.py +++ b/tests/core/test_derivative.py @@ -1,6 +1,6 @@ import unittest import numpy as np -from simsopt._core.graph_optimizable import Optimizable, ScaledOptimizable, OptimizableSum +from simsopt._core.optimizable import Optimizable, ScaledOptimizable, OptimizableSum from simsopt._core.derivative import Derivative, derivative_dec diff --git a/tests/core/test_graph_dofs.py b/tests/core/test_dofs.py similarity index 99% rename from tests/core/test_graph_dofs.py rename to tests/core/test_dofs.py index 4c5528d7a..9b6966eb4 100755 --- a/tests/core/test_graph_dofs.py +++ b/tests/core/test_dofs.py @@ -1,8 +1,8 @@ import unittest import numpy as np -from simsopt._core.graph_optimizable import DOFs -from simsopt.objectives.graph_functions import Identity, Adder, Rosenbrock +from simsopt._core.optimizable import DOFs +from simsopt.objectives.functions import Identity, Adder, Rosenbrock from simsopt._core.util import DofLengthMismatchError diff --git a/tests/core/test_finite_difference.py b/tests/core/test_finite_difference.py index d13bf3508..d7c284b81 100755 --- a/tests/core/test_finite_difference.py +++ b/tests/core/test_finite_difference.py @@ -7,7 +7,7 @@ except: MPI = None -from simsopt._core.graph_optimizable import Optimizable, make_optimizable +from simsopt._core.optimizable import Optimizable, make_optimizable from simsopt._core.finite_difference import FiniteDifference if MPI is not None: from simsopt.util.mpi import MpiPartition diff --git a/tests/core/test_integrated.py b/tests/core/test_integrated.py index 406581b3b..0606bbaff 100755 --- a/tests/core/test_integrated.py +++ b/tests/core/test_integrated.py @@ -10,11 +10,11 @@ from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.geo.surfacegarabedian import SurfaceGarabedian -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve if MPI is not None: from simsopt.util.mpi import MpiPartition - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve def mpi_solve_1group(prob, **kwargs): diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_optimizable.py similarity index 99% rename from tests/core/test_graph_optimizable.py rename to tests/core/test_optimizable.py index 0134e44bd..5976a14f1 100755 --- a/tests/core/test_graph_optimizable.py +++ b/tests/core/test_optimizable.py @@ -3,8 +3,8 @@ import numpy as np -from simsopt._core.graph_optimizable import Optimizable, make_optimizable -from simsopt.objectives.graph_functions import Identity, Rosenbrock, TestObject2 +from simsopt._core.optimizable import Optimizable, make_optimizable +from simsopt.objectives.functions import Identity, Rosenbrock, TestObject2 class Adder(Optimizable): diff --git a/tests/geo/test_curve_optimizable.py b/tests/geo/test_curve_optimizable.py index cd357f566..cf2a9e716 100644 --- a/tests/geo/test_curve_optimizable.py +++ b/tests/geo/test_curve_optimizable.py @@ -6,8 +6,8 @@ from simsopt.geo.curve import RotatedCurve from simsopt.geo import parameters from simsopt.geo.curveobjectives import CurveLength -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve parameters['jit'] = False diff --git a/tests/mhd/test_boozer.py b/tests/mhd/test_boozer.py index 42b1afa07..4a21841c7 100755 --- a/tests/mhd/test_boozer.py +++ b/tests/mhd/test_boozer.py @@ -18,7 +18,7 @@ except ImportError as e: MPI = None -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable if MPI is not None: from simsopt.mhd.boozer import Boozer, Quasisymmetry # , booz_xform_found from simsopt.mhd.vmec import Vmec # , vmec_found diff --git a/tests/mhd/test_integrated_vmec_mpi.py b/tests/mhd/test_integrated_vmec_mpi.py index 4e699ca38..3fc4b0b1e 100755 --- a/tests/mhd/test_integrated_vmec_mpi.py +++ b/tests/mhd/test_integrated_vmec_mpi.py @@ -11,11 +11,11 @@ except ImportError: vmec = None -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem if MPI is not None: from simsopt.mhd.vmec import Vmec - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve from simsopt.util.mpi import MpiPartition #logging.basicConfig(level=logging.DEBUG) diff --git a/tests/mhd/test_spec.py b/tests/mhd/test_spec.py index 7c2bc11ec..312a9c71e 100755 --- a/tests/mhd/test_spec.py +++ b/tests/mhd/test_spec.py @@ -26,9 +26,9 @@ if (MPI is not None) and spec_found: from simsopt.mhd.spec import Spec, Residue -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.geo.surfacegarabedian import SurfaceGarabedian -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.solve.serial import least_squares_serial_solve from . import TEST_DIR logger = logging.getLogger(__name__) diff --git a/tests/mhd/test_vmec.py b/tests/mhd/test_vmec.py index 53c1dbc64..547bd89ee 100755 --- a/tests/mhd/test_vmec.py +++ b/tests/mhd/test_vmec.py @@ -14,11 +14,11 @@ except ImportError: vmec_found = False -from simsopt._core.graph_optimizable import make_optimizable -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt._core.optimizable import make_optimizable +from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.mhd.profiles import ProfilePolynomial, ProfileSpline, ProfileScaled, ProfilePressure -from simsopt.solve.graph_serial import least_squares_serial_solve +from simsopt.solve.serial import least_squares_serial_solve from simsopt.util.constants import ELEMENTARY_CHARGE from simsopt.mhd.vmec import Vmec diff --git a/tests/mhd/test_vmec_diagnostics.py b/tests/mhd/test_vmec_diagnostics.py index 61884aa1d..a4c1462ef 100755 --- a/tests/mhd/test_vmec_diagnostics.py +++ b/tests/mhd/test_vmec_diagnostics.py @@ -6,7 +6,7 @@ from simsopt.mhd.vmec_diagnostics import QuasisymmetryRatioResidual, \ B_cartesian, IotaTargetMetric, IotaWeighted, WellWeighted, \ vmec_fieldlines -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem try: import vmec diff --git a/tests/objectives/test_graph_least_squares.py b/tests/objectives/test_least_squares.py similarity index 95% rename from tests/objectives/test_graph_least_squares.py rename to tests/objectives/test_least_squares.py index 62e46f77c..a67d7f535 100755 --- a/tests/objectives/test_graph_least_squares.py +++ b/tests/objectives/test_least_squares.py @@ -1,9 +1,9 @@ import unittest import logging import numpy as np -from simsopt.objectives.graph_functions import Identity, Rosenbrock +from simsopt.objectives.functions import Identity, Rosenbrock #from simsopt.core.optimizable import Target -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.objectives.least_squares import LeastSquaresProblem #logging.basicConfig(level=logging.DEBUG) diff --git a/tests/solve/test_graph_least_squares.py b/tests/solve/test_least_squares.py similarity index 89% rename from tests/solve/test_graph_least_squares.py rename to tests/solve/test_least_squares.py index b7ce355da..36cdba6f5 100755 --- a/tests/solve/test_graph_least_squares.py +++ b/tests/solve/test_least_squares.py @@ -6,12 +6,12 @@ except ImportError: MPI = None -from simsopt.objectives.graph_functions import Identity, Rosenbrock -from simsopt.objectives.graph_least_squares import LeastSquaresProblem -from simsopt.solve.graph_serial import least_squares_serial_solve, serial_solve +from simsopt.objectives.functions import Identity, Rosenbrock +from simsopt.objectives.least_squares import LeastSquaresProblem +from simsopt.solve.serial import least_squares_serial_solve, serial_solve if MPI is not None: from simsopt.util.mpi import MpiPartition - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve def mpi_solve_1group(prob, **kwargs): diff --git a/tests/solve/test_graph_mpi.py b/tests/solve/test_mpi.py similarity index 94% rename from tests/solve/test_graph_mpi.py rename to tests/solve/test_mpi.py index 1d74d0b7d..734d65ae3 100755 --- a/tests/solve/test_graph_mpi.py +++ b/tests/solve/test_mpi.py @@ -7,12 +7,12 @@ except: MPI = None -from simsopt._core.graph_optimizable import Optimizable -from simsopt.objectives.graph_functions import Beale -from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt._core.optimizable import Optimizable +from simsopt.objectives.functions import Beale +from simsopt.objectives.least_squares import LeastSquaresProblem if MPI is not None: from simsopt.util.mpi import MpiPartition - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve #logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) diff --git a/tests/util/test_dev.py b/tests/util/test_dev.py index f1f3db475..11303a1ff 100644 --- a/tests/util/test_dev.py +++ b/tests/util/test_dev.py @@ -6,7 +6,7 @@ np = None from simsopt.util.dev import SimsoptRequires, deprecated -from simsopt._core.graph_optimizable import Optimizable +from simsopt._core.optimizable import Optimizable @SimsoptRequires(np is not None, "numpy is not installed.") From 61f4894faf63ad858692780f4a3e5c2c2af5518d Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 28 Mar 2022 07:25:15 -0400 Subject: [PATCH 07/95] Fix the import after removing graph prefix --- src/simsopt/mhd/boozer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/mhd/boozer.py b/src/simsopt/mhd/boozer.py index 8d082060c..9afeee6ed 100644 --- a/src/simsopt/mhd/boozer.py +++ b/src/simsopt/mhd/boozer.py @@ -28,7 +28,7 @@ from .vmec import Vmec -from .._core.graph_optimizable import Optimizable +from .._core.optimizable import Optimizable class Boozer(Optimizable): From fa636c86346fab22bb23946c467877fadbee7933 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 28 Mar 2022 08:37:20 -0400 Subject: [PATCH 08/95] Update the example name in the script --- examples/run_serial_examples | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/run_serial_examples b/examples/run_serial_examples index 4812f275b..9dbb3447d 100755 --- a/examples/run_serial_examples +++ b/examples/run_serial_examples @@ -5,7 +5,7 @@ set -ex ./1_Simple/just_a_quadratic.py -./1_Simple/graph_surf_vol_area.py +./1_Simple/surf_vol_area.py ./1_Simple/minimize_curve_length.py ./1_Simple/tracing_fieldline.py ./1_Simple/tracing_particle.py From f23a9e9a4df7705a33065cb39943118959b7b7f4 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 16:55:50 -0400 Subject: [PATCH 09/95] Get rid of print --- src/simsopt/_core/optimizable.py | 95 +++++++++++++++++++++++++---- src/simsopt/objectives/functions.py | 42 ++++++++++++- tests/core/test_optimizable.py | 54 ++++++++++++++++ 3 files changed, 177 insertions(+), 14 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 0633fa560..db96b761b 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -19,8 +19,10 @@ MutableSequence as MutSeq, List from functools import lru_cache import logging +import json import numpy as np +from monty.json import MSONable, MontyEncoder, MontyDecoder from ..util.dev import SimsoptRequires from ..util.types import RealArray, StrArray, BoolArray, Key @@ -113,8 +115,8 @@ def __init__(self, == len(names)) self._x = x self._free = free - self._lb = lower_bounds - self._ub = upper_bounds + self._lower_bounds = lower_bounds + self._upper_bounds = upper_bounds self._names = list(names) def __len__(self): @@ -305,7 +307,11 @@ def lower_bounds(self) -> RealArray: Returns: Lower bounds of the DOFs """ - return self._lb[self._free] + return self._lower_bounds[self._free] + + @property + def full_lower_bounds(self) -> RealArray: + return self._lower_bounds @lower_bounds.setter def lower_bounds(self, lower_bounds: RealArray) -> None: @@ -318,7 +324,7 @@ def lower_bounds(self, lower_bounds: RealArray) -> None: # and to prevent broadcasting of a single DOF if self.reduced_len != len(lower_bounds): raise DofLengthMismatchError(len(lower_bounds), self.reduced_len) - self._lb[self._free] = np.asarray(lower_bounds, dtype=np.double) + self._lower_bounds[self._free] = np.asarray(lower_bounds, dtype=np.double) @property def upper_bounds(self) -> RealArray: @@ -327,7 +333,11 @@ def upper_bounds(self) -> RealArray: Returns: Upper bounds of the DOFs """ - return self._ub[self._free] + return self._upper_bounds[self._free] + + @property + def full_upper_bounds(self) -> RealArray: + return self._upper_bounds @upper_bounds.setter def upper_bounds(self, upper_bounds: RealArray) -> None: @@ -340,7 +350,7 @@ def upper_bounds(self, upper_bounds: RealArray) -> None: # and to prevent broadcasting of a single DOF if self.reduced_len != len(upper_bounds): raise DofLengthMismatchError(len(upper_bounds), self.reduced_len) - self._ub[self._free] = np.asarray(upper_bounds, dtype=np.double) + self._upper_bounds[self._free] = np.asarray(upper_bounds, dtype=np.double) @property def bounds(self) -> Tuple[RealArray, RealArray]: @@ -351,6 +361,10 @@ def bounds(self) -> Tuple[RealArray, RealArray]: """ return (self.lower_bounds, self.upper_bounds) + @property + def full_bounds(self) -> Tuple[RealArray, RealArray]: + return (self.full_lower_bounds, self.full_upper_bounds) + def update_lower_bound(self, key: Key, val: Real) -> None: """ Updates the lower bound of the specified DOF to the given value @@ -361,7 +375,7 @@ def update_lower_bound(self, key: Key, val: Real) -> None: """ if isinstance(key, str): key = self._names.index(key) - self._lb[key] = val + self._lower_bounds[key] = val def update_upper_bound(self, key: Key, val: Real) -> None: """ @@ -373,7 +387,7 @@ def update_upper_bound(self, key: Key, val: Real) -> None: """ if isinstance(key, str): key = self._names.index(key) - self._ub[key] = val + self._upper_bounds[key] = val def update_bounds(self, key: Key, val: Tuple[Real, Real]) -> None: """ @@ -385,8 +399,8 @@ def update_bounds(self, key: Key, val: Tuple[Real, Real]) -> None: """ if isinstance(key, str): key = self._names.index(key) - self._lb[key] = val[0] - self._ub[key] = val[1] + self._lower_bounds[key] = val[0] + self._upper_bounds[key] = val[1] @property def names(self): @@ -409,7 +423,7 @@ def full_names(self): return self._names -class Optimizable(ABC_Callable, Hashable, metaclass=OptimizableMeta): +class Optimizable(ABC_Callable, Hashable, MSONable, metaclass=OptimizableMeta): """ Experimental callable ABC that provides lego-like optimizable objects that can be used to partition the optimization problem into a graph. @@ -1051,6 +1065,10 @@ def local_lower_bounds(self) -> RealArray: """ return self._dofs.lower_bounds + @property + def local_full_lower_bounds(self) -> RealArray: + return self._dofs.full_lower_bounds + @property def upper_bounds(self) -> RealArray: """ @@ -1068,6 +1086,10 @@ def local_upper_bounds(self) -> RealArray: """ return self._dofs.upper_bounds + @property + def local_full_upper_bounds(self) -> RealArray: + return self._dofs.full_upper_bounds + @local_upper_bounds.setter def local_upper_bounds(self, lub: RealArray) -> None: self._dofs.upper_bounds = lub @@ -1253,6 +1275,33 @@ def traversal(root): return G, pos + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + if len(self.local_full_x): + d["x0"] = list(self.local_full_x) + d["names"] = self.local_full_dof_names + d["fixed"] = list(np.logical_not(self.local_dofs_free_status)) + d["lower_bounds"] = list(self.local_full_lower_bounds) + d["upper_bounds"] = list(self.local_full_upper_bounds) + d["external_dof_setter"] = self.local_dof_setter + if self.parents: + d["depends_on"] = [] + for parent in self.parents: + d["depends_on"].append(parent.as_dict()) + + return d + + @classmethod + def from_dict(cls, d): + parents_dict = d.pop("depends_on") if "depends_on" in d else None + if parents_dict: + parents = [] + for pdict in parents_dict: + parents.append(json.load(pdict, cls=MontyDecoder)) + return cls(depends_on=parents, **d) + def make_optimizable(func, *args, dof_indicators=None, **kwargs): """ @@ -1409,6 +1458,18 @@ def dJ(self): # Next line uses __rmul__ function for the Derivative class return self.factor * self.opt.dJ(partials=True) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["factor"] = self.factor + d["opt"] = self.opt + return d + + @classmethod + def from_dict(cls, d): + return cls(d["factor"], d["opt"]) + class OptimizableSum(Optimizable): """ @@ -1434,3 +1495,15 @@ def J(self): def dJ(self): # Next line uses __add__ function for the Derivative class return sum(opt.dJ(partials=True) for opt in self.opts) + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["opts"] = self.opts + return d + + @classmethod + def from_dict(cls, d): + return cls(d["opts"]) + diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index 6ffcaf516..2c54fdb62 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -57,6 +57,20 @@ def dJ(self, x: RealArray = None): return_fn_map = {'f': f} + def as_dict(self) -> dict: + d = super().as_dict() + del d["x0"] + del d["names"] + del d["fixed"] + d["x"] = self.local_full_x[0] + d["dof_name"] = self.local_full_dof_names[0] + d["dof_fixed"] = np.logical_not(self.local_dofs_free_status)[0] + return d + + @classmethod + def from_dict(cls, d): + return cls(d["x"], d["dof_name"], d["dof_fixed"]) + class Adder(Optimizable): """ @@ -72,10 +86,9 @@ class Adder(Optimizable): dof_names: Identifiers for the DOFs """ - def __init__(self, n=3, x0=None, dof_names=None): + def __init__(self, n=3, **kwargs): self.n = n - x = x0 if x0 is not None else np.zeros(n) - super().__init__(x, names=dof_names) + super().__init__(**kwargs) def sum(self): """ @@ -93,6 +106,17 @@ def df(self): """ return self.dJ() + def as_dict(self) -> dict: + d = super().as_dict() + print('dict is ', d) + d["n"] = self.n + return d + + @classmethod + def from_dict(cls, d): + n = d.pop("n") + return cls(n=n, **d) + return_fn_map = {'sum': sum} @@ -173,6 +197,18 @@ def dterms(self): """ return np.array([[1.0, 0.0], [2 * self.local_full_x['x'] / self._sqrtb, -1.0 / self._sqrtb]]) + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["b"] = self._sqrtb * self._sqrtb + d["x"] = self.get("x") + d["y"] = self.get("y") + + @classmethod + def from_dict(cls, d): + return cls(d["b"], d["x"], d["y"]) class TestObject1(Optimizable): diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 5976a14f1..dd15bc4cc 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -1,10 +1,14 @@ import unittest import re +import json import numpy as np +from monty.json import MontyDecoder, MontyEncoder +from monty.serialization import loadfn, dumpfn from simsopt._core.optimizable import Optimizable, make_optimizable from simsopt.objectives.functions import Identity, Rosenbrock, TestObject2 +from simsopt.objectives.functions import Adder as FAdder class Adder(Optimizable): @@ -23,6 +27,21 @@ def sum(self): return_fn_map = {'sum': sum} + def as_dict(self) -> dict: + d = super().as_dict() + d["dof_names"] = d["names"] + d["dof_fixed"] = d["fixed"] + del d["names"] + del d["fixed"] + d["n"] = self.n + return d + + @classmethod + def from_dict(cls, d): + return cls(d["n"], + d.get("x0", None), + d.get("dof_names", None), + d.get("dof_fixed", None)) class OptClassWithParents(Optimizable): def __init__(self, val, depends_on=None): @@ -36,6 +55,17 @@ def f(self): return_fn_map = {'f': f} + def as_dict(self) -> dict: + d = super().as_dict() + del d["x0"] + del d["names"] + d['val'] = self.local_full_x[0] + return d + + @classmethod + def from_dict(cls, d: dict): + return cls(d["val"], d["depends_on"]) + class N_No(Optimizable): """This class defines a minimal object that can be optimized. It has @@ -1070,5 +1100,29 @@ def test_arb_func_dofs_opts(self): self.assertAlmostEqual(opt.J(), 9.0) +class TestOptimizableSerialize(unittest.TestCase): + """ + Test the serialization of the Optimizable class based on as_dict and + from_dict methods using various sub-classes + """ + def test_serialize(self): + adder = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder, cls=MontyEncoder) + print(s) + + def test_deserialize(self): + adder_orig = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder_orig, cls=MontyEncoder) + adder = json.loads(s, cls=MontyDecoder) + print(adder.name) + print(adder.n) + print(adder.full_x) + print(adder.dofs_free_status) + print(adder.local_full_dof_names) + + + if __name__ == "__main__": unittest.main() From 9fbd2f70210512123013f62ae35b949f4d120c74 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 16:58:36 -0400 Subject: [PATCH 10/95] Serialization working for optimizable objects without parents --- tests/core/test_dofs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_dofs.py b/tests/core/test_dofs.py index 9b6966eb4..4ee8995cb 100755 --- a/tests/core/test_dofs.py +++ b/tests/core/test_dofs.py @@ -10,7 +10,7 @@ class DOFsTests(unittest.TestCase): def setUp(self): self.identity_dofs = Identity(x=1, dof_name='x')._dofs - self.adder_dofs = Adder(3, x0=[2, 3, 4], dof_names=["x", "y", "z"])._dofs + self.adder_dofs = Adder(3, x0=[2, 3, 4], names=["x", "y", "z"])._dofs self.rosenbrock_dofs = Rosenbrock()._dofs def tearDown(self) -> None: From 86de19abd7bd06aa2edffaf00828d2285ac5d604 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 18:54:56 -0400 Subject: [PATCH 11/95] Add fix_local and fix_full methods --- src/simsopt/_core/optimizable.py | 53 +++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index db96b761b..664407038 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -20,6 +20,7 @@ from functools import lru_cache import logging import json +from warnings import warn import numpy as np from monty.json import MSONable, MontyEncoder, MontyDecoder @@ -1196,17 +1197,55 @@ def fix_all(self) -> None: Set the 'fixed' attribute for all degrees of freedom associated with the current Optimizable object. """ + warn("fix_all method is deprecated in favor of fix_local", + DeprecationWarning, stacklevel=2) self._dofs.fix_all() self._update_free_dof_size_indices() + def fix_local(self) -> None: + """ + Set the 'fixed' attribute for all local degrees of freedom associated + with the current Optimizable object. + """ + self._dofs.fix_all() + self._update_free_dof_size_indices() + + def fix_full(self) -> None: + """ + Set the 'fixed' attribute for all degrees of freedom associated with + the current Optimizable object including those of the ancestors. + """ + opts = self.ancestors + [self] + for opt in opts: + opts.fix_local() + def unfix_all(self) -> None: """ Unset the 'fixed' attribute for all degrees of freedom associated with the current Optimizable object. """ + warn("unfix_all method is deprecated in favor of unfix_local", + DeprecationWarning, stacklevel=2) self._dofs.unfix_all() self._update_free_dof_size_indices() + def unfix_local(self) -> None: + """ + Unset the 'fixed' attribute for all degrees of freedom associated + with the current Optimizable object. + """ + self._dofs.unfix_all() + self._update_free_dof_size_indices() + + def unfix_full(self): + """ + Unset the 'fixed' attribute for all degrees of freedom associated + with the current Optimizable object including those of ancestors. + """ + opts = self.ancestors + [self] + for opt in opts: + opts.unfix_local() + def __add__(self, other): """ Add two Optimizable objects """ return OptimizableSum([self, other]) @@ -1463,12 +1502,13 @@ def as_dict(self) -> dict: d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ d["factor"] = self.factor - d["opt"] = self.opt + d["opt"] = self.opt.as_dict() return d @classmethod def from_dict(cls, d): - return cls(d["factor"], d["opt"]) + opt = json.load(d["opt"], cls=MontyDecoder) + return cls(d["factor"], opt) class OptimizableSum(Optimizable): @@ -1500,10 +1540,15 @@ def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - d["opts"] = self.opts + d["opts"] = [] + for opt in self.opts: + d["opts"].append(opt.as_dict()) return d @classmethod def from_dict(cls, d): - return cls(d["opts"]) + opts = [] + for odict in d["opts"]: + opts.append(json.load(odict, cls=MontyDecoder)) + return cls(opts) From abe9bbaafdfe4281b199e4594fed27cf6a3c05e4 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 19:07:03 -0400 Subject: [PATCH 12/95] Modules with graph framework are deprecated --- src/simsopt/_core/graph_optimizable.py | 6 ++++++ src/simsopt/objectives/graph_functions.py | 7 +++++++ src/simsopt/objectives/graph_least_squares.py | 7 +++++++ src/simsopt/solve/graph_mpi.py | 7 +++++++ src/simsopt/solve/graph_serial.py | 7 +++++++ 5 files changed, 34 insertions(+) create mode 100644 src/simsopt/_core/graph_optimizable.py create mode 100644 src/simsopt/objectives/graph_functions.py create mode 100644 src/simsopt/objectives/graph_least_squares.py create mode 100644 src/simsopt/solve/graph_mpi.py create mode 100644 src/simsopt/solve/graph_serial.py diff --git a/src/simsopt/_core/graph_optimizable.py b/src/simsopt/_core/graph_optimizable.py new file mode 100644 index 000000000..40e03f3f2 --- /dev/null +++ b/src/simsopt/_core/graph_optimizable.py @@ -0,0 +1,6 @@ +from .optimizable import * + +import warnings + +warnings.warn("Importing of graph_optimizable module is deprecated. Import optimizable module instead", + DeprecationWarning, stacklevel=2) diff --git a/src/simsopt/objectives/graph_functions.py b/src/simsopt/objectives/graph_functions.py new file mode 100644 index 000000000..b018cb950 --- /dev/null +++ b/src/simsopt/objectives/graph_functions.py @@ -0,0 +1,7 @@ +from .functions import * + +import warnings + +warnings.warn("Importing of simsopt.objectives.functions module is deprecated. " + "Instead import simsopt.objectives.functions module", + DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/src/simsopt/objectives/graph_least_squares.py b/src/simsopt/objectives/graph_least_squares.py new file mode 100644 index 000000000..dd52751c7 --- /dev/null +++ b/src/simsopt/objectives/graph_least_squares.py @@ -0,0 +1,7 @@ +from .least_squares import * + +import warnings + +warnings.warn("Importing of simsopt.objectives.graph_least_squares module is deprecated. " + "Instead import simsopt.objectives.least_squares module", + DeprecationWarning, stacklevel=2) diff --git a/src/simsopt/solve/graph_mpi.py b/src/simsopt/solve/graph_mpi.py new file mode 100644 index 000000000..f8412bd65 --- /dev/null +++ b/src/simsopt/solve/graph_mpi.py @@ -0,0 +1,7 @@ +from .mpi import * + +import warnings + +warnings.warn("Importing of simsopt.solve.graph_mpi module is deprecated. " + "Instead import simsopt.solve.mpi module", + DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/src/simsopt/solve/graph_serial.py b/src/simsopt/solve/graph_serial.py new file mode 100644 index 000000000..56ce4241a --- /dev/null +++ b/src/simsopt/solve/graph_serial.py @@ -0,0 +1,7 @@ +from .serial import * + +import warnings + +warnings.warn("Importing of simsopt.solve.graph_serial module is deprecated. " + "Instead import simsopt.solve.serial module", + DeprecationWarning, stacklevel=2) \ No newline at end of file From d94bab8b58d31a1b54f13dcc68e94ecd75db38e3 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 20:18:42 -0400 Subject: [PATCH 13/95] Unit tests for unfix_local and unfix_full --- src/simsopt/_core/optimizable.py | 4 +- src/simsopt/objectives/graph_functions.py | 2 +- tests/core/test_graph_optimizable.py | 1128 ++++++++++++++++++ tests/core/test_optimizable.py | 141 +++ tests/objectives/test_graph_least_squares.py | 82 ++ tests/solve/test_graph_least_squares.py | 79 ++ tests/solve/test_graph_mpi.py | 137 +++ 7 files changed, 1570 insertions(+), 3 deletions(-) create mode 100755 tests/core/test_graph_optimizable.py create mode 100755 tests/objectives/test_graph_least_squares.py create mode 100755 tests/solve/test_graph_least_squares.py create mode 100755 tests/solve/test_graph_mpi.py diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 664407038..308ba2a3b 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1217,7 +1217,7 @@ def fix_full(self) -> None: """ opts = self.ancestors + [self] for opt in opts: - opts.fix_local() + opt.fix_local() def unfix_all(self) -> None: """ @@ -1244,7 +1244,7 @@ def unfix_full(self): """ opts = self.ancestors + [self] for opt in opts: - opts.unfix_local() + opt.unfix_local() def __add__(self, other): """ Add two Optimizable objects """ diff --git a/src/simsopt/objectives/graph_functions.py b/src/simsopt/objectives/graph_functions.py index b018cb950..0a4bbba50 100644 --- a/src/simsopt/objectives/graph_functions.py +++ b/src/simsopt/objectives/graph_functions.py @@ -2,6 +2,6 @@ import warnings -warnings.warn("Importing of simsopt.objectives.functions module is deprecated. " +warnings.warn("Importing of simsopt.objectives.graph_functions module is deprecated. " "Instead import simsopt.objectives.functions module", DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_graph_optimizable.py new file mode 100755 index 000000000..60f06c042 --- /dev/null +++ b/tests/core/test_graph_optimizable.py @@ -0,0 +1,1128 @@ +import unittest +import re +import json + +import numpy as np +from monty.json import MontyDecoder, MontyEncoder +from monty.serialization import loadfn, dumpfn + +from simsopt._core.graph_optimizable import Optimizable, make_optimizable +from simsopt.objectives.graph_functions import Identity, Rosenbrock, TestObject2 +from simsopt.objectives.graph_functions import Adder as FAdder + + +class Adder(Optimizable): + """This class defines a minimal object that can be optimized. It has + n degrees of freedom, and has a function that just returns the sum + of these dofs. This class is used for testing. + """ + + def __init__(self, n=3, x0=None, dof_names=None, dof_fixed=None): + self.n = n + x = x0 if x0 is not None else np.zeros(n) + super().__init__(x, names=dof_names, fixed=dof_fixed) + + def sum(self): + return np.sum(self.local_full_x) + + return_fn_map = {'sum': sum} + + def as_dict(self) -> dict: + d = super().as_dict() + d["dof_names"] = d["names"] + d["dof_fixed"] = d["fixed"] + del d["names"] + del d["fixed"] + d["n"] = self.n + return d + + @classmethod + def from_dict(cls, d): + return cls(d["n"], + d.get("x0", None), + d.get("dof_names", None), + d.get("dof_fixed", None)) + +class OptClassWithParents(Optimizable): + def __init__(self, val, depends_on=None): + if depends_on is None: + depends_on = [Adder(3), Adder(2)] + super().__init__(x0=[val], names=['val'], depends_on=depends_on) + + def f(self): + return (self.local_full_x[0] + 2 * self.parents[0](child=self)) \ + / (10.0 + self.parents[1](child=self)) + + return_fn_map = {'f': f} + + def as_dict(self) -> dict: + d = super().as_dict() + del d["x0"] + del d["names"] + d['val'] = self.local_full_x[0] + return d + + @classmethod + def from_dict(cls, d: dict): + return cls(d["val"], d["depends_on"]) + + +class N_No(Optimizable): + """This class defines a minimal object that can be optimized. It has + n degrees of freedom, and has couple of functions that return the sum + and product of these dofs. This class is used for testing. + """ + + def __init__(self, n=3, x0=None, dof_names=None, dof_fixed=None): + self.n = n + x = x0 if x0 is not None else np.zeros(n) + super().__init__(x, names=dof_names, fixed=dof_fixed) + + def sum(self): + return np.sum(self.local_full_x) + + def product(self): + return np.product(self.local_full_x) + + return_fn_map = {'sum': sum, 'prod': product} + + +class OptClassWithParentsReturnFns(Optimizable): + def __init__(self, val): + self.opt1 = N_No(3, x0=[2, 3, 4]) # Computes to [9, 24] + self.opt2 = N_No(2, x0=[1, 2]) # Computes to [3, 2] + super().__init__(x0=[val], names=['val'], + depends_on=[self.opt1, self.opt2], + opt_return_fns=[['sum'], ['sum', 'prod']]) + + # Pay attention to the arguments passed in f1 and f2 + def f1(self): + return (self.local_full_x[0] + 2 * self.opt1(child=self)) \ + / (10.0 + np.sum(self.opt2(child=self))) + + # If child=self is not passed, full return array is returned, because + # parent class is not aware of the calling child class + def f2(self): + return (self.local_full_x[0] + 2 * self.opt1()[0]) \ + / (10.0 + np.sum(self.opt2())) + + return_fn_map = {'f1': f1, 'f2': f2} + + +class OptimizableTestsWithParentsReturnFns(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassWithParentsReturnFns(10) + + def tearDown(self) -> None: + self.opt = None + + def test_name(self): + self.assertTrue('OptClassWithParentsReturnFns' in self.opt.name) + self.assertNotEqual(self.opt.name, + OptClassWithParentsReturnFns(10).name) + + def test_hash(self): + hash1 = hash(self.opt) + hash2 = hash(OptClassWithParentsReturnFns(10)) + self.assertNotEqual(hash1, hash2) + + def test_eq(self): + self.assertNotEqual(self.opt, OptClassWithParentsReturnFns(10)) + + def test_get_return_fn_names(self): + ret_fn_names = self.opt.get_return_fn_names() + self.assertEqual(ret_fn_names[0], 'f1') + self.assertEqual(ret_fn_names[1], 'f2') + + def test_add_return_fn_by_name(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, 'prod') + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_add_return_fn_by_reference(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, opt1.product) + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_call(self): + # Test for leaf nodes + self.assertAlmostEqual(self.opt.f1(), 28.0/15) + self.assertAlmostEqual(self.opt.f2(), 28.0/15) + np.allclose(self.opt(), [28.0/15, 28.0/15]) + + # Change parent objects and see if the DOFs are propagated + opt1 = self.opt.opt1 + opt1.set('x1', 5) + opt2 = self.opt.opt2 + opt2.set('x1', 4) + np.allclose(self.opt(), 34.0/24) + + +class OptClassWithDirectParentFnCalls(Optimizable): + def __init__(self, val): + self.opt1 = N_No(3, x0=[2, 3, 4]) + self.opt2 = N_No(2, x0=[1, 2]) + super().__init__(x0=[val], names=['val'], + depends_on=[self.opt1, self.opt2]) + + # The value returned by f3 should be identical to f1 + def f(self): + return (self.local_full_x[0] + 2 * self.opt1.sum()) \ + / (10.0 + self.opt2.sum() + self.opt2.product()) + + return_fn_map = {'f': f} + + +class OptimizableTestsWithDirectParentFnCalls(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassWithDirectParentFnCalls(10) + + def tearDown(self) -> None: + self.opt = None + + def test_name(self): + self.assertTrue('OptClassWithDirectParentFnCalls' in self.opt.name) + + def test_equal(self): + self.assertNotEqual(self.opt.name, + OptClassWithDirectParentFnCalls(10).name) + + def test_hash(self): + hash1 = hash(self.opt) + hash2 = hash(OptClassWithDirectParentFnCalls(10)) + self.assertNotEqual(hash1, hash2) + + def test_eq(self): + self.assertNotEqual(self.opt, OptClassWithDirectParentFnCalls(10)) + + def test_get_return_fn_names(self): + ret_fn_names = self.opt.get_return_fn_names() + self.assertEqual(ret_fn_names[0], 'f') + + @unittest.skip + def test_add_return_fn_by_name(self): + """ + This test is not needed + """ + pass + + @unittest.skip + def test_add_return_fn_by_reference(self): + """ + This test is not need for the used function + """ + pass + + def test_call(self): + # Test for leaf nodes + self.assertAlmostEqual(self.opt.f(), 28.0/15) + np.allclose(self.opt(), 28.0/15) + + # Change parent objects and see if the DOFs are propagated + opt1 = self.opt.opt1 + opt1.set('x1', 5) + opt2 = self.opt.opt2 + opt2.set('x1', 4) + np.allclose(self.opt(), 34.0/24) + + +class OptClassWithDirectRegisterParentFn(Optimizable): + def __init__(self, val): + self.opt1 = N_No(3, x0=[2, 3, 4]) + self.opt2 = N_No(2, x0=[1, 2]) + super().__init__(x0=[val], names=['val'], + funcs_in=[self.opt1.sum, self.opt2.sum, + self.opt2.product]) + + # Pay attention to the arguments passed in f1 and f2 + def f1(self): + return (self.local_full_x[0] + 2 * self.opt1(child=self)) \ + / (10.0 + np.sum(self.opt2(child=self))) + + # If child=self is not passed, full return array is returned, because + # parent class is not aware of the calling child class + def f2(self): + return (self.local_full_x[0] + 2 * self.opt1()[0]) \ + / (10.0 + np.sum(self.opt2())) + + return_fn_map = {'f1': f1, 'f2': f2} + + +class OptimizableTestsWithDirectRegisterParentFns(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassWithDirectRegisterParentFn(10) + + def tearDown(self) -> None: + self.opt = None + + def test_name(self): + self.assertTrue('OptClassWithDirectRegisterParentFn' in self.opt.name) + self.assertNotEqual(self.opt.name, + OptClassWithDirectRegisterParentFn(10).name) + + def test_hash(self): + hash1 = hash(self.opt) + hash2 = hash(OptClassWithDirectRegisterParentFn(10)) + self.assertNotEqual(hash1, hash2) + + def test_eq(self): + self.assertNotEqual(self.opt, OptClassWithDirectRegisterParentFn(10)) + + def test_get_return_fn_names(self): + ret_fn_names = self.opt.get_return_fn_names() + self.assertEqual(ret_fn_names[0], 'f1') + self.assertEqual(ret_fn_names[1], 'f2') + + def test_add_return_fn_by_name(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, 'prod') + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_add_return_fn_by_reference(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, opt1.product) + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_call(self): + # Test for leaf nodes + self.assertAlmostEqual(self.opt.f1(), 28.0/15) + self.assertAlmostEqual(self.opt.f2(), 28.0/15) + np.allclose(self.opt(), [28.0/15, 28.0/15]) + + # Change parent objects and see if the DOFs are propagated + opt1 = self.opt.opt1 + opt1.set('x1', 5) + opt2 = self.opt.opt2 + opt2.set('x1', 4) + np.allclose(self.opt(), 34.0/24) + + +class OptClassWith2LevelParents(Optimizable): + def __init__(self, val1, val2): + x = [val1, val2] + names = ['v1', 'v2'] + opts = [OptClassWithParents(0.0), Adder(2)] + super().__init__(x0=x, names=names, depends_on=opts) + + def f(self): + x = self.local_full_x + v1 = x[0] + v2 = x[1] + t = self.parents[0](self) + a = self.parents[1](self) + return v1 + a * np.cos(v2 + t) + + return_fn_map = {'f': f} + + +class OptWithInputParent(Optimizable): + def __init__(self, obj): + super().__init__(depends_on=[obj]) + + +class ThreeDofOpt(Optimizable): + def __init__(self): + super().__init__(x0=[1, 2, 3]) + + +class TwoDofOpt(Optimizable): + def __init__(self): + super().__init__(x0=[10, 20]) + + +class OptimizableTests(unittest.TestCase): + def setUp(self) -> None: + self.iden = Identity(x=10) + self.adder = Adder(n=3, dof_names=['x', 'y', 'z']) + self.rosen = Rosenbrock() + + def tearDown(self) -> None: + self.iden = None + self.adder = None + self.rosen = None + + def test_name(self): + self.assertTrue('Identity' in self.iden.name) + self.assertTrue('Adder' in self.adder.name) + self.assertTrue('Rosenbrock' in self.rosen.name) + self.assertNotEqual(self.iden.name, Identity().name) + self.assertNotEqual(self.adder.name, Adder().name) + self.assertNotEqual(self.rosen.name, Rosenbrock().name) + + def test_hash(self): + hash1 = hash(self.adder) + hash2 = hash(Adder()) + self.assertNotEqual(hash1, hash2) + + def test_add_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1]) + + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + opt_with_parents.add_parent(1, opt2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + + def test_append_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1]) + + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + opt_with_parents.append_parent(opt2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + + def test_append_parent_dof_sizes(self): + # vmec is the parent, prob is the child + three_dof_opt = ThreeDofOpt() + opt_with_inp_parent = OptWithInputParent(three_dof_opt) + + # Test dof sizes before adding grandparent + self.assertEqual(three_dof_opt.dof_size, 3) + self.assertEqual(opt_with_inp_parent.dof_size, 3) + + two_dof_opt = TwoDofOpt() + three_dof_opt.append_parent(two_dof_opt) + # Now two_dof_opt is the grandparent + + # Test dof sizes after adding grandparent + self.assertEqual(three_dof_opt.dof_size, 5) + self.assertEqual(three_dof_opt.dof_size, 5) + + def test_pop_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1, opt2]) + + self.assertEqual(len(opt_with_parents.parents), 2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + opt_with_parents.pop_parent() + self.assertEqual(len(opt_with_parents.parents), 1) + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + def test_remove_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1, opt2]) + + self.assertEqual(len(opt_with_parents.parents), 2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + opt_with_parents.remove_parent(opt1) + self.assertEqual(len(opt_with_parents.parents), 1) + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + def test_dof_size(self): + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + return_fn_map = {'f': f} + + opt = EmptyOptimizable() + self.assertEqual(opt.dof_size, 0) + + self.assertEqual(self.iden.dof_size, 1) + self.assertEqual(self.adder.dof_size, 3) + self.assertEqual(self.rosen.dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.dof_size, 0) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.dof_size, 6) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(n=3, x0=[1, 2, 3])]) + self.assertEqual(test_obj1.dof_size, 4) + + def test_full_dof_size(self): + + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + + opt = EmptyOptimizable() + self.assertEqual(opt.full_dof_size, 0) + + self.assertEqual(self.iden.full_dof_size, 1) + self.assertEqual(self.adder.full_dof_size, 3) + self.assertEqual(self.rosen.full_dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.full_dof_size, 1) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.full_dof_size, 6) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(3)]) + self.assertEqual(test_obj1.full_dof_size, 5) + + def test_local_dof_size(self): + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + + opt = EmptyOptimizable() + self.assertEqual(opt.local_dof_size, 0) + + self.assertEqual(self.iden.local_dof_size, 1) + self.assertEqual(self.adder.local_dof_size, 3) + self.assertEqual(self.rosen.local_dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.local_dof_size, 0) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.local_dof_size, 1) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(3)]) + self.assertEqual(test_obj1.local_dof_size, 1) + + def test_local_full_dof_size(self): + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + + opt = EmptyOptimizable() + self.assertEqual(opt.local_full_dof_size, 0) + + self.assertEqual(self.iden.local_full_dof_size, 1) + self.assertEqual(self.adder.local_full_dof_size, 3) + self.assertEqual(self.rosen.local_full_dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.local_full_dof_size, 1) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.local_full_dof_size, 1) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(3)]) + self.assertEqual(test_obj1.local_full_dof_size, 1) + + def test_x(self): + # Check with leaf type Optimizable objects + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_dofs = adder.x + iden_dofs = iden.x + self.assertAlmostEqual(adder_dofs[0], 1) + self.assertAlmostEqual(adder_dofs[1], 2) + self.assertAlmostEqual(adder_dofs[2], 3) + self.assertEqual(len(iden_dofs), 0) + + adder.x = [4, 5, 6] + self.assertAlmostEqual(adder.local_x[0], 4) + self.assertAlmostEqual(adder.local_x[1], 5) + self.assertAlmostEqual(adder.local_x[2], 6) + with self.assertRaises(ValueError): + iden.x = np.array([11, ], dtype=float) + self.assertAlmostEqual(iden.full_x[0], 10) + + # Check with Optimizable objects containing parents + adder2 = Adder(3) + iden2 = Identity(x=10) + test_obj1 = OptClassWithParents(10, depends_on=[iden2, adder2]) + with self.assertRaises(ValueError): + test_obj1.x = np.array([20]) + + test_obj1.x = np.array([4, 5, 6, 20, 25]) + self.assertAlmostEqual(iden2.local_x[0], 20) + self.assertAlmostEqual(adder2.local_x[0], 4) + self.assertAlmostEqual(adder2.local_x[1], 5) + self.assertAlmostEqual(adder2.local_x[2], 6) + self.assertAlmostEqual(test_obj1.local_x[0], 25) + + adder3 = Adder(3) + test_obj2 = OptClassWithParents(10, depends_on=[iden, adder3]) + with self.assertRaises(ValueError): + test_obj2.x = np.array([20, 4, 5, 6, 25]) + + test_obj2.x = np.array([4, 5, 6, 25]) + self.assertAlmostEqual(iden.local_full_x[0], 10) + self.assertAlmostEqual(adder3.local_x[0], 4) + self.assertAlmostEqual(adder3.local_x[1], 5) + self.assertAlmostEqual(adder3.local_x[2], 6) + self.assertAlmostEqual(test_obj2.local_x[0], 25) + + def test_local_x(self): + # Check with leaf type Optimizable objects + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.local_x + iden_x = iden.local_x + self.assertAlmostEqual(adder_x[0], 1) + self.assertAlmostEqual(adder_x[1], 2) + self.assertAlmostEqual(adder_x[2], 3) + self.assertTrue(len(iden_x) == 0) + + adder.local_x = [4, 5, 6] + with self.assertRaises(ValueError): + iden.local_x = np.array([11, ], dtype=float) + self.assertAlmostEqual(iden.full_x[0], 10) + + # Check with Optimizable objects containing parents + adder2 = Adder(3) + iden2 = Identity(x=10) + test_obj1 = OptClassWithParents(10, depends_on=[iden2, adder2]) + test_obj1.local_x = np.array([25]) + + adder3 = Adder(3) + test_obj2 = OptClassWithParents(10, depends_on=[iden, adder3]) + + test_obj2.local_x = np.array([25]) + + def test_full_x(self): + # Check with leaf type Optimizable objects + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_full_x = adder.full_x + self.assertAlmostEqual(adder_full_x[0], 1) + self.assertAlmostEqual(adder_full_x[1], 2) + self.assertAlmostEqual(adder_full_x[2], 3) + self.assertEqual(len(iden.full_x), 1) + self.assertAlmostEqual(iden.full_x[0], 10) + + # Check with Optimizable objects containing parents + test_obj1 = OptClassWithParents(20, depends_on=[iden, adder]) + full_x = test_obj1.full_x + self.assertTrue(np.allclose(full_x, np.array([1, 2, 3, 10, 20]))) + + test_obj1.x = np.array([4, 5, 6, 25]) + full_x = test_obj1.full_x + self.assertTrue(np.allclose(full_x, np.array([4, 5, 6, 10, 25]))) + + def test_local_full_x(self): + # Check with leaf type Optimizable objects + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_local_full_x = adder.local_full_x + self.assertAlmostEqual(adder_local_full_x[0], 1) + self.assertAlmostEqual(adder_local_full_x[1], 2) + self.assertAlmostEqual(adder_local_full_x[2], 3) + self.assertEqual(len(iden.local_full_x), 1) + self.assertAlmostEqual(iden.local_full_x[0], 10) + + # Check with Optimizable objects containing parents + test_obj1 = OptClassWithParents(20, depends_on=[iden, adder]) + local_full_x = test_obj1.local_full_x + self.assertTrue(np.allclose(local_full_x, np.array([20]))) + + test_obj1.x = np.array([4, 5, 6, 25]) + local_full_x = test_obj1.local_full_x + self.assertTrue(np.allclose(local_full_x, np.array([25]))) + + def test_get(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + + self.assertAlmostEqual(adder.get(0), 1.) + self.assertAlmostEqual(adder.get('y'), 2.) + self.assertAlmostEqual(iden.get('x0'), 10.) + + def test_set(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + + adder.set(0, 2) + adder.set('y', 20) + iden.set('x0', 20) + self.assertAlmostEqual(adder.full_x[0], 2) + self.assertAlmostEqual(adder.full_x[1], 20) + self.assertAlmostEqual(iden.full_x[0], 20) + + def test_dofs_free_status(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(20, depends_on=[iden, adder]) + + adder_status = [False, True, True] + self.assertTrue(np.equal(adder.dofs_free_status, adder_status).all()) + self.assertTrue(np.equal(iden.dofs_free_status, [False]).all()) + obj_status = [False, True, True, False, True] + self.assertTrue(np.equal(test_obj.dofs_free_status, obj_status).all()) + + def test_local_dofs_free_status(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + + self.assertTrue( + np.equal(adder.local_dofs_free_status, [False, True, True]).all()) + self.assertTrue(np.equal(iden.local_dofs_free_status, [False]).all()) + + def test_call(self): + # Test for leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + self.assertAlmostEqual(adder(), 6.0) + adder.fix('y') + self.assertAlmostEqual(adder(), 6.0) + + iden = Identity(x=10, dof_fixed=True) + self.assertAlmostEqual(iden(), 10.0) + + # Set dofs and call + adder.x = [6] + self.assertAlmostEqual(adder(), 9.0) + adder.unfix_all() + adder.x = [4, 5, 6] + self.assertAlmostEqual(adder(), 15.0) + iden.unfix_all() + iden.x = [20] + self.assertAlmostEqual(iden(), 20.0) + + # Call with arguments + self.assertAlmostEqual(adder(x=[10, 11, 12]), 33) + self.assertAlmostEqual(iden(x=[20]), 20) + + # Now call without arguments to make sure the previous value is returned + self.assertAlmostEqual(adder(), 33) + self.assertAlmostEqual(iden(), 20) + + # Fix dofs and now call + adder.fix('x') + self.assertAlmostEqual(adder([1, 2]), 13) + adder.fix_all() + self.assertAlmostEqual(adder(), 13) + iden.fix_all() + self.assertAlmostEqual(iden(), 20) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj1 = OptClassWithParents(20, depends_on=[iden, adder]) + # Value returned by test_obj1 is (val + 2*iden())/(10.0 + adder()) + self.assertAlmostEqual(test_obj1(), 2.5) + + # Set the parents nodes' x and call + adder.x = [4, 5] + self.assertAlmostEqual(test_obj1(), 2.0) + + # Set the dofs and call + test_obj1.x = np.array([14, 15, 30]) + self.assertAlmostEqual(test_obj1(), 1.25) + + # Set only the node local dofs and call + test_obj1.local_x = [20] + self.assertAlmostEqual(test_obj1(), 1.0) + + # Call with arguments + self.assertAlmostEqual(test_obj1([2, 3, 20]), 2.5) + # Followed by Call with no arguments + self.assertAlmostEqual(test_obj1(), 2.5) + + def test_bounds(self): + pass + + def test_local_bounds(self): + pass + + def test_lower_bounds(self): + pass + + def test_local_lower_bounds(self): + pass + + def test_upper_bounds(self): + pass + + def test_local_upper_bounds(self): + pass + + def test_dof_names(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3]) + self.assertEqual(len(iden.dof_names), 0) + self.assertEqual(len(adder.dof_names), 3) + patt = re.compile("Adder\d+:x\d+") + for name in adder.dof_names: + self.assertTrue(patt.match(name)) + + patt1 = "Adder\d+:x\d+" + patt2 = "OptClassWithParents\d+:val" + comb_patt = re.compile("|".join([patt1, patt2])) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + self.assertEqual(len(test_obj.dof_names), 4) + for name in test_obj.dof_names: + self.assertTrue(comb_patt.match(name)) + + test_obj.fix('val') + self.assertEqual(len(test_obj.dof_names), 3) + for name in test_obj.dof_names: + self.assertTrue(comb_patt.match(name)) + exc_patt = re.compile("OptClassWithParents\d+:val") + for name in test_obj.dof_names: + self.assertFalse(exc_patt.match(name)) + + adder.fix('x1') + self.assertEqual(len(test_obj.dof_names), 2) + for name in test_obj.dof_names: + self.assertTrue(comb_patt.match(name)) + exc_patt = re.compile("Adder\d+:x1") + for name in test_obj.dof_names: + self.assertFalse(exc_patt.match(name)) + + test_obj2 = OptClassWith2LevelParents(10, 20) + patt1 = "Adder\d+:x\d+" + patt2 = "OptClassWithParents\d+:val" + patt3 = "OptClassWith2LevelParents\d+:v\d" + comb_patt = re.compile("|".join([patt1, patt2, patt3])) + self.assertEqual(len(test_obj2.dof_names), 10) + for name in test_obj2.dof_names: + self.assertTrue(comb_patt.match(name)) + + test_obj2.fix(0) + self.assertEqual(len(test_obj2.dof_names), 9) + for name in test_obj2.dof_names: + self.assertTrue(comb_patt.match(name)) + exc_patt = re.compile("OptClassWith2LevelParents\d+:v1") + for name in test_obj.dof_names: + self.assertFalse(exc_patt.match(name)) + + def test_full_dof_names(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3]) + self.assertEqual(len(iden.full_dof_names), 1) + self.assertEqual(len(adder.full_dof_names), 3) + + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + self.assertEqual(len(test_obj.full_dof_names), 5) + test_obj.fix('val') + self.assertEqual(len(test_obj.full_dof_names), 5) + adder.fix('x1') + self.assertEqual(len(test_obj.full_dof_names), 5) + + test_obj2 = OptClassWith2LevelParents(10, 20) + self.assertEqual(len(test_obj2.full_dof_names), 10) + test_obj2.fix(0) + self.assertEqual(len(test_obj2.full_dof_names), 10) + patt1 = "Adder\d+:x\d+" + patt2 = "OptClassWithParents\d+:val" + patt3 = "OptClassWith2LevelParents\d+:v\d" + comb_patt = re.compile("|".join([patt1, patt2, patt3])) + for name in test_obj2.full_dof_names: + self.assertTrue(comb_patt.match(name)) + + def test_local_dof_names(self): + # Test in DOFs class is sufficient + pass + + def test_local_full_dof_names(self): + # Test in DOFs class is sufficient + pass + + def test_is_fixed(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + self.assertTrue(adder.is_fixed(0)) + self.assertTrue(adder.is_fixed('x')) + self.assertFalse(adder.is_fixed(1)) + self.assertFalse(adder.is_fixed('y')) + self.assertTrue(iden.is_fixed(0)) + self.assertTrue(iden.is_fixed('x0')) + + def test_is_free(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + self.assertFalse(adder.is_free(0)) + self.assertFalse(adder.is_free('x')) + self.assertTrue(adder.is_free(1)) + self.assertTrue(adder.is_free('y')) + self.assertFalse(iden.is_free(0)) + self.assertFalse(iden.is_free('x0')) + + def test_fix(self): + self.iden.fix(0) + self.adder.fix('x') + self.rosen.fix('y') + + self.assertEqual(self.iden.dof_size, 0) + self.assertEqual(self.adder.dof_size, 2) + self.assertEqual(self.rosen.dof_size, 1) + + def test_fix_all(self): + self.iden.fix_all() + self.adder.fix_all() + self.rosen.fix_all() + + self.assertEqual(self.iden.dof_size, 0) + self.assertEqual(self.adder.dof_size, 0) + self.assertEqual(self.rosen.dof_size, 0) + + def test_unfix(self): + pass + + def test_unfix_all(self): + # Test with leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertEqual(adder.dof_size, 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 0) + + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + iden.unfix_all() + adder.unfix_all() + iden.x = [10] + adder.x = [4, 5, 6] + self.assertEqual(iden.dof_size, 1) + self.assertEqual(adder.dof_size, 3) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 4, 5, 6, 25]) + + adder.unfix_all() + test_obj.x = np.array([4, 5, 6, 25]) + self.assertAlmostEqual(adder.local_full_x[0], 4) + self.assertAlmostEqual(adder.local_full_x[1], 5) + self.assertAlmostEqual(adder.local_full_x[2], 6) + self.assertAlmostEqual(test_obj.local_full_x[0], 25) + + iden.unfix_all() + test_obj.x = np.array([1, 2, 3, 1, 10]) + + self.assertAlmostEqual(adder.local_full_x[0], 1) + self.assertAlmostEqual(adder.local_full_x[1], 2) + self.assertAlmostEqual(adder.local_full_x[2], 3) + self.assertAlmostEqual(iden.local_full_x[0], 1) + self.assertAlmostEqual(test_obj.local_full_x[0], 10) + + def test_get_ancestors(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3]) + self.assertEqual(len(iden._get_ancestors()), 0) + self.assertEqual(len(adder._get_ancestors()), 0) + + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + ancestors = test_obj._get_ancestors() + self.assertEqual(len(ancestors), 2) + self.assertIn(iden, ancestors) + self.assertIn(adder, ancestors) + + test_obj2 = OptClassWith2LevelParents(10, 20) + ancestors = test_obj2._get_ancestors() + self.assertEqual(len(ancestors), 4) + + def test_plot(self): + """ + Verify that a DAG can be plotted. + The ``show`` argument is set to ``False`` so the + tests do not require human intervention to close plot windows. + However, if you do want to actually display the figure, you + can change ``show`` to ``True`` in the first line of this + function. + """ + show = False + + try: + import matplotlib + except ImportError: + return + try: + import networkx + except ImportError: + return + try: + import pygraphviz + except ImportError: + return + + # optimizable with no parents + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + G1, pos1 = adder.plot_graph(show=show) + + # optimizable with two parents + opt1 = OptClassWithParentsReturnFns(10) + G2, pos2 = opt1.plot_graph(show=show) + + # optimizable with many parents + opt2 = OptClassWith2LevelParents(10, 20) + G3, pos3 = opt2.plot_graph(show=show) + + +class OptClassExternalDofs(Optimizable): + def __init__(self): + self.vals = [1, 2] + Optimizable.__init__(self, external_dof_setter=OptClassExternalDofs.set_dofs, + x0=self.get_dofs()) + + def get_dofs(self): + return self.vals + + def set_dofs(self, x): + self.vals = x + + def recompute_bell(self, parent=None): + pass + + +class OptimizableTestsExternalDofs(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassExternalDofs() + + def tearDown(self) -> None: + self.opt = None + + def test_get_dofs(self): + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([1, 2])).all()) + + def test_set_dofs(self): + self.opt.set_dofs([3, 4]) + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([3, 4])).all()) + + def test_set_x(self): + self.opt.x = [3, 4] + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([3, 4])).all()) + + def test_set_local_x(self): + self.opt.local_x = [3, 4] + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([3, 4])).all()) + + +class TestMakeOptimizable(unittest.TestCase): + def setUp(self) -> None: + def arb_fun_dofs_noopts(a, b, c): + return a ** 2 + 2 * b ** 2 + 3 * c ** 2 - 10 + + def arb_fun_nodofs_opts(adder): + return adder.sum()**2 - 10 + + def arb_fun_dofs_opts(a, b, adder): + return a**2 + b**2 + adder.sum()**2 - 10 + + self.arb_fun_dofs_noopts = arb_fun_dofs_noopts + self.arb_fun_nodofs_opts = arb_fun_nodofs_opts + self.arb_fun_dofs_opts = arb_fun_dofs_opts + + def test_arb_func_dofs_noopts(self): + x, y, z = 1, 2, 3 + opt = make_optimizable(self.arb_fun_dofs_noopts, + x, y, z, + dof_indicators=["dof", "dof", "dof"]) + self.assertAlmostEqual(opt.J(), 26.0) + opt.x = np.array([1.2, 0.8, 0.5]) + self.assertAlmostEqual(opt.J(), -6.53) + + def test_arb_func_nodofs_opts(self): + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + opt = make_optimizable(self.arb_fun_nodofs_opts, + adder, + dof_indicators=["opt"]) + self.assertAlmostEqual(opt.J(), 26.0) + x = opt.x # Length of x is 3 + opt.x = x / 2.0 + self.assertAlmostEqual(opt.J(), -1.0) + + # When dof_indicators argument is not passed only opts and non_dofs are + # considered. Below a and b are treated as non-dofs and the adder should + # be recognized as optimizable object + a = 2.0 + b = 3.0 + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + opt = make_optimizable(self.arb_fun_dofs_opts, a, b, adder) + self.assertAlmostEqual(opt.J(), 39.0) + x = opt.x # Length of x is 3 + self.assertEqual(len(x), 3) + opt.x = x / 2.0 + self.assertAlmostEqual(opt.J(), 12.0) + + def test_arb_func_dofs_opts(self): + # Below a is passed as dof b is passed as non-dof and the adder is + # passed as optimizable object + a = 2.0 + b = 3.0 + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + opt = make_optimizable(self.arb_fun_dofs_opts, + a, b, adder, + dof_indicators=['dof', 'non-dof', 'opt']) + self.assertAlmostEqual(opt.J(), 39.0) + x = opt.x # Length of x is 4 + self.assertEqual(len(x), 4) + opt.x = x / 2.0 + self.assertAlmostEqual(opt.J(), 9.0) + + +class TestOptimizableSerialize(unittest.TestCase): + """ + Test the serialization of the Optimizable class based on as_dict and + from_dict methods using various sub-classes + """ + def test_serialize(self): + adder = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder, cls=MontyEncoder) + print(s) + + def test_deserialize(self): + adder_orig = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder_orig, cls=MontyEncoder) + adder = json.loads(s, cls=MontyDecoder) + print(adder.name) + print(adder.n) + print(adder.full_x) + print(adder.dofs_free_status) + print(adder.local_full_dof_names) + + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index dd15bc4cc..1de4a61a0 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -888,6 +888,53 @@ def test_fix_all(self): self.assertEqual(self.adder.dof_size, 0) self.assertEqual(self.rosen.dof_size, 0) + def test_fix_local(self): + self.iden.fix_local() + self.adder.fix_local() + self.rosen.fix_local() + + self.assertEqual(self.iden.dof_size, 0) + self.assertEqual(self.adder.dof_size, 0) + self.assertEqual(self.rosen.dof_size, 0) + + def test_fix_full(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=False) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 1) + + iden.fix_full() + adder.fix_full() + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + self.assertEqual(iden.dof_size, 0) + self.assertEqual(adder.dof_size, 0) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=False) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + + test_x = test_obj.x + self.assertEqual(len(test_x), 4) + test_obj.fix_full() + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 5, 6, 25]) + + self.assertEqual(test_obj.dof_size, 0) + self.assertEqual(adder.dof_size, 0) + self.assertEqual(iden.dof_size, 0) + def test_unfix(self): pass @@ -941,6 +988,100 @@ def test_unfix_all(self): self.assertAlmostEqual(iden.local_full_x[0], 1) self.assertAlmostEqual(test_obj.local_full_x[0], 10) + def test_unfix_local(self): + # Test with leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertEqual(adder.dof_size, 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 0) + + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + iden.unfix_local() + adder.unfix_local() + iden.x = [10] + adder.x = [4, 5, 6] + self.assertEqual(iden.dof_size, 1) + self.assertEqual(adder.dof_size, 3) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 4, 5, 6, 25]) + + adder.unfix_local() + test_obj.x = np.array([4, 5, 6, 25]) + self.assertAlmostEqual(adder.local_full_x[0], 4) + self.assertAlmostEqual(adder.local_full_x[1], 5) + self.assertAlmostEqual(adder.local_full_x[2], 6) + self.assertAlmostEqual(test_obj.local_full_x[0], 25) + + iden.unfix_local() + test_obj.x = np.array([1, 2, 3, 1, 10]) + + self.assertAlmostEqual(adder.local_full_x[0], 1) + self.assertAlmostEqual(adder.local_full_x[1], 2) + self.assertAlmostEqual(adder.local_full_x[2], 3) + self.assertAlmostEqual(iden.local_full_x[0], 1) + self.assertAlmostEqual(test_obj.local_full_x[0], 10) + + def test_unfix_full(self): + # Test with leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertEqual(adder.dof_size, 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 0) + + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + iden.unfix_full() + adder.unfix_full() + iden.x = [10] + adder.x = [4, 5, 6] + self.assertEqual(iden.dof_size, 1) + self.assertEqual(adder.dof_size, 3) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + test_obj.fix('val') + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 4, 5, 6, 25]) + + test_obj.unfix_full() + test_obj.x = np.array([4, 5, 6, 20, 25]) + print(iden.x) + self.assertAlmostEqual(adder.local_full_x[0], 4) + self.assertAlmostEqual(adder.local_full_x[1], 5) + self.assertAlmostEqual(adder.local_full_x[2], 6) + self.assertAlmostEqual(iden.local_full_x[0], 20) + self.assertAlmostEqual(test_obj.local_full_x[0], 25) + def test_get_ancestors(self): iden = Identity(x=10, dof_fixed=True) adder = Adder(n=3, x0=[1, 2, 3]) diff --git a/tests/objectives/test_graph_least_squares.py b/tests/objectives/test_graph_least_squares.py new file mode 100755 index 000000000..62e46f77c --- /dev/null +++ b/tests/objectives/test_graph_least_squares.py @@ -0,0 +1,82 @@ +import unittest +import logging +import numpy as np +from simsopt.objectives.graph_functions import Identity, Rosenbrock +#from simsopt.core.optimizable import Target +from simsopt.objectives.graph_least_squares import LeastSquaresProblem + +#logging.basicConfig(level=logging.DEBUG) + + +class LeastSquaresProblemTests(unittest.TestCase): + def test_single_value_opt_in(self): + iden = Identity() + lst = LeastSquaresProblem.from_sigma(3, 0.1, depends_on=iden) + + iden.x = [17] + correct_value = ((17 - 3) / 0.1) # ** 2 + self.assertAlmostEqual(np.abs(lst.residuals()[0]), + correct_value, + places=11) + + iden.x = [0] + term1 = LeastSquaresProblem.from_sigma(3, 2, depends_on=iden) + self.assertAlmostEqual(np.abs(term1.residuals()[0]), 1.5) + + term1.x = [10] + self.assertAlmostEqual(np.abs(term1.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(term1.residuals(x=[0])), 1.5) + self.assertAlmostEqual(np.abs(term1.residuals(x=[5])), 1) + + def test_exceptions(self): + """ + Test that exceptions are thrown when invalid inputs are + provided. + """ + iden = Identity() + + # sigma cannot be zero + with self.assertRaises(ValueError): + lst = LeastSquaresProblem.from_sigma(3, 0, depends_on=iden) + + # Weight cannot be negative + with self.assertRaises(ValueError): + lst = LeastSquaresProblem(3, -1.0, depends_on=iden) + + def test_multiple_funcs_single_input(self): + iden1 = Identity(x=10) + iden2 = Identity() + # Objective function + # f(x,y) = ((x - 3) / 2) ** 2 + ((y + 4) / 5) ** 2 + lsp = LeastSquaresProblem.from_sigma([3, -4], [2, 5], depends_on=[iden1, iden2]) + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.8) + lsp.x = [5, -7] + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 1.0) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.6) + self.assertAlmostEqual(np.abs(lsp.residuals([10, 0])[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals([10, 0])[1]), 0.8) + self.assertAlmostEqual(np.abs(lsp.residuals([5, -7])[0]), 1.0) + self.assertAlmostEqual(np.abs(lsp.residuals([5, -7])[1]), 0.6) + + def test_parent_dof_transitive_behavior(self): + iden1 = Identity() + iden2 = Identity() + lsp = LeastSquaresProblem.from_sigma([3, -4], [2, 5], depends_on=[iden1, iden2]) + iden1.x = [10] + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.8) + + def test_least_squares_combination(self): + iden1 = Identity() + iden2 = Identity() + term1 = LeastSquaresProblem.from_sigma(3, 2, depends_on=[iden1]) + term2 = LeastSquaresProblem.from_sigma(-4, 5, depends_on=[iden2]) + lsp = term1 + term2 + iden1.x = [10] + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.8) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/solve/test_graph_least_squares.py b/tests/solve/test_graph_least_squares.py new file mode 100755 index 000000000..b7ce355da --- /dev/null +++ b/tests/solve/test_graph_least_squares.py @@ -0,0 +1,79 @@ +import unittest + +import numpy as np +try: + from mpi4py import MPI +except ImportError: + MPI = None + +from simsopt.objectives.graph_functions import Identity, Rosenbrock +from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.solve.graph_serial import least_squares_serial_solve, serial_solve +if MPI is not None: + from simsopt.util.mpi import MpiPartition + from simsopt.solve.graph_mpi import least_squares_mpi_solve + + +def mpi_solve_1group(prob, **kwargs): + least_squares_mpi_solve(prob, MpiPartition(ngroups=1), **kwargs) + + +solvers = [least_squares_serial_solve] +if MPI is not None: + solvers.append(mpi_solve_1group) + + +class LeastSquaresProblemTests(unittest.TestCase): + def test_solve_quadratic(self): + """ + Minimize f(x,y,z) = 1 * (x - 1) ^ 2 + 2 * (y - 2) ^ 2 + 3 * (z - 3) ^ 2. + The optimum is at (x,y,z)=(1,2,3), and f=0 at this point. + """ + for solver in solvers: + iden1 = Identity() + iden2 = Identity() + iden3 = Identity() + term1 = (iden1.f, 1, 1) + term2 = (iden2.f, 2, 2) + term3 = (iden3.f, 3, 3) + prob = LeastSquaresProblem.from_tuples([term1, term2, term3]) + solver(prob) + self.assertAlmostEqual(prob.objective(), 0) + self.assertTrue(np.allclose(iden1.x, [1])) + self.assertTrue(np.allclose(iden2.x, [2])) + self.assertTrue(np.allclose(iden3.x, [3])) + + def test_solve_quadratic_fixed(self): + """ + Same as test_solve_quadratic, except with different weights and x + and z are fixed, so only y is optimized. + """ + for solver in solvers: + iden1 = Identity(4, dof_name='x1', dof_fixed=True) + iden2 = Identity(5, dof_name='x2') + iden3 = Identity(6, dof_name='x3', dof_fixed=True) + term1 = (iden1.f, 1, 1) + term2 = (iden2.f, 2, 1 / 4.) + term3 = (iden3.f, 3, 1 / 9.) + prob = LeastSquaresProblem.from_tuples([term1, term2, term3]) + solver(prob) + self.assertAlmostEqual(prob.objective(), 10) + self.assertTrue(np.allclose(iden1.x, [4])) + self.assertTrue(np.allclose(iden2.x, [2])) + self.assertTrue(np.allclose(iden3.x, [6])) + + def test_solve_rosenbrock(self): + """ + Minimize the Rosenbrock function using two separate least-squares + terms. + """ + for solver in solvers: + #for grad in [True, False]: + r = Rosenbrock() + prob = LeastSquaresProblem(0, 1, depends_on=r) + solver(prob) # , grad=grad) + self.assertAlmostEqual(prob.objective(), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/solve/test_graph_mpi.py b/tests/solve/test_graph_mpi.py new file mode 100755 index 000000000..1d74d0b7d --- /dev/null +++ b/tests/solve/test_graph_mpi.py @@ -0,0 +1,137 @@ +import logging +import unittest + +import numpy as np +try: + from mpi4py import MPI +except: + MPI = None + +from simsopt._core.graph_optimizable import Optimizable +from simsopt.objectives.graph_functions import Beale +from simsopt.objectives.graph_least_squares import LeastSquaresProblem +if MPI is not None: + from simsopt.util.mpi import MpiPartition + from simsopt.solve.graph_mpi import least_squares_mpi_solve + +#logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + + +class TestFunction1(Optimizable): + def __init__(self): + x = np.array([1.2, 0.9, -0.4]) + fixed = np.full(3, False) + super().__init__(x0=x, fixed=fixed) + + def J(self): + return np.exp(self.full_x[0] ** 2 - np.exp(self.full_x[1]) \ + + np.sin(self.full_x[2])) + + return_fn_map = {'J': J} + + +class TestFunction2(Optimizable): + def __init__(self): + x = np.array([1.2, 0.9]) + fixed = np.full(2, False) + super().__init__(x0=x, fixed=fixed) + + def f0(self): + return np.exp(0 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + def f1(self): + return np.exp(1 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + def f2(self): + return np.exp(2 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + def f3(self): + return np.exp(3 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + return_fn_map = {'f0': f0, 'f1': f1, 'f2': f2, 'f3': f3} + + +class TestFunction3(Optimizable): + """ + This is the Rosenbrock function again, but with some unnecessary + MPI communication added in order to test optimization with MPI. + """ + + def __init__(self, comm, x=[0, 0]): + self.comm = comm + self.dummy = 42 + self.f0_call_cnt = 0 + self.f1_call_cnt = 0 + logger.debug("inside test function 3 init") + super().__init__(x0=x) + + def f0(self): + # Do some random MPI stuff just for the sake of testing. + self.comm.barrier() + self.comm.bcast(self.local_full_x) + self.f0_call_cnt += 1 + print(f"x is {self.local_full_x}") + print(f"TestFunction3.f0 called {self.f0_call_cnt} times") + return self.local_full_x[0] - 1 + + def f1(self): + # Do some random MPI stuff just for the sake of testing. + self.comm.bcast(self.dummy) + self.comm.barrier() + self.f1_call_cnt += 1 + print(f"x is {self.local_full_x}") + print(f"TestFunction3.f1 called {self.f1_call_cnt} times") + return self.local_full_x[0] ** 2 - self.local_full_x[1] + + return_fn_map = {'f0': f0, 'f1': f1} + + +@unittest.skipIf(MPI is None, "Requires mpi4py") +class MPISolveTests(unittest.TestCase): + + def test_parallel_optimization_without_grad(self): + """ + Test a full least-squares optimization. + """ + for ngroups in range(1, 4): + mpi = MpiPartition(ngroups=ngroups) + o = TestFunction3(mpi.comm_groups) + term1 = (o.f0, 0, 1) + term2 = (o.f1, 0, 1) + prob = LeastSquaresProblem.from_tuples([term1, term2]) + least_squares_mpi_solve(prob, mpi, grad=False) + self.assertAlmostEqual(prob.x[0], 1) + self.assertAlmostEqual(prob.x[1], 1) + + def test_parallel_optimization_with_grad(self): + """ + Test a full least-squares optimization. + """ + for ngroups in range(1, 4): + for abs_step in [0, 1.0e-7]: + # Only try rel_step=0 if abs_step is positive: + rel_steps = [0, 1.0e-7] + if abs_step == 0: + rel_steps = [1.0e-7] + + for rel_step in rel_steps: + for diff_method in ["forward", "centered"]: + logger.debug(f'ngroups={ngroups} abs_step={abs_step} ' \ + f'rel_step={rel_step} diff_method={diff_method}') + mpi = MpiPartition(ngroups=ngroups) + o = TestFunction3(mpi.comm_groups) + term1 = (o.f0, 0, 1) + term2 = (o.f1, 0, 1) + prob = LeastSquaresProblem.from_tuples([term1, term2]) + # Set initial condition different from 0, + # because otherwise abs_step=0 causes step + # size to be 0. + prob.x = [-0.1, 0.2] + least_squares_mpi_solve(prob, mpi, grad=True, + diff_method=diff_method, + abs_step=abs_step, + rel_step=rel_step) + self.assertAlmostEqual(prob.x[0], 1) + self.assertAlmostEqual(prob.x[1], 1) + From 5b7b0f92c3b7c8b18632ec11c5698868717b6801 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 20:32:08 -0400 Subject: [PATCH 14/95] Readd the tests with graph module imports --- tests/core/test_graph_optimizable.py | 1269 ++++++++++++++++++ tests/objectives/test_graph_least_squares.py | 82 ++ tests/solve/test_graph_least_squares.py | 79 ++ tests/solve/test_graph_mpi.py | 137 ++ 4 files changed, 1567 insertions(+) create mode 100755 tests/core/test_graph_optimizable.py create mode 100644 tests/objectives/test_graph_least_squares.py create mode 100755 tests/solve/test_graph_least_squares.py create mode 100755 tests/solve/test_graph_mpi.py diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_graph_optimizable.py new file mode 100755 index 000000000..a55b8bba1 --- /dev/null +++ b/tests/core/test_graph_optimizable.py @@ -0,0 +1,1269 @@ +import unittest +import re +import json + +import numpy as np +from monty.json import MontyDecoder, MontyEncoder +from monty.serialization import loadfn, dumpfn + +from simsopt._core.graph_optimizable import Optimizable, make_optimizable +from simsopt.objectives.graph_functions import Identity, Rosenbrock, TestObject2 +from simsopt.objectives.graph_functions import Adder as FAdder + + +class Adder(Optimizable): + """This class defines a minimal object that can be optimized. It has + n degrees of freedom, and has a function that just returns the sum + of these dofs. This class is used for testing. + """ + + def __init__(self, n=3, x0=None, dof_names=None, dof_fixed=None): + self.n = n + x = x0 if x0 is not None else np.zeros(n) + super().__init__(x, names=dof_names, fixed=dof_fixed) + + def sum(self): + return np.sum(self.local_full_x) + + return_fn_map = {'sum': sum} + + def as_dict(self) -> dict: + d = super().as_dict() + d["dof_names"] = d["names"] + d["dof_fixed"] = d["fixed"] + del d["names"] + del d["fixed"] + d["n"] = self.n + return d + + @classmethod + def from_dict(cls, d): + return cls(d["n"], + d.get("x0", None), + d.get("dof_names", None), + d.get("dof_fixed", None)) + +class OptClassWithParents(Optimizable): + def __init__(self, val, depends_on=None): + if depends_on is None: + depends_on = [Adder(3), Adder(2)] + super().__init__(x0=[val], names=['val'], depends_on=depends_on) + + def f(self): + return (self.local_full_x[0] + 2 * self.parents[0](child=self)) \ + / (10.0 + self.parents[1](child=self)) + + return_fn_map = {'f': f} + + def as_dict(self) -> dict: + d = super().as_dict() + del d["x0"] + del d["names"] + d['val'] = self.local_full_x[0] + return d + + @classmethod + def from_dict(cls, d: dict): + return cls(d["val"], d["depends_on"]) + + +class N_No(Optimizable): + """This class defines a minimal object that can be optimized. It has + n degrees of freedom, and has couple of functions that return the sum + and product of these dofs. This class is used for testing. + """ + + def __init__(self, n=3, x0=None, dof_names=None, dof_fixed=None): + self.n = n + x = x0 if x0 is not None else np.zeros(n) + super().__init__(x, names=dof_names, fixed=dof_fixed) + + def sum(self): + return np.sum(self.local_full_x) + + def product(self): + return np.product(self.local_full_x) + + return_fn_map = {'sum': sum, 'prod': product} + + +class OptClassWithParentsReturnFns(Optimizable): + def __init__(self, val): + self.opt1 = N_No(3, x0=[2, 3, 4]) # Computes to [9, 24] + self.opt2 = N_No(2, x0=[1, 2]) # Computes to [3, 2] + super().__init__(x0=[val], names=['val'], + depends_on=[self.opt1, self.opt2], + opt_return_fns=[['sum'], ['sum', 'prod']]) + + # Pay attention to the arguments passed in f1 and f2 + def f1(self): + return (self.local_full_x[0] + 2 * self.opt1(child=self)) \ + / (10.0 + np.sum(self.opt2(child=self))) + + # If child=self is not passed, full return array is returned, because + # parent class is not aware of the calling child class + def f2(self): + return (self.local_full_x[0] + 2 * self.opt1()[0]) \ + / (10.0 + np.sum(self.opt2())) + + return_fn_map = {'f1': f1, 'f2': f2} + + +class OptimizableTestsWithParentsReturnFns(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassWithParentsReturnFns(10) + + def tearDown(self) -> None: + self.opt = None + + def test_name(self): + self.assertTrue('OptClassWithParentsReturnFns' in self.opt.name) + self.assertNotEqual(self.opt.name, + OptClassWithParentsReturnFns(10).name) + + def test_hash(self): + hash1 = hash(self.opt) + hash2 = hash(OptClassWithParentsReturnFns(10)) + self.assertNotEqual(hash1, hash2) + + def test_eq(self): + self.assertNotEqual(self.opt, OptClassWithParentsReturnFns(10)) + + def test_get_return_fn_names(self): + ret_fn_names = self.opt.get_return_fn_names() + self.assertEqual(ret_fn_names[0], 'f1') + self.assertEqual(ret_fn_names[1], 'f2') + + def test_add_return_fn_by_name(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, 'prod') + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_add_return_fn_by_reference(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, opt1.product) + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_call(self): + # Test for leaf nodes + self.assertAlmostEqual(self.opt.f1(), 28.0/15) + self.assertAlmostEqual(self.opt.f2(), 28.0/15) + np.allclose(self.opt(), [28.0/15, 28.0/15]) + + # Change parent objects and see if the DOFs are propagated + opt1 = self.opt.opt1 + opt1.set('x1', 5) + opt2 = self.opt.opt2 + opt2.set('x1', 4) + np.allclose(self.opt(), 34.0/24) + + +class OptClassWithDirectParentFnCalls(Optimizable): + def __init__(self, val): + self.opt1 = N_No(3, x0=[2, 3, 4]) + self.opt2 = N_No(2, x0=[1, 2]) + super().__init__(x0=[val], names=['val'], + depends_on=[self.opt1, self.opt2]) + + # The value returned by f3 should be identical to f1 + def f(self): + return (self.local_full_x[0] + 2 * self.opt1.sum()) \ + / (10.0 + self.opt2.sum() + self.opt2.product()) + + return_fn_map = {'f': f} + + +class OptimizableTestsWithDirectParentFnCalls(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassWithDirectParentFnCalls(10) + + def tearDown(self) -> None: + self.opt = None + + def test_name(self): + self.assertTrue('OptClassWithDirectParentFnCalls' in self.opt.name) + + def test_equal(self): + self.assertNotEqual(self.opt.name, + OptClassWithDirectParentFnCalls(10).name) + + def test_hash(self): + hash1 = hash(self.opt) + hash2 = hash(OptClassWithDirectParentFnCalls(10)) + self.assertNotEqual(hash1, hash2) + + def test_eq(self): + self.assertNotEqual(self.opt, OptClassWithDirectParentFnCalls(10)) + + def test_get_return_fn_names(self): + ret_fn_names = self.opt.get_return_fn_names() + self.assertEqual(ret_fn_names[0], 'f') + + @unittest.skip + def test_add_return_fn_by_name(self): + """ + This test is not needed + """ + pass + + @unittest.skip + def test_add_return_fn_by_reference(self): + """ + This test is not need for the used function + """ + pass + + def test_call(self): + # Test for leaf nodes + self.assertAlmostEqual(self.opt.f(), 28.0/15) + np.allclose(self.opt(), 28.0/15) + + # Change parent objects and see if the DOFs are propagated + opt1 = self.opt.opt1 + opt1.set('x1', 5) + opt2 = self.opt.opt2 + opt2.set('x1', 4) + np.allclose(self.opt(), 34.0/24) + + +class OptClassWithDirectRegisterParentFn(Optimizable): + def __init__(self, val): + self.opt1 = N_No(3, x0=[2, 3, 4]) + self.opt2 = N_No(2, x0=[1, 2]) + super().__init__(x0=[val], names=['val'], + funcs_in=[self.opt1.sum, self.opt2.sum, + self.opt2.product]) + + # Pay attention to the arguments passed in f1 and f2 + def f1(self): + return (self.local_full_x[0] + 2 * self.opt1(child=self)) \ + / (10.0 + np.sum(self.opt2(child=self))) + + # If child=self is not passed, full return array is returned, because + # parent class is not aware of the calling child class + def f2(self): + return (self.local_full_x[0] + 2 * self.opt1()[0]) \ + / (10.0 + np.sum(self.opt2())) + + return_fn_map = {'f1': f1, 'f2': f2} + + +class OptimizableTestsWithDirectRegisterParentFns(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassWithDirectRegisterParentFn(10) + + def tearDown(self) -> None: + self.opt = None + + def test_name(self): + self.assertTrue('OptClassWithDirectRegisterParentFn' in self.opt.name) + self.assertNotEqual(self.opt.name, + OptClassWithDirectRegisterParentFn(10).name) + + def test_hash(self): + hash1 = hash(self.opt) + hash2 = hash(OptClassWithDirectRegisterParentFn(10)) + self.assertNotEqual(hash1, hash2) + + def test_eq(self): + self.assertNotEqual(self.opt, OptClassWithDirectRegisterParentFn(10)) + + def test_get_return_fn_names(self): + ret_fn_names = self.opt.get_return_fn_names() + self.assertEqual(ret_fn_names[0], 'f1') + self.assertEqual(ret_fn_names[1], 'f2') + + def test_add_return_fn_by_name(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, 'prod') + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_add_return_fn_by_reference(self): + opt1 = self.opt.opt1 + self.assertEqual(len(opt1.return_fns[self.opt]), 1) + opt2 = self.opt.opt2 + self.assertEqual(len(opt2.return_fns[self.opt]), 2) + opt1.add_return_fn(self.opt, opt1.product) + self.assertEqual(len(opt1.return_fns[self.opt]), 2) + + def test_call(self): + # Test for leaf nodes + self.assertAlmostEqual(self.opt.f1(), 28.0/15) + self.assertAlmostEqual(self.opt.f2(), 28.0/15) + np.allclose(self.opt(), [28.0/15, 28.0/15]) + + # Change parent objects and see if the DOFs are propagated + opt1 = self.opt.opt1 + opt1.set('x1', 5) + opt2 = self.opt.opt2 + opt2.set('x1', 4) + np.allclose(self.opt(), 34.0/24) + + +class OptClassWith2LevelParents(Optimizable): + def __init__(self, val1, val2): + x = [val1, val2] + names = ['v1', 'v2'] + opts = [OptClassWithParents(0.0), Adder(2)] + super().__init__(x0=x, names=names, depends_on=opts) + + def f(self): + x = self.local_full_x + v1 = x[0] + v2 = x[1] + t = self.parents[0](self) + a = self.parents[1](self) + return v1 + a * np.cos(v2 + t) + + return_fn_map = {'f': f} + + +class OptWithInputParent(Optimizable): + def __init__(self, obj): + super().__init__(depends_on=[obj]) + + +class ThreeDofOpt(Optimizable): + def __init__(self): + super().__init__(x0=[1, 2, 3]) + + +class TwoDofOpt(Optimizable): + def __init__(self): + super().__init__(x0=[10, 20]) + + +class OptimizableTests(unittest.TestCase): + def setUp(self) -> None: + self.iden = Identity(x=10) + self.adder = Adder(n=3, dof_names=['x', 'y', 'z']) + self.rosen = Rosenbrock() + + def tearDown(self) -> None: + self.iden = None + self.adder = None + self.rosen = None + + def test_name(self): + self.assertTrue('Identity' in self.iden.name) + self.assertTrue('Adder' in self.adder.name) + self.assertTrue('Rosenbrock' in self.rosen.name) + self.assertNotEqual(self.iden.name, Identity().name) + self.assertNotEqual(self.adder.name, Adder().name) + self.assertNotEqual(self.rosen.name, Rosenbrock().name) + + def test_hash(self): + hash1 = hash(self.adder) + hash2 = hash(Adder()) + self.assertNotEqual(hash1, hash2) + + def test_add_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1]) + + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + opt_with_parents.add_parent(1, opt2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + + def test_append_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1]) + + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + opt_with_parents.append_parent(opt2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + + def test_append_parent_dof_sizes(self): + # vmec is the parent, prob is the child + three_dof_opt = ThreeDofOpt() + opt_with_inp_parent = OptWithInputParent(three_dof_opt) + + # Test dof sizes before adding grandparent + self.assertEqual(three_dof_opt.dof_size, 3) + self.assertEqual(opt_with_inp_parent.dof_size, 3) + + two_dof_opt = TwoDofOpt() + three_dof_opt.append_parent(two_dof_opt) + # Now two_dof_opt is the grandparent + + # Test dof sizes after adding grandparent + self.assertEqual(three_dof_opt.dof_size, 5) + self.assertEqual(three_dof_opt.dof_size, 5) + + def test_pop_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1, opt2]) + + self.assertEqual(len(opt_with_parents.parents), 2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + opt_with_parents.pop_parent() + self.assertEqual(len(opt_with_parents.parents), 1) + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + def test_remove_parent(self): + opt1 = Adder(3, x0=[2, 3, 4]) + opt2 = Adder(2, x0=[1, 2]) + opt_with_parents = OptClassWithParents(10, depends_on=[opt1, opt2]) + + self.assertEqual(len(opt_with_parents.parents), 2) + self.assertAlmostEqual(opt_with_parents(), 28.0/13.0) + opt_with_parents.remove_parent(opt1) + self.assertEqual(len(opt_with_parents.parents), 1) + with self.assertRaises(IndexError): # Missing second parent + opt_with_parents() + + def test_dof_size(self): + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + return_fn_map = {'f': f} + + opt = EmptyOptimizable() + self.assertEqual(opt.dof_size, 0) + + self.assertEqual(self.iden.dof_size, 1) + self.assertEqual(self.adder.dof_size, 3) + self.assertEqual(self.rosen.dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.dof_size, 0) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.dof_size, 6) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(n=3, x0=[1, 2, 3])]) + self.assertEqual(test_obj1.dof_size, 4) + + def test_full_dof_size(self): + + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + + opt = EmptyOptimizable() + self.assertEqual(opt.full_dof_size, 0) + + self.assertEqual(self.iden.full_dof_size, 1) + self.assertEqual(self.adder.full_dof_size, 3) + self.assertEqual(self.rosen.full_dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.full_dof_size, 1) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.full_dof_size, 6) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(3)]) + self.assertEqual(test_obj1.full_dof_size, 5) + + def test_local_dof_size(self): + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + + opt = EmptyOptimizable() + self.assertEqual(opt.local_dof_size, 0) + + self.assertEqual(self.iden.local_dof_size, 1) + self.assertEqual(self.adder.local_dof_size, 3) + self.assertEqual(self.rosen.local_dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.local_dof_size, 0) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.local_dof_size, 1) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(3)]) + self.assertEqual(test_obj1.local_dof_size, 1) + + def test_local_full_dof_size(self): + # Define Null class + class EmptyOptimizable(Optimizable): + def f(self): + return 0 + + opt = EmptyOptimizable() + self.assertEqual(opt.local_full_dof_size, 0) + + self.assertEqual(self.iden.local_full_dof_size, 1) + self.assertEqual(self.adder.local_full_dof_size, 3) + self.assertEqual(self.rosen.local_full_dof_size, 2) + + iden2 = Identity(x=10, dof_fixed=True) + self.assertEqual(iden2.local_full_dof_size, 1) + + # Use Optimizable object with parents + test_obj = OptClassWithParents(10) + self.assertEqual(test_obj.local_full_dof_size, 1) + + test_obj1 = OptClassWithParents(10, + depends_on=[Identity(x=10, dof_fixed=True), + Adder(3)]) + self.assertEqual(test_obj1.local_full_dof_size, 1) + + def test_x(self): + # Check with leaf type Optimizable objects + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_dofs = adder.x + iden_dofs = iden.x + self.assertAlmostEqual(adder_dofs[0], 1) + self.assertAlmostEqual(adder_dofs[1], 2) + self.assertAlmostEqual(adder_dofs[2], 3) + self.assertEqual(len(iden_dofs), 0) + + adder.x = [4, 5, 6] + self.assertAlmostEqual(adder.local_x[0], 4) + self.assertAlmostEqual(adder.local_x[1], 5) + self.assertAlmostEqual(adder.local_x[2], 6) + with self.assertRaises(ValueError): + iden.x = np.array([11, ], dtype=float) + self.assertAlmostEqual(iden.full_x[0], 10) + + # Check with Optimizable objects containing parents + adder2 = Adder(3) + iden2 = Identity(x=10) + test_obj1 = OptClassWithParents(10, depends_on=[iden2, adder2]) + with self.assertRaises(ValueError): + test_obj1.x = np.array([20]) + + test_obj1.x = np.array([4, 5, 6, 20, 25]) + self.assertAlmostEqual(iden2.local_x[0], 20) + self.assertAlmostEqual(adder2.local_x[0], 4) + self.assertAlmostEqual(adder2.local_x[1], 5) + self.assertAlmostEqual(adder2.local_x[2], 6) + self.assertAlmostEqual(test_obj1.local_x[0], 25) + + adder3 = Adder(3) + test_obj2 = OptClassWithParents(10, depends_on=[iden, adder3]) + with self.assertRaises(ValueError): + test_obj2.x = np.array([20, 4, 5, 6, 25]) + + test_obj2.x = np.array([4, 5, 6, 25]) + self.assertAlmostEqual(iden.local_full_x[0], 10) + self.assertAlmostEqual(adder3.local_x[0], 4) + self.assertAlmostEqual(adder3.local_x[1], 5) + self.assertAlmostEqual(adder3.local_x[2], 6) + self.assertAlmostEqual(test_obj2.local_x[0], 25) + + def test_local_x(self): + # Check with leaf type Optimizable objects + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.local_x + iden_x = iden.local_x + self.assertAlmostEqual(adder_x[0], 1) + self.assertAlmostEqual(adder_x[1], 2) + self.assertAlmostEqual(adder_x[2], 3) + self.assertTrue(len(iden_x) == 0) + + adder.local_x = [4, 5, 6] + with self.assertRaises(ValueError): + iden.local_x = np.array([11, ], dtype=float) + self.assertAlmostEqual(iden.full_x[0], 10) + + # Check with Optimizable objects containing parents + adder2 = Adder(3) + iden2 = Identity(x=10) + test_obj1 = OptClassWithParents(10, depends_on=[iden2, adder2]) + test_obj1.local_x = np.array([25]) + + adder3 = Adder(3) + test_obj2 = OptClassWithParents(10, depends_on=[iden, adder3]) + + test_obj2.local_x = np.array([25]) + + def test_full_x(self): + # Check with leaf type Optimizable objects + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_full_x = adder.full_x + self.assertAlmostEqual(adder_full_x[0], 1) + self.assertAlmostEqual(adder_full_x[1], 2) + self.assertAlmostEqual(adder_full_x[2], 3) + self.assertEqual(len(iden.full_x), 1) + self.assertAlmostEqual(iden.full_x[0], 10) + + # Check with Optimizable objects containing parents + test_obj1 = OptClassWithParents(20, depends_on=[iden, adder]) + full_x = test_obj1.full_x + self.assertTrue(np.allclose(full_x, np.array([1, 2, 3, 10, 20]))) + + test_obj1.x = np.array([4, 5, 6, 25]) + full_x = test_obj1.full_x + self.assertTrue(np.allclose(full_x, np.array([4, 5, 6, 10, 25]))) + + def test_local_full_x(self): + # Check with leaf type Optimizable objects + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z']) + iden = Identity(x=10, dof_fixed=True) + adder_local_full_x = adder.local_full_x + self.assertAlmostEqual(adder_local_full_x[0], 1) + self.assertAlmostEqual(adder_local_full_x[1], 2) + self.assertAlmostEqual(adder_local_full_x[2], 3) + self.assertEqual(len(iden.local_full_x), 1) + self.assertAlmostEqual(iden.local_full_x[0], 10) + + # Check with Optimizable objects containing parents + test_obj1 = OptClassWithParents(20, depends_on=[iden, adder]) + local_full_x = test_obj1.local_full_x + self.assertTrue(np.allclose(local_full_x, np.array([20]))) + + test_obj1.x = np.array([4, 5, 6, 25]) + local_full_x = test_obj1.local_full_x + self.assertTrue(np.allclose(local_full_x, np.array([25]))) + + def test_get(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + + self.assertAlmostEqual(adder.get(0), 1.) + self.assertAlmostEqual(adder.get('y'), 2.) + self.assertAlmostEqual(iden.get('x0'), 10.) + + def test_set(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + + adder.set(0, 2) + adder.set('y', 20) + iden.set('x0', 20) + self.assertAlmostEqual(adder.full_x[0], 2) + self.assertAlmostEqual(adder.full_x[1], 20) + self.assertAlmostEqual(iden.full_x[0], 20) + + def test_dofs_free_status(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(20, depends_on=[iden, adder]) + + adder_status = [False, True, True] + self.assertTrue(np.equal(adder.dofs_free_status, adder_status).all()) + self.assertTrue(np.equal(iden.dofs_free_status, [False]).all()) + obj_status = [False, True, True, False, True] + self.assertTrue(np.equal(test_obj.dofs_free_status, obj_status).all()) + + def test_local_dofs_free_status(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + + self.assertTrue( + np.equal(adder.local_dofs_free_status, [False, True, True]).all()) + self.assertTrue(np.equal(iden.local_dofs_free_status, [False]).all()) + + def test_call(self): + # Test for leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + self.assertAlmostEqual(adder(), 6.0) + adder.fix('y') + self.assertAlmostEqual(adder(), 6.0) + + iden = Identity(x=10, dof_fixed=True) + self.assertAlmostEqual(iden(), 10.0) + + # Set dofs and call + adder.x = [6] + self.assertAlmostEqual(adder(), 9.0) + adder.unfix_all() + adder.x = [4, 5, 6] + self.assertAlmostEqual(adder(), 15.0) + iden.unfix_all() + iden.x = [20] + self.assertAlmostEqual(iden(), 20.0) + + # Call with arguments + self.assertAlmostEqual(adder(x=[10, 11, 12]), 33) + self.assertAlmostEqual(iden(x=[20]), 20) + + # Now call without arguments to make sure the previous value is returned + self.assertAlmostEqual(adder(), 33) + self.assertAlmostEqual(iden(), 20) + + # Fix dofs and now call + adder.fix('x') + self.assertAlmostEqual(adder([1, 2]), 13) + adder.fix_all() + self.assertAlmostEqual(adder(), 13) + iden.fix_all() + self.assertAlmostEqual(iden(), 20) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj1 = OptClassWithParents(20, depends_on=[iden, adder]) + # Value returned by test_obj1 is (val + 2*iden())/(10.0 + adder()) + self.assertAlmostEqual(test_obj1(), 2.5) + + # Set the parents nodes' x and call + adder.x = [4, 5] + self.assertAlmostEqual(test_obj1(), 2.0) + + # Set the dofs and call + test_obj1.x = np.array([14, 15, 30]) + self.assertAlmostEqual(test_obj1(), 1.25) + + # Set only the node local dofs and call + test_obj1.local_x = [20] + self.assertAlmostEqual(test_obj1(), 1.0) + + # Call with arguments + self.assertAlmostEqual(test_obj1([2, 3, 20]), 2.5) + # Followed by Call with no arguments + self.assertAlmostEqual(test_obj1(), 2.5) + + def test_bounds(self): + pass + + def test_local_bounds(self): + pass + + def test_lower_bounds(self): + pass + + def test_local_lower_bounds(self): + pass + + def test_upper_bounds(self): + pass + + def test_local_upper_bounds(self): + pass + + def test_dof_names(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3]) + self.assertEqual(len(iden.dof_names), 0) + self.assertEqual(len(adder.dof_names), 3) + patt = re.compile("Adder\d+:x\d+") + for name in adder.dof_names: + self.assertTrue(patt.match(name)) + + patt1 = "Adder\d+:x\d+" + patt2 = "OptClassWithParents\d+:val" + comb_patt = re.compile("|".join([patt1, patt2])) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + self.assertEqual(len(test_obj.dof_names), 4) + for name in test_obj.dof_names: + self.assertTrue(comb_patt.match(name)) + + test_obj.fix('val') + self.assertEqual(len(test_obj.dof_names), 3) + for name in test_obj.dof_names: + self.assertTrue(comb_patt.match(name)) + exc_patt = re.compile("OptClassWithParents\d+:val") + for name in test_obj.dof_names: + self.assertFalse(exc_patt.match(name)) + + adder.fix('x1') + self.assertEqual(len(test_obj.dof_names), 2) + for name in test_obj.dof_names: + self.assertTrue(comb_patt.match(name)) + exc_patt = re.compile("Adder\d+:x1") + for name in test_obj.dof_names: + self.assertFalse(exc_patt.match(name)) + + test_obj2 = OptClassWith2LevelParents(10, 20) + patt1 = "Adder\d+:x\d+" + patt2 = "OptClassWithParents\d+:val" + patt3 = "OptClassWith2LevelParents\d+:v\d" + comb_patt = re.compile("|".join([patt1, patt2, patt3])) + self.assertEqual(len(test_obj2.dof_names), 10) + for name in test_obj2.dof_names: + self.assertTrue(comb_patt.match(name)) + + test_obj2.fix(0) + self.assertEqual(len(test_obj2.dof_names), 9) + for name in test_obj2.dof_names: + self.assertTrue(comb_patt.match(name)) + exc_patt = re.compile("OptClassWith2LevelParents\d+:v1") + for name in test_obj.dof_names: + self.assertFalse(exc_patt.match(name)) + + def test_full_dof_names(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3]) + self.assertEqual(len(iden.full_dof_names), 1) + self.assertEqual(len(adder.full_dof_names), 3) + + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + self.assertEqual(len(test_obj.full_dof_names), 5) + test_obj.fix('val') + self.assertEqual(len(test_obj.full_dof_names), 5) + adder.fix('x1') + self.assertEqual(len(test_obj.full_dof_names), 5) + + test_obj2 = OptClassWith2LevelParents(10, 20) + self.assertEqual(len(test_obj2.full_dof_names), 10) + test_obj2.fix(0) + self.assertEqual(len(test_obj2.full_dof_names), 10) + patt1 = "Adder\d+:x\d+" + patt2 = "OptClassWithParents\d+:val" + patt3 = "OptClassWith2LevelParents\d+:v\d" + comb_patt = re.compile("|".join([patt1, patt2, patt3])) + for name in test_obj2.full_dof_names: + self.assertTrue(comb_patt.match(name)) + + def test_local_dof_names(self): + # Test in DOFs class is sufficient + pass + + def test_local_full_dof_names(self): + # Test in DOFs class is sufficient + pass + + def test_is_fixed(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + self.assertTrue(adder.is_fixed(0)) + self.assertTrue(adder.is_fixed('x')) + self.assertFalse(adder.is_fixed(1)) + self.assertFalse(adder.is_fixed('y')) + self.assertTrue(iden.is_fixed(0)) + self.assertTrue(iden.is_fixed('x0')) + + def test_is_free(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + self.assertFalse(adder.is_free(0)) + self.assertFalse(adder.is_free('x')) + self.assertTrue(adder.is_free(1)) + self.assertTrue(adder.is_free('y')) + self.assertFalse(iden.is_free(0)) + self.assertFalse(iden.is_free('x0')) + + def test_fix(self): + self.iden.fix(0) + self.adder.fix('x') + self.rosen.fix('y') + + self.assertEqual(self.iden.dof_size, 0) + self.assertEqual(self.adder.dof_size, 2) + self.assertEqual(self.rosen.dof_size, 1) + + def test_fix_all(self): + self.iden.fix_all() + self.adder.fix_all() + self.rosen.fix_all() + + self.assertEqual(self.iden.dof_size, 0) + self.assertEqual(self.adder.dof_size, 0) + self.assertEqual(self.rosen.dof_size, 0) + + def test_fix_local(self): + self.iden.fix_local() + self.adder.fix_local() + self.rosen.fix_local() + + self.assertEqual(self.iden.dof_size, 0) + self.assertEqual(self.adder.dof_size, 0) + self.assertEqual(self.rosen.dof_size, 0) + + def test_fix_full(self): + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=False) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 1) + + iden.fix_full() + adder.fix_full() + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + self.assertEqual(iden.dof_size, 0) + self.assertEqual(adder.dof_size, 0) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=False) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + + test_x = test_obj.x + self.assertEqual(len(test_x), 4) + test_obj.fix_full() + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 5, 6, 25]) + + self.assertEqual(test_obj.dof_size, 0) + self.assertEqual(adder.dof_size, 0) + self.assertEqual(iden.dof_size, 0) + + def test_unfix(self): + pass + + def test_unfix_all(self): + # Test with leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertEqual(adder.dof_size, 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 0) + + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + iden.unfix_all() + adder.unfix_all() + iden.x = [10] + adder.x = [4, 5, 6] + self.assertEqual(iden.dof_size, 1) + self.assertEqual(adder.dof_size, 3) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 4, 5, 6, 25]) + + adder.unfix_all() + test_obj.x = np.array([4, 5, 6, 25]) + self.assertAlmostEqual(adder.local_full_x[0], 4) + self.assertAlmostEqual(adder.local_full_x[1], 5) + self.assertAlmostEqual(adder.local_full_x[2], 6) + self.assertAlmostEqual(test_obj.local_full_x[0], 25) + + iden.unfix_all() + test_obj.x = np.array([1, 2, 3, 1, 10]) + + self.assertAlmostEqual(adder.local_full_x[0], 1) + self.assertAlmostEqual(adder.local_full_x[1], 2) + self.assertAlmostEqual(adder.local_full_x[2], 3) + self.assertAlmostEqual(iden.local_full_x[0], 1) + self.assertAlmostEqual(test_obj.local_full_x[0], 10) + + def test_unfix_local(self): + # Test with leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertEqual(adder.dof_size, 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 0) + + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + iden.unfix_local() + adder.unfix_local() + iden.x = [10] + adder.x = [4, 5, 6] + self.assertEqual(iden.dof_size, 1) + self.assertEqual(adder.dof_size, 3) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 4, 5, 6, 25]) + + adder.unfix_local() + test_obj.x = np.array([4, 5, 6, 25]) + self.assertAlmostEqual(adder.local_full_x[0], 4) + self.assertAlmostEqual(adder.local_full_x[1], 5) + self.assertAlmostEqual(adder.local_full_x[2], 6) + self.assertAlmostEqual(test_obj.local_full_x[0], 25) + + iden.unfix_local() + test_obj.x = np.array([1, 2, 3, 1, 10]) + + self.assertAlmostEqual(adder.local_full_x[0], 1) + self.assertAlmostEqual(adder.local_full_x[1], 2) + self.assertAlmostEqual(adder.local_full_x[2], 3) + self.assertAlmostEqual(iden.local_full_x[0], 1) + self.assertAlmostEqual(test_obj.local_full_x[0], 10) + + def test_unfix_full(self): + # Test with leaf nodes + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + adder_x = adder.x + iden_x = iden.x + self.assertEqual(len(adder_x), 2) + self.assertEqual(adder.dof_size, 2) + self.assertAlmostEqual(adder_x[0], 2) + self.assertAlmostEqual(adder_x[1], 3) + self.assertEqual(len(iden_x), 0) + + with self.assertRaises(ValueError): + iden.x = [10] + with self.assertRaises(ValueError): + adder.x = [4, 5, 6] + + iden.unfix_full() + adder.unfix_full() + iden.x = [10] + adder.x = [4, 5, 6] + self.assertEqual(iden.dof_size, 1) + self.assertEqual(adder.dof_size, 3) + + # Check with Optimizable objects containing parents + adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], + dof_fixed=[True, False, False]) + iden = Identity(x=10, dof_fixed=True) + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + test_obj.fix('val') + + with self.assertRaises(ValueError): + test_obj.x = np.array([20, 4, 5, 6, 25]) + + test_obj.unfix_full() + test_obj.x = np.array([4, 5, 6, 20, 25]) + print(iden.x) + self.assertAlmostEqual(adder.local_full_x[0], 4) + self.assertAlmostEqual(adder.local_full_x[1], 5) + self.assertAlmostEqual(adder.local_full_x[2], 6) + self.assertAlmostEqual(iden.local_full_x[0], 20) + self.assertAlmostEqual(test_obj.local_full_x[0], 25) + + def test_get_ancestors(self): + iden = Identity(x=10, dof_fixed=True) + adder = Adder(n=3, x0=[1, 2, 3]) + self.assertEqual(len(iden._get_ancestors()), 0) + self.assertEqual(len(adder._get_ancestors()), 0) + + test_obj = OptClassWithParents(10, depends_on=[iden, adder]) + ancestors = test_obj._get_ancestors() + self.assertEqual(len(ancestors), 2) + self.assertIn(iden, ancestors) + self.assertIn(adder, ancestors) + + test_obj2 = OptClassWith2LevelParents(10, 20) + ancestors = test_obj2._get_ancestors() + self.assertEqual(len(ancestors), 4) + + def test_plot(self): + """ + Verify that a DAG can be plotted. + The ``show`` argument is set to ``False`` so the + tests do not require human intervention to close plot windows. + However, if you do want to actually display the figure, you + can change ``show`` to ``True`` in the first line of this + function. + """ + show = False + + try: + import matplotlib + except ImportError: + return + try: + import networkx + except ImportError: + return + try: + import pygraphviz + except ImportError: + return + + # optimizable with no parents + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + G1, pos1 = adder.plot_graph(show=show) + + # optimizable with two parents + opt1 = OptClassWithParentsReturnFns(10) + G2, pos2 = opt1.plot_graph(show=show) + + # optimizable with many parents + opt2 = OptClassWith2LevelParents(10, 20) + G3, pos3 = opt2.plot_graph(show=show) + + +class OptClassExternalDofs(Optimizable): + def __init__(self): + self.vals = [1, 2] + Optimizable.__init__(self, external_dof_setter=OptClassExternalDofs.set_dofs, + x0=self.get_dofs()) + + def get_dofs(self): + return self.vals + + def set_dofs(self, x): + self.vals = x + + def recompute_bell(self, parent=None): + pass + + +class OptimizableTestsExternalDofs(unittest.TestCase): + def setUp(self) -> None: + self.opt = OptClassExternalDofs() + + def tearDown(self) -> None: + self.opt = None + + def test_get_dofs(self): + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([1, 2])).all()) + + def test_set_dofs(self): + self.opt.set_dofs([3, 4]) + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([3, 4])).all()) + + def test_set_x(self): + self.opt.x = [3, 4] + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([3, 4])).all()) + + def test_set_local_x(self): + self.opt.local_x = [3, 4] + vals = self.opt.get_dofs() + self.assertTrue((vals == np.array([3, 4])).all()) + + +class TestMakeOptimizable(unittest.TestCase): + def setUp(self) -> None: + def arb_fun_dofs_noopts(a, b, c): + return a ** 2 + 2 * b ** 2 + 3 * c ** 2 - 10 + + def arb_fun_nodofs_opts(adder): + return adder.sum()**2 - 10 + + def arb_fun_dofs_opts(a, b, adder): + return a**2 + b**2 + adder.sum()**2 - 10 + + self.arb_fun_dofs_noopts = arb_fun_dofs_noopts + self.arb_fun_nodofs_opts = arb_fun_nodofs_opts + self.arb_fun_dofs_opts = arb_fun_dofs_opts + + def test_arb_func_dofs_noopts(self): + x, y, z = 1, 2, 3 + opt = make_optimizable(self.arb_fun_dofs_noopts, + x, y, z, + dof_indicators=["dof", "dof", "dof"]) + self.assertAlmostEqual(opt.J(), 26.0) + opt.x = np.array([1.2, 0.8, 0.5]) + self.assertAlmostEqual(opt.J(), -6.53) + + def test_arb_func_nodofs_opts(self): + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + opt = make_optimizable(self.arb_fun_nodofs_opts, + adder, + dof_indicators=["opt"]) + self.assertAlmostEqual(opt.J(), 26.0) + x = opt.x # Length of x is 3 + opt.x = x / 2.0 + self.assertAlmostEqual(opt.J(), -1.0) + + # When dof_indicators argument is not passed only opts and non_dofs are + # considered. Below a and b are treated as non-dofs and the adder should + # be recognized as optimizable object + a = 2.0 + b = 3.0 + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + opt = make_optimizable(self.arb_fun_dofs_opts, a, b, adder) + self.assertAlmostEqual(opt.J(), 39.0) + x = opt.x # Length of x is 3 + self.assertEqual(len(x), 3) + opt.x = x / 2.0 + self.assertAlmostEqual(opt.J(), 12.0) + + def test_arb_func_dofs_opts(self): + # Below a is passed as dof b is passed as non-dof and the adder is + # passed as optimizable object + a = 2.0 + b = 3.0 + adder = Adder(n=3, x0=[1.0, 2.0, 3.0]) + opt = make_optimizable(self.arb_fun_dofs_opts, + a, b, adder, + dof_indicators=['dof', 'non-dof', 'opt']) + self.assertAlmostEqual(opt.J(), 39.0) + x = opt.x # Length of x is 4 + self.assertEqual(len(x), 4) + opt.x = x / 2.0 + self.assertAlmostEqual(opt.J(), 9.0) + + +class TestOptimizableSerialize(unittest.TestCase): + """ + Test the serialization of the Optimizable class based on as_dict and + from_dict methods using various sub-classes + """ + def test_serialize(self): + adder = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder, cls=MontyEncoder) + print(s) + + def test_deserialize(self): + adder_orig = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + fixed=[True, False, True]) + s = json.dumps(adder_orig, cls=MontyEncoder) + adder = json.loads(s, cls=MontyDecoder) + print(adder.name) + print(adder.n) + print(adder.full_x) + print(adder.dofs_free_status) + print(adder.local_full_dof_names) + + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/objectives/test_graph_least_squares.py b/tests/objectives/test_graph_least_squares.py new file mode 100644 index 000000000..62e46f77c --- /dev/null +++ b/tests/objectives/test_graph_least_squares.py @@ -0,0 +1,82 @@ +import unittest +import logging +import numpy as np +from simsopt.objectives.graph_functions import Identity, Rosenbrock +#from simsopt.core.optimizable import Target +from simsopt.objectives.graph_least_squares import LeastSquaresProblem + +#logging.basicConfig(level=logging.DEBUG) + + +class LeastSquaresProblemTests(unittest.TestCase): + def test_single_value_opt_in(self): + iden = Identity() + lst = LeastSquaresProblem.from_sigma(3, 0.1, depends_on=iden) + + iden.x = [17] + correct_value = ((17 - 3) / 0.1) # ** 2 + self.assertAlmostEqual(np.abs(lst.residuals()[0]), + correct_value, + places=11) + + iden.x = [0] + term1 = LeastSquaresProblem.from_sigma(3, 2, depends_on=iden) + self.assertAlmostEqual(np.abs(term1.residuals()[0]), 1.5) + + term1.x = [10] + self.assertAlmostEqual(np.abs(term1.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(term1.residuals(x=[0])), 1.5) + self.assertAlmostEqual(np.abs(term1.residuals(x=[5])), 1) + + def test_exceptions(self): + """ + Test that exceptions are thrown when invalid inputs are + provided. + """ + iden = Identity() + + # sigma cannot be zero + with self.assertRaises(ValueError): + lst = LeastSquaresProblem.from_sigma(3, 0, depends_on=iden) + + # Weight cannot be negative + with self.assertRaises(ValueError): + lst = LeastSquaresProblem(3, -1.0, depends_on=iden) + + def test_multiple_funcs_single_input(self): + iden1 = Identity(x=10) + iden2 = Identity() + # Objective function + # f(x,y) = ((x - 3) / 2) ** 2 + ((y + 4) / 5) ** 2 + lsp = LeastSquaresProblem.from_sigma([3, -4], [2, 5], depends_on=[iden1, iden2]) + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.8) + lsp.x = [5, -7] + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 1.0) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.6) + self.assertAlmostEqual(np.abs(lsp.residuals([10, 0])[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals([10, 0])[1]), 0.8) + self.assertAlmostEqual(np.abs(lsp.residuals([5, -7])[0]), 1.0) + self.assertAlmostEqual(np.abs(lsp.residuals([5, -7])[1]), 0.6) + + def test_parent_dof_transitive_behavior(self): + iden1 = Identity() + iden2 = Identity() + lsp = LeastSquaresProblem.from_sigma([3, -4], [2, 5], depends_on=[iden1, iden2]) + iden1.x = [10] + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.8) + + def test_least_squares_combination(self): + iden1 = Identity() + iden2 = Identity() + term1 = LeastSquaresProblem.from_sigma(3, 2, depends_on=[iden1]) + term2 = LeastSquaresProblem.from_sigma(-4, 5, depends_on=[iden2]) + lsp = term1 + term2 + iden1.x = [10] + self.assertAlmostEqual(np.abs(lsp.residuals()[0]), 3.5) + self.assertAlmostEqual(np.abs(lsp.residuals()[1]), 0.8) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/solve/test_graph_least_squares.py b/tests/solve/test_graph_least_squares.py new file mode 100755 index 000000000..b7ce355da --- /dev/null +++ b/tests/solve/test_graph_least_squares.py @@ -0,0 +1,79 @@ +import unittest + +import numpy as np +try: + from mpi4py import MPI +except ImportError: + MPI = None + +from simsopt.objectives.graph_functions import Identity, Rosenbrock +from simsopt.objectives.graph_least_squares import LeastSquaresProblem +from simsopt.solve.graph_serial import least_squares_serial_solve, serial_solve +if MPI is not None: + from simsopt.util.mpi import MpiPartition + from simsopt.solve.graph_mpi import least_squares_mpi_solve + + +def mpi_solve_1group(prob, **kwargs): + least_squares_mpi_solve(prob, MpiPartition(ngroups=1), **kwargs) + + +solvers = [least_squares_serial_solve] +if MPI is not None: + solvers.append(mpi_solve_1group) + + +class LeastSquaresProblemTests(unittest.TestCase): + def test_solve_quadratic(self): + """ + Minimize f(x,y,z) = 1 * (x - 1) ^ 2 + 2 * (y - 2) ^ 2 + 3 * (z - 3) ^ 2. + The optimum is at (x,y,z)=(1,2,3), and f=0 at this point. + """ + for solver in solvers: + iden1 = Identity() + iden2 = Identity() + iden3 = Identity() + term1 = (iden1.f, 1, 1) + term2 = (iden2.f, 2, 2) + term3 = (iden3.f, 3, 3) + prob = LeastSquaresProblem.from_tuples([term1, term2, term3]) + solver(prob) + self.assertAlmostEqual(prob.objective(), 0) + self.assertTrue(np.allclose(iden1.x, [1])) + self.assertTrue(np.allclose(iden2.x, [2])) + self.assertTrue(np.allclose(iden3.x, [3])) + + def test_solve_quadratic_fixed(self): + """ + Same as test_solve_quadratic, except with different weights and x + and z are fixed, so only y is optimized. + """ + for solver in solvers: + iden1 = Identity(4, dof_name='x1', dof_fixed=True) + iden2 = Identity(5, dof_name='x2') + iden3 = Identity(6, dof_name='x3', dof_fixed=True) + term1 = (iden1.f, 1, 1) + term2 = (iden2.f, 2, 1 / 4.) + term3 = (iden3.f, 3, 1 / 9.) + prob = LeastSquaresProblem.from_tuples([term1, term2, term3]) + solver(prob) + self.assertAlmostEqual(prob.objective(), 10) + self.assertTrue(np.allclose(iden1.x, [4])) + self.assertTrue(np.allclose(iden2.x, [2])) + self.assertTrue(np.allclose(iden3.x, [6])) + + def test_solve_rosenbrock(self): + """ + Minimize the Rosenbrock function using two separate least-squares + terms. + """ + for solver in solvers: + #for grad in [True, False]: + r = Rosenbrock() + prob = LeastSquaresProblem(0, 1, depends_on=r) + solver(prob) # , grad=grad) + self.assertAlmostEqual(prob.objective(), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/solve/test_graph_mpi.py b/tests/solve/test_graph_mpi.py new file mode 100755 index 000000000..d7357e31b --- /dev/null +++ b/tests/solve/test_graph_mpi.py @@ -0,0 +1,137 @@ +import logging +import unittest + +import numpy as np +try: + from mpi4py import MPI +except: + MPI = None + +from simsopt._core.graph_optimizable import Optimizable +from simsopt.objectives.graph_functions import Beale +from simsopt.objectives.graph_least_squares import LeastSquaresProblem +if MPI is not None: + from simsopt.util.graph_mpi import MpiPartition + from simsopt.solve.graph_mpi import least_squares_mpi_solve + +#logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + + +class TestFunction1(Optimizable): + def __init__(self): + x = np.array([1.2, 0.9, -0.4]) + fixed = np.full(3, False) + super().__init__(x0=x, fixed=fixed) + + def J(self): + return np.exp(self.full_x[0] ** 2 - np.exp(self.full_x[1]) \ + + np.sin(self.full_x[2])) + + return_fn_map = {'J': J} + + +class TestFunction2(Optimizable): + def __init__(self): + x = np.array([1.2, 0.9]) + fixed = np.full(2, False) + super().__init__(x0=x, fixed=fixed) + + def f0(self): + return np.exp(0 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + def f1(self): + return np.exp(1 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + def f2(self): + return np.exp(2 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + def f3(self): + return np.exp(3 + self.full_x[0] ** 2 - np.exp(self.full_x[1])) + + return_fn_map = {'f0': f0, 'f1': f1, 'f2': f2, 'f3': f3} + + +class TestFunction3(Optimizable): + """ + This is the Rosenbrock function again, but with some unnecessary + MPI communication added in order to test optimization with MPI. + """ + + def __init__(self, comm, x=[0, 0]): + self.comm = comm + self.dummy = 42 + self.f0_call_cnt = 0 + self.f1_call_cnt = 0 + logger.debug("inside test function 3 init") + super().__init__(x0=x) + + def f0(self): + # Do some random MPI stuff just for the sake of testing. + self.comm.barrier() + self.comm.bcast(self.local_full_x) + self.f0_call_cnt += 1 + print(f"x is {self.local_full_x}") + print(f"TestFunction3.f0 called {self.f0_call_cnt} times") + return self.local_full_x[0] - 1 + + def f1(self): + # Do some random MPI stuff just for the sake of testing. + self.comm.bcast(self.dummy) + self.comm.barrier() + self.f1_call_cnt += 1 + print(f"x is {self.local_full_x}") + print(f"TestFunction3.f1 called {self.f1_call_cnt} times") + return self.local_full_x[0] ** 2 - self.local_full_x[1] + + return_fn_map = {'f0': f0, 'f1': f1} + + +@unittest.skipIf(MPI is None, "Requires mpi4py") +class MPISolveTests(unittest.TestCase): + + def test_parallel_optimization_without_grad(self): + """ + Test a full least-squares optimization. + """ + for ngroups in range(1, 4): + mpi = MpiPartition(ngroups=ngroups) + o = TestFunction3(mpi.comm_groups) + term1 = (o.f0, 0, 1) + term2 = (o.f1, 0, 1) + prob = LeastSquaresProblem.from_tuples([term1, term2]) + least_squares_mpi_solve(prob, mpi, grad=False) + self.assertAlmostEqual(prob.x[0], 1) + self.assertAlmostEqual(prob.x[1], 1) + + def test_parallel_optimization_with_grad(self): + """ + Test a full least-squares optimization. + """ + for ngroups in range(1, 4): + for abs_step in [0, 1.0e-7]: + # Only try rel_step=0 if abs_step is positive: + rel_steps = [0, 1.0e-7] + if abs_step == 0: + rel_steps = [1.0e-7] + + for rel_step in rel_steps: + for diff_method in ["forward", "centered"]: + logger.debug(f'ngroups={ngroups} abs_step={abs_step} ' \ + f'rel_step={rel_step} diff_method={diff_method}') + mpi = MpiPartition(ngroups=ngroups) + o = TestFunction3(mpi.comm_groups) + term1 = (o.f0, 0, 1) + term2 = (o.f1, 0, 1) + prob = LeastSquaresProblem.from_tuples([term1, term2]) + # Set initial condition different from 0, + # because otherwise abs_step=0 causes step + # size to be 0. + prob.x = [-0.1, 0.2] + least_squares_mpi_solve(prob, mpi, grad=True, + diff_method=diff_method, + abs_step=abs_step, + rel_step=rel_step) + self.assertAlmostEqual(prob.x[0], 1) + self.assertAlmostEqual(prob.x[1], 1) + From fdcdeacf4ff3a3e7b8a69fc0fa13d03da85cdb62 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 20:37:16 -0400 Subject: [PATCH 15/95] Modules with graph prefixes added which import everything from other modules --- src/simsopt/_core/graph_optimizable.py | 7 +++++++ src/simsopt/objectives/graph_functions.py | 7 +++++++ src/simsopt/objectives/graph_least_squares.py | 7 +++++++ src/simsopt/solve/graph_mpi.py | 7 +++++++ src/simsopt/solve/graph_serial.py | 7 +++++++ 5 files changed, 35 insertions(+) create mode 100644 src/simsopt/_core/graph_optimizable.py create mode 100644 src/simsopt/objectives/graph_functions.py create mode 100644 src/simsopt/objectives/graph_least_squares.py create mode 100644 src/simsopt/solve/graph_mpi.py create mode 100644 src/simsopt/solve/graph_serial.py diff --git a/src/simsopt/_core/graph_optimizable.py b/src/simsopt/_core/graph_optimizable.py new file mode 100644 index 000000000..f555d6133 --- /dev/null +++ b/src/simsopt/_core/graph_optimizable.py @@ -0,0 +1,7 @@ +from .optimizable import * + +import warnings + +warnings.warn("Import of graph_optimizable module deprecated." + " Instead use optimizable module", + DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/src/simsopt/objectives/graph_functions.py b/src/simsopt/objectives/graph_functions.py new file mode 100644 index 000000000..2d1c06a58 --- /dev/null +++ b/src/simsopt/objectives/graph_functions.py @@ -0,0 +1,7 @@ +from .functions import * + +import warnings + +warnings.warn("Import of graph_functions module deprecated." + " Instead use functions module", + DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/src/simsopt/objectives/graph_least_squares.py b/src/simsopt/objectives/graph_least_squares.py new file mode 100644 index 000000000..d5c63bb75 --- /dev/null +++ b/src/simsopt/objectives/graph_least_squares.py @@ -0,0 +1,7 @@ +from .least_squares import * + +import warnings + +warnings.warn("Import of graph_least_squares module deprecated." + " Instead use least_squares module", + DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/src/simsopt/solve/graph_mpi.py b/src/simsopt/solve/graph_mpi.py new file mode 100644 index 000000000..753853938 --- /dev/null +++ b/src/simsopt/solve/graph_mpi.py @@ -0,0 +1,7 @@ +from .mpi import * + +import warnings + +warnings.warn("Import of graph_mpi module deprecated." + " Instead use mpi module", + DeprecationWarning, stacklevel=2) \ No newline at end of file diff --git a/src/simsopt/solve/graph_serial.py b/src/simsopt/solve/graph_serial.py new file mode 100644 index 000000000..798add12c --- /dev/null +++ b/src/simsopt/solve/graph_serial.py @@ -0,0 +1,7 @@ +from .serial import * + +import warnings + +warnings.warn("Import of graph_serial module deprecated." + " Instead use serial module", + DeprecationWarning, stacklevel=2) \ No newline at end of file From 32b849cc7222ea43489d52a5e8c5bf42a18f794f Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 20:39:29 -0400 Subject: [PATCH 16/95] Linter fixes --- src/simsopt/objectives/functions.py | 2 +- tests/core/test_graph_optimizable.py | 9 +++++---- tests/core/test_optimizable.py | 9 +++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index 2c54fdb62..08f0184d9 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -197,7 +197,7 @@ def dterms(self): """ return np.array([[1.0, 0.0], [2 * self.local_full_x['x'] / self._sqrtb, -1.0 / self._sqrtb]]) - + def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_graph_optimizable.py index a55b8bba1..175ff82a0 100755 --- a/tests/core/test_graph_optimizable.py +++ b/tests/core/test_graph_optimizable.py @@ -43,6 +43,7 @@ def from_dict(cls, d): d.get("dof_names", None), d.get("dof_fixed", None)) + class OptClassWithParents(Optimizable): def __init__(self, val, depends_on=None): if depends_on is None: @@ -1246,14 +1247,15 @@ class TestOptimizableSerialize(unittest.TestCase): Test the serialization of the Optimizable class based on as_dict and from_dict methods using various sub-classes """ + def test_serialize(self): - adder = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], - fixed=[True, False, True]) + adder = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], + fixed=[True, False, True]) s = json.dumps(adder, cls=MontyEncoder) print(s) def test_deserialize(self): - adder_orig = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + adder_orig = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], fixed=[True, False, True]) s = json.dumps(adder_orig, cls=MontyEncoder) adder = json.loads(s, cls=MontyDecoder) @@ -1264,6 +1266,5 @@ def test_deserialize(self): print(adder.local_full_dof_names) - if __name__ == "__main__": unittest.main() diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 1de4a61a0..1285e393a 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -43,6 +43,7 @@ def from_dict(cls, d): d.get("dof_names", None), d.get("dof_fixed", None)) + class OptClassWithParents(Optimizable): def __init__(self, val, depends_on=None): if depends_on is None: @@ -1246,14 +1247,15 @@ class TestOptimizableSerialize(unittest.TestCase): Test the serialization of the Optimizable class based on as_dict and from_dict methods using various sub-classes """ + def test_serialize(self): - adder = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], - fixed=[True, False, True]) + adder = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], + fixed=[True, False, True]) s = json.dumps(adder, cls=MontyEncoder) print(s) def test_deserialize(self): - adder_orig = FAdder(n=3, x0=[1,2,3], names=["x", "y", "z"], + adder_orig = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], fixed=[True, False, True]) s = json.dumps(adder_orig, cls=MontyEncoder) adder = json.loads(s, cls=MontyDecoder) @@ -1264,6 +1266,5 @@ def test_deserialize(self): print(adder.local_full_dof_names) - if __name__ == "__main__": unittest.main() From 2a70d7df2c5ee93ca79c4be7d4354da0b18731a0 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 20:46:25 -0400 Subject: [PATCH 17/95] Add missing import warnings --- src/simsopt/_core/optimizable.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 97c8a46cf..2971b2837 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -20,6 +20,7 @@ from functools import lru_cache import logging import json +import warnings import numpy as np from monty.json import MSONable, MontyEncoder, MontyDecoder @@ -168,7 +169,8 @@ def free_status(self) -> BoolArray: def get(self, key: Key) -> Real: """ - Get the value of specified DOF. Even fixed DOFs can + Get the value of specifboundary ci COPYING docs MANIFEST.in.bak requirements.txt run_tests_mpi src tox.ini +ied DOF. Even fixed DOFs can be obtained with this method Args: @@ -1196,8 +1198,8 @@ def fix_all(self) -> None: Set the 'fixed' attribute for all degrees of freedom associated with the current Optimizable object. """ - warn("fix_all method is deprecated in favor of fix_local", - DeprecationWarning, stacklevel=2) + warnings.warn("fix_all method is deprecated in favor of fix_local", + DeprecationWarning, stacklevel=2) self._dofs.fix_all() self._update_free_dof_size_indices() @@ -1223,8 +1225,8 @@ def unfix_all(self) -> None: Unset the 'fixed' attribute for all degrees of freedom associated with the current Optimizable object. """ - warn("unfix_all method is deprecated in favor of unfix_local", - DeprecationWarning, stacklevel=2) + warnings.warn("unfix_all method is deprecated in favor of unfix_local", + DeprecationWarning, stacklevel=2) self._dofs.unfix_all() self._update_free_dof_size_indices() From 13e8c7ec3671f0111581e429d22ae74eeb06e03a Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 21:01:54 -0400 Subject: [PATCH 18/95] Include thirdparty folders for source dist --- MANIFEST.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 8a5d34b21..b93e9de39 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -recursive-exclude thirdparty .git .gitignore .github/* .appveyor.yml *readthedocs.yml .travis.yml azure-pipelines.yml recursive-exclude .git recursive-exclude .github * .appveyor.yml *readthedocs.yml .travis.yml azure-pipelines.yml recursive-exclude docs * @@ -7,5 +6,7 @@ recursive-exclude ci * recursive-exclude conda.recipe * recursive-exclude examples wout*.nc mercier* jxbout* spec*.sp spec*.sp.* .gitignore recursive-exclude tests .gitignore +recursive-include thirdparty * +recursive-exclude thirdparty .git .gitignore .github .appveyor.yml *readthedocs.yml .travis.yml azure-pipelines.yml exclude .gitignore *readthedocs.yaml MANIFEST* run_autopep .gitmodules From 5a9a932b79eba38ea8949e66016030fb72f93d88 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 19 Apr 2022 22:35:53 -0400 Subject: [PATCH 19/95] Bug fix in import --- tests/solve/test_graph_mpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/solve/test_graph_mpi.py b/tests/solve/test_graph_mpi.py index d7357e31b..1d74d0b7d 100755 --- a/tests/solve/test_graph_mpi.py +++ b/tests/solve/test_graph_mpi.py @@ -11,7 +11,7 @@ from simsopt.objectives.graph_functions import Beale from simsopt.objectives.graph_least_squares import LeastSquaresProblem if MPI is not None: - from simsopt.util.graph_mpi import MpiPartition + from simsopt.util.mpi import MpiPartition from simsopt.solve.graph_mpi import least_squares_mpi_solve #logging.basicConfig(level=logging.DEBUG) From 91b4aca4057aa1668851306b33e1a68d275fb44e Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 06:59:44 -0400 Subject: [PATCH 20/95] Fix spurious copy --- src/simsopt/_core/optimizable.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 2971b2837..476a995e8 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -169,8 +169,7 @@ def free_status(self) -> BoolArray: def get(self, key: Key) -> Real: """ - Get the value of specifboundary ci COPYING docs MANIFEST.in.bak requirements.txt run_tests_mpi src tox.ini -ied DOF. Even fixed DOFs can + Get the value of specified DOF. Even fixed DOFs can be obtained with this method Args: From 74474cb89472412864ccf09581d17a050c1dcdd4 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:08:00 -0400 Subject: [PATCH 21/95] Two level serialization for optimizable --- tests/core/test_optimizable.py | 50 ++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 1285e393a..931ec5471 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -7,7 +7,8 @@ from monty.serialization import loadfn, dumpfn from simsopt._core.optimizable import Optimizable, make_optimizable -from simsopt.objectives.functions import Identity, Rosenbrock, TestObject2 +from simsopt.objectives.functions import Identity, Rosenbrock, TestObject1, \ + TestObject2 from simsopt.objectives.functions import Adder as FAdder @@ -1247,23 +1248,44 @@ class TestOptimizableSerialize(unittest.TestCase): Test the serialization of the Optimizable class based on as_dict and from_dict methods using various sub-classes """ - - def test_serialize(self): - adder = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], - fixed=[True, False, True]) - s = json.dumps(adder, cls=MontyEncoder) - print(s) - - def test_deserialize(self): + def test_adder_serialize(self): adder_orig = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], fixed=[True, False, True]) s = json.dumps(adder_orig, cls=MontyEncoder) adder = json.loads(s, cls=MontyDecoder) - print(adder.name) - print(adder.n) - print(adder.full_x) - print(adder.dofs_free_status) - print(adder.local_full_dof_names) + self.assertEqual(adder.n, adder_orig.n) + self.assertTrue(np.allclose(adder.full_x, adder_orig.full_x)) + self.assertTrue(np.array_equal(adder.dofs_free_status, + adder_orig.dofs_free_status)) + self.assertEqual(adder.local_full_dof_names, + adder_orig.local_full_dof_names) + + def test_identity_serialize(self): + iden_orig = Identity(x=10.0, dof_name="x", dof_fixed=False) + s = json.dumps(iden_orig, cls=MontyEncoder) + iden = json.loads(s, cls=MontyDecoder) + self.assertAlmostEqual(iden.x[0], iden_orig.x[0]) + self.assertEqual(iden.local_full_dof_names[0], + iden_orig.local_full_dof_names[0]) + self.assertEqual(iden.dofs_free_status[0], + iden_orig.dofs_free_status[0]) + + def test_rosenbrock_serialize(self): + r_orig = Rosenbrock(b=100.0, x=10.0, y=20.0) + s = json.dumps(r_orig, cls=MontyEncoder) + r = json.loads(s, cls=MontyDecoder) + self.assertAlmostEqual(r.term1, r_orig.term1) + self.assertAlmostEqual(r.term2, r_orig.term2) + + def test_twolevel_serialize(self): + adder1 = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], + fixed=[True, False, True]) + adder2 = FAdder(n=2, x0=[10, 11], names=["a", "b"], fixed=[True, False]) + test_opt_orig = TestObject1(100.0, depends_on=[adder1, adder2]) + s = json.dumps(test_opt_orig, cls=MontyEncoder) + test_opt = json.loads(s, cls=MontyDecoder) + self.assertAlmostEqual(test_opt.f(), test_opt_orig.f()) + if __name__ == "__main__": From 6954b14d2354c566642737ec5ef4d82995e5b631 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:08:39 -0400 Subject: [PATCH 22/95] Add as_dict to two level opt class and bug fixes in functions module --- src/simsopt/objectives/functions.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index 08f0184d9..c9352cb0f 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -108,7 +108,6 @@ def df(self): def as_dict(self) -> dict: d = super().as_dict() - print('dict is ', d) d["n"] = self.n return d @@ -205,6 +204,7 @@ def as_dict(self) -> dict: d["b"] = self._sqrtb * self._sqrtb d["x"] = self.get("x") d["y"] = self.get("y") + return d @classmethod def from_dict(cls, d): @@ -224,16 +224,18 @@ class TestObject1(Optimizable): added as parents """ - def __init__(self, val: Real, opts: Sequence[Optimizable] = None): - if opts is None: - opts = [Adder(3), Adder(2)] - super().__init__(x0=[val], names=['val'], funcs_in=opts) + def __init__(self, val: Real, depends_on: Sequence[Optimizable] = None, + **kwargs): + if depends_on is None: + depends_on = [Adder(3), Adder(2)] + super().__init__(x0=[val], names=['val'], depends_on=depends_on, + **kwargs) def f(self): """ Implements an objective function """ - return (self._dofs.full_x[0] + 2 * self.parents[0]()) / \ + return (self.local_full_x[0] + 2 * self.parents[0]()) / \ (10.0 + self.parents[1]()) return_fn_map = {'f': f} @@ -250,6 +252,16 @@ def dJ(self): np.full(self.parents[0].n, 2.0 / (10.0 + a2)), np.full(self.parents[1].n, -(v + 2 * a1) / ((10.0 + a2) ** 2)))) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["val"] = self.local_full_x[0] + d["depends_on"] = [] + for opt in self.parents: + d["depends_on"].append(opt.as_dict()) + return d + class TestObject2(Optimizable): """ From e0e6ec9dc1ba3f998cc7f71d3db1c98eef9360e6 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:17:04 -0400 Subject: [PATCH 23/95] Fixed implementation of from_dict for multi-level serialization --- src/simsopt/_core/optimizable.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 2971b2837..c255afebe 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -23,7 +23,7 @@ import warnings import numpy as np -from monty.json import MSONable, MontyEncoder, MontyDecoder +from monty.json import MSONable, MontyDecoder from ..util.dev import SimsoptRequires from ..util.types import RealArray, StrArray, BoolArray, Key @@ -592,7 +592,8 @@ def func(*args, **kwargs): self._update_full_dof_size_indices() # Inform the object that it doesn't have valid cache self._set_new_x() - super().__init__(**kwargs) + log.debug(f"Unused arguments for {self.__class__} are {kwargs}") + super().__init__() def __str__(self): return self.name @@ -1338,8 +1339,9 @@ def from_dict(cls, d): parents_dict = d.pop("depends_on") if "depends_on" in d else None if parents_dict: parents = [] + decoder = MontyDecoder() for pdict in parents_dict: - parents.append(json.load(pdict, cls=MontyDecoder)) + parents.append(decoder.process_decoded(pdict)) return cls(depends_on=parents, **d) @@ -1508,7 +1510,7 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - opt = json.load(d["opt"], cls=MontyDecoder) + opt = MontyDecoder().process_decoded(d["opt"]) return cls(d["factor"], opt) @@ -1549,7 +1551,8 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): opts = [] + decoder = MontyDecoder() for odict in d["opts"]: - opts.append(json.load(odict, cls=MontyDecoder)) + opts.append(decoder.process_decoded(odict)) return cls(opts) From d8f1462214273d9538deb758c8332b2b87d70558 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:21:26 -0400 Subject: [PATCH 24/95] remove graph prefixes in optimizable.py --- src/simsopt/_core/optimizable.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index c255afebe..b4fb8b513 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1475,7 +1475,7 @@ def J(self): class ScaledOptimizable(Optimizable): """ - Represents an :obj:`~simsopt._core.graph_optimizable.Optimizable` + Represents an :obj:`~simsopt._core.optimizable.Optimizable` object scaled by a constant factor. This class is useful for including a weight in front of terms in an objective function. For now, this feature works on classes for which ``.J()`` returns an @@ -1484,7 +1484,7 @@ class ScaledOptimizable(Optimizable): Args: factor: (float) The constant scale factor. - opt: An :obj:`~simsopt._core.graph_optimizable.Optimizable` object to scale. + opt: An :obj:`~simsopt._core.optimizable.Optimizable` object to scale. """ def __init__(self, factor, opt): @@ -1517,14 +1517,14 @@ def from_dict(cls, d): class OptimizableSum(Optimizable): """ Represents a sum of - :obj:`~simsopt._core.graph_optimizable.Optimizable` objects. This + :obj:`~simsopt._core.optimizable.Optimizable` objects. This class is useful for combining terms in an objective function. For now, this feature works on classes for which ``.J()`` returns an objective value and ``.dJ()`` returns the gradient, e.g. coil optimization. Args: - opts: A python list of :obj:`~simsopt._core.graph_optimizable.Optimizable` object to sum. + opts: A python list of :obj:`~simsopt._core.optimizable.Optimizable` object to sum. """ def __init__(self, opts): From f37721b8aeed86844e6faf71c6ae278806a06ac9 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:29:57 -0400 Subject: [PATCH 25/95] Change [..]fix_all to [..]fix_local in tests --- tests/core/test_integrated.py | 2 +- tests/core/test_optimizable.py | 8 ++++---- tests/geo/test_plot.py | 2 +- tests/geo/test_surface.py | 2 +- tests/geo/test_surface_garabedian.py | 4 ++-- tests/geo/test_surfacehenneberg.py | 2 +- tests/mhd/test_integrated_vmec_mpi.py | 2 +- tests/mhd/test_profiles.py | 16 ++++++++-------- tests/mhd/test_spec.py | 6 +++--- tests/mhd/test_vmec.py | 14 +++++++------- tests/objectives/test_fluxobjective.py | 2 +- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/core/test_integrated.py b/tests/core/test_integrated.py index 0606bbaff..adbf4fb5c 100755 --- a/tests/core/test_integrated.py +++ b/tests/core/test_integrated.py @@ -105,7 +105,7 @@ def test_2dof_surface_Garabedian_opt(self): # optimized. You can choose to exclude any subset of the variables # from the space of independent variables by setting their 'fixed' # property to True. - surf.fix_all() + surf.fix_local() surf.unfix('Delta(0,0)') # Minor radius surf.unfix('Delta(2,0)') # Elongation diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 931ec5471..f83822b55 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -702,10 +702,10 @@ def test_call(self): # Set dofs and call adder.x = [6] self.assertAlmostEqual(adder(), 9.0) - adder.unfix_all() + adder.unfix_local() adder.x = [4, 5, 6] self.assertAlmostEqual(adder(), 15.0) - iden.unfix_all() + iden.unfix_local() iden.x = [20] self.assertAlmostEqual(iden(), 20.0) @@ -720,9 +720,9 @@ def test_call(self): # Fix dofs and now call adder.fix('x') self.assertAlmostEqual(adder([1, 2]), 13) - adder.fix_all() + adder.fix_local() self.assertAlmostEqual(adder(), 13) - iden.fix_all() + iden.fix_local() self.assertAlmostEqual(iden(), 20) # Check with Optimizable objects containing parents diff --git a/tests/geo/test_plot.py b/tests/geo/test_plot.py index 6575ad6a9..35c604c18 100755 --- a/tests/geo/test_plot.py +++ b/tests/geo/test_plot.py @@ -65,7 +65,7 @@ def test_curves_and_surface(self): base_curves = create_equally_spaced_curves(ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order) base_currents = [Current(1e5) for i in range(ncoils)] - base_currents[0].fix_all() + base_currents[0].fix_local() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) items_to_plot = coils + [s] # Coils and surface together items_to_plot2 = [c.curve for c in coils] + [s] # Curves and surface together diff --git a/tests/geo/test_surface.py b/tests/geo/test_surface.py index 2ba1b4ce4..2b12515bc 100755 --- a/tests/geo/test_surface.py +++ b/tests/geo/test_surface.py @@ -281,7 +281,7 @@ def test_fixed(self): surf1 = SurfaceRZFourier(mpol=2, ntor=3, nfp=2) scale_factors = np.random.rand(len(surf1.x)) surf_scaled = SurfaceScaled(surf1, scale_factors) - surf1.fix_all() + surf1.fix_local() surf1.fixed_range(mmin=0, mmax=1, nmin=-2, nmax=3, fixed=False) surf1.fix("rc(0,0)") # Major radius diff --git a/tests/geo/test_surface_garabedian.py b/tests/geo/test_surface_garabedian.py index 0853bbe00..e77489603 100755 --- a/tests/geo/test_surface_garabedian.py +++ b/tests/geo/test_surface_garabedian.py @@ -88,7 +88,7 @@ def test_fix_range(self): Test the fix_range() function for SurfaceGarabedian. """ s = SurfaceGarabedian(mmin=-3, mmax=2, nmin=-4, nmax=3) - s.fix_all() + s.fix_local() s.fix_range(0, 1, -2, 3, False) for m in range(-3, 3): for n in range(-4, 4): @@ -100,7 +100,7 @@ def test_fix_range(self): self.assertTrue(s.is_fixed(f'Delta({m},{n})')) s = SurfaceGarabedian(mmin=0, mmax=3, nmin=-4, nmax=4) - s.unfix_all() + s.unfix_local() s.fix_range(1, 2, -3, 2) for m in range(0, 4): for n in range(-4, 5): diff --git a/tests/geo/test_surfacehenneberg.py b/tests/geo/test_surfacehenneberg.py index 883d4abb3..529e93f53 100755 --- a/tests/geo/test_surfacehenneberg.py +++ b/tests/geo/test_surfacehenneberg.py @@ -112,7 +112,7 @@ def test_fixed_range(self): np.testing.assert_equal(surf.local_dofs_free_status, [False, True, True, False, True, True, True, False, True, True, False, True]) - surf.fix_all() + surf.fix_local() surf.fixed_range(0, 1, False) np.testing.assert_equal(surf.local_dofs_free_status, [True, True, True, False, False, True, diff --git a/tests/mhd/test_integrated_vmec_mpi.py b/tests/mhd/test_integrated_vmec_mpi.py index 3fc4b0b1e..2dacba415 100755 --- a/tests/mhd/test_integrated_vmec_mpi.py +++ b/tests/mhd/test_integrated_vmec_mpi.py @@ -70,7 +70,7 @@ def test_stellopt_scenarios_1DOF_circularCrossSection_varyR0_targetVolume(self): # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. - surf.fix_all() + surf.fix_local() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/tests/mhd/test_profiles.py b/tests/mhd/test_profiles.py index ef70c2aae..5c83a35c3 100755 --- a/tests/mhd/test_profiles.py +++ b/tests/mhd/test_profiles.py @@ -35,7 +35,7 @@ def test_scaled(self): prof1 = ProfilePolynomial([3, 0, 0, -3]) scalefac = 0.6 prof2 = ProfileScaled(prof1, scalefac) - prof2.unfix_all() + prof2.unfix_local() s = np.linspace(0, 1, 10) np.testing.assert_allclose(prof2(s), scalefac * prof1(s)) np.testing.assert_allclose(prof2.dfds(s), scalefac * prof1.dfds(s)) @@ -62,7 +62,7 @@ def test_spline(self): # Try changing the dofs f2 = -2.1 - 0.4 * s + 0.7 * s * s f2_fine = -2.1 - 0.4 * s_fine + 0.7 * s_fine * s_fine - profile.unfix_all() + profile.unfix_local() profile.x = f2 np.testing.assert_allclose(profile(s), f2) np.testing.assert_allclose(profile.dfds(s), -0.4 + 2 * 0.7 * s) @@ -122,12 +122,12 @@ def test_pressure(self): TD = ProfilePolynomial(12.0e3 * np.array([1.0, -1.0])) TT = TD pressure = ProfilePressure(ne, Te, nD, TD, nT, TT) - ne.unfix_all() - nD.unfix_all() - nT.unfix_all() - Te.unfix_all() - TD.unfix_all() - TT.unfix_all() + ne.unfix_local() + nD.unfix_local() + nT.unfix_local() + Te.unfix_local() + TD.unfix_local() + TT.unfix_local() for j in range(2): np.testing.assert_allclose(pressure(s), ne(s) * Te(s) + nD(s) * TD(s) + nT(s) * TT(s), atol=atol) diff --git a/tests/mhd/test_spec.py b/tests/mhd/test_spec.py index 312a9c71e..61447fb20 100755 --- a/tests/mhd/test_spec.py +++ b/tests/mhd/test_spec.py @@ -132,7 +132,7 @@ def test_integrated_stellopt_scenarios_1dof(self): # parameters are all non-fixed by default. You can choose # which parameters are optimized by setting their 'fixed' # attributes. - surf.fix_all() + surf.fix_local() surf.unfix('rc(0,0)') # Turn off Poincare plots and use low resolution, for speed: @@ -194,7 +194,7 @@ def test_integrated_stellopt_scenarios_1dof_Garabedian(self): # parameters are all non-fixed by default. You can choose # which parameters are optimized by setting their 'fixed' # attributes. - surf.fix_all() + surf.fix_local() surf.unfix('Delta(1,-1)') # Use low resolution, for speed: @@ -243,7 +243,7 @@ def test_integrated_stellopt_scenarios_2dof(self): # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. - surf.fix_all() + surf.fix_local() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/tests/mhd/test_vmec.py b/tests/mhd/test_vmec.py index 547bd89ee..426ff9504 100755 --- a/tests/mhd/test_vmec.py +++ b/tests/mhd/test_vmec.py @@ -367,7 +367,7 @@ def test_pressure_profile(self): s = vmec.s_half_grid np.testing.assert_allclose(vmec.wout.pres[1:], pressure(s)) # Change the Profile dofs, and confirm that the output pressure from VMEC is updated: - pressure.unfix_all() + pressure.unfix_local() pressure.x = 1.0e4 * np.array([1, 2, -3.0]) vmec.run() self.assertAlmostEqual(vmec.indata.pres_scale, 1.0) @@ -443,7 +443,7 @@ def test_current_profile(self): factor * (1 + s_test - 1.5 * s_test ** 2), rtol=1e-3) # Change the Profile dofs, and confirm that the output current from VMEC is updated: - current.unfix_all() + current.unfix_local() current.x = factor * np.array([1, 2, -2.2]) # Now the total (s-integrated) current is factor * 1.2666666666 vmec.run() @@ -468,7 +468,7 @@ def test_current_profile(self): # "cubic_spline" option in VMEC is replaced by # "cubic_spline_ip" or "cubic_spline_i" vmec.indata.pcurr_type = 'cubic_spline_ip' - current2.unfix_all() + current2.unfix_local() current2.x = factor * (1.0 + 1.0 * s_spline - 1.5 * s_spline ** 2) vmec.run() np.testing.assert_allclose(vmec.wout.ctor, factor * 1.0, rtol=1e-2) @@ -501,7 +501,7 @@ def test_iota_profile(self): s = vmec.s_half_grid np.testing.assert_allclose(vmec.wout.iotas[1:], iota(s)) # Change the Profile dofs, and confirm that the output iota from VMEC is updated: - iota.unfix_all() + iota.unfix_local() iota.x = np.array([1, 2, -3.0]) vmec.run() np.testing.assert_allclose(vmec.wout.iotas[1:], (1 + 2 * s - 3 * s * s)) @@ -517,7 +517,7 @@ def test_iota_profile(self): # Now try a spline Profile with vmec using splines: vmec.indata.piota_type = 'cubic_spline' - iota2.unfix_all() + iota2.unfix_local() newx = (2.2 - 0.7 * s_spline - 1.1 * s_spline ** 2) iota2.x = (2.2 - 0.7 * s_spline - 1.1 * s_spline ** 2) vmec.run() @@ -572,10 +572,10 @@ def test_profile_optimization(self): # Initial pressure profile is p(s) = (1.0e4) * (1 - s) base_pressure = ProfilePolynomial([1, -1]) pressure = ProfileScaled(base_pressure, 1.0e4) - pressure.unfix_all() + pressure.unfix_local() filename = os.path.join(TEST_DIR, 'input.circular_tokamak') vmec = Vmec(filename) - vmec.boundary.fix_all() + vmec.boundary.fix_local() vmec.pressure_profile = pressure def beta_func(vmec): diff --git a/tests/objectives/test_fluxobjective.py b/tests/objectives/test_fluxobjective.py index 52431cdf4..38e0a0a20 100755 --- a/tests/objectives/test_fluxobjective.py +++ b/tests/objectives/test_fluxobjective.py @@ -45,7 +45,7 @@ def test_flux(self): for i in range(ncoils): curr = Current(1e5) if i == 0: - curr.fix_all() + curr.fix_local() base_currents.append(curr) coils = coils_via_symmetries(base_curves, base_currents, s.nfp, s.stellsym) From 86bef1a2c9c42e275f7a84921a46e20d5a7b90cb Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:36:42 -0400 Subject: [PATCH 26/95] Change [..]fix_all to [..]fix_local in examples --- examples/2_Intermediate/QH_fixed_resolution.py | 2 +- examples/2_Intermediate/QH_fixed_resolution_boozer.py | 2 +- examples/2_Intermediate/QSC.py | 2 +- examples/2_Intermediate/eliminate_magnetic_islands.py | 2 +- examples/2_Intermediate/resolution_increase.py | 2 +- examples/2_Intermediate/resolution_increase_boozer.py | 2 +- examples/2_Intermediate/stage_two_optimization.py | 2 +- examples/2_Intermediate/stage_two_optimization_stochastic.py | 2 +- examples/2_Intermediate/vmec_adjoint.py | 2 +- examples/3_Advanced/optimize_qs_and_islands_simultaneously.py | 2 +- .../1DOF_circularCrossSection_varyAxis_targetIota.py | 2 +- .../1DOF_circularCrossSection_varyAxis_targetIota_spec.py | 2 +- .../1DOF_circularCrossSection_varyR0_targetVolume.py | 2 +- .../1DOF_circularCrossSection_varyR0_targetVolume_spec.py | 2 +- ..._circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py | 2 +- .../stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py | 2 +- examples/stellarator_benchmarks/2DOF_vmecAndSpec.py | 2 +- .../stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py | 2 +- examples/stellarator_benchmarks/7dof.py | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/2_Intermediate/QH_fixed_resolution.py b/examples/2_Intermediate/QH_fixed_resolution.py index ef818bcd8..2fef7c20b 100755 --- a/examples/2_Intermediate/QH_fixed_resolution.py +++ b/examples/2_Intermediate/QH_fixed_resolution.py @@ -27,7 +27,7 @@ # Define parameter space: surf = vmec.boundary -surf.fix_all() +surf.fix_local() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) diff --git a/examples/2_Intermediate/QH_fixed_resolution_boozer.py b/examples/2_Intermediate/QH_fixed_resolution_boozer.py index 11dede2f4..3b1335c19 100755 --- a/examples/2_Intermediate/QH_fixed_resolution_boozer.py +++ b/examples/2_Intermediate/QH_fixed_resolution_boozer.py @@ -23,7 +23,7 @@ # Define parameter space: surf = vmec.boundary -surf.fix_all() +surf.fix_local() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) diff --git a/examples/2_Intermediate/QSC.py b/examples/2_Intermediate/QSC.py index 9e0ecd130..93db91c85 100755 --- a/examples/2_Intermediate/QSC.py +++ b/examples/2_Intermediate/QSC.py @@ -39,7 +39,7 @@ def get_max_elongation(self): print('Names of the dofs: ', stel.names) # Decide which degrees of freedom to optimize -stel.fix_all() +stel.fix_local() stel.unfix('rc(1)') stel.unfix('zs(1)') stel.unfix('etabar') diff --git a/examples/2_Intermediate/eliminate_magnetic_islands.py b/examples/2_Intermediate/eliminate_magnetic_islands.py index 87690395a..5b39bf48f 100755 --- a/examples/2_Intermediate/eliminate_magnetic_islands.py +++ b/examples/2_Intermediate/eliminate_magnetic_islands.py @@ -34,7 +34,7 @@ # To make this example run relatively quickly, we will optimize in a # small parameter space. Here we pick out just 2 Fourier modes to vary # in the optimization: -s.boundary.fix_all() +s.boundary.fix_local() s.boundary.unfix('zs(6,1)') s.boundary.unfix('zs(6,2)') diff --git a/examples/2_Intermediate/resolution_increase.py b/examples/2_Intermediate/resolution_increase.py index c08befc09..f9cdc735c 100755 --- a/examples/2_Intermediate/resolution_increase.py +++ b/examples/2_Intermediate/resolution_increase.py @@ -61,7 +61,7 @@ ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_all() + surf.fix_local() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/2_Intermediate/resolution_increase_boozer.py b/examples/2_Intermediate/resolution_increase_boozer.py index 297344e07..c2fd15cfd 100755 --- a/examples/2_Intermediate/resolution_increase_boozer.py +++ b/examples/2_Intermediate/resolution_increase_boozer.py @@ -67,7 +67,7 @@ ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_all() + surf.fix_local() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/2_Intermediate/stage_two_optimization.py b/examples/2_Intermediate/stage_two_optimization.py index 75b61f68c..66d76192e 100755 --- a/examples/2_Intermediate/stage_two_optimization.py +++ b/examples/2_Intermediate/stage_two_optimization.py @@ -88,7 +88,7 @@ # Since the target field is zero, one possible solution is just to set all # currents to 0. To avoid the minimizer finding that solution, we fix one # of the currents: -base_currents[0].fix_all() +base_currents[0].fix_local() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) bs = BiotSavart(coils) diff --git a/examples/2_Intermediate/stage_two_optimization_stochastic.py b/examples/2_Intermediate/stage_two_optimization_stochastic.py index c4ba5ec18..57976fffd 100755 --- a/examples/2_Intermediate/stage_two_optimization_stochastic.py +++ b/examples/2_Intermediate/stage_two_optimization_stochastic.py @@ -119,7 +119,7 @@ def pprint(*args, **kwargs): # Since the target field is zero, one possible solution is just to set all # currents to 0. To avoid the minimizer finding that solution, we fix one # of the currents: -base_currents[0].fix_all() +base_currents[0].fix_local() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) bs = BiotSavart(coils) diff --git a/examples/2_Intermediate/vmec_adjoint.py b/examples/2_Intermediate/vmec_adjoint.py index 12d982547..e63a0cb1e 100755 --- a/examples/2_Intermediate/vmec_adjoint.py +++ b/examples/2_Intermediate/vmec_adjoint.py @@ -47,7 +47,7 @@ obj = IotaTargetMetric(vmec, target_function, adjoint_epsilon) surf = vmec.boundary -surf.fix_all() +surf.fix_local() # Slowly increase range of modes in optimization space for max_mode in range(3, maxres): surf.fixed_range(mmin=0, mmax=max_mode, diff --git a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py index aa8415654..783c7b03e 100755 --- a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py +++ b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py @@ -36,7 +36,7 @@ spec.boundary = surf # Define parameter space: -surf.fix_all() +surf.fix_local() surf.fixed_range(mmin=0, mmax=3, nmin=-3, nmax=3, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py index 4ea51df9f..001b3bd34 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py @@ -50,7 +50,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('Delta(1,-1)') # Each function we want in the objective function is then equipped diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py index 09c50e97f..8552e09e4 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py @@ -50,7 +50,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('Delta(1,-1)') # Each function we want in the objective function is then equipped diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py index 3050c87a7..ce0ca2401 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py @@ -52,7 +52,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py index 1dab40b07..f28623219 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py @@ -55,7 +55,7 @@ # Surface parameters are all non-fixed by default. You can choose # which parameters are optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py index ce934ec8a..7b896a123 100755 --- a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py +++ b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py @@ -17,7 +17,7 @@ vmec = Vmec(os.path.join(os.path.dirname(__file__), 'inputs', 'input.2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry')) # Define parameter space: -vmec.boundary.fix_all() +vmec.boundary.fix_local() vmec.boundary.unfix("rc(0,1)") vmec.boundary.unfix("zs(0,1)") diff --git a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py index 3937161f3..17b19c6fe 100755 --- a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py @@ -43,7 +43,7 @@ # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py index 6483c399d..ef88eaa9d 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py +++ b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py @@ -46,7 +46,7 @@ # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py index 9ba544b93..591aecc25 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py @@ -45,7 +45,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_all() +surf.fix_local() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/7dof.py b/examples/stellarator_benchmarks/7dof.py index cad0e9904..187027101 100755 --- a/examples/stellarator_benchmarks/7dof.py +++ b/examples/stellarator_benchmarks/7dof.py @@ -26,7 +26,7 @@ vmec.boundary = surf # Define parameter space: -surf.fix_all() +surf.fix_local() surf.fix_range(mmin=0, mmax=2, nmin=-1, nmax=1, fixed=False) surf.fix("Delta(1,0)") # toroidally-averaged major radius surf.fix("Delta(0,0)") # toroidally-averaged minor radius From d5af2d51ce5e20cfc3e093efa7a8498b4b86a9a6 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:41:14 -0400 Subject: [PATCH 27/95] Change [..]fix_all to [..]fix_local in docs --- docs/source/example_coils.rst | 4 ++-- docs/source/example_islands.rst | 2 +- docs/source/example_quasisymmetry.rst | 6 +++--- docs/source/example_vmec_only.rst | 4 ++-- docs/source/optimizable.rst | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/source/example_coils.rst b/docs/source/example_coils.rst index 0ba6baf9a..6269e2638 100644 --- a/docs/source/example_coils.rst +++ b/docs/source/example_coils.rst @@ -110,10 +110,10 @@ optimizer can "cheat" by making all the currents go to zero, which makes the quadratic flux vanish. To close this loophole, we can fix the current of the first base coil:: - base_currents[0].fix_all() + base_currents[0].fix_local() (A ``Current`` object only has one degree of freedom, hence we can use -``fix_all()``.) If you wish, you can fix the currents in all the +``fix_local()``.) If you wish, you can fix the currents in all the coils to force them to have the same value. Now the full set of 16 coils can be obtained using stellarator symmetry and field-period symmetry:: diff --git a/docs/source/example_islands.rst b/docs/source/example_islands.rst index 9c548a0e2..ec4af12b4 100644 --- a/docs/source/example_islands.rst +++ b/docs/source/example_islands.rst @@ -50,7 +50,7 @@ available to vary:: Now we can pick out a few modes of the boundary shape to vary in the optimization:: - s.boundary.fix_all() + s.boundary.fix_local() s.boundary.unfix('zs(6,1)') s.boundary.unfix('zs(6,2)') diff --git a/docs/source/example_quasisymmetry.rst b/docs/source/example_quasisymmetry.rst index 79df22571..5b276df62 100644 --- a/docs/source/example_quasisymmetry.rst +++ b/docs/source/example_quasisymmetry.rst @@ -85,7 +85,7 @@ property of the boundary's Fourier modes as follows:: # Define parameter space: surf = vmec.boundary - surf.fix_all() + surf.fix_local() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) @@ -296,7 +296,7 @@ and toroidal mode numbers are set to be varied in the optimization:: ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_all() + surf.fix_local() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius @@ -419,7 +419,7 @@ toroidal mode numbers are set to be varied in the optimization:: ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_all() + surf.fix_local() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/docs/source/example_vmec_only.rst b/docs/source/example_vmec_only.rst index 18454c5ed..a4f30dde7 100644 --- a/docs/source/example_vmec_only.rst +++ b/docs/source/example_vmec_only.rst @@ -76,7 +76,7 @@ case, the independent variables are two of the Fourier amplitudes defining the boundary. We choose the independent variables as follows:: surf = equil.boundary - surf.fix_all() + surf.fix_local() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') @@ -160,7 +160,7 @@ The complete example is then as follows:: surf = equil.boundary # You can choose which parameters are optimized by setting their 'fixed' attributes. - surf.fix_all() + surf.fix_local() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 93d952a9c..d89ff2503 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -167,10 +167,10 @@ In addition to :obj:`~simsopt._core.optimizable.Optimizable.fix()`, you can also manipulate the fixed/free status of dofs using the functions :obj:`~simsopt._core.optimizable.Optimizable.unfix()`, -:obj:`~simsopt._core.optimizable.Optimizable.fix_all()`, and -:obj:`~simsopt._core.optimizable.Optimizable.unfix_all()`:: +:obj:`~simsopt._core.optimizable.Optimizable.fix_local()`, and +:obj:`~simsopt._core.optimizable.Optimizable.unfix_local()`:: - >>> c.fix_all() + >>> c.fix_local() >>> c.x array([], dtype=float64) @@ -180,7 +180,7 @@ also manipulate the fixed/free status of dofs using the functions array([-2.]) - >>> c.unfix_all() + >>> c.unfix_local() >>> c.x array([ 1. , 0.1, 0. , -2. , 0. , 0.3, 3. , -0.5, 0.4]) @@ -359,7 +359,7 @@ automatically removed from the global state vector :obj:`~simsopt._core.optimizable.Optimizable.x` of a child object:: - >>> curve.fix_all() + >>> curve.fix_local() >>> curve.unfix('zc(0)') >>> coil.x From e96a5d3bc85d4e27cae3b491d474c9ecafc5a4ab Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:42:27 -0400 Subject: [PATCH 28/95] Remove graph prefix in mpi.rst --- docs/source/mpi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/mpi.rst b/docs/source/mpi.rst index fb73228de..b74d16900 100644 --- a/docs/source/mpi.rst +++ b/docs/source/mpi.rst @@ -53,7 +53,7 @@ The same :obj:`~simsopt.util.mpi.MpiPartition` instance should be passed to the # ... code to define an optimization problem "prob" ... - from simsopt.solve.graph_mpi import least_squares_mpi_solve + from simsopt.solve.mpi import least_squares_mpi_solve least_squares_mpi_solve(prob, mpi, grad=True) From f9a300f876f72df1b246d79087f9dbaf948b0660 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:47:25 -0400 Subject: [PATCH 29/95] Updated README.md to remove references to deleted example --- examples/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/README.md b/examples/README.md index b57b1fd6b..97443b706 100755 --- a/examples/README.md +++ b/examples/README.md @@ -14,10 +14,8 @@ Minimize f(x,y,z) = ((x-1)/1)^2 + ((y-2)/2)^2 + ((z-3)/3)^2. Example file for transparently logging both MPI and serial jobs ### minimize_curve_length Minimize the length of a curve, holding the 0-frequency Fourier mode fixed resulting in a circle. -### surface_volume_and_area +### surf_vol_area Optimize the minor radius and elongation of an axisymmetric torus to obtain a desired volume and area. -### graph_surf_vol_area -Optimize the minor radius and elongation of an axisymmetric torus to obtain a desired volume and area using the graph optimizable objects. ## 2_Intermediate From 09c44b8c91a4e77f9a8e27b1a4eeb9ebbbe30897 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 20 Apr 2022 15:56:38 -0400 Subject: [PATCH 30/95] Linter fixes --- src/simsopt/_core/graph_optimizable.py | 2 +- src/simsopt/objectives/graph_functions.py | 2 +- src/simsopt/objectives/graph_least_squares.py | 2 +- src/simsopt/solve/graph_mpi.py | 2 +- src/simsopt/solve/graph_serial.py | 2 +- tests/core/test_optimizable.py | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/simsopt/_core/graph_optimizable.py b/src/simsopt/_core/graph_optimizable.py index f555d6133..0f40e04b9 100644 --- a/src/simsopt/_core/graph_optimizable.py +++ b/src/simsopt/_core/graph_optimizable.py @@ -4,4 +4,4 @@ warnings.warn("Import of graph_optimizable module deprecated." " Instead use optimizable module", - DeprecationWarning, stacklevel=2) \ No newline at end of file + DeprecationWarning, stacklevel=2) diff --git a/src/simsopt/objectives/graph_functions.py b/src/simsopt/objectives/graph_functions.py index 2d1c06a58..45da9bd1a 100644 --- a/src/simsopt/objectives/graph_functions.py +++ b/src/simsopt/objectives/graph_functions.py @@ -4,4 +4,4 @@ warnings.warn("Import of graph_functions module deprecated." " Instead use functions module", - DeprecationWarning, stacklevel=2) \ No newline at end of file + DeprecationWarning, stacklevel=2) diff --git a/src/simsopt/objectives/graph_least_squares.py b/src/simsopt/objectives/graph_least_squares.py index d5c63bb75..27ccc4786 100644 --- a/src/simsopt/objectives/graph_least_squares.py +++ b/src/simsopt/objectives/graph_least_squares.py @@ -4,4 +4,4 @@ warnings.warn("Import of graph_least_squares module deprecated." " Instead use least_squares module", - DeprecationWarning, stacklevel=2) \ No newline at end of file + DeprecationWarning, stacklevel=2) diff --git a/src/simsopt/solve/graph_mpi.py b/src/simsopt/solve/graph_mpi.py index 753853938..2393f5b04 100644 --- a/src/simsopt/solve/graph_mpi.py +++ b/src/simsopt/solve/graph_mpi.py @@ -4,4 +4,4 @@ warnings.warn("Import of graph_mpi module deprecated." " Instead use mpi module", - DeprecationWarning, stacklevel=2) \ No newline at end of file + DeprecationWarning, stacklevel=2) diff --git a/src/simsopt/solve/graph_serial.py b/src/simsopt/solve/graph_serial.py index 798add12c..603baed9e 100644 --- a/src/simsopt/solve/graph_serial.py +++ b/src/simsopt/solve/graph_serial.py @@ -4,4 +4,4 @@ warnings.warn("Import of graph_serial module deprecated." " Instead use serial module", - DeprecationWarning, stacklevel=2) \ No newline at end of file + DeprecationWarning, stacklevel=2) diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index f83822b55..c8f475c3b 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -1248,6 +1248,7 @@ class TestOptimizableSerialize(unittest.TestCase): Test the serialization of the Optimizable class based on as_dict and from_dict methods using various sub-classes """ + def test_adder_serialize(self): adder_orig = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], fixed=[True, False, True]) @@ -1256,7 +1257,7 @@ def test_adder_serialize(self): self.assertEqual(adder.n, adder_orig.n) self.assertTrue(np.allclose(adder.full_x, adder_orig.full_x)) self.assertTrue(np.array_equal(adder.dofs_free_status, - adder_orig.dofs_free_status)) + adder_orig.dofs_free_status)) self.assertEqual(adder.local_full_dof_names, adder_orig.local_full_dof_names) @@ -1287,6 +1288,5 @@ def test_twolevel_serialize(self): self.assertAlmostEqual(test_opt.f(), test_opt_orig.f()) - if __name__ == "__main__": unittest.main() From 28c1317553941bd8bd9c53e12c06e0e78bbe5a77 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 16:06:45 -0400 Subject: [PATCH 31/95] Remove leftover spurious tests --- tests/core/test_graph_optimizable.py | 141 --------------------------- 1 file changed, 141 deletions(-) diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_graph_optimizable.py index d054df41c..da8e91f44 100755 --- a/tests/core/test_graph_optimizable.py +++ b/tests/core/test_graph_optimizable.py @@ -889,53 +889,6 @@ def test_local_fix_all(self): self.assertEqual(self.adder.dof_size, 0) self.assertEqual(self.rosen.dof_size, 0) - def test_fix_local(self): - self.iden.fix_local() - self.adder.fix_local() - self.rosen.fix_local() - - self.assertEqual(self.iden.dof_size, 0) - self.assertEqual(self.adder.dof_size, 0) - self.assertEqual(self.rosen.dof_size, 0) - - def test_fix_full(self): - adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], - dof_fixed=[True, False, False]) - iden = Identity(x=10, dof_fixed=False) - adder_x = adder.x - iden_x = iden.x - self.assertEqual(len(adder_x), 2) - self.assertAlmostEqual(adder_x[0], 2) - self.assertAlmostEqual(adder_x[1], 3) - self.assertEqual(len(iden_x), 1) - - iden.fix_full() - adder.fix_full() - with self.assertRaises(ValueError): - iden.x = [10] - with self.assertRaises(ValueError): - adder.x = [4, 5, 6] - - self.assertEqual(iden.dof_size, 0) - self.assertEqual(adder.dof_size, 0) - - # Check with Optimizable objects containing parents - adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], - dof_fixed=[True, False, False]) - iden = Identity(x=10, dof_fixed=False) - test_obj = OptClassWithParents(10, depends_on=[iden, adder]) - - test_x = test_obj.x - self.assertEqual(len(test_x), 4) - test_obj.fix_full() - - with self.assertRaises(ValueError): - test_obj.x = np.array([20, 5, 6, 25]) - - self.assertEqual(test_obj.dof_size, 0) - self.assertEqual(adder.dof_size, 0) - self.assertEqual(iden.dof_size, 0) - def test_unfix(self): pass @@ -989,100 +942,6 @@ def test_local_unfix_all(self): self.assertAlmostEqual(iden.local_full_x[0], 1) self.assertAlmostEqual(test_obj.local_full_x[0], 10) - def test_unfix_local(self): - # Test with leaf nodes - adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], - dof_fixed=[True, False, False]) - iden = Identity(x=10, dof_fixed=True) - adder_x = adder.x - iden_x = iden.x - self.assertEqual(len(adder_x), 2) - self.assertEqual(adder.dof_size, 2) - self.assertAlmostEqual(adder_x[0], 2) - self.assertAlmostEqual(adder_x[1], 3) - self.assertEqual(len(iden_x), 0) - - with self.assertRaises(ValueError): - iden.x = [10] - with self.assertRaises(ValueError): - adder.x = [4, 5, 6] - - iden.unfix_local() - adder.unfix_local() - iden.x = [10] - adder.x = [4, 5, 6] - self.assertEqual(iden.dof_size, 1) - self.assertEqual(adder.dof_size, 3) - - # Check with Optimizable objects containing parents - adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], - dof_fixed=[True, False, False]) - iden = Identity(x=10, dof_fixed=True) - test_obj = OptClassWithParents(10, depends_on=[iden, adder]) - - with self.assertRaises(ValueError): - test_obj.x = np.array([20, 4, 5, 6, 25]) - - adder.unfix_local() - test_obj.x = np.array([4, 5, 6, 25]) - self.assertAlmostEqual(adder.local_full_x[0], 4) - self.assertAlmostEqual(adder.local_full_x[1], 5) - self.assertAlmostEqual(adder.local_full_x[2], 6) - self.assertAlmostEqual(test_obj.local_full_x[0], 25) - - iden.unfix_local() - test_obj.x = np.array([1, 2, 3, 1, 10]) - - self.assertAlmostEqual(adder.local_full_x[0], 1) - self.assertAlmostEqual(adder.local_full_x[1], 2) - self.assertAlmostEqual(adder.local_full_x[2], 3) - self.assertAlmostEqual(iden.local_full_x[0], 1) - self.assertAlmostEqual(test_obj.local_full_x[0], 10) - - def test_unfix_full(self): - # Test with leaf nodes - adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], - dof_fixed=[True, False, False]) - iden = Identity(x=10, dof_fixed=True) - adder_x = adder.x - iden_x = iden.x - self.assertEqual(len(adder_x), 2) - self.assertEqual(adder.dof_size, 2) - self.assertAlmostEqual(adder_x[0], 2) - self.assertAlmostEqual(adder_x[1], 3) - self.assertEqual(len(iden_x), 0) - - with self.assertRaises(ValueError): - iden.x = [10] - with self.assertRaises(ValueError): - adder.x = [4, 5, 6] - - iden.unfix_full() - adder.unfix_full() - iden.x = [10] - adder.x = [4, 5, 6] - self.assertEqual(iden.dof_size, 1) - self.assertEqual(adder.dof_size, 3) - - # Check with Optimizable objects containing parents - adder = Adder(n=3, x0=[1, 2, 3], dof_names=['x', 'y', 'z'], - dof_fixed=[True, False, False]) - iden = Identity(x=10, dof_fixed=True) - test_obj = OptClassWithParents(10, depends_on=[iden, adder]) - test_obj.fix('val') - - with self.assertRaises(ValueError): - test_obj.x = np.array([20, 4, 5, 6, 25]) - - test_obj.unfix_full() - test_obj.x = np.array([4, 5, 6, 20, 25]) - print(iden.x) - self.assertAlmostEqual(adder.local_full_x[0], 4) - self.assertAlmostEqual(adder.local_full_x[1], 5) - self.assertAlmostEqual(adder.local_full_x[2], 6) - self.assertAlmostEqual(iden.local_full_x[0], 20) - self.assertAlmostEqual(test_obj.local_full_x[0], 25) - def test_get_ancestors(self): iden = Identity(x=10, dof_fixed=True) adder = Adder(n=3, x0=[1, 2, 3]) From 248dfd72f625e83859852cb4d9b0fb253ca8f227 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 16:07:23 -0400 Subject: [PATCH 32/95] Serialization for coils --- src/simsopt/field/coil.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index b8f5157bb..83a9e3fe8 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -1,10 +1,11 @@ from simsopt._core.optimizable import Optimizable from simsopt._core.derivative import Derivative from simsopt.geo.curvexyzfourier import CurveXYZFourier -from simsopt.geo.curve import RotatedCurve +from simsopt.geo.curve import RotatedCurve, Curve import simsoptpp as sopp from math import pi import numpy as np +from monty.json import MontyDecoder class Coil(sopp.Coil, Optimizable): @@ -33,6 +34,22 @@ def plot(self, **kwargs): :obj:`simsopt.geo.curve.Curve.plot()` """ return self.curve.plot(**kwargs) + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["curve"] = self.curve.as_dict() + d["current"] = self.current.as_dict() + del d["current"]["@module"] + del d["current"]["@class"] + return d + + @classmethod + def from_dict(cls, d): + current = Current.from_dict(d["current"]) + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve, current) class Current(sopp.Current, Optimizable): @@ -53,6 +70,22 @@ def vjp(self, v_current): def __neg__(self): return ScaledCurrent(self, -1.) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + current = self.get_value() + if hasattr(self, 'scale'): + current *= self.scale + d["current"] = current + return d + + @classmethod + def from_dict(cls, d): + current = d["current"] + return Current(current) + + class ScaledCurrent(sopp.ScaledCurrent, Optimizable): """ From 179b7778084d633e1b70af3441f0bc65a9ea82b1 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 16:07:53 -0400 Subject: [PATCH 33/95] Serialization for rotated coils --- src/simsopt/geo/curve.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/simsopt/geo/curve.py b/src/simsopt/geo/curve.py index 1e5a5cf90..0eb2b038c 100644 --- a/src/simsopt/geo/curve.py +++ b/src/simsopt/geo/curve.py @@ -5,6 +5,7 @@ from .jit import jit import jax.numpy as jnp from monty.dev import requires +from monty.json import MontyDecoder import simsoptpp as sopp from .._core.optimizable import Optimizable @@ -624,6 +625,7 @@ def __init__(self, curve, phi, flip): self.curve = curve sopp.Curve.__init__(self, curve.quadpoints) Curve.__init__(self, depends_on=[curve]) + self._phi = phi self.rotmat = np.asarray( [[cos(phi), -sin(phi), 0], [sin(phi), cos(phi), 0], @@ -809,6 +811,20 @@ def dgammadashdashdash_by_dcoeff_vjp(self, v): v = sopp.matmult(v, self.rotmatT) # v = v @ self.rotmatT return self.curve.dgammadashdashdash_by_dcoeff_vjp(v) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["curve"] = self.curve.as_dict() + d["phi"] = self._phi + d["flip"] = True if self.rotmat[2][2] == -1 else False + return d + + @classmethod + def from_dict(cls, d): + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve, d["phi"], d["flip"]) + def curves_to_vtk(curves, filename, close=False): """ From c9019a074a8badf149f1b64a32a9093a8e4ed477 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 16:08:11 -0400 Subject: [PATCH 34/95] Serialization for helical curves --- src/simsopt/geo/curvehelical.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/simsopt/geo/curvehelical.py b/src/simsopt/geo/curvehelical.py index 99e662896..24de5ba1f 100644 --- a/src/simsopt/geo/curvehelical.py +++ b/src/simsopt/geo/curvehelical.py @@ -59,3 +59,20 @@ def set_dofs_impl(self, dofs): order = int(len(dofs)/2) for i in range(2): self.coefficients[i] = dofs[i*order:(i+1)*order] + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["quadpoints"] = self.quadpoints + d["order"] = self.order + d["n0"] = self.n0 + d["l0"] = self.l0 + d["R0"] = self.R0 + d["r0"] = self.r0 + return d + + @classmethod + def from_dict(cls, d): + return cls(d["quadpoints, "], d["order"], d["n0"], + d["l0"], d["R0"], d["r0"]) \ No newline at end of file From e8f6ad66480bdb2e13e7c405e8d22e6b2c95a2f0 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 18:48:44 -0400 Subject: [PATCH 35/95] Serialization of curves --- src/simsopt/field/coil.py | 3 +-- src/simsopt/geo/curveperturbed.py | 27 +++++++++++++++++++++++---- src/simsopt/geo/curverzfourier.py | 17 +++++++++++++++++ src/simsopt/geo/curvexyzfourier.py | 24 ++++++++++++++++++++++++ src/simsopt/objectives/functions.py | 5 +++-- 5 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 83a9e3fe8..f8c4a9fb4 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -34,7 +34,7 @@ def plot(self, **kwargs): :obj:`simsopt.geo.curve.Curve.plot()` """ return self.curve.plot(**kwargs) - + def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ @@ -86,7 +86,6 @@ def from_dict(cls, d): return Current(current) - class ScaledCurrent(sopp.ScaledCurrent, Optimizable): """ Scales :mod:`Current` by a factor. To be used for example to flip currents diff --git a/src/simsopt/geo/curveperturbed.py b/src/simsopt/geo/curveperturbed.py index 743f9d34a..67796ba55 100644 --- a/src/simsopt/geo/curveperturbed.py +++ b/src/simsopt/geo/curveperturbed.py @@ -1,10 +1,12 @@ import numpy as np +from sympy import Symbol, lambdify, exp +from monty.json import MSONable, MontyDecoder + import simsoptpp as sopp from simsopt.geo.curve import Curve -from sympy import Symbol, lambdify, exp -class GaussianSampler(): +class GaussianSampler(MSONable): def __init__(self, points, sigma, length_scale, n_derivs=1): r""" @@ -37,6 +39,8 @@ def __init__(self, points, sigma, length_scale, n_derivs=1): n_derivs: number of derivatives of the gaussian process to sample. """ self.points = points + self.sigma = sigma + self.length_scale = length_scale xs = self.points n = len(xs) self.n_derivs = n_derivs @@ -74,7 +78,7 @@ def draw_sample(self, randomgen=None): return [curve_and_derivs[(i*n):((i+1)*n), :] for i in range(n_derivs+1)] -class PerturbationSample(): +class PerturbationSample(MSONable): """ This class represents a single sample of a perturbation. The point of having a dedicated class for this is so that we can apply the same @@ -91,7 +95,7 @@ class PerturbationSample(): def __init__(self, sampler, randomgen=None): self.sampler = sampler - self.randomgen = randomgen + self.randomgen = randomgen # If not None, most likely fail with serialization self.resample() def resample(self): @@ -186,3 +190,18 @@ def dgammadashdash_by_dcoeff_vjp(self, v): def dgammadashdashdash_by_dcoeff_vjp(self, v): return self.curve.dgammadashdashdash_by_dcoeff_vjp(v) + + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["curve"] = self.curve.as_dict() + d["sample"] = self.sample.as_dict() + return d + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + curve = decoder.process_decoded(d["curve"]) + sample = decoder.process_decoded(d["sample"]) + return cls(curve, sample) diff --git a/src/simsopt/geo/curverzfourier.py b/src/simsopt/geo/curverzfourier.py index 8b2d4a619..408023c70 100644 --- a/src/simsopt/geo/curverzfourier.py +++ b/src/simsopt/geo/curverzfourier.py @@ -47,3 +47,20 @@ def set_dofs(self, dofs): """ self.local_x = dofs sopp.CurveRZFourier.set_dofs(self, dofs) + + def as_method(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["quadpoints"] = self.quadpoints + d["order"] = self.order + d["nfp"] = self.nfp + d["stellsym"] = self.stellsym + return d + + @classmethod + def from_dict(cls, d): + return cls(d["quadpoints"], + d["order"], + d["nfp"], + d["stellsym"]) diff --git a/src/simsopt/geo/curvexyzfourier.py b/src/simsopt/geo/curvexyzfourier.py index 3fda885db..c97336bf1 100644 --- a/src/simsopt/geo/curvexyzfourier.py +++ b/src/simsopt/geo/curvexyzfourier.py @@ -96,6 +96,18 @@ def load_curves_from_file(filename, order=None, ppp=20, delimiter=','): coils[ic].local_x = np.concatenate(dofs) return coils + def as_method(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["quadpoints"] = self.quadpoints + d["order"] = self.order + return d + + @classmethod + def from_dict(cls, d): + return cls(d["quadpoints", d["order"]]) + def jaxfouriercurve_pure(dofs, quadpoints, order): k = len(dofs)//3 @@ -153,3 +165,15 @@ def set_dofs_impl(self, dofs): counter += 1 self.coefficients[i][2*j] = dofs[counter] counter += 1 + + def as_method(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["quadpoints"] = self.quadpoints + d["order"] = self.order + return d + + @classmethod + def from_dict(cls, d): + return cls(d["quadpoints"], d["order"]) diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index c9352cb0f..d9c97dccc 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -376,8 +376,8 @@ class Beale(Optimizable): https://en.wikipedia.org/wiki/Test_functions_for_optimization """ - def __init__(self): - x = np.zeros(2) + def __init__(self, x0=None): + x = np.zeros(2) if not x0 else x0 super().__init__(x0=x) def J(self): @@ -386,3 +386,4 @@ def J(self): return np.array([1.5 - x + x * y, 2.25 - x + x * y * y, 2.625 - x + x * y * y * y]) + From 3a47cb3a4a3a2756ef07e6b808cf144d1476a383 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 19:34:19 -0400 Subject: [PATCH 36/95] Test scaffolding for curve serialization --- tests/geo/test_curve.py | 9 +++++++++ tests/geo/test_curveperturbed.py | 3 +++ 2 files changed, 12 insertions(+) diff --git a/tests/geo/test_curve.py b/tests/geo/test_curve.py index 1f878b09f..682230b3a 100644 --- a/tests/geo/test_curve.py +++ b/tests/geo/test_curve.py @@ -442,6 +442,15 @@ def test_rotated_curve_gamma_impl(self): rc.gamma_impl(tmp, quadpoints[:10]) assert np.allclose(cg[:10, :]@mat, tmp) + def subtest_serialization(self, curvetype, rotated): + raise NotImplementedError + + def test_serialization(self): + for curvetype in self.curvetypes: + for rotated in [True, False]: + with self.subTest(curvetype=curvetype, rotated=rotated): + self.subtest_serialization(curvetype, rotated) + if __name__ == "__main__": unittest.main() diff --git a/tests/geo/test_curveperturbed.py b/tests/geo/test_curveperturbed.py index d0afa841a..c03569b6f 100644 --- a/tests/geo/test_curveperturbed.py +++ b/tests/geo/test_curveperturbed.py @@ -133,3 +133,6 @@ def test_perturbed_objective_distance(self): # print("err_new %s" % (err_new)) assert err_new < 0.55 * err err = err_new + + def test_serialization(self): + raise NotImplementedError \ No newline at end of file From fef51a027962b651a20d72ca7264a29a31e28b05 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 22:43:40 -0400 Subject: [PATCH 37/95] Tests for curve serialization are added --- src/simsopt/geo/curvehelical.py | 9 ++++++--- src/simsopt/geo/curverzfourier.py | 15 +++++++++------ src/simsopt/geo/curvexyzfourier.py | 18 ++++++++++++------ tests/geo/test_curve.py | 11 ++++++++++- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/simsopt/geo/curvehelical.py b/src/simsopt/geo/curvehelical.py index 24de5ba1f..22ad0f6d4 100644 --- a/src/simsopt/geo/curvehelical.py +++ b/src/simsopt/geo/curvehelical.py @@ -64,15 +64,18 @@ def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - d["quadpoints"] = self.quadpoints + d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["n0"] = self.n0 d["l0"] = self.l0 d["R0"] = self.R0 d["r0"] = self.r0 + d["x0"] = list(self.local_full_x) return d @classmethod def from_dict(cls, d): - return cls(d["quadpoints, "], d["order"], d["n0"], - d["l0"], d["R0"], d["r0"]) \ No newline at end of file + curve = cls(d["quadpoints"], d["order"], d["n0"], d["l0"], d["R0"], + d["r0"]) + curve.local_full_x = d["x0"] + return curve \ No newline at end of file diff --git a/src/simsopt/geo/curverzfourier.py b/src/simsopt/geo/curverzfourier.py index 408023c70..6cbb2cce8 100644 --- a/src/simsopt/geo/curverzfourier.py +++ b/src/simsopt/geo/curverzfourier.py @@ -48,19 +48,22 @@ def set_dofs(self, dofs): self.local_x = dofs sopp.CurveRZFourier.set_dofs(self, dofs) - def as_method(self) -> dict: + def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - d["quadpoints"] = self.quadpoints + d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["nfp"] = self.nfp d["stellsym"] = self.stellsym + d["x0"] = list(self.local_full_x) return d @classmethod def from_dict(cls, d): - return cls(d["quadpoints"], - d["order"], - d["nfp"], - d["stellsym"]) + curve = cls(d["quadpoints"], + d["order"], + d["nfp"], + d["stellsym"]) + curve.local_full_x = d["x0"] + return curve diff --git a/src/simsopt/geo/curvexyzfourier.py b/src/simsopt/geo/curvexyzfourier.py index c97336bf1..6edb101d3 100644 --- a/src/simsopt/geo/curvexyzfourier.py +++ b/src/simsopt/geo/curvexyzfourier.py @@ -96,17 +96,20 @@ def load_curves_from_file(filename, order=None, ppp=20, delimiter=','): coils[ic].local_x = np.concatenate(dofs) return coils - def as_method(self) -> dict: + def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - d["quadpoints"] = self.quadpoints + d["quadpoints"] = list(self.quadpoints) d["order"] = self.order + d["x0"] = list(self.local_full_x) return d @classmethod def from_dict(cls, d): - return cls(d["quadpoints", d["order"]]) + curve = cls(d["quadpoints"], d["order"]) + curve.local_full_x = d["x0"] + return curve def jaxfouriercurve_pure(dofs, quadpoints, order): @@ -166,14 +169,17 @@ def set_dofs_impl(self, dofs): self.coefficients[i][2*j] = dofs[counter] counter += 1 - def as_method(self) -> dict: + def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - d["quadpoints"] = self.quadpoints + d["quadpoints"] = list(self.quadpoints) d["order"] = self.order + d["x0"] = list(self.local_full_x) return d @classmethod def from_dict(cls, d): - return cls(d["quadpoints"], d["order"]) + curve = cls(d["quadpoints"], d["order"]) + curve.local_full_x = d["x0"] + return curve diff --git a/tests/geo/test_curve.py b/tests/geo/test_curve.py index 682230b3a..f9d05822f 100644 --- a/tests/geo/test_curve.py +++ b/tests/geo/test_curve.py @@ -1,6 +1,9 @@ import logging import unittest +import json + import numpy as np +from monty.json import MontyEncoder, MontyDecoder from simsopt.geo.curvexyzfourier import CurveXYZFourier, JaxCurveXYZFourier from simsopt.geo.curverzfourier import CurveRZFourier @@ -443,7 +446,13 @@ def test_rotated_curve_gamma_impl(self): assert np.allclose(cg[:10, :]@mat, tmp) def subtest_serialization(self, curvetype, rotated): - raise NotImplementedError + epss = [0.5**i for i in range(10, 15)] + x = np.asarray([0.6] + [0.6 + eps for eps in epss]) + curve = get_curve(curvetype, rotated, x) + + curve_json_str = json.dumps(curve, cls=MontyEncoder) + curve_regen = json.loads(curve_json_str, cls=MontyDecoder) + self.assertTrue(np.allclose(curve.gamma(), curve_regen.gamma())) def test_serialization(self): for curvetype in self.curvetypes: From 45669a1b085a8e9bd5ff906a81ea17abc5670f1b Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 22:44:34 -0400 Subject: [PATCH 38/95] Support for loading and saving simsopt objects to json files --- src/simsopt/_core/optimizable.py | 55 +++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 9a051678c..894b1ae4d 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -16,13 +16,16 @@ from collections import defaultdict from numbers import Real, Integral from typing import Union, Tuple, Dict, Callable, Sequence, \ - MutableSequence as MutSeq, List + MutableSequence as MutSeq, List, Literal from functools import lru_cache import logging import json +from pathlib import Path +from fnmatch import fnmatch import numpy as np from monty.json import MSONable, MontyDecoder +from monty.io import zopen from ..util.dev import SimsoptRequires from ..util.types import RealArray, StrArray, BoolArray, Key @@ -1324,6 +1327,56 @@ def from_dict(cls, d): parents.append(decoder.process_decoded(pdict)) return cls(depends_on=parents, **d) + def save(self, filename=None, fmt=None, **kwargs): + filename = filename or "" + fmt = "" if fmt is None else fmt.lower() + fname = Path(filename).name + + if fmt == "json" or fnmatch(fname.lower(), "*.json"): + s = json.dumps(self.as_dict(), **kwargs) + if filename: + with zopen(filename, "wt") as f: + f.write(s) + return s + else: + raise ValueError(f"Invalid format: `{str(fmt)}`") + + @classmethod + def from_str(cls, input_str: str, fmt=Literal["json"]): + fmt_low = fmt.lower() + if fmt_low == "json": + d = json.loads(input_str, cls=MontyDecoder) + else: + raise ValueError(f"Invalid format: `{str(fmt)}`") + + @classmethod + def from_file(cls, filename: str): + fname = Path(filename).name + if fnmatch(filename, "*.json*") or fnmatch(fname, "*.bson*"): + with zopen(filename, "rt") as f: + contents = f.read() + return cls.from_str(contents, fmt="json") + +def loadfn(filename, *args, **kwargs): + """ + Function to load simsopt object from a file. + Only JSON format is supported at this time. Support for additional + formats will be added in future + Args: + filename: + Name of file from which simsopt object has to be initialized + Returns: + Simsopt object + """ + fname = Path(filename).suffix.lower() + if (not fname == '.json'): + raise ValueError(f"Invalid format: `{str(fname[1:])}`") + + with zopen(filename, "rt") as fp: + if "cls" not in kwargs: + kwargs["cls"] = MontyDecoder + return json.load(fp, *args, **kwargs) + def make_optimizable(func, *args, dof_indicators=None, **kwargs): """ From 2b4b2c73bf18e54d8d2f8a47e02274876d0b1804 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 25 Apr 2022 23:15:52 -0400 Subject: [PATCH 39/95] Update fix_local to local_fix_all --- docs/source/example_coils.rst | 4 ++-- docs/source/example_islands.rst | 2 +- docs/source/example_quasisymmetry.rst | 6 +++--- docs/source/example_vmec_only.rst | 4 ++-- docs/source/optimizable.rst | 2 +- examples/2_Intermediate/QH_fixed_resolution.py | 2 +- examples/2_Intermediate/QH_fixed_resolution_boozer.py | 2 +- examples/2_Intermediate/QSC.py | 2 +- examples/2_Intermediate/eliminate_magnetic_islands.py | 2 +- examples/2_Intermediate/resolution_increase.py | 2 +- examples/2_Intermediate/resolution_increase_boozer.py | 2 +- examples/2_Intermediate/stage_two_optimization.py | 2 +- .../2_Intermediate/stage_two_optimization_stochastic.py | 2 +- examples/2_Intermediate/vmec_adjoint.py | 2 +- .../3_Advanced/optimize_qs_and_islands_simultaneously.py | 2 +- .../1DOF_circularCrossSection_varyAxis_targetIota.py | 2 +- .../1DOF_circularCrossSection_varyAxis_targetIota_spec.py | 2 +- .../1DOF_circularCrossSection_varyR0_targetVolume.py | 2 +- .../1DOF_circularCrossSection_varyR0_targetVolume_spec.py | 2 +- ...cularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py | 2 +- .../2DOF_specOnly_targetIotaAndVolume.py | 2 +- examples/stellarator_benchmarks/2DOF_vmecAndSpec.py | 2 +- .../2DOF_vmecOnly_targetIotaAndVolume.py | 2 +- examples/stellarator_benchmarks/7dof.py | 2 +- src/simsopt/_core/optimizable.py | 1 + tests/geo/test_surfacehenneberg.py | 2 +- 26 files changed, 30 insertions(+), 29 deletions(-) diff --git a/docs/source/example_coils.rst b/docs/source/example_coils.rst index 78e66b9d5..ce4293255 100644 --- a/docs/source/example_coils.rst +++ b/docs/source/example_coils.rst @@ -110,10 +110,10 @@ optimizer can "cheat" by making all the currents go to zero, which makes the quadratic flux vanish. To close this loophole, we can fix the current of the first base coil:: - base_currents[0].fix_local() + base_currents[0].fix_all() (A ``Current`` object only has one degree of freedom, hence we can use -``fix_local()``.) If you wish, you can fix the currents in all the +``fix_all()``.) If you wish, you can fix the currents in all the coils to force them to have the same value. Now the full set of 16 coils can be obtained using stellarator symmetry and field-period symmetry:: diff --git a/docs/source/example_islands.rst b/docs/source/example_islands.rst index ec4af12b4..9c548a0e2 100644 --- a/docs/source/example_islands.rst +++ b/docs/source/example_islands.rst @@ -50,7 +50,7 @@ available to vary:: Now we can pick out a few modes of the boundary shape to vary in the optimization:: - s.boundary.fix_local() + s.boundary.fix_all() s.boundary.unfix('zs(6,1)') s.boundary.unfix('zs(6,2)') diff --git a/docs/source/example_quasisymmetry.rst b/docs/source/example_quasisymmetry.rst index 5b276df62..79df22571 100644 --- a/docs/source/example_quasisymmetry.rst +++ b/docs/source/example_quasisymmetry.rst @@ -85,7 +85,7 @@ property of the boundary's Fourier modes as follows:: # Define parameter space: surf = vmec.boundary - surf.fix_local() + surf.fix_all() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) @@ -296,7 +296,7 @@ and toroidal mode numbers are set to be varied in the optimization:: ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_local() + surf.fix_all() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius @@ -419,7 +419,7 @@ toroidal mode numbers are set to be varied in the optimization:: ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_local() + surf.fix_all() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/docs/source/example_vmec_only.rst b/docs/source/example_vmec_only.rst index a4f30dde7..18454c5ed 100644 --- a/docs/source/example_vmec_only.rst +++ b/docs/source/example_vmec_only.rst @@ -76,7 +76,7 @@ case, the independent variables are two of the Fourier amplitudes defining the boundary. We choose the independent variables as follows:: surf = equil.boundary - surf.fix_local() + surf.fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') @@ -160,7 +160,7 @@ The complete example is then as follows:: surf = equil.boundary # You can choose which parameters are optimized by setting their 'fixed' attributes. - surf.fix_local() + surf.fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 76367ebcc..6f5484fb9 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -366,7 +366,7 @@ automatically removed from the global state vector :obj:`~simsopt._core.optimizable.Optimizable.x` of a child object:: - >>> curve.fix_local() + >>> curve.local_fix_all() >>> curve.unfix('zc(0)') >>> coil.x diff --git a/examples/2_Intermediate/QH_fixed_resolution.py b/examples/2_Intermediate/QH_fixed_resolution.py index 2fef7c20b..c50e85525 100755 --- a/examples/2_Intermediate/QH_fixed_resolution.py +++ b/examples/2_Intermediate/QH_fixed_resolution.py @@ -27,7 +27,7 @@ # Define parameter space: surf = vmec.boundary -surf.fix_local() +surf.local_fix_all() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) diff --git a/examples/2_Intermediate/QH_fixed_resolution_boozer.py b/examples/2_Intermediate/QH_fixed_resolution_boozer.py index 3b1335c19..ea08085d0 100755 --- a/examples/2_Intermediate/QH_fixed_resolution_boozer.py +++ b/examples/2_Intermediate/QH_fixed_resolution_boozer.py @@ -23,7 +23,7 @@ # Define parameter space: surf = vmec.boundary -surf.fix_local() +surf.local_fix_all() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) diff --git a/examples/2_Intermediate/QSC.py b/examples/2_Intermediate/QSC.py index 93db91c85..c84b41af7 100755 --- a/examples/2_Intermediate/QSC.py +++ b/examples/2_Intermediate/QSC.py @@ -39,7 +39,7 @@ def get_max_elongation(self): print('Names of the dofs: ', stel.names) # Decide which degrees of freedom to optimize -stel.fix_local() +stel.local_fix_all() stel.unfix('rc(1)') stel.unfix('zs(1)') stel.unfix('etabar') diff --git a/examples/2_Intermediate/eliminate_magnetic_islands.py b/examples/2_Intermediate/eliminate_magnetic_islands.py index 5b39bf48f..c96cab0dc 100755 --- a/examples/2_Intermediate/eliminate_magnetic_islands.py +++ b/examples/2_Intermediate/eliminate_magnetic_islands.py @@ -34,7 +34,7 @@ # To make this example run relatively quickly, we will optimize in a # small parameter space. Here we pick out just 2 Fourier modes to vary # in the optimization: -s.boundary.fix_local() +s.boundary.local_fix_all() s.boundary.unfix('zs(6,1)') s.boundary.unfix('zs(6,2)') diff --git a/examples/2_Intermediate/resolution_increase.py b/examples/2_Intermediate/resolution_increase.py index f9cdc735c..6af1e8331 100755 --- a/examples/2_Intermediate/resolution_increase.py +++ b/examples/2_Intermediate/resolution_increase.py @@ -61,7 +61,7 @@ ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_local() + surf.local_fix_all() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/2_Intermediate/resolution_increase_boozer.py b/examples/2_Intermediate/resolution_increase_boozer.py index c2fd15cfd..0082a008e 100755 --- a/examples/2_Intermediate/resolution_increase_boozer.py +++ b/examples/2_Intermediate/resolution_increase_boozer.py @@ -67,7 +67,7 @@ ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.fix_local() + surf.local_fix_all() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/2_Intermediate/stage_two_optimization.py b/examples/2_Intermediate/stage_two_optimization.py index 2e737b7a7..4fab1449b 100755 --- a/examples/2_Intermediate/stage_two_optimization.py +++ b/examples/2_Intermediate/stage_two_optimization.py @@ -92,7 +92,7 @@ # Since the target field is zero, one possible solution is just to set all # currents to 0. To avoid the minimizer finding that solution, we fix one # of the currents: -base_currents[0].fix_local() +base_currents[0].local_fix_all() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) bs = BiotSavart(coils) diff --git a/examples/2_Intermediate/stage_two_optimization_stochastic.py b/examples/2_Intermediate/stage_two_optimization_stochastic.py index 943057969..22d37565f 100755 --- a/examples/2_Intermediate/stage_two_optimization_stochastic.py +++ b/examples/2_Intermediate/stage_two_optimization_stochastic.py @@ -119,7 +119,7 @@ def pprint(*args, **kwargs): # Since the target field is zero, one possible solution is just to set all # currents to 0. To avoid the minimizer finding that solution, we fix one # of the currents: -base_currents[0].fix_local() +base_currents[0].local_fix_all() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) bs = BiotSavart(coils) diff --git a/examples/2_Intermediate/vmec_adjoint.py b/examples/2_Intermediate/vmec_adjoint.py index e63a0cb1e..53aa57283 100755 --- a/examples/2_Intermediate/vmec_adjoint.py +++ b/examples/2_Intermediate/vmec_adjoint.py @@ -47,7 +47,7 @@ obj = IotaTargetMetric(vmec, target_function, adjoint_epsilon) surf = vmec.boundary -surf.fix_local() +surf.local_fix_all() # Slowly increase range of modes in optimization space for max_mode in range(3, maxres): surf.fixed_range(mmin=0, mmax=max_mode, diff --git a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py index 783c7b03e..73ec53c1c 100755 --- a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py +++ b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py @@ -36,7 +36,7 @@ spec.boundary = surf # Define parameter space: -surf.fix_local() +surf.local_fix_all() surf.fixed_range(mmin=0, mmax=3, nmin=-3, nmax=3, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py index 001b3bd34..add05652d 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py @@ -50,7 +50,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('Delta(1,-1)') # Each function we want in the objective function is then equipped diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py index 8552e09e4..9ea943d7a 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py @@ -50,7 +50,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('Delta(1,-1)') # Each function we want in the objective function is then equipped diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py index ce0ca2401..de2a756e2 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py @@ -52,7 +52,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py index f28623219..e01ec9208 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py @@ -55,7 +55,7 @@ # Surface parameters are all non-fixed by default. You can choose # which parameters are optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py index 7b896a123..744832fa6 100755 --- a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py +++ b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py @@ -17,7 +17,7 @@ vmec = Vmec(os.path.join(os.path.dirname(__file__), 'inputs', 'input.2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry')) # Define parameter space: -vmec.boundary.fix_local() +vmec.boundary.local_fix_all() vmec.boundary.unfix("rc(0,1)") vmec.boundary.unfix("zs(0,1)") diff --git a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py index 17b19c6fe..b4cb21c71 100755 --- a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py @@ -43,7 +43,7 @@ # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py index ef88eaa9d..c6f76b365 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py +++ b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py @@ -46,7 +46,7 @@ # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py index 591aecc25..2da205896 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py @@ -45,7 +45,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.fix_local() +surf.local_fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/7dof.py b/examples/stellarator_benchmarks/7dof.py index 187027101..7d5c29fca 100755 --- a/examples/stellarator_benchmarks/7dof.py +++ b/examples/stellarator_benchmarks/7dof.py @@ -26,7 +26,7 @@ vmec.boundary = surf # Define parameter space: -surf.fix_local() +surf.local_fix_all() surf.fix_range(mmin=0, mmax=2, nmin=-1, nmax=1, fixed=False) surf.fix("Delta(1,0)") # toroidally-averaged major radius surf.fix("Delta(0,0)") # toroidally-averaged minor radius diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 894b1ae4d..189ce4dfe 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1357,6 +1357,7 @@ def from_file(cls, filename: str): contents = f.read() return cls.from_str(contents, fmt="json") + def loadfn(filename, *args, **kwargs): """ Function to load simsopt object from a file. diff --git a/tests/geo/test_surfacehenneberg.py b/tests/geo/test_surfacehenneberg.py index 529e93f53..427f122a8 100755 --- a/tests/geo/test_surfacehenneberg.py +++ b/tests/geo/test_surfacehenneberg.py @@ -112,7 +112,7 @@ def test_fixed_range(self): np.testing.assert_equal(surf.local_dofs_free_status, [False, True, True, False, True, True, True, False, True, True, False, True]) - surf.fix_local() + surf.local_fix_all() surf.fixed_range(0, 1, False) np.testing.assert_equal(surf.local_dofs_free_status, [True, True, True, False, False, True, From a36f172bd25bc8d0c0e76eb4d5c865749d6cacb2 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 27 Apr 2022 11:34:20 -0400 Subject: [PATCH 40/95] Test for perturbed curve serialization --- src/simsopt/geo/curveperturbed.py | 15 +++++++++------ tests/geo/test_curveperturbed.py | 28 +++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/simsopt/geo/curveperturbed.py b/src/simsopt/geo/curveperturbed.py index 67796ba55..9c0b9ac0e 100644 --- a/src/simsopt/geo/curveperturbed.py +++ b/src/simsopt/geo/curveperturbed.py @@ -93,25 +93,28 @@ class PerturbationSample(MSONable): gd = sample[1] # get the first derivative of the perturbation """ - def __init__(self, sampler, randomgen=None): + def __init__(self, sampler, randomgen=None, sample=None): self.sampler = sampler self.randomgen = randomgen # If not None, most likely fail with serialization - self.resample() + if sample: + self._sample = sample + else: + self.resample() def resample(self): - self.__sample = self.sampler.draw_sample(self.randomgen) + self._sample = self.sampler.draw_sample(self.randomgen) def __getitem__(self, deriv): """ Get the perturbation (if ``deriv=0``) or its ``deriv``-th derivative. """ assert isinstance(deriv, int) - if deriv >= len(self.__sample): + if deriv >= len(self._sample): raise ValueError(""" -The sample on has {len(self.__sample)-1} derivatives. +The sample on has {len(self._sample)-1} derivatives. Adjust the `n_derivs` parameter of the sampler to access higher derivatives. """) - return self.__sample[deriv] + return self._sample[deriv] class CurvePerturbed(sopp.Curve, Curve): diff --git a/tests/geo/test_curveperturbed.py b/tests/geo/test_curveperturbed.py index c03569b6f..25a1a00ae 100644 --- a/tests/geo/test_curveperturbed.py +++ b/tests/geo/test_curveperturbed.py @@ -1,8 +1,12 @@ import unittest -from simsopt.geo.curveperturbed import GaussianSampler, PerturbationSample, CurvePerturbed -from simsopt.geo.curvexyzfourier import CurveXYZFourier from randomgen import PCG64 +import json + import numpy as np +from monty.json import MontyDecoder, MontyEncoder + +from simsopt.geo.curveperturbed import GaussianSampler, PerturbationSample, CurvePerturbed +from simsopt.geo.curvexyzfourier import CurveXYZFourier from simsopt.geo.curveobjectives import LpCurveTorsion, CurveCurveDistance @@ -135,4 +139,22 @@ def test_perturbed_objective_distance(self): err = err_new def test_serialization(self): - raise NotImplementedError \ No newline at end of file + sigma = 1 + length_scale = 0.5 + points = np.linspace(0, 1, 200, endpoint=False) + sampler = GaussianSampler(points, sigma, length_scale, n_derivs=2) + sample = PerturbationSample(sampler) + + order = 4 + nquadpoints = 200 + curve = CurveXYZFourier(nquadpoints, order) + dofs = np.zeros((curve.dof_size,)) + dofs[1] = 1. + dofs[2 * order + 3] = 1. + dofs[4 * order + 3] = 1. + curve.x = dofs + curve_per = CurvePerturbed(curve, sample) + + curve_str = json.dumps(curve_per, cls=MontyEncoder) + curve_per_regen = json.loads(curve_str, cls=MontyDecoder) + self.assertTrue(np.allclose(curve_per.gamma(), curve_per_regen.gamma())) From aef7eb2e44230b781ef1f281dc29af3d523365bd Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 29 Apr 2022 17:31:43 -0400 Subject: [PATCH 41/95] Serialization of coils --- src/simsopt/_core/optimizable.py | 11 ++-- src/simsopt/field/coil.py | 43 ++++++++-------- tests/field/test_coil.py | 87 ++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 24 deletions(-) create mode 100644 tests/field/test_coil.py diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 189ce4dfe..25a3c39b9 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1317,14 +1317,19 @@ def as_dict(self) -> dict: return d - @classmethod - def from_dict(cls, d): + @staticmethod + def _decode(d): parents_dict = d.pop("depends_on") if "depends_on" in d else None if parents_dict: parents = [] decoder = MontyDecoder() for pdict in parents_dict: parents.append(decoder.process_decoded(pdict)) + return parents + + @classmethod + def from_dict(cls, d): + parents = Optimizable._decode(d) return cls(depends_on=parents, **d) def save(self, filename=None, fmt=None, **kwargs): @@ -1358,7 +1363,7 @@ def from_file(cls, filename: str): return cls.from_str(contents, fmt="json") -def loadfn(filename, *args, **kwargs): +def load_simsopt(filename, *args, **kwargs): """ Function to load simsopt object from a file. Only JSON format is supported at this time. Support for additional diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index f8c4a9fb4..690ac467f 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -5,7 +5,7 @@ import simsoptpp as sopp from math import pi import numpy as np -from monty.json import MontyDecoder +from monty.json import MontyDecoder, MSONable class Coil(sopp.Coil, Optimizable): @@ -16,8 +16,8 @@ class Coil(sopp.Coil, Optimizable): """ def __init__(self, curve, current): - self.__curve = curve - self.__current = current + self._curve = curve + self._current = current sopp.Coil.__init__(self, curve, current) Optimizable.__init__(self, x0=np.asarray([]), depends_on=[curve, current]) @@ -36,19 +36,13 @@ def plot(self, **kwargs): return self.curve.plot(**kwargs) def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["curve"] = self.curve.as_dict() - d["current"] = self.current.as_dict() - del d["current"]["@module"] - del d["current"]["@class"] - return d + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): - current = Current.from_dict(d["current"]) - curve = MontyDecoder().process_decoded(d["curve"]) + decoder = MontyDecoder() + current = decoder.process_decoded(d["current"]) + curve = decoder.process_decoded(d["curve"]) return cls(curve, current) @@ -74,16 +68,12 @@ def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - current = self.get_value() - if hasattr(self, 'scale'): - current *= self.scale - d["current"] = current + d["current"] = self.get_value() return d @classmethod def from_dict(cls, d): - current = d["current"] - return Current(current) + return cls(d["current"]) class ScaledCurrent(sopp.ScaledCurrent, Optimizable): @@ -93,16 +83,27 @@ class ScaledCurrent(sopp.ScaledCurrent, Optimizable): """ def __init__(self, basecurrent, scale): - self.__basecurrent = basecurrent + self._basecurrent = basecurrent sopp.ScaledCurrent.__init__(self, basecurrent, scale) Optimizable.__init__(self, x0=np.asarray([]), depends_on=[basecurrent]) def vjp(self, v_current): - return self.__basecurrent.vjp(self.scale * v_current) + return self._basecurrent.vjp(self.scale * v_current) def __neg__(self): return ScaledCurrent(self, -1.) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["current"] = self.get_value() + return d + + @classmethod + def from_dict(cls, d): + return cls(Current(d["current"]), 1.0) + def coils_via_symmetries(curves, currents, nfp, stellsym): """ diff --git a/tests/field/test_coil.py b/tests/field/test_coil.py new file mode 100644 index 000000000..3f6e0c9ea --- /dev/null +++ b/tests/field/test_coil.py @@ -0,0 +1,87 @@ +import unittest +import json + +import numpy as np +from monty.json import MontyEncoder, MontyDecoder + +from simsopt.geo.curvexyzfourier import CurveXYZFourier, JaxCurveXYZFourier +from simsopt.geo.curverzfourier import CurveRZFourier +from simsopt.geo.curvehelical import CurveHelical +from simsopt.geo.curve import RotatedCurve +from simsopt.field.coil import Coil, Current, ScaledCurrent +from simsopt.field.biotsavart import BiotSavart + + +def get_curve(curvetype, rotated, x=np.asarray([0.5])): + np.random.seed(2) + rand_scale = 0.01 + order = 4 + + if curvetype == "CurveXYZFourier": + curve = CurveXYZFourier(x, order) + elif curvetype == "JaxCurveXYZFourier": + curve = JaxCurveXYZFourier(x, order) + elif curvetype == "CurveRZFourier": + curve = CurveRZFourier(x, order, 2, True) + elif curvetype == "CurveHelical": + curve = CurveHelical(x, order, 5, 2, 1.0, 0.3) + else: + assert False + dofs = np.zeros((curve.dof_size, )) + if curvetype in ["CurveXYZFourier", "JaxCurveXYZFourier"]: + dofs[1] = 1. + dofs[2*order + 3] = 1. + dofs[4*order + 3] = 1. + elif curvetype in ["CurveRZFourier"]: + dofs[0] = 1. + dofs[1] = 0.1 + dofs[order+1] = 0.1 + elif curvetype in ["CurveHelical"]: + dofs[0] = np.pi/2 + else: + assert False + + curve.x = dofs + rand_scale * np.random.rand(len(dofs)).reshape(dofs.shape) + if rotated: + curve = RotatedCurve(curve, 0.5, flip=False) + return curve + +class TestCoil(unittest.TestCase): + + curvetypes = ["CurveXYZFourier", "JaxCurveXYZFourier", "CurveRZFourier", "CurveHelical"] + + def subtest_serialization(self, curvetype, rotated): + epss = [0.5**i for i in range(10, 15)] + x = np.asarray([0.6] + [0.6 + eps for eps in epss]) + curve = get_curve(curvetype, rotated, x) + + for current in (Current(1e4), ScaledCurrent(Current(1e4), 4)): + coil = Coil(curve, current) + coil_str = json.dumps(coil, cls=MontyEncoder) + coil_regen = json.loads(coil_str, cls=MontyDecoder) + + points = np.asarray(10 * [[-1.41513202e-03, 8.99999382e-01, -3.14473221e-04]]) + B1 = BiotSavart([coil]).set_points(points).B() + B2 = BiotSavart([coil_regen]).set_points(points).B() + self.assertTrue(np.allclose(B1, B2)) + + def test_serialization(self): + for curvetype in self.curvetypes: + for rotated in [True, False]: + with self.subTest(curvetype=curvetype, rotated=rotated): + self.subtest_serialization(curvetype, rotated) + +class TestCurrent(unittest.TestCase): + def test_current_serialization(self): + current = Current(1e4) + current_str = json.dumps(current, cls=MontyEncoder) + current_regen = json.loads(current_str, cls=MontyDecoder) + self.assertAlmostEqual(current.get_value(), current_regen.get_value()) + + def test_scaled_current_serialization(self): + current = Current(1e4) + scaled_current = ScaledCurrent(current, 3) + current_str = json.dumps(scaled_current, cls=MontyEncoder) + current_regen = json.loads(current_str, cls=MontyDecoder) + self.assertAlmostEqual(scaled_current.get_value(), + current_regen.get_value()) From b8e7b6f4c03009acd444d25e434e7509ecef255b Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Sat, 30 Apr 2022 12:22:56 -0400 Subject: [PATCH 42/95] Serialization of surface classes --- src/simsopt/field/biotsavart.py | 32 ++++++--- src/simsopt/field/magneticfield.py | 17 +++++ src/simsopt/field/magneticfieldclasses.py | 80 ++++++++++++++++++++++ src/simsopt/geo/boozersurface.py | 32 +++++---- src/simsopt/geo/qfmsurface.py | 14 +++- src/simsopt/geo/surface.py | 19 ++++- src/simsopt/geo/surfacegarabedian.py | 15 ++++ src/simsopt/geo/surfacehenneberg.py | 13 ++++ src/simsopt/geo/surfacerzfourier.py | 21 ++++++ src/simsopt/geo/surfacexyzfourier.py | 14 ++++ src/simsopt/geo/surfacexyztensorfourier.py | 17 +++++ src/simsoptpp/python_surfaces.cpp | 3 +- 12 files changed, 249 insertions(+), 28 deletions(-) diff --git a/src/simsopt/field/biotsavart.py b/src/simsopt/field/biotsavart.py index a8235f036..92fd2259c 100644 --- a/src/simsopt/field/biotsavart.py +++ b/src/simsopt/field/biotsavart.py @@ -1,4 +1,6 @@ import numpy as np +from monty.json import MSONable, MontyDecoder + import simsoptpp as sopp from .magneticfield import MagneticField @@ -19,14 +21,14 @@ class BiotSavart(sopp.BiotSavart, MagneticField): """ def __init__(self, coils): - self.__coils = coils + self._coils = coils sopp.BiotSavart.__init__(self, coils) MagneticField.__init__(self, depends_on=coils) def dB_by_dcoilcurrents(self, compute_derivatives=0): points = self.get_points_cart_ref() npoints = len(points) - ncoils = len(self.__coils) + ncoils = len(self._coils) if any([not self.fieldcache_get_status(f'B_{i}') for i in range(ncoils)]): assert compute_derivatives >= 0 self.compute(compute_derivatives) @@ -36,7 +38,7 @@ def dB_by_dcoilcurrents(self, compute_derivatives=0): def d2B_by_dXdcoilcurrents(self, compute_derivatives=1): points = self.get_points_cart_ref() npoints = len(points) - ncoils = len(self.__coils) + ncoils = len(self._coils) if any([not self.fieldcache_get_status(f'dB_{i}') for i in range(ncoils)]): assert compute_derivatives >= 1 self.compute(compute_derivatives) @@ -46,7 +48,7 @@ def d2B_by_dXdcoilcurrents(self, compute_derivatives=1): def d3B_by_dXdXdcoilcurrents(self, compute_derivatives=2): points = self.get_points_cart_ref() npoints = len(points) - ncoils = len(self.__coils) + ncoils = len(self._coils) if any([not self.fieldcache_get_status(f'ddB_{i}') for i in range(ncoils)]): assert compute_derivatives >= 2 self.compute(compute_derivatives) @@ -62,7 +64,7 @@ def B_and_dB_vjp(self, v, vgrad): \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{B}_i \}_k, \{ \sum_{i=1}^{n} {\mathbf{v}_\mathrm{grad}}_i \cdot \partial_{\mathbf{c}_k} \nabla \mathbf{B}_i \}_k. """ - coils = self.__coils + coils = self._coils gammas = [coil.curve.gamma() for coil in coils] gammadashs = [coil.curve.gammadash() for coil in coils] currents = [coil.current.get_value() for coil in coils] @@ -100,7 +102,7 @@ def B_vjp(self, v): """ - coils = self.__coils + coils = self._coils gammas = [coil.curve.gamma() for coil in coils] gammadashs = [coil.curve.gammadash() for coil in coils] currents = [coil.current.get_value() for coil in coils] @@ -117,7 +119,7 @@ def B_vjp(self, v): def dA_by_dcoilcurrents(self, compute_derivatives=0): points = self.get_points_cart_ref() npoints = len(points) - ncoils = len(self.__coils) + ncoils = len(self._coils) if any([not self.fieldcache_get_status(f'A_{i}') for i in range(ncoils)]): assert compute_derivatives >= 0 self.compute(compute_derivatives) @@ -127,7 +129,7 @@ def dA_by_dcoilcurrents(self, compute_derivatives=0): def d2A_by_dXdcoilcurrents(self, compute_derivatives=1): points = self.get_points_cart_ref() npoints = len(points) - ncoils = len(self.__coils) + ncoils = len(self._coils) if any([not self.fieldcache_get_status(f'dA_{i}') for i in range(ncoils)]): assert compute_derivatives >= 1 self.compute(compute_derivatives) @@ -137,7 +139,7 @@ def d2A_by_dXdcoilcurrents(self, compute_derivatives=1): def d3A_by_dXdXdcoilcurrents(self, compute_derivatives=2): points = self.get_points_cart_ref() npoints = len(points) - ncoils = len(self.__coils) + ncoils = len(self._coils) if any([not self.fieldcache_get_status(f'ddA_{i}') for i in range(ncoils)]): assert compute_derivatives >= 2 self.compute(compute_derivatives) @@ -153,7 +155,7 @@ def A_and_dA_vjp(self, v, vgrad): \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{A}_i \}_k, \{ \sum_{i=1}^{n} {\mathbf{v}_\mathrm{grad}}_i \cdot \partial_{\mathbf{c}_k} \nabla \mathbf{A}_i \}_k. """ - coils = self.__coils + coils = self._coils gammas = [coil.curve.gamma() for coil in coils] gammadashs = [coil.curve.gammadash() for coil in coils] currents = [coil.current.get_value() for coil in coils] @@ -191,7 +193,7 @@ def A_vjp(self, v): """ - coils = self.__coils + coils = self._coils gammas = [coil.curve.gamma() for coil in coils] gammadashs = [coil.curve.gammadash() for coil in coils] currents = [coil.current.get_value() for coil in coils] @@ -204,3 +206,11 @@ def A_vjp(self, v): dA_by_dcoilcurrents = self.dA_by_dcoilcurrents() res_current = [np.sum(v * dA_by_dcoilcurrents[i]) for i in range(len(dA_by_dcoilcurrents))] return sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]) + + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + coils = MontyDecoder().process_decoded(d["coils"]) + return cls(coils) diff --git a/src/simsopt/field/magneticfield.py b/src/simsopt/field/magneticfield.py index fa2756178..ecfaa3a92 100644 --- a/src/simsopt/field/magneticfield.py +++ b/src/simsopt/field/magneticfield.py @@ -1,4 +1,5 @@ import numpy as np +from monty.json import MontyDecoder, MSONable import simsoptpp as sopp from .._core.optimizable import Optimizable @@ -123,6 +124,14 @@ def _dA_by_dX_impl(self, dA): def _d2A_by_dXdX_impl(self, ddA): ddA[:] = self.scalar*self.Bfield.d2A_by_dXdX() + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + Bfield = MontyDecoder().process_decoded(d["Bfield"]) + return cls(d["scalar"], Bfield) + class MagneticFieldSum(MagneticField): """ @@ -160,3 +169,11 @@ def _d2A_by_dXdX_impl(self, ddA): def B_vjp(self, v): return sum([bf.B_vjp(v) for bf in self.Bfields if np.any(bf.dofs_free_status)]) + + @classmethod + def from_dict(cls, d): + Bfields = Optimizable._decode(d) + return cls(Bfields) + + + diff --git a/src/simsopt/field/magneticfieldclasses.py b/src/simsopt/field/magneticfieldclasses.py index 7fd09bf4a..b6120f402 100644 --- a/src/simsopt/field/magneticfieldclasses.py +++ b/src/simsopt/field/magneticfieldclasses.py @@ -106,6 +106,17 @@ def _d2A_by_dXdX_impl(self, ddA): (points[:, 0]**4-points[:, 1]**4)/(2*points[:, 2]), np.zeros((len(points)))]], np.zeros((3, 3, len(points)))])).transpose((3, 0, 1, 2)) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["R0"] = self.R0 + d["B0"] = self.B0 + + @classmethod + def from_dict(cls, d): + return cls(d["R0"], d["B0"]) + class PoloidalField(MagneticField): ''' @@ -201,6 +212,18 @@ def _dB_by_dX_impl(self, dB): dB[:] = self.B0/self.R0/self.q*np.array([dB_by_dX1_term1+dB_by_dX1_term2, dB_by_dX2_term1+dB_by_dX2_term2, dB_by_dX3_term1+dB_by_dX3_term2]).T + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["R0"] = self.R0 + d["B0"] = self.B0 + d["q"] = self.q + + @classmethod + def from_dict(cls, d): + return cls(d["R0"], d["B0"], d["q"]) + class ScalarPotentialRZMagneticField(MagneticField): """ @@ -287,6 +310,17 @@ def _dB_by_dX_impl(self, dB): + Bphi * dcosphidy dB[:, 2, 1] = dBrdz * np.sin(phi) + dBphidz * np.cos(phi) + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["phi_str"] = self.phi_str + return d + + @classmethod + def from_dict(cls, d): + return cls(d["phi_str"]) + class CircularCoil(MagneticField): ''' @@ -438,6 +472,20 @@ def _A_impl(self, A): ((points[:, 0]**2+points[:, 1]**2+1e-31)*np.sqrt(self.r0**2+points[:, 0]**2+points[:, 1]**2+2*self.r0*np.sqrt(points[:, 0]**2+points[:, 1]**2)+points[:, 2]**2+1e-31)) * np.array([-points[:, 1], points[:, 0], 0])).T) + def as_dict(self): + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["r0"] = self.r0 + d["center"] = self.center + d["I"] = self.Inorm * 25e5 + d["normal"] = self.normal + return d + + @classmethod + def from_dict(cls, d): + return cls(d["r0"], d["center"], d["I"], d["normal"]) + class Dommaschk(MagneticField): """ @@ -470,6 +518,19 @@ def _dB_by_dX_impl(self, dB): points = self.get_points_cart_ref() dB[:] = np.add.reduce(sopp.DommaschkdB(self.m, self.n, self.coeffs, points))+self.Btor.dB_by_dX() + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + mn = [list(self.m), list(self.n)] + d["mn"] = mn + d["coeffs"] = self.coeffs + return d + + @classmethod + def from_dict(cls, d): + return cls(d["mn"], d["coeffs"]) + class Reiman(MagneticField): ''' @@ -502,6 +563,25 @@ def _dB_by_dX_impl(self, dB): points = self.get_points_cart_ref() dB[:] = sopp.ReimandB(self.iota0, self.iota1, self.k, self.epsilonk, self.m0, points) + def as_dict(self): + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["iota0"] = self.iota0 + d["iota1"] = self.iota1 + d["k"] = self.k + d["epsilonk"] = self.epsilonk + d["m0"] = self.m0 + return d + + @classmethod + def from_dict(cls, d): + return cls(d["iota0"], + d["iota1"], + d["k"], + d["epsilonk"], + d["m0"]) + class UniformInterpolationRule(sopp.UniformInterpolationRule): pass diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index 3bfe64b5b..db2de1ea0 100644 --- a/src/simsopt/geo/boozersurface.py +++ b/src/simsopt/geo/boozersurface.py @@ -1,9 +1,11 @@ from scipy.optimize import minimize, least_squares import numpy as np +from monty.json import MontyDecoder, MSONable + from simsopt.geo.surfaceobjectives import boozer_surface_residual -class BoozerSurface(): +class BoozerSurface(MSONable): r""" BoozerSurface and its associated methods can be used to compute the Boozer angles on a surface. It takes a Surface representation (e.g. SurfaceXYZFourier, @@ -36,7 +38,7 @@ class BoozerSurface(): """ def __init__(self, biotsavart, surface, label, targetlabel): - self.bs = biotsavart + self.biotsavart = biotsavart self.surface = surface self.label = label self.targetlabel = targetlabel @@ -75,11 +77,11 @@ def boozer_penalty_constraints(self, x, derivatives=0, constraint_weight=1., sca nsurfdofs = sdofs.size s = self.surface - bs = self.bs + biotsavart = self.biotsavart s.set_dofs(sdofs) - boozer = boozer_surface_residual(s, iota, G, bs, derivatives=derivatives) + boozer = boozer_surface_residual(s, iota, G, biotsavart, derivatives=derivatives) r = boozer[0] @@ -160,11 +162,11 @@ def boozer_exact_constraints(self, xl, derivatives=0, optimize_G=True): G = None lm = xl[-2:] s = self.surface - bs = self.bs + biotsavart = self.biotsavart s.set_dofs(sdofs) nsurfdofs = sdofs.size - boozer = boozer_surface_residual(s, iota, G, bs, derivatives=derivatives+1) + boozer = boozer_surface_residual(s, iota, G, biotsavart, derivatives=derivatives+1) r, J = boozer[0:2] dl = np.zeros((xl.shape[0]-2,)) @@ -498,10 +500,10 @@ def solve_residual_equation_exactly_newton(self, tol=1e-10, maxiter=10, iota=0., label = self.label if G is None: - G = 2. * np.pi * np.sum(np.abs(self.bs.coil_currents)) * (4 * np.pi * 10**(-7) / (2 * np.pi)) + G = 2. * np.pi * np.sum(np.abiotsavart(self.biotsavart.coil_currents)) * (4 * np.pi * 10**(-7) / (2 * np.pi)) x = np.concatenate((s.get_dofs(), [iota, G])) i = 0 - r, J = boozer_surface_residual(s, iota, G, self.bs, derivatives=1) + r, J = boozer_surface_residual(s, iota, G, self.biotsavart, derivatives=1) norm = 1e6 while i < maxiter: if s.stellsym: @@ -529,9 +531,15 @@ def solve_residual_equation_exactly_newton(self, tol=1e-10, maxiter=10, iota=0., iota = x[-2] G = x[-1] i += 1 - r, J = boozer_surface_residual(s, iota, G, self.bs, derivatives=1) + r, J = boozer_surface_residual(s, iota, G, self.biotsavart, derivatives=1) - res = { - "residual": r, "jacobian": J, "iter": i, "success": norm <= tol, "G": G, "s": s, "iota": iota - } + res = {"residual": r, "jacobian": J, "iter": i, "success": norm <= tol, + "G": G, "s": s, "iota": iota} return res + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + bs = decoder.process_decoded(d["biotsavart"]) + surf = deocder.process_decoded(d["surface"]) + return cls(bs, surf, d["label"], d["targetlabel"] \ No newline at end of file diff --git a/src/simsopt/geo/qfmsurface.py b/src/simsopt/geo/qfmsurface.py index 61b2ea9ab..22f820b35 100644 --- a/src/simsopt/geo/qfmsurface.py +++ b/src/simsopt/geo/qfmsurface.py @@ -1,9 +1,11 @@ from scipy.optimize import minimize, least_squares, NonlinearConstraint import numpy as np +from monty.json import MontyDecoder, MSONable + from simsopt.geo.surfaceobjectives import QfmResidual -class QfmSurface(): +class QfmSurface(MSONable): r""" QfmSurface is used to compute a quadratic-flux minimizing surface, defined as the minimum of the objective function, @@ -26,7 +28,7 @@ class QfmSurface(): """ def __init__(self, biotsavart, surface, label, targetlabel): - self.bs = biotsavart + self.biotsavart = biotsavart self.surface = surface self.label = label self.targetlabel = targetlabel @@ -90,7 +92,6 @@ def qfm_penalty_constraints(self, x, derivatives=0, constraint_weight=1): assert derivatives in [0, 1] s = self.surface - bs = self.bs s.x = x qfm = self.qfm @@ -203,3 +204,10 @@ def minimize_qfm(self, tol=1e-3, maxiter=1000, method='SLSQP', tol=tol, maxiter=maxiter, constraint_weight=constraint_weight) else: raise ValueError + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + bs = decoder.process_decoded(d["biotsavart"]) + surf = decoder.process_decoded(d["surface"]) + return cls(bs, surf, d["label"], d["targetlabel"]) \ No newline at end of file diff --git a/src/simsopt/geo/surface.py b/src/simsopt/geo/surface.py index 784167714..a9ed81d4c 100644 --- a/src/simsopt/geo/surface.py +++ b/src/simsopt/geo/surface.py @@ -1,7 +1,7 @@ import abc import numpy as np - +from monty.json import MSONable, MontyDecoder try: from pyevtk.hl import gridToVTK @@ -502,6 +502,15 @@ def interpolate_on_arclength_grid(self, function, theta_evaluate): return function_interpolated + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["nfp"] = self.nfp + d["quadpoints_phi"] = list(self.quadpoints_phi) + d["quadpoints_theta"] = list(self.quadpoints_theta) + return d + def signed_distance_from_surface(xyz, surface): """ @@ -632,3 +641,11 @@ def update_fixed(self): self.unfix(j) else: self.fix(j) + + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + surf = MontyDecoder.process_decoded(d["surf"]) + return cls(surf, d["scale_factors"]) diff --git a/src/simsopt/geo/surfacegarabedian.py b/src/simsopt/geo/surfacegarabedian.py index 223bf2d6a..4a4e9f33e 100644 --- a/src/simsopt/geo/surfacegarabedian.py +++ b/src/simsopt/geo/surfacegarabedian.py @@ -242,6 +242,21 @@ def volume(self): self.area_volume() return self._volume + def as_dict(self) -> dict: + d = Surface.as_dict(self) + d["mmax"] = self.mmax + d["mmin"] = self.mmin + d["nmax"] = self.nmax + d["nmin"] = self.nmin + return d + + @classmethod + def from_dict(cls, d): + return cls(nfp=d["nfp"], mmax=d["mmax"], mmin=d["mmin"], + nmax=d["nmax"], nmin=d["nmin"], + quadpoints_phi=d["quadpoints_phi"], + quadpoints_theta = d["quadpoints_theta"]) + return_fn_map = {'area': area, 'volume': volume, 'aspect-ratio': Surface.aspect_ratio} diff --git a/src/simsopt/geo/surfacehenneberg.py b/src/simsopt/geo/surfacehenneberg.py index 863733c53..dcd6fa839 100644 --- a/src/simsopt/geo/surfacehenneberg.py +++ b/src/simsopt/geo/surfacehenneberg.py @@ -740,4 +740,17 @@ def gammadash2_impl(self, data): data[:, :, 1] = (2 * np.pi * d_R_d_theta * np.sin(phi)).T data[:, :, 2] = 2 * np.pi * d_Z_d_theta.T + def as_dict(self) -> dict: + d = Surface.as_dict(self) + d["alpha_fac"] = self.alpha_fac + d["mmax"] = self.mmax + d["nmax"] = self.nmax + return d + + @classmethod + def from_dict(cls, d): + return cls(nfp=d["nfp"], alpha_fac=d["alpha_fac"], + mmax=d["mmax"], nmax=d["nmax"], + quadpoints_phi=d["quadpoints_phi"], + quadpoints_theta=d["quadpoints_theta"]) diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index f693b6449..a2c422ab4 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -579,6 +579,20 @@ def write_nml(self, filename: str): with open(filename, 'w') as f: f.write(self.get_nml()) + def as_dict(self) -> dict: + d = Surface.as_dict(self) + d["stellsym"] = self.stellsym + d["mpol"] = self.mpol + d["ntor"] = self.ntor + return d + + @classmethod + def from_dict(cls, d): + return cls(nfp=d["nfp"], stellsym=d["stellsym"], + mpol=d["mpol"], ntor=d["ntor"], + quadpoints_phi=d["quadpoints_phi"], + quadpoints_theta=d["quadpoints_theta"]) + return_fn_map = {'area': sopp.SurfaceRZFourier.area, 'volume': sopp.SurfaceRZFourier.volume, 'aspect-ratio': Surface.aspect_ratio} @@ -826,3 +840,10 @@ def change_resolution(self, mpol, ntor): r_shift=self.r_shift, a_scale=self.a_scale) return surf3 + + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + return cls(d["mpol"], d["ntor"], d["nfp"], d["r_shift"], d["a_scale"]) diff --git a/src/simsopt/geo/surfacexyzfourier.py b/src/simsopt/geo/surfacexyzfourier.py index bcc040c20..7fa820c4c 100644 --- a/src/simsopt/geo/surfacexyzfourier.py +++ b/src/simsopt/geo/surfacexyzfourier.py @@ -112,6 +112,20 @@ def to_RZFourier(self): surf.least_squares_fit(gamma) return surf + def as_dict(self) -> dict: + d = Surface.as_dict(self) + d["mpol"] = self.mpol + d["ntor"] = self.ntor + d["stellsym"] = self.stellsym + return d + + @classmethod + def from_dict(cls, d): + return cls(nfp=d["nfp"], stellsym=d["stellsym"], + mpol=d["mpol"], ntor=d["ntor"], + quadpoints_phi=d["quadpoints_phi"], + quadpoints_theta=d["quadpoints_theta"]) + return_fn_map = {'area': sopp.SurfaceXYZFourier.area, 'volume': sopp.SurfaceXYZFourier.volume, 'aspect-ratio': Surface.aspect_ratio} diff --git a/src/simsopt/geo/surfacexyztensorfourier.py b/src/simsopt/geo/surfacexyztensorfourier.py index 90b2f4e9a..196441918 100644 --- a/src/simsopt/geo/surfacexyztensorfourier.py +++ b/src/simsopt/geo/surfacexyztensorfourier.py @@ -160,3 +160,20 @@ def npsame(a, b): npsame(thetas, np.linspace(0, 1, 2*mpol+1, endpoint=False)): mask[0, mpol+1:] = False return mask + + def as_dict(self) -> dict: + d = Surface.as_dict(self) + d["stellsym"] = self.stellsym + d["mpol"] = self.mpol + d["ntor"] = self.ntor + d["clamped_dims"] = list(self.clamped_dims) + return d + + @classmethod + def from_dict(cls, d): + return cls(nfp=d["nfp"], stellsym=d["stellsym"], + mpol=d["mpol"], ntor=d["ntor"], + clamped_dims=d["clamped_dims"], + quadpoints_phi=d["quadpoints_phi"], + quadpoints_theta=d["quadpoints_theta"]) + diff --git a/src/simsoptpp/python_surfaces.cpp b/src/simsoptpp/python_surfaces.cpp index 5e4ab83be..814b2843a 100644 --- a/src/simsoptpp/python_surfaces.cpp +++ b/src/simsoptpp/python_surfaces.cpp @@ -187,6 +187,7 @@ void init_surfaces(py::module_ &m){ .def_readwrite("ntor", &PySurfaceXYZTensorFourier::ntor) .def_readwrite("mpol", &PySurfaceXYZTensorFourier::mpol) .def_readwrite("nfp", &PySurfaceXYZTensorFourier::nfp) - .def_readwrite("stellsym", &PySurfaceXYZTensorFourier::stellsym); + .def_readwrite("stellsym", &PySurfaceXYZTensorFourier::stellsym) + .def_readwrite("clamped_dims", &PySurfaceXYZTensorFourier::clamped_dims); register_common_surface_methods(pysurfacexyztensorfourier); } From 7d64959252f667eede9d6454c848e3f04846ed91 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Sat, 30 Apr 2022 12:43:40 -0400 Subject: [PATCH 43/95] Bug fix in boozersurface --- src/simsopt/geo/boozersurface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index db2de1ea0..547f566bb 100644 --- a/src/simsopt/geo/boozersurface.py +++ b/src/simsopt/geo/boozersurface.py @@ -542,4 +542,4 @@ def from_dict(cls, d): decoder = MontyDecoder() bs = decoder.process_decoded(d["biotsavart"]) surf = deocder.process_decoded(d["surface"]) - return cls(bs, surf, d["label"], d["targetlabel"] \ No newline at end of file + return cls(bs, surf, d["label"], d["targetlabel"]) \ No newline at end of file From 7c4db988ed4a0af3d9817e5edc3d266f27331f2f Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Sat, 30 Apr 2022 12:45:25 -0400 Subject: [PATCH 44/95] Bug fix in boozersurface --- src/simsopt/geo/boozersurface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index 547f566bb..a30ae125c 100644 --- a/src/simsopt/geo/boozersurface.py +++ b/src/simsopt/geo/boozersurface.py @@ -541,5 +541,5 @@ def solve_residual_equation_exactly_newton(self, tol=1e-10, maxiter=10, iota=0., def from_dict(cls, d): decoder = MontyDecoder() bs = decoder.process_decoded(d["biotsavart"]) - surf = deocder.process_decoded(d["surface"]) + surf = decoder.process_decoded(d["surface"]) return cls(bs, surf, d["label"], d["targetlabel"]) \ No newline at end of file From 8cbb16a122bed618274cdd1c62916a049b325c3e Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Sat, 30 Apr 2022 12:48:04 -0400 Subject: [PATCH 45/95] Linter fixes --- src/simsopt/geo/boozersurface.py | 2 +- src/simsopt/geo/surfacegarabedian.py | 2 +- tests/field/test_coil.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index a30ae125c..c742d8bf1 100644 --- a/src/simsopt/geo/boozersurface.py +++ b/src/simsopt/geo/boozersurface.py @@ -534,7 +534,7 @@ def solve_residual_equation_exactly_newton(self, tol=1e-10, maxiter=10, iota=0., r, J = boozer_surface_residual(s, iota, G, self.biotsavart, derivatives=1) res = {"residual": r, "jacobian": J, "iter": i, "success": norm <= tol, - "G": G, "s": s, "iota": iota} + "G": G, "s": s, "iota": iota} return res @classmethod diff --git a/src/simsopt/geo/surfacegarabedian.py b/src/simsopt/geo/surfacegarabedian.py index 4a4e9f33e..912a941be 100644 --- a/src/simsopt/geo/surfacegarabedian.py +++ b/src/simsopt/geo/surfacegarabedian.py @@ -255,7 +255,7 @@ def from_dict(cls, d): return cls(nfp=d["nfp"], mmax=d["mmax"], mmin=d["mmin"], nmax=d["nmax"], nmin=d["nmin"], quadpoints_phi=d["quadpoints_phi"], - quadpoints_theta = d["quadpoints_theta"]) + quadpoints_theta=d["quadpoints_theta"]) return_fn_map = {'area': area, 'volume': volume, diff --git a/tests/field/test_coil.py b/tests/field/test_coil.py index 3f6e0c9ea..335e93782 100644 --- a/tests/field/test_coil.py +++ b/tests/field/test_coil.py @@ -46,6 +46,7 @@ def get_curve(curvetype, rotated, x=np.asarray([0.5])): curve = RotatedCurve(curve, 0.5, flip=False) return curve + class TestCoil(unittest.TestCase): curvetypes = ["CurveXYZFourier", "JaxCurveXYZFourier", "CurveRZFourier", "CurveHelical"] @@ -71,6 +72,7 @@ def test_serialization(self): with self.subTest(curvetype=curvetype, rotated=rotated): self.subtest_serialization(curvetype, rotated) + class TestCurrent(unittest.TestCase): def test_current_serialization(self): current = Current(1e4) From 55141ba161a13745939152b703a0948d737b81d3 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 2 May 2022 17:42:58 -0400 Subject: [PATCH 46/95] Serialization tests added for some of the surface classes --- src/simsopt/_core/optimizable.py | 2 +- src/simsopt/geo/surface.py | 7 +++--- src/simsopt/geo/surfacerzfourier.py | 6 +++-- src/simsopt/geo/surfacexyzfourier.py | 6 +++-- tests/geo/test_surface_rzfourier.py | 20 +++++++++++++++ tests/geo/test_surface_xyzfourier.py | 37 ++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 25a3c39b9..4861bfca5 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1309,7 +1309,7 @@ def as_dict(self) -> dict: d["fixed"] = list(np.logical_not(self.local_dofs_free_status)) d["lower_bounds"] = list(self.local_full_lower_bounds) d["upper_bounds"] = list(self.local_full_upper_bounds) - d["external_dof_setter"] = self.local_dof_setter + # d["external_dof_setter"] = self.local_dof_setter if self.parents: d["depends_on"] = [] for parent in self.parents: diff --git a/src/simsopt/geo/surface.py b/src/simsopt/geo/surface.py index a9ed81d4c..e35d2f239 100644 --- a/src/simsopt/geo/surface.py +++ b/src/simsopt/geo/surface.py @@ -503,9 +503,10 @@ def interpolate_on_arclength_grid(self, function, theta_evaluate): return function_interpolated def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ + d = super().as_dict() + # d = {} + # d["@module"] = self.__class__.__module__ + # d["@class"] = self.__class__.__name__ d["nfp"] = self.nfp d["quadpoints_phi"] = list(self.quadpoints_phi) d["quadpoints_theta"] = list(self.quadpoints_theta) diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index a2c422ab4..c7b02a514 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -580,7 +580,7 @@ def write_nml(self, filename: str): f.write(self.get_nml()) def as_dict(self) -> dict: - d = Surface.as_dict(self) + d = super().as_dict() d["stellsym"] = self.stellsym d["mpol"] = self.mpol d["ntor"] = self.ntor @@ -588,10 +588,12 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - return cls(nfp=d["nfp"], stellsym=d["stellsym"], + surf = cls(nfp=d["nfp"], stellsym=d["stellsym"], mpol=d["mpol"], ntor=d["ntor"], quadpoints_phi=d["quadpoints_phi"], quadpoints_theta=d["quadpoints_theta"]) + surf.set_dofs(d["x0"]) + return surf return_fn_map = {'area': sopp.SurfaceRZFourier.area, 'volume': sopp.SurfaceRZFourier.volume, diff --git a/src/simsopt/geo/surfacexyzfourier.py b/src/simsopt/geo/surfacexyzfourier.py index 7fa820c4c..4e2837c39 100644 --- a/src/simsopt/geo/surfacexyzfourier.py +++ b/src/simsopt/geo/surfacexyzfourier.py @@ -113,7 +113,7 @@ def to_RZFourier(self): return surf def as_dict(self) -> dict: - d = Surface.as_dict(self) + d = super().as_dict() d["mpol"] = self.mpol d["ntor"] = self.ntor d["stellsym"] = self.stellsym @@ -121,10 +121,12 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - return cls(nfp=d["nfp"], stellsym=d["stellsym"], + surf = cls(nfp=d["nfp"], stellsym=d["stellsym"], mpol=d["mpol"], ntor=d["ntor"], quadpoints_phi=d["quadpoints_phi"], quadpoints_theta=d["quadpoints_theta"]) + surf.set_dofs(d["x0"]) + return surf return_fn_map = {'area': sopp.SurfaceXYZFourier.area, 'volume': sopp.SurfaceXYZFourier.volume, diff --git a/tests/geo/test_surface_rzfourier.py b/tests/geo/test_surface_rzfourier.py index f97c1e363..f0b294e45 100755 --- a/tests/geo/test_surface_rzfourier.py +++ b/tests/geo/test_surface_rzfourier.py @@ -1,7 +1,9 @@ import unittest from pathlib import Path +import json import numpy as np +from monty.json import MontyDecoder, MontyEncoder from simsopt.geo.surfacerzfourier import SurfaceRZFourier, SurfaceRZPseudospectral @@ -562,6 +564,24 @@ def test_mn_matches_names(self): names2 = [f'({m},{n})' for m, n in zip(surf.m, surf.n)] self.assertEqual(names, names2) + def test_serialization(self): + """ + Test the serialization of an axisymmetric surface using area and volume + """ + s = SurfaceRZFourier() + s.rc[0, 0] = 1.3 + s.rc[1, 0] = 0.4 + s.zs[1, 0] = 0.2 + # TODO: x should be updated whenever rc and zs are modified without + # TODO: explict setting of x + s.local_full_x = s.get_dofs() + + surf_str = json.dumps(s, cls=MontyEncoder) + s_regen = json.loads(surf_str, cls=MontyDecoder) + + self.assertAlmostEqual(s.area(), s_regen.area(), places=4) + self.assertAlmostEqual(s.volume(), s_regen.volume(), places=3) + class SurfaceRZPseudospectralTests(unittest.TestCase): def test_names(self): diff --git a/tests/geo/test_surface_xyzfourier.py b/tests/geo/test_surface_xyzfourier.py index 21f2771e6..8d854290a 100755 --- a/tests/geo/test_surface_xyzfourier.py +++ b/tests/geo/test_surface_xyzfourier.py @@ -1,6 +1,8 @@ import unittest +import json import numpy as np +from monty.json import MontyDecoder, MontyEncoder from simsopt.geo.surfacexyzfourier import SurfaceXYZFourier from .surface_test_helpers import get_surface, get_exact_surface @@ -286,6 +288,41 @@ def test_to_vtk(self): s.to_vtk('/tmp/surface') + def test_serialization(self): + mpol = 4 + ntor = 3 + nfp = 2 + phis = np.linspace(0, 1, 31, endpoint=False) + thetas = np.linspace(0, 1, 31, endpoint=False) + + np.random.seed(0) + + stellsym = False + s = SurfaceXYZFourier(mpol=mpol, ntor=ntor, nfp=nfp, stellsym=stellsym, + quadpoints_phi=phis, quadpoints_theta=thetas) + s.xc = s.xc * 0 + s.xs = s.xs * 0 + s.ys = s.ys * 0 + s.yc = s.yc * 0 + s.zs = s.zs * 0 + s.zc = s.zc * 0 + r1 = np.random.random_sample() + 0.1 + r2 = np.random.random_sample() + 0.1 + major_R = np.max([r1, r2]) + minor_R = np.min([r1, r2]) + s.xc[0, ntor] = major_R + s.xc[1, ntor] = minor_R + s.zs[1, ntor] = minor_R + # TODO: x should be updated whenever [x,y,z][c,s] are modified without + # TODO: explict setting of local_full_x + s.local_full_x = s.get_dofs() + + surf_str = json.dumps(s, cls=MontyEncoder) + s_regen = json.loads(surf_str, cls=MontyDecoder) + + self.assertAlmostEqual(s.area(), s_regen.area(), places=4) + self.assertAlmostEqual(s.volume(), s_regen.volume(), places=3) + if __name__ == "__main__": unittest.main() From 3fcfe0dbe394cfa306a5245d851199e25afe141a Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 2 May 2022 22:24:28 -0400 Subject: [PATCH 47/95] Serialization for surfacehennberg class --- src/simsopt/geo/surfacehenneberg.py | 6 ++++-- tests/geo/test_surfacehenneberg.py | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/simsopt/geo/surfacehenneberg.py b/src/simsopt/geo/surfacehenneberg.py index dcd6fa839..d3e667192 100644 --- a/src/simsopt/geo/surfacehenneberg.py +++ b/src/simsopt/geo/surfacehenneberg.py @@ -741,7 +741,7 @@ def gammadash2_impl(self, data): data[:, :, 2] = 2 * np.pi * d_Z_d_theta.T def as_dict(self) -> dict: - d = Surface.as_dict(self) + d = super().as_dict() d["alpha_fac"] = self.alpha_fac d["mmax"] = self.mmax d["nmax"] = self.nmax @@ -749,8 +749,10 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - return cls(nfp=d["nfp"], alpha_fac=d["alpha_fac"], + surf = cls(nfp=d["nfp"], alpha_fac=d["alpha_fac"], mmax=d["mmax"], nmax=d["nmax"], quadpoints_phi=d["quadpoints_phi"], quadpoints_theta=d["quadpoints_theta"]) + surf.local_full_x = d["x0"] + return surf diff --git a/tests/geo/test_surfacehenneberg.py b/tests/geo/test_surfacehenneberg.py index 427f122a8..69a9a3ac1 100755 --- a/tests/geo/test_surfacehenneberg.py +++ b/tests/geo/test_surfacehenneberg.py @@ -1,8 +1,11 @@ import unittest import os import logging +import json from pathlib import Path + import numpy as np +from monty.json import MontyEncoder, MontyDecoder from simsopt.geo.surfacehenneberg import SurfaceHenneberg @@ -267,6 +270,25 @@ def test_vmec(self): # not have actually used the converted boundary. self.assertTrue(np.max(np.abs(iota1 - iota2)) > 1e-12) + def test_serialization(self): + R0 = 1.5 + a = 0.3 + for nfp in range(1, 3): + for alpha_fac in [-1, 0, 1]: + for mmax in range(1, 3): + for nmax in range(3): + surfH = SurfaceHenneberg(nfp=nfp, alpha_fac=alpha_fac, + mmax=mmax, nmax=nmax) + surfH.R0nH[0] = R0 + surfH.bn[0] = a + surfH.set_rhomn(1, 0, a) + surfH.local_full_x = surfH.get_dofs() + surf_str = json.dumps(surfH, cls=MontyEncoder) + surfH_regen = json.loads(surf_str, cls=MontyDecoder) + self.assertAlmostEqual(surfH.area(), surfH_regen.area(), + places=4) + self.assertAlmostEqual(surfH.volume(), surfH_regen.volume(), + places=3) if __name__ == "__main__": unittest.main() From acab7df2cfe42b4a5a2a908e39fd9f3219d68bf1 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 2 May 2022 22:25:37 -0400 Subject: [PATCH 48/95] Bug fixes in serialization of surface classes --- src/simsopt/geo/surfacegarabedian.py | 6 ++++-- src/simsopt/geo/surfacerzfourier.py | 13 ++++++++----- src/simsopt/geo/surfacexyztensorfourier.py | 6 ++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/simsopt/geo/surfacegarabedian.py b/src/simsopt/geo/surfacegarabedian.py index 912a941be..9d130c718 100644 --- a/src/simsopt/geo/surfacegarabedian.py +++ b/src/simsopt/geo/surfacegarabedian.py @@ -243,7 +243,7 @@ def volume(self): return self._volume def as_dict(self) -> dict: - d = Surface.as_dict(self) + d = super().as_dict() d["mmax"] = self.mmax d["mmin"] = self.mmin d["nmax"] = self.nmax @@ -252,10 +252,12 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - return cls(nfp=d["nfp"], mmax=d["mmax"], mmin=d["mmin"], + surf = cls(nfp=d["nfp"], mmax=d["mmax"], mmin=d["mmin"], nmax=d["nmax"], nmin=d["nmin"], quadpoints_phi=d["quadpoints_phi"], quadpoints_theta=d["quadpoints_theta"]) + surf.set_dofs(d["x0"]) + return surf return_fn_map = {'area': area, 'volume': volume, diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index c7b02a514..47ada2728 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -4,6 +4,7 @@ from scipy.io import netcdf from scipy.interpolate import interp1d import f90nml +from monty.json import MSONable import simsoptpp as sopp from .surface import Surface @@ -487,7 +488,7 @@ def set_zc(self, m, n, val): def set_zs(self, m, n, val): """ - Set a particular `zs` Parameter. + Set a particular `zs` Parametfollow uper. """ self._validate_mn(m, n) self.zs[m, n + self.ntor] = val @@ -592,7 +593,7 @@ def from_dict(cls, d): mpol=d["mpol"], ntor=d["ntor"], quadpoints_phi=d["quadpoints_phi"], quadpoints_theta=d["quadpoints_theta"]) - surf.set_dofs(d["x0"]) + surf.local_full_x = d["x0"] return surf return_fn_map = {'area': sopp.SurfaceRZFourier.area, @@ -844,8 +845,10 @@ def change_resolution(self, mpol, ntor): return surf3 def as_dict(self) -> dict: - return MSONable.as_dict(self) - + d = MSONable.as_dict(self) + d["x0"] = list(self.local_full_x) @classmethod def from_dict(cls, d): - return cls(d["mpol"], d["ntor"], d["nfp"], d["r_shift"], d["a_scale"]) + surf = cls(d["mpol"], d["ntor"], d["nfp"], d["r_shift"], d["a_scale"]) + surf.local_full_x = d["x0"] + return surf diff --git a/src/simsopt/geo/surfacexyztensorfourier.py b/src/simsopt/geo/surfacexyztensorfourier.py index 196441918..b097ec05e 100644 --- a/src/simsopt/geo/surfacexyztensorfourier.py +++ b/src/simsopt/geo/surfacexyztensorfourier.py @@ -162,7 +162,7 @@ def npsame(a, b): return mask def as_dict(self) -> dict: - d = Surface.as_dict(self) + d = super().as_dict() d["stellsym"] = self.stellsym d["mpol"] = self.mpol d["ntor"] = self.ntor @@ -171,9 +171,11 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - return cls(nfp=d["nfp"], stellsym=d["stellsym"], + surf = cls(nfp=d["nfp"], stellsym=d["stellsym"], mpol=d["mpol"], ntor=d["ntor"], clamped_dims=d["clamped_dims"], quadpoints_phi=d["quadpoints_phi"], quadpoints_theta=d["quadpoints_theta"]) + surf.set_dofs(d["x0"]) + return surf From ca8d03707037bff1706c9de7e04954df0228a6d5 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 3 May 2022 15:16:03 -0400 Subject: [PATCH 49/95] import serialization routines to top --- src/simsopt/__init__.py | 2 +- src/simsopt/_core/__init__.py | 2 +- src/simsopt/_core/optimizable.py | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/simsopt/__init__.py b/src/simsopt/__init__.py index c2b5e5cee..4f1b81c1e 100644 --- a/src/simsopt/__init__.py +++ b/src/simsopt/__init__.py @@ -10,7 +10,7 @@ # "from xyz import *". If xyz[.py] contains __all__ = ['XYZ'], only XYZ is # imported -from ._core import make_optimizable +from ._core import make_optimizable, Optimizable, load_simsopt, save_simsopt from .objectives import LeastSquaresProblem from .solve import least_squares_serial_solve diff --git a/src/simsopt/_core/__init__.py b/src/simsopt/_core/__init__.py index 583c418af..24e159a71 100644 --- a/src/simsopt/_core/__init__.py +++ b/src/simsopt/_core/__init__.py @@ -1 +1 @@ -from .optimizable import Optimizable, make_optimizable +from .optimizable import Optimizable, make_optimizable, load_simsopt, save_simsopt diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 4861bfca5..53cc53335 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -24,7 +24,7 @@ from fnmatch import fnmatch import numpy as np -from monty.json import MSONable, MontyDecoder +from monty.json import MSONable, MontyDecoder, MontyEncoder from monty.io import zopen from ..util.dev import SimsoptRequires @@ -1384,6 +1384,17 @@ def load_simsopt(filename, *args, **kwargs): return json.load(fp, *args, **kwargs) +def save_simsopt(simsopt_objects, filename, *args, **kwargs): + fname = Path(filename).suffix.lower() + if (not fname == '.json'): + raise ValueError(f"Invalid format: `{str(fname[1:])}`") + + with zopen(filename, "wt") as fp: + if "cls" not in kwargs: + kwargs["cls"] = MontyEncoder + return json.dump(simsopt_objects, fp, *args, **kwargs) + + def make_optimizable(func, *args, dof_indicators=None, **kwargs): """ Factory function to generate an Optimizable instance from a function From b987e3ccdfbc9dbb47ae3efc23f78f996246a5b0 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 3 May 2022 15:17:35 -0400 Subject: [PATCH 50/95] Serialization for field and curve objectives --- src/simsopt/geo/curveobjectives.py | 66 ++++++++++++++++++++++++- src/simsopt/objectives/fluxobjective.py | 15 +++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/simsopt/geo/curveobjectives.py b/src/simsopt/geo/curveobjectives.py index a0159bfa1..7a5fd0d12 100644 --- a/src/simsopt/geo/curveobjectives.py +++ b/src/simsopt/geo/curveobjectives.py @@ -1,11 +1,13 @@ +from deprecated import deprecated + import numpy as np from jax import grad import jax.numpy as jnp from .jit import jit +from monty.json import MontyDecoder, MSONable from .._core.optimizable import Optimizable from .._core.derivative import derivative_dec -from deprecated import deprecated import simsoptpp as sopp @@ -46,6 +48,14 @@ def dJ(self): return self.curve.dincremental_arclength_by_dcoeff_vjp( self.thisgrad(self.curve.incremental_arclength())) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve) + return_fn_map = {'J': J, 'dJ': dJ} @@ -71,6 +81,8 @@ class LpCurveCurvature(Optimizable): def __init__(self, curve, p, threshold=0.): self.curve = curve + self.p = p + self.threshold = threshold super().__init__(depends_on=[curve]) self.J_jax = jit(lambda kappa, gammadash: Lp_curvature_pure(kappa, gammadash, p, threshold)) self.thisgrad0 = jit(lambda kappa, gammadash: grad(self.J_jax, argnums=0)(kappa, gammadash)) @@ -91,6 +103,14 @@ def dJ(self): grad1 = self.thisgrad1(self.curve.kappa(), self.curve.gammadash()) return self.curve.dkappa_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve, d["p"], d["threshold"]) + return_fn_map = {'J': J, 'dJ': dJ} @@ -115,6 +135,8 @@ class LpCurveTorsion(Optimizable): def __init__(self, curve, p, threshold=0.): self.curve = curve + self.p = p + self.threshold = threshold self.J_jax = jit(lambda torsion, gammadash: Lp_torsion_pure(torsion, gammadash, p, threshold)) self.thisgrad0 = jit(lambda torsion, gammadash: grad(self.J_jax, argnums=0)(torsion, gammadash)) self.thisgrad1 = jit(lambda torsion, gammadash: grad(self.J_jax, argnums=1)(torsion, gammadash)) @@ -135,6 +157,14 @@ def dJ(self): grad1 = self.thisgrad1(self.curve.torsion(), self.curve.gammadash()) return self.curve.dtorsion_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve, d["p"], d["threshold"]) + return_fn_map = {'J': J, 'dJ': dJ} @@ -240,6 +270,14 @@ def dJ(self): res = [self.curves[i].dgamma_by_dcoeff_vjp(dgamma_by_dcoeff_vjp_vecs[i]) + self.curves[i].dgammadash_by_dcoeff_vjp(dgammadash_by_dcoeff_vjp_vecs[i]) for i in range(len(self.curves))] return sum(res) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + curves = MontyDecoder().process_decoded(d["curves"]) + return cls(curves, d["minimum_distance"], d["num_basecurves"]) + return_fn_map = {'J': J, 'dJ': dJ} @@ -343,6 +381,16 @@ def dJ(self): res = [self.curves[i].dgamma_by_dcoeff_vjp(dgamma_by_dcoeff_vjp_vecs[i]) + self.curves[i].dgammadash_by_dcoeff_vjp(dgammadash_by_dcoeff_vjp_vecs[i]) for i in range(len(self.curves))] return sum(res) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + curves = decoder.process_decoded(d["curves"]) + surf = decoder.process_decoded(d["surface"]) + return cls(curves, surf, d["minimum_distance"]) + return_fn_map = {'J': J, 'dJ': dJ} @@ -420,6 +468,14 @@ def dJ(self): return self.curve.dincremental_arclength_by_dcoeff_vjp( self.thisgrad(self.curve.incremental_arclength())) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve, d["nintervals"]) + return_fn_map = {'J': J, 'dJ': dJ} @@ -461,6 +517,14 @@ def dJ(self): grad1 = self.thisgrad1(self.curve.kappa(), self.curve.gammadash()) return self.curve.dkappa_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + curve = MontyDecoder().process_decoded(d["curve"]) + return cls(curve) + @deprecated("`MinimumDistance` has been deprecated and will be removed. Please use `CurveCurveDistance` instead.") class MinimumDistance(CurveCurveDistance): diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index 8225cff44..d64f0947d 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -1,7 +1,9 @@ -from simsopt._core.optimizable import Optimizable -from .._core.derivative import derivative_dec import numpy as np +from monty.json import MSONable, MontyDecoder, MontyEncoder + import simsoptpp as sopp +from .._core.optimizable import Optimizable +from .._core.derivative import derivative_dec class SquaredFlux(Optimizable): @@ -52,3 +54,12 @@ def dJ(self): dJdB = (B_n[..., None] * unitn * absn[..., None])/absn.size dJdB = dJdB.reshape((-1, 3)) return self.field.B_vjp(dJdB) + + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + def from_dict(cls, d): + decoder = MontyDecoder() + surface = decoder.process_decoded(d["surface"]) + field = decoder.process_decoded(d["field"]) + return cls(surface, d["target"], field) \ No newline at end of file From 0772a7449fbf040a95b83b3ba8b07c178db1b78b Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 3 May 2022 16:21:11 -0400 Subject: [PATCH 51/95] Linter fixes --- src/simsopt/geo/surfacerzfourier.py | 1 + tests/geo/test_surfacehenneberg.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index 47ada2728..aa0bbe2f1 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -847,6 +847,7 @@ def change_resolution(self, mpol, ntor): def as_dict(self) -> dict: d = MSONable.as_dict(self) d["x0"] = list(self.local_full_x) + @classmethod def from_dict(cls, d): surf = cls(d["mpol"], d["ntor"], d["nfp"], d["r_shift"], d["a_scale"]) diff --git a/tests/geo/test_surfacehenneberg.py b/tests/geo/test_surfacehenneberg.py index 69a9a3ac1..e702c798d 100755 --- a/tests/geo/test_surfacehenneberg.py +++ b/tests/geo/test_surfacehenneberg.py @@ -290,5 +290,6 @@ def test_serialization(self): self.assertAlmostEqual(surfH.volume(), surfH_regen.volume(), places=3) + if __name__ == "__main__": unittest.main() From 33d258acf33df8283e3d33a84138a6e70a5f5f6f Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 3 May 2022 16:21:37 -0400 Subject: [PATCH 52/95] Add print statement to debug magneticfield failure in CI --- tests/field/test_magneticfields.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index 2f7687f67..9f63b3f0a 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -598,6 +598,7 @@ def test_interpolated_field_close_no_sym(self): dBh = bsh.GradAbsB() Bhc = bsh.B_cyl() dBhc = bsh.GradAbsB_cyl() + print(dBc, dBhc) assert np.allclose(B, Bh, rtol=1e-2) assert np.allclose(dB, dBh, rtol=1e-2) assert np.allclose(Bc, Bhc, rtol=1e-2) From d69cde4b77bae526cef0f16f60cac80f892ec35c Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 3 May 2022 17:40:59 -0400 Subject: [PATCH 53/95] Update print in test_magneticfields --- tests/field/test_magneticfields.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index 9f63b3f0a..da7116a5c 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -598,7 +598,8 @@ def test_interpolated_field_close_no_sym(self): dBh = bsh.GradAbsB() Bhc = bsh.B_cyl() dBhc = bsh.GradAbsB_cyl() - print(dBc, dBhc) + print('dBc', dBc) + print('dBhc', dBhc) assert np.allclose(B, Bh, rtol=1e-2) assert np.allclose(dB, dBh, rtol=1e-2) assert np.allclose(Bc, Bhc, rtol=1e-2) From 6d85cf042a1fde33e8381712707c48ec7db45ed2 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Thu, 5 May 2022 09:33:37 -0400 Subject: [PATCH 54/95] Wait to complete dBhc printing --- tests/field/test_magneticfields.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index da7116a5c..fc71754f3 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -592,14 +592,16 @@ def test_interpolated_field_close_no_sym(self): dB = btotal.GradAbsB() B = btotal.B() dBc = btotal.GradAbsB_cyl() + print('dBc', dBc) Bc = btotal.B_cyl() bsh.set_points_cyl(points) Bh = bsh.B() dBh = bsh.GradAbsB() Bhc = bsh.B_cyl() dBhc = bsh.GradAbsB_cyl() - print('dBc', dBc) print('dBhc', dBhc) + import time + time.sleep(5) assert np.allclose(B, Bh, rtol=1e-2) assert np.allclose(dB, dBh, rtol=1e-2) assert np.allclose(Bc, Bhc, rtol=1e-2) From ede2d7c8abcd9d75613369364d9a64acd0a8a960 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Thu, 5 May 2022 10:51:29 -0400 Subject: [PATCH 55/95] Add atol for np.allclose --- tests/field/test_magneticfields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index fc71754f3..809752ac5 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -605,7 +605,7 @@ def test_interpolated_field_close_no_sym(self): assert np.allclose(B, Bh, rtol=1e-2) assert np.allclose(dB, dBh, rtol=1e-2) assert np.allclose(Bc, Bhc, rtol=1e-2) - assert np.allclose(dBc, dBhc, rtol=1e-2) + assert np.allclose(dBc, dBhc, rtol=1e-2, atol=1e-5) def test_interpolated_field_convergence_rate(self): R0test = 1.5 From f90f7b9c26dc78a4876c256a92f0d80500654f9f Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 00:06:22 -0400 Subject: [PATCH 56/95] Tests for serializing scale and adder optimizer classes --- src/simsopt/objectives/functions.py | 7 +++++-- tests/core/test_optimizable.py | 21 +++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index d9c97dccc..31906767e 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -96,6 +96,9 @@ def sum(self): """ return np.sum(self._dofs.full_x) + def J(self): + return self.sum() + def dJ(self): return np.ones(self.n) @@ -376,9 +379,9 @@ class Beale(Optimizable): https://en.wikipedia.org/wiki/Test_functions_for_optimization """ - def __init__(self, x0=None): + def __init__(self, x0=None, **kwargs): x = np.zeros(2) if not x0 else x0 - super().__init__(x0=x) + super().__init__(x0=x, **kwargs) def J(self): x = self.local_full_x[0] diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index a4cd7efdf..da258d189 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -6,9 +6,10 @@ from monty.json import MontyDecoder, MontyEncoder from monty.serialization import loadfn, dumpfn -from simsopt._core.optimizable import Optimizable, make_optimizable +from simsopt._core.optimizable import Optimizable, make_optimizable, \ + ScaledOptimizable, OptimizableSum from simsopt.objectives.functions import Identity, Rosenbrock, TestObject1, \ - TestObject2 + TestObject2, Beale from simsopt.objectives.functions import Adder as FAdder @@ -1228,6 +1229,22 @@ def test_twolevel_serialize(self): test_opt = json.loads(s, cls=MontyDecoder) self.assertAlmostEqual(test_opt.f(), test_opt_orig.f()) + def test_scaled_optimizer_serialize(self): + beale = Beale(x0=[2.2, 3.3]) + scaled_beale = ScaledOptimizable(2.0, beale) + s = json.dumps(scaled_beale, cls=MontyEncoder) + scaled_beale_regen = json.loads(s, cls=MontyDecoder) + self.assertTrue(np.allclose(scaled_beale_regen.J(), 2*beale.J())) + + def test_optimizable_sum_serializer(self): + adder1 = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], + fixed=[True, False, True]) + adder2 = FAdder(n=2, x0=[10, 11], names=["a", "b"], fixed=[True, False]) + opt_sum = OptimizableSum(opts=[adder1, adder2]) + s = json.dumps(opt_sum, cls=MontyEncoder) + opt_sum_regen = json.loads(s, cls=MontyDecoder) + self.assertAlmostEqual(opt_sum_regen.J(), adder1.J() + adder2.J()) + if __name__ == "__main__": unittest.main() From d22c8086a31cf644b66f7c3523b84ed13a31d87e Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 09:14:58 -0400 Subject: [PATCH 57/95] Tests for saving and loading simsopt objects --- src/simsopt/_core/optimizable.py | 6 ++++-- tests/core/test_optimizable.py | 34 +++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 53cc53335..38cf9e259 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1338,6 +1338,8 @@ def save(self, filename=None, fmt=None, **kwargs): fname = Path(filename).name if fmt == "json" or fnmatch(fname.lower(), "*.json"): + if "cls" not in kwargs: + kwargs["cls"] = MontyEncoder s = json.dumps(self.as_dict(), **kwargs) if filename: with zopen(filename, "wt") as f: @@ -1347,10 +1349,10 @@ def save(self, filename=None, fmt=None, **kwargs): raise ValueError(f"Invalid format: `{str(fmt)}`") @classmethod - def from_str(cls, input_str: str, fmt=Literal["json"]): + def from_str(cls, input_str: str, fmt="json"): fmt_low = fmt.lower() if fmt_low == "json": - d = json.loads(input_str, cls=MontyDecoder) + return json.loads(input_str, cls=MontyDecoder) else: raise ValueError(f"Invalid format: `{str(fmt)}`") diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index da258d189..2d7bf0724 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -7,7 +7,7 @@ from monty.serialization import loadfn, dumpfn from simsopt._core.optimizable import Optimizable, make_optimizable, \ - ScaledOptimizable, OptimizableSum + ScaledOptimizable, OptimizableSum, load_simsopt, save_simsopt from simsopt.objectives.functions import Identity, Rosenbrock, TestObject1, \ TestObject2, Beale from simsopt.objectives.functions import Adder as FAdder @@ -1245,6 +1245,38 @@ def test_optimizable_sum_serializer(self): opt_sum_regen = json.loads(s, cls=MontyDecoder) self.assertAlmostEqual(opt_sum_regen.J(), adder1.J() + adder2.J()) + def test_load_save_simsopt(self): + import tempfile + from pathlib import Path + + adder1 = FAdder(n=3, x0=[1, 2, 3], names=["x", "y", "z"], + fixed=[True, False, True]) + adder2 = FAdder(n=2, x0=[10, 11], names=["a", "b"], fixed=[True, False]) + with tempfile.TemporaryDirectory() as tmpdir: + fpath = Path(tmpdir) / "adders.json" + save_simsopt([adder1, adder2], fpath, indent=2) + self.assertTrue(fpath.is_file()) + + adders = load_simsopt(fpath) + self.assertAlmostEqual(adder1.J(), adders[0].J()) + self.assertAlmostEqual(adder2.J(), adders[1].J()) + + with tempfile.TemporaryDirectory() as tmpdir: + fpath = Path(tmpdir) / "adder.json" + adder_str = adder1.save(fpath, indent=2) + self.assertTrue(fpath.is_file()) + + adder1_str_regen = FAdder.from_str(adder_str) + self.assertAlmostEqual(adder1.J(), adder1_str_regen.J()) + adder1_file_regen = FAdder.from_file(fpath) + self.assertAlmostEqual(adder1.J(), adder1_file_regen.J()) + + adder_str1 = adder1.save(fmt='json', indent=2) + adder1_str_regen1 = FAdder.from_str(adder_str1) + self.assertAlmostEqual(adder1.J(), adder1_str_regen1.J()) + + + if __name__ == "__main__": unittest.main() From a71a071ce4643b272ba11300fefd73abc8763cb4 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 09:51:21 -0400 Subject: [PATCH 58/95] Use optimizable in loading opt object --- tests/core/test_optimizable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 2d7bf0724..186512d15 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -1272,7 +1272,7 @@ def test_load_save_simsopt(self): self.assertAlmostEqual(adder1.J(), adder1_file_regen.J()) adder_str1 = adder1.save(fmt='json', indent=2) - adder1_str_regen1 = FAdder.from_str(adder_str1) + adder1_str_regen1 = Optimizable.from_str(adder_str1) self.assertAlmostEqual(adder1.J(), adder1_str_regen1.J()) From a19e02e882e3c4511dffd10bf89f3a4119eff08c Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 10:00:20 -0400 Subject: [PATCH 59/95] Omit Optimizable from top level imports --- src/simsopt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/__init__.py b/src/simsopt/__init__.py index 4f1b81c1e..28470f55b 100644 --- a/src/simsopt/__init__.py +++ b/src/simsopt/__init__.py @@ -10,7 +10,7 @@ # "from xyz import *". If xyz[.py] contains __all__ = ['XYZ'], only XYZ is # imported -from ._core import make_optimizable, Optimizable, load_simsopt, save_simsopt +from ._core import make_optimizable, load_simsopt, save_simsopt from .objectives import LeastSquaresProblem from .solve import least_squares_serial_solve From bd18f30b5723b14542d673141ff921021c12e1ee Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 10:01:44 -0400 Subject: [PATCH 60/95] Merge inconsistencies removed from docs and examples --- docs/source/optimizable.rst | 6 +++--- docs/source/simsopt._core.rst | 2 +- docs/source/simsopt.objectives.rst | 4 ++-- docs/source/simsopt.solve.rst | 4 ++-- examples/2_Intermediate/QH_fixed_resolution.py | 2 +- examples/2_Intermediate/QH_fixed_resolution_boozer.py | 2 +- examples/2_Intermediate/eliminate_magnetic_islands.py | 2 +- examples/2_Intermediate/resolution_increase_boozer.py | 2 +- examples/2_Intermediate/stage_two_optimization.py | 2 +- .../2_Intermediate/stage_two_optimization_stochastic.py | 2 +- examples/2_Intermediate/vmec_adjoint.py | 2 +- .../3_Advanced/optimize_qs_and_islands_simultaneously.py | 2 +- .../1DOF_circularCrossSection_varyAxis_targetIota.py | 2 +- .../1DOF_circularCrossSection_varyAxis_targetIota_spec.py | 2 +- .../1DOF_circularCrossSection_varyR0_targetVolume.py | 2 +- .../1DOF_circularCrossSection_varyR0_targetVolume_spec.py | 2 +- ...cularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py | 2 +- .../2DOF_specOnly_targetIotaAndVolume.py | 2 +- examples/stellarator_benchmarks/2DOF_vmecAndSpec.py | 2 +- .../2DOF_vmecOnly_targetIotaAndVolume.py | 2 +- examples/stellarator_benchmarks/7dof.py | 2 +- 21 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 6f5484fb9..72c53f236 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -172,7 +172,7 @@ also manipulate the fixed/free status of dofs using the functions :obj:`~simsopt._core.optimizable.Optimizable.fix_all()`, and :obj:`~simsopt._core.optimizable.Optimizable.unfix_all()`:: - >>> c.local_fix_all() + >>> c.fix_all() >>> c.x array([], dtype=float64) @@ -182,7 +182,7 @@ also manipulate the fixed/free status of dofs using the functions array([-2.]) - >>> c.local_unfix_all() + >>> c.unfix_all() >>> c.x array([ 1. , 0.1, 0. , -2. , 0. , 0.3, 3. , -0.5, 0.4]) @@ -366,7 +366,7 @@ automatically removed from the global state vector :obj:`~simsopt._core.optimizable.Optimizable.x` of a child object:: - >>> curve.local_fix_all() + >>> curve.fix_all() >>> curve.unfix('zc(0)') >>> coil.x diff --git a/docs/source/simsopt._core.rst b/docs/source/simsopt._core.rst index 124ea4bee..e462eeb2b 100644 --- a/docs/source/simsopt._core.rst +++ b/docs/source/simsopt._core.rst @@ -23,7 +23,7 @@ simsopt.\_core.finite_difference module :private-members: simsopt.\_core.optimizable module ----------------------------------------- +--------------------------------- .. automodule:: simsopt._core.optimizable :members: diff --git a/docs/source/simsopt.objectives.rst b/docs/source/simsopt.objectives.rst index cfc385ccb..a693c3642 100644 --- a/docs/source/simsopt.objectives.rst +++ b/docs/source/simsopt.objectives.rst @@ -13,7 +13,7 @@ simsopt.objectives.fluxobjective module :show-inheritance: simsopt.objectives.functions module ------------------------------------------- +----------------------------------- .. automodule:: simsopt.objectives.functions :members: @@ -21,7 +21,7 @@ simsopt.objectives.functions module :show-inheritance: simsopt.objectives.least\_squares module ------------------------------------------------ +---------------------------------------- .. automodule:: simsopt.objectives.least_squares :members: diff --git a/docs/source/simsopt.solve.rst b/docs/source/simsopt.solve.rst index 4cf6d38c4..80875dcec 100644 --- a/docs/source/simsopt.solve.rst +++ b/docs/source/simsopt.solve.rst @@ -5,7 +5,7 @@ Submodules ---------- simsopt.solve.mpi module -------------------------------- +------------------------ .. automodule:: simsopt.solve.mpi :members: @@ -13,7 +13,7 @@ simsopt.solve.mpi module :show-inheritance: simsopt.solve.serial module ----------------------------------- +--------------------------- .. automodule:: simsopt.solve.serial :members: diff --git a/examples/2_Intermediate/QH_fixed_resolution.py b/examples/2_Intermediate/QH_fixed_resolution.py index c50e85525..ef818bcd8 100755 --- a/examples/2_Intermediate/QH_fixed_resolution.py +++ b/examples/2_Intermediate/QH_fixed_resolution.py @@ -27,7 +27,7 @@ # Define parameter space: surf = vmec.boundary -surf.local_fix_all() +surf.fix_all() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) diff --git a/examples/2_Intermediate/QH_fixed_resolution_boozer.py b/examples/2_Intermediate/QH_fixed_resolution_boozer.py index ea08085d0..11dede2f4 100755 --- a/examples/2_Intermediate/QH_fixed_resolution_boozer.py +++ b/examples/2_Intermediate/QH_fixed_resolution_boozer.py @@ -23,7 +23,7 @@ # Define parameter space: surf = vmec.boundary -surf.local_fix_all() +surf.fix_all() max_mode = 2 surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) diff --git a/examples/2_Intermediate/eliminate_magnetic_islands.py b/examples/2_Intermediate/eliminate_magnetic_islands.py index c96cab0dc..87690395a 100755 --- a/examples/2_Intermediate/eliminate_magnetic_islands.py +++ b/examples/2_Intermediate/eliminate_magnetic_islands.py @@ -34,7 +34,7 @@ # To make this example run relatively quickly, we will optimize in a # small parameter space. Here we pick out just 2 Fourier modes to vary # in the optimization: -s.boundary.local_fix_all() +s.boundary.fix_all() s.boundary.unfix('zs(6,1)') s.boundary.unfix('zs(6,2)') diff --git a/examples/2_Intermediate/resolution_increase_boozer.py b/examples/2_Intermediate/resolution_increase_boozer.py index 0082a008e..297344e07 100755 --- a/examples/2_Intermediate/resolution_increase_boozer.py +++ b/examples/2_Intermediate/resolution_increase_boozer.py @@ -67,7 +67,7 @@ ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.local_fix_all() + surf.fix_all() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/2_Intermediate/stage_two_optimization.py b/examples/2_Intermediate/stage_two_optimization.py index 4fab1449b..2e344b7ec 100755 --- a/examples/2_Intermediate/stage_two_optimization.py +++ b/examples/2_Intermediate/stage_two_optimization.py @@ -92,7 +92,7 @@ # Since the target field is zero, one possible solution is just to set all # currents to 0. To avoid the minimizer finding that solution, we fix one # of the currents: -base_currents[0].local_fix_all() +base_currents[0].fix_all() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) bs = BiotSavart(coils) diff --git a/examples/2_Intermediate/stage_two_optimization_stochastic.py b/examples/2_Intermediate/stage_two_optimization_stochastic.py index 22d37565f..e0fc34722 100755 --- a/examples/2_Intermediate/stage_two_optimization_stochastic.py +++ b/examples/2_Intermediate/stage_two_optimization_stochastic.py @@ -119,7 +119,7 @@ def pprint(*args, **kwargs): # Since the target field is zero, one possible solution is just to set all # currents to 0. To avoid the minimizer finding that solution, we fix one # of the currents: -base_currents[0].local_fix_all() +base_currents[0].fix_all() coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True) bs = BiotSavart(coils) diff --git a/examples/2_Intermediate/vmec_adjoint.py b/examples/2_Intermediate/vmec_adjoint.py index 53aa57283..12d982547 100755 --- a/examples/2_Intermediate/vmec_adjoint.py +++ b/examples/2_Intermediate/vmec_adjoint.py @@ -47,7 +47,7 @@ obj = IotaTargetMetric(vmec, target_function, adjoint_epsilon) surf = vmec.boundary -surf.local_fix_all() +surf.fix_all() # Slowly increase range of modes in optimization space for max_mode in range(3, maxres): surf.fixed_range(mmin=0, mmax=max_mode, diff --git a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py index 73ec53c1c..aa8415654 100755 --- a/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py +++ b/examples/3_Advanced/optimize_qs_and_islands_simultaneously.py @@ -36,7 +36,7 @@ spec.boundary = surf # Define parameter space: -surf.local_fix_all() +surf.fix_all() surf.fixed_range(mmin=0, mmax=3, nmin=-3, nmax=3, fixed=False) surf.fix("rc(0,0)") # Major radius diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py index add05652d..4ea51df9f 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota.py @@ -50,7 +50,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('Delta(1,-1)') # Each function we want in the objective function is then equipped diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py index 9ea943d7a..09c50e97f 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyAxis_targetIota_spec.py @@ -50,7 +50,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('Delta(1,-1)') # Each function we want in the objective function is then equipped diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py index de2a756e2..3050c87a7 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume.py @@ -52,7 +52,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py index e01ec9208..1dab40b07 100755 --- a/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py +++ b/examples/stellarator_benchmarks/1DOF_circularCrossSection_varyR0_targetVolume_spec.py @@ -55,7 +55,7 @@ # Surface parameters are all non-fixed by default. You can choose # which parameters are optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('rc(0,0)') # Each Target is then equipped with a shift and weight, to become a diff --git a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py index 744832fa6..ce934ec8a 100755 --- a/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py +++ b/examples/stellarator_benchmarks/2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry.py @@ -17,7 +17,7 @@ vmec = Vmec(os.path.join(os.path.dirname(__file__), 'inputs', 'input.2DOF_circularCrossSection_varyAxis_targetIotaAndQuasisymmetry')) # Define parameter space: -vmec.boundary.local_fix_all() +vmec.boundary.fix_all() vmec.boundary.unfix("rc(0,1)") vmec.boundary.unfix("zs(0,1)") diff --git a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py index b4cb21c71..3937161f3 100755 --- a/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_specOnly_targetIotaAndVolume.py @@ -43,7 +43,7 @@ # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py index c6f76b365..6483c399d 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py +++ b/examples/stellarator_benchmarks/2DOF_vmecAndSpec.py @@ -46,7 +46,7 @@ # VMEC parameters are all fixed by default, while surface parameters are all non-fixed by default. # You can choose which parameters are optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py index 2da205896..9ba544b93 100755 --- a/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py +++ b/examples/stellarator_benchmarks/2DOF_vmecOnly_targetIotaAndVolume.py @@ -45,7 +45,7 @@ # VMEC parameters are all fixed by default, while surface parameters # are all non-fixed by default. You can choose which parameters are # optimized by setting their 'fixed' attributes. -surf.local_fix_all() +surf.fix_all() surf.unfix('rc(1,1)') surf.unfix('zs(1,1)') diff --git a/examples/stellarator_benchmarks/7dof.py b/examples/stellarator_benchmarks/7dof.py index 7d5c29fca..cad0e9904 100755 --- a/examples/stellarator_benchmarks/7dof.py +++ b/examples/stellarator_benchmarks/7dof.py @@ -26,7 +26,7 @@ vmec.boundary = surf # Define parameter space: -surf.local_fix_all() +surf.fix_all() surf.fix_range(mmin=0, mmax=2, nmin=-1, nmax=1, fixed=False) surf.fix("Delta(1,0)") # toroidally-averaged major radius surf.fix("Delta(0,0)") # toroidally-averaged minor radius From 9bb32956039686b8628eae67305e8b375ab5f0bc Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 10:03:09 -0400 Subject: [PATCH 61/95] Merge inconsistencies removed from docs and examples --- examples/2_Intermediate/QSC.py | 2 +- examples/2_Intermediate/resolution_increase.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/2_Intermediate/QSC.py b/examples/2_Intermediate/QSC.py index c84b41af7..9e0ecd130 100755 --- a/examples/2_Intermediate/QSC.py +++ b/examples/2_Intermediate/QSC.py @@ -39,7 +39,7 @@ def get_max_elongation(self): print('Names of the dofs: ', stel.names) # Decide which degrees of freedom to optimize -stel.local_fix_all() +stel.fix_all() stel.unfix('rc(1)') stel.unfix('zs(1)') stel.unfix('etabar') diff --git a/examples/2_Intermediate/resolution_increase.py b/examples/2_Intermediate/resolution_increase.py index 6af1e8331..c08befc09 100755 --- a/examples/2_Intermediate/resolution_increase.py +++ b/examples/2_Intermediate/resolution_increase.py @@ -61,7 +61,7 @@ ". Previous vmec iteration = ", vmec.iter) # Define parameter space: - surf.local_fix_all() + surf.fix_all() surf.fixed_range(mmin=0, mmax=max_mode, nmin=-max_mode, nmax=max_mode, fixed=False) surf.fix("rc(0,0)") # Major radius From 817aa9aaf4b0c06add7be77c2389be9cba880cce Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 10:06:38 -0400 Subject: [PATCH 62/95] Linter fix --- tests/core/test_optimizable.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 186512d15..72ff78843 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -1276,7 +1276,5 @@ def test_load_save_simsopt(self): self.assertAlmostEqual(adder1.J(), adder1_str_regen1.J()) - - if __name__ == "__main__": unittest.main() From 0971ed48e3d82bd59c2a54ce24f15801183d8588 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 10:59:29 -0400 Subject: [PATCH 63/95] Add serialization section --- docs/source/optimizable.rst | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 72c53f236..4dd2ce7e2 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -673,3 +673,45 @@ mode", using vector-Jacobian products, which is efficient for cases in which the objective function is a scalar or a vector with fewer dimensions than the number of dofs. For objects that return a gradient, the gradient function is typically named ``.dJ()``. + +Serialization +------------- + +Simsopt has the ability to serialize geometric and field objects +into JSON objects for archiving and sharing. To save a single simsopt +object, one can use the ``save`` method, which returns a json string with +optional file saving. + +.. code-block:: + + curve = CurveRZFourier(...) + curve_json_str = curve.save(filename='curve.json') + # or + curve_json_str = curve.save(fmt='json') + +To load individual serialized simsopt objects, you can use ``from_str`` or ``from_file`` +class methods. One could use the base class name such as ``Optimizable`` instead of trying to figure +out the exact class name of the saved object. + +.. code-block:: + + curve = Optimizable.from_str(curve_json_str) + # or + curve = Optimizable.from_file('curve.json') + +To save multiple simsopt objects use ``save_simsopt`` function. + +.. code-block:: + + from simsopt import save_simsopt + + curves = [CurveRZFourier(...), CurveXYZFourier(...), CurveHelical(...), ...] + save_simsopt(curves, 'curves.json') + +To load the geometric objects from the saved json file, use the ``load_simsopt`` function. + +.. code-block:: + + from simsopt import load_simsopt + + curves = load_simsopt('curves.json') From ac9ce43cf23b4d15b865ad3fa2f885827daeb343 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 10:59:57 -0400 Subject: [PATCH 64/95] Remove unused imports in optimizable module --- src/simsopt/_core/optimizable.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 38cf9e259..1659829c8 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -9,14 +9,11 @@ from __future__ import annotations -import types import weakref import hashlib from collections.abc import Callable as ABC_Callable, Hashable -from collections import defaultdict from numbers import Real, Integral -from typing import Union, Tuple, Dict, Callable, Sequence, \ - MutableSequence as MutSeq, List, Literal +from typing import Union, Tuple, Dict, Callable, Sequence, List from functools import lru_cache import logging import json From 7339f7f74c3fedf80231d830c77b529c413ffd0f Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 11:20:00 -0400 Subject: [PATCH 65/95] Update surf_vol_area to include serialization --- examples/1_Simple/surf_vol_area.py | 57 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/examples/1_Simple/surf_vol_area.py b/examples/1_Simple/surf_vol_area.py index 484154550..0653917bd 100755 --- a/examples/1_Simple/surf_vol_area.py +++ b/examples/1_Simple/surf_vol_area.py @@ -3,6 +3,7 @@ from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.solve.serial import least_squares_serial_solve +from simsopt import load_simsopt """ Optimize the minor radius and elongation of an axisymmetric torus to obtain a desired volume and area. @@ -23,36 +24,42 @@ # property to True. surf.fix('rc(0,0)') -# Approach 1 -surf3 = SurfaceRZFourier() -surf3.fix('rc(0,0)') -prob3 = LeastSquaresProblem(funcs_in=[surf3.area, surf3.volume], +# LeastSquaresProble can be initialized in couple of ways. +prob1 = LeastSquaresProblem(funcs_in=[surf.area, surf.volume], goals=[desired_area, desired_volume], weights=[1, 1]) -least_squares_serial_solve(prob3) -print("At the optimum using approach 3,") -print(" rc(m=1,n=0) = ", surf3.get_rc(1, 0)) -print(" zs(m=1,n=0) = ", surf3.get_zs(1, 0)) -print(" volume = ", surf3.volume()) -print(" area = ", surf3.area()) -print(" objective function = ", prob3.objective()) -print(" -------------------------\n\n") -# Approach 2 -surf4 = SurfaceRZFourier() -surf4.fix('rc(0,0)') -prob4 = LeastSquaresProblem.from_tuples([(surf4.area, desired_area, 1), - (surf4.volume, desired_volume, 1)]) -print(prob4) -least_squares_serial_solve(prob4) -print("At the optimum using approach 3,") -print(" rc(m=1,n=0) = ", surf4.get_rc(1, 0)) -print(" zs(m=1,n=0) = ", surf4.get_zs(1, 0)) -print(" volume = ", surf4.volume()) -print(" area = ", surf4.area()) -print(" objective function = ", prob4.objective()) + +# Optimize the problem using the defaults +least_squares_serial_solve(prob1) +print("At the first optimum") +print(" rc(m=1,n=0) = ", surf.get_rc(1, 0)) +print(" zs(m=1,n=0) = ", surf.get_zs(1, 0)) +print(" volume = ", surf.volume()) +print(" area = ", surf.area()) +print(" objective function = ", prob1.objective()) print(" -------------------------\n\n") +# Save the optimized surface +surf.save("surf_fw.json") + +# Load the saved surface, and redo optimization using central difference scheme +surf1 = load_simsopt("surf_fw.json") +desired_volume1 = 0.8 +desired_area1 = 9.0 # These are different from previous values + +# An alternative initialization of LSP +prob2 = LeastSquaresProblem.from_tuples([(surf1.area, desired_area1, 1), + (surf1.volume, desired_volume1, 1)]) +least_squares_serial_solve(prob2, diff_method="centered") + +print("\n\nAt the second optimum") +print(" rc(m=1,n=0) = ", surf1.get_rc(1, 0)) +print(" zs(m=1,n=0) = ", surf1.get_zs(1, 0)) +print(" volume = ", surf1.volume()) +print(" area = ", surf1.area()) +print(" objective function = ", prob2.objective()) +print(" -------------------------\n\n") print("End of 1_Simple/surf_vol_area.py") print("=======================================") From 32d874b2b460a827bfc67a74b9fb84aa462bd5f5 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 6 May 2022 13:12:41 -0400 Subject: [PATCH 66/95] Updated surf vol example --- examples/1_Simple/surf_vol_area.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/1_Simple/surf_vol_area.py b/examples/1_Simple/surf_vol_area.py index 0653917bd..36c4f32b4 100755 --- a/examples/1_Simple/surf_vol_area.py +++ b/examples/1_Simple/surf_vol_area.py @@ -3,7 +3,7 @@ from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.solve.serial import least_squares_serial_solve -from simsopt import load_simsopt +from simsopt import load_simsopt, save_simsopt """ Optimize the minor radius and elongation of an axisymmetric torus to obtain a desired volume and area. @@ -41,7 +41,7 @@ print(" -------------------------\n\n") # Save the optimized surface -surf.save("surf_fw.json") +surf.save("surf_fw.json", indent=2) # Load the saved surface, and redo optimization using central difference scheme surf1 = load_simsopt("surf_fw.json") @@ -60,6 +60,7 @@ print(" volume = ", surf1.volume()) print(" area = ", surf1.area()) print(" objective function = ", prob2.objective()) +save_simsopt(surf1, "surf_centered.json", indent=2) print(" -------------------------\n\n") print("End of 1_Simple/surf_vol_area.py") print("=======================================") From d341af716c8ac85be1f1ec351625d58fbe0bc833 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 9 May 2022 17:14:04 -0400 Subject: [PATCH 67/95] Bug fix in deserialization of SquaredFlux --- src/simsopt/objectives/fluxobjective.py | 1 + src/simsopt/objectives/utilities.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index d64f0947d..ed86cfe5e 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -58,6 +58,7 @@ def dJ(self): def as_dict(self) -> dict: return MSONable.as_dict(self) + @classmethod def from_dict(cls, d): decoder = MontyDecoder() surface = decoder.process_decoded(d["surface"]) diff --git a/src/simsopt/objectives/utilities.py b/src/simsopt/objectives/utilities.py index 49fa535d0..f56dea518 100644 --- a/src/simsopt/objectives/utilities.py +++ b/src/simsopt/objectives/utilities.py @@ -1,4 +1,5 @@ import numpy as np +from monty.json import MSONable, MontyDecoder from .._core.optimizable import Optimizable from .._core.derivative import Derivative, derivative_dec @@ -84,4 +85,12 @@ def dJ(self): dval = self.obj.dJ(partials=True) return np.maximum(val-self.threshold, 0)*dval + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + obj = MontyDecoder().process_decoded(d["obj"]) + return cls(obj, d["threshold"]) + return_fn_map = {'J': J, 'dJ': dJ} From 0092fd7293bb9c0859f8029f04a638fb7300dd41 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 9 May 2022 17:17:53 -0400 Subject: [PATCH 68/95] Bug fix in deserialization of SquaredFlux --- src/simsopt/objectives/fluxobjective.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index ed86cfe5e..9d77cb1b8 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -63,4 +63,4 @@ def from_dict(cls, d): decoder = MontyDecoder() surface = decoder.process_decoded(d["surface"]) field = decoder.process_decoded(d["field"]) - return cls(surface, d["target"], field) \ No newline at end of file + return cls(surface, field, d["target"]) \ No newline at end of file From bd026353a8db193335d17b413fafb72230bc82a4 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 23 May 2022 08:33:01 -0400 Subject: [PATCH 69/95] Add serialization for scaled current --- src/simsopt/field/coil.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 057d9ae16..1666ad52a 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -83,10 +83,10 @@ class Current(sopp.Current, CurrentBase): of coils that are constrained to use the same current. """ - def __init__(self, current): + def __init__(self, current, **kwargs): sopp.Current.__init__(self, current) CurrentBase.__init__(self, external_dof_setter=sopp.Current.set_dofs, - x0=self.get_dofs()) + x0=self.get_dofs(), **kwargs) def vjp(self, v_current): return Derivative({self: v_current}) @@ -109,11 +109,12 @@ class ScaledCurrent(sopp.CurrentBase, CurrentBase): for stellarator symmetric coils. """ - def __init__(self, current_to_scale, scale): + def __init__(self, current_to_scale, scale, **kwargs): self.current_to_scale = current_to_scale self.scale = scale sopp.CurrentBase.__init__(self) - CurrentBase.__init__(self, x0=np.asarray([]), depends_on=[current_to_scale]) + CurrentBase.__init__(self, x0=np.asarray([]), + depends_on=[current_to_scale], **kwargs) def vjp(self, v_current): return self.scale * self.current_to_scale.vjp(v_current) @@ -121,6 +122,19 @@ def vjp(self, v_current): def get_value(self): return self.scale * self.current_to_scale.get_value() + def as_dict(self) -> dict: + d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ + d["current_to_scale"] = self.current_to_scale.as_dict() + d["scale"] = self.scale + return d + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + current = decoder.process_decoded(d["current_to_scale"]) + return cls(current, d["scale"]) class CurrentSum(sopp.CurrentBase, CurrentBase): """ From 270c7eca90af32c5d5b45a35a171eb9c9b12e501 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 23 May 2022 08:43:41 -0400 Subject: [PATCH 70/95] Linter fixes --- src/simsopt/field/coil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 1666ad52a..5c5a6a173 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -136,6 +136,7 @@ def from_dict(cls, d): current = decoder.process_decoded(d["current_to_scale"]) return cls(current, d["scale"]) + class CurrentSum(sopp.CurrentBase, CurrentBase): """ Take the sum of two :mod:`Current` objects. From d878311313aa7ecaf15835307e619f732bd01f04 Mon Sep 17 00:00:00 2001 From: mattland Date: Wed, 25 May 2022 08:40:21 -0400 Subject: [PATCH 71/95] Saving optimized coils in stage 2 example --- .../{tracing_fieldline.py => tracing_fieldline_NCSX.py} | 0 examples/2_Intermediate/stage_two_optimization.py | 3 +++ 2 files changed, 3 insertions(+) rename examples/1_Simple/{tracing_fieldline.py => tracing_fieldline_NCSX.py} (100%) diff --git a/examples/1_Simple/tracing_fieldline.py b/examples/1_Simple/tracing_fieldline_NCSX.py similarity index 100% rename from examples/1_Simple/tracing_fieldline.py rename to examples/1_Simple/tracing_fieldline_NCSX.py diff --git a/examples/2_Intermediate/stage_two_optimization.py b/examples/2_Intermediate/stage_two_optimization.py index d613d2ba1..95b61fd6e 100755 --- a/examples/2_Intermediate/stage_two_optimization.py +++ b/examples/2_Intermediate/stage_two_optimization.py @@ -184,3 +184,6 @@ def fun(dofs): curves_to_vtk(curves, OUT_DIR + f"curves_opt_long") pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} s.to_vtk(OUT_DIR + "surf_opt_long", extra_data=pointData) + +# Save the optimized coil shapes and currents so they can be loaded into other scripts for analysis: +bs.save(OUT_DIR + "biot_savart_opt.json") From b058c9ac60b84722cf1804858dfd01a498f0c3d8 Mon Sep 17 00:00:00 2001 From: mattland Date: Wed, 25 May 2022 11:20:19 -0400 Subject: [PATCH 72/95] Added example tracing_fieldlines_QA.py --- examples/1_Simple/inputs/biot_savart_opt.json | 1 + ...ine_NCSX.py => tracing_fieldlines_NCSX.py} | 18 ++- examples/1_Simple/tracing_fieldlines_QA.py | 136 ++++++++++++++++++ examples/run_parallel_examples | 3 +- 4 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 examples/1_Simple/inputs/biot_savart_opt.json rename examples/1_Simple/{tracing_fieldline_NCSX.py => tracing_fieldlines_NCSX.py} (88%) create mode 100755 examples/1_Simple/tracing_fieldlines_QA.py diff --git a/examples/1_Simple/inputs/biot_savart_opt.json b/examples/1_Simple/inputs/biot_savart_opt.json new file mode 100644 index 000000000..82c2eadf5 --- /dev/null +++ b/examples/1_Simple/inputs/biot_savart_opt.json @@ -0,0 +1 @@ +{"@module": "simsopt.field.biotsavart", "@class": "BiotSavart", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "coils": [{"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}, "scale": -1.0}}]} \ No newline at end of file diff --git a/examples/1_Simple/tracing_fieldline_NCSX.py b/examples/1_Simple/tracing_fieldlines_NCSX.py similarity index 88% rename from examples/1_Simple/tracing_fieldline_NCSX.py rename to examples/1_Simple/tracing_fieldlines_NCSX.py index 9edc0ccf5..1f9a08819 100755 --- a/examples/1_Simple/tracing_fieldline_NCSX.py +++ b/examples/1_Simple/tracing_fieldlines_NCSX.py @@ -2,6 +2,11 @@ """ This example demonstrates how to use SIMSOPT to compute Poincare plots. + +This script uses the NCSX coil shapes available in +``simsopt.util.zoo.get_ncsx_data()``. For an example in which coils +optimized from a simsopt stage-2 optimization are used, see the +example tracing_fieldlines_QA.py. """ import time @@ -24,8 +29,8 @@ from simsopt.geo.curve import curves_to_vtk from simsopt.util.zoo import get_ncsx_data -print("Running 1_Simple/tracing_fieldline.py") -print("=====================================") +print("Running 1_Simple/tracing_fieldlines_NCSX.py") +print("===========================================") sys.path.append(os.path.join("..", "tests", "geo")) logging.basicConfig() @@ -111,15 +116,20 @@ def skip(rs, phis, zs): return skip +print('Initializing InterpolatedField') bsh = InterpolatedField( bs, degree, rrange, phirange, zrange, True, nfp=nfp, stellsym=True, skip=skip ) +print('Done initializing InterpolatedField') bsh.set_points(ma.gamma().reshape((-1, 3))) bs.set_points(ma.gamma().reshape((-1, 3))) Bh = bsh.B() B = bs.B() print("|B-Bh| on axis", np.sort(np.abs(B-Bh).flatten())) + +print('Beginning field line tracing') trace_fieldlines(bsh, 'bsh') -print("End of 1_Simple/tracing_fieldline.py") -print("=====================================") + +print("End of 1_Simple/tracing_fieldlines_NCSX.py") +print("==========================================") diff --git a/examples/1_Simple/tracing_fieldlines_QA.py b/examples/1_Simple/tracing_fieldlines_QA.py new file mode 100755 index 000000000..e4ec7c669 --- /dev/null +++ b/examples/1_Simple/tracing_fieldlines_QA.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 + +""" +This example demonstrates how to use SIMSOPT to compute Poincare plots. + +This example also illustrates how the coil shapes resulting from a +simsopt stage-2 optimization can be loaded in to another script for +analysis. The coil shape data used in this script, +``inputs/biot_savart_opt.json``, can be re-generated by running the +example 2_Intermediate/stage_two_optimization.py. +""" + +import time +import os +import logging +import sys +from pathlib import Path +import numpy as np +try: + from mpi4py import MPI + comm = MPI.COMM_WORLD +except ImportError: + comm = None + +import simsopt +from simsopt.field.biotsavart import BiotSavart +from simsopt.field.magneticfieldclasses import InterpolatedField, UniformInterpolationRule +from simsopt.geo.surfacexyztensorfourier import SurfaceRZFourier +from simsopt.field.coil import coils_via_symmetries +from simsopt.field.tracing import SurfaceClassifier, \ + particles_to_vtk, compute_fieldlines, LevelsetStoppingCriterion, plot_poincare_data +from simsopt.geo.curve import curves_to_vtk + +print("Running 1_Simple/tracing_fieldlines_QA.py") +print("=========================================") + +logging.basicConfig() +logger = logging.getLogger('simsopt.field.tracing') +logger.setLevel(1) + +# check whether we're in CI, in that case we make the run a bit cheaper +ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] +nfieldlines = 3 if ci else 10 +tmax_fl = 10000 if ci else 20000 +degree = 2 if ci else 4 + +# Directory for output +OUT_DIR = "./output/" +os.makedirs(OUT_DIR, exist_ok=True) + +# Load in the boundary surface: +TEST_DIR = (Path(__file__).parent / ".." / ".." / "tests" / "test_files").resolve() +filename = TEST_DIR / 'input.LandremanPaul2021_QA' +# Note that the range must be "full torus"! +surf = SurfaceRZFourier.from_vmec_input(filename, nphi=200, ntheta=30, range="full torus") +nfp = surf.nfp + +# Load in coils from stage_two_optimization.py: +bs = simsopt.load_simsopt("inputs/biot_savart_opt.json") + +surf.to_vtk(OUT_DIR + 'surface') +sc_fieldline = SurfaceClassifier(surf, h=0.03, p=2) +sc_fieldline.to_vtk(OUT_DIR + 'levelset', h=0.02) + + +def trace_fieldlines(bfield, label): + t1 = time.time() + # Set initial grid of points for field line tracing, going from + # the magnetic axis to the surface. The actual plasma boundary is + # at R=1.300425, but the outermost initial point is a bit inward + # from that, R = 1.295, so the SurfaceClassifier does not think we + # have exited the surface + R0 = np.linspace(1.2125346, 1.295, nfieldlines) + Z0 = np.zeros(nfieldlines) + phis = [(i/4)*(2*np.pi/nfp) for i in range(4)] + fieldlines_tys, fieldlines_phi_hits = compute_fieldlines( + bfield, R0, Z0, tmax=tmax_fl, tol=1e-16, comm=comm, + phis=phis, stopping_criteria=[LevelsetStoppingCriterion(sc_fieldline.dist)]) + t2 = time.time() + print(f"Time for fieldline tracing={t2-t1:.3f}s. Num steps={sum([len(l) for l in fieldlines_tys])//nfieldlines}", flush=True) + if comm is None or comm.rank == 0: + particles_to_vtk(fieldlines_tys, OUT_DIR + f'fieldlines_{label}') + plot_poincare_data(fieldlines_phi_hits, phis, OUT_DIR + f'poincare_fieldline_{label}.png', dpi=150) + + +# uncomment this to run tracing using the biot savart field (very slow!) +# trace_fieldlines(bs, 'bs') + + +# Bounds for the interpolated magnetic field chosen so that the surface is +# entirely contained in it +n = 20 +rs = np.linalg.norm(surf.gamma()[:, :, 0:2], axis=2) +zs = surf.gamma()[:, :, 2] +rrange = (np.min(rs), np.max(rs), n) +phirange = (0, 2*np.pi/nfp, n*2) +# exploit stellarator symmetry and only consider positive z values: +zrange = (0, np.max(zs), n//2) + + +def skip(rs, phis, zs): + # The RegularGrindInterpolant3D class allows us to specify a function that + # is used in order to figure out which cells to be skipped. Internally, + # the class will evaluate this function on the nodes of the regular mesh, + # and if *all* of the eight corners are outside the domain, then the cell + # is skipped. Since the surface may be curved in a way that for some + # cells, all mesh nodes are outside the surface, but the surface still + # intersects with a cell, we need to have a bit of buffer in the signed + # distance (essentially blowing up the surface a bit), to avoid ignoring + # cells that shouldn't be ignored + rphiz = np.asarray([rs, phis, zs]).T.copy() + dists = sc_fieldline.evaluate_rphiz(rphiz) + skip = list((dists < -0.05).flatten()) + print("Skip", sum(skip), "cells out of", len(skip), flush=True) + return skip + + +print('Initializing InterpolatedField') +bsh = InterpolatedField( + bs, degree, rrange, phirange, zrange, True, nfp=nfp, stellsym=True, skip=skip +) +print('Done initializing InterpolatedField.') + +bsh.set_points(surf.gamma().reshape((-1, 3))) +bs.set_points(surf.gamma().reshape((-1, 3))) +Bh = bsh.B() +B = bs.B() +print("Mean(|B|) on plasma surface =", np.mean(bs.AbsB())) + +print("|B-Bh| on surface:", np.sort(np.abs(B-Bh).flatten())) + +print('Beginning field line tracing') +trace_fieldlines(bsh, 'bsh') + +print("End of 1_Simple/tracing_fieldlines_QA.py") +print("========================================") diff --git a/examples/run_parallel_examples b/examples/run_parallel_examples index e6237bb25..efe2f4f2f 100755 --- a/examples/run_parallel_examples +++ b/examples/run_parallel_examples @@ -14,5 +14,6 @@ set -ex MPI_OPTIONS=${GITHUB_ACTIONS:+--oversubscribe} echo MPI_OPTIONS=$MPI_OPTIONS -mpiexec $MPI_OPTIONS -n 2 ./1_Simple/tracing_fieldline.py +mpiexec $MPI_OPTIONS -n 2 ./1_Simple/tracing_fieldlines_NCSX.py +mpiexec $MPI_OPTIONS -n 2 ./1_Simple/tracing_fieldlines_QA.py mpiexec $MPI_OPTIONS -n 2 ./1_Simple/tracing_particle.py From c21bf8be68c9b5e9df104bb38c8530534cafda53 Mon Sep 17 00:00:00 2001 From: mattland Date: Wed, 25 May 2022 12:30:34 -0400 Subject: [PATCH 73/95] Forgot to update run_serial_examples --- examples/run_serial_examples | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/run_serial_examples b/examples/run_serial_examples index cb8d84711..c65e4753f 100755 --- a/examples/run_serial_examples +++ b/examples/run_serial_examples @@ -7,7 +7,8 @@ set -ex ./1_Simple/just_a_quadratic.py ./1_Simple/surf_vol_area.py ./1_Simple/minimize_curve_length.py -./1_Simple/tracing_fieldline.py +./1_Simple/tracing_fieldlines_NCSX.py +./1_Simple/tracing_fieldlines_QA.py ./1_Simple/tracing_particle.py ./1_Simple/qfm.py ./2_Intermediate/QSC.py From 3ed96d1ff482e81a5a7e8be214d31b2bede120ca Mon Sep 17 00:00:00 2001 From: mattland Date: Wed, 25 May 2022 14:27:03 -0400 Subject: [PATCH 74/95] Fixed path issue --- examples/1_Simple/tracing_fieldlines_QA.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/1_Simple/tracing_fieldlines_QA.py b/examples/1_Simple/tracing_fieldlines_QA.py index e4ec7c669..33d92849a 100755 --- a/examples/1_Simple/tracing_fieldlines_QA.py +++ b/examples/1_Simple/tracing_fieldlines_QA.py @@ -55,8 +55,9 @@ surf = SurfaceRZFourier.from_vmec_input(filename, nphi=200, ntheta=30, range="full torus") nfp = surf.nfp -# Load in coils from stage_two_optimization.py: -bs = simsopt.load_simsopt("inputs/biot_savart_opt.json") +# Load in the optimized coils from stage_two_optimization.py: +coils_filename = Path(__file__).parent / "inputs" / "biot_savart_opt.json" +bs = simsopt.load_simsopt(coils_filename) surf.to_vtk(OUT_DIR + 'surface') sc_fieldline = SurfaceClassifier(surf, h=0.03, p=2) From 90aaac81b915e79bce2f37d6b36f1d030e683f65 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 31 May 2022 10:33:54 -0400 Subject: [PATCH 75/95] Cleanup --- docs/source/optimizable.rst | 12 ++++++------ examples/1_Simple/surf_vol_area.py | 6 +++--- examples/1_Simple/tracing_fieldlines_QA.py | 2 +- src/simsopt/__init__.py | 2 +- src/simsopt/_core/__init__.py | 2 +- src/simsopt/_core/optimizable.py | 8 ++++++-- src/simsopt/geo/surfacerzfourier.py | 2 +- tests/core/test_optimizable.py | 8 ++++---- tests/field/test_magneticfields.py | 4 ---- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 4dd2ce7e2..2f4fdf5c7 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -699,19 +699,19 @@ out the exact class name of the saved object. # or curve = Optimizable.from_file('curve.json') -To save multiple simsopt objects use ``save_simsopt`` function. +To save multiple simsopt objects use ``save`` function implemented in simsopt. .. code-block:: - from simsopt import save_simsopt + from simsopt import save curves = [CurveRZFourier(...), CurveXYZFourier(...), CurveHelical(...), ...] - save_simsopt(curves, 'curves.json') + save(curves, 'curves.json') -To load the geometric objects from the saved json file, use the ``load_simsopt`` function. +To load the geometric objects from the saved json file, use the ``load`` function. .. code-block:: - from simsopt import load_simsopt + from simsopt import load - curves = load_simsopt('curves.json') + curves = load('curves.json') diff --git a/examples/1_Simple/surf_vol_area.py b/examples/1_Simple/surf_vol_area.py index 36c4f32b4..f96a4011d 100755 --- a/examples/1_Simple/surf_vol_area.py +++ b/examples/1_Simple/surf_vol_area.py @@ -3,7 +3,7 @@ from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.objectives.least_squares import LeastSquaresProblem from simsopt.solve.serial import least_squares_serial_solve -from simsopt import load_simsopt, save_simsopt +from simsopt import load, save """ Optimize the minor radius and elongation of an axisymmetric torus to obtain a desired volume and area. @@ -44,7 +44,7 @@ surf.save("surf_fw.json", indent=2) # Load the saved surface, and redo optimization using central difference scheme -surf1 = load_simsopt("surf_fw.json") +surf1 = load("surf_fw.json") desired_volume1 = 0.8 desired_area1 = 9.0 # These are different from previous values @@ -60,7 +60,7 @@ print(" volume = ", surf1.volume()) print(" area = ", surf1.area()) print(" objective function = ", prob2.objective()) -save_simsopt(surf1, "surf_centered.json", indent=2) +save(surf1, "surf_centered.json", indent=2) print(" -------------------------\n\n") print("End of 1_Simple/surf_vol_area.py") print("=======================================") diff --git a/examples/1_Simple/tracing_fieldlines_QA.py b/examples/1_Simple/tracing_fieldlines_QA.py index 33d92849a..519c19caa 100755 --- a/examples/1_Simple/tracing_fieldlines_QA.py +++ b/examples/1_Simple/tracing_fieldlines_QA.py @@ -57,7 +57,7 @@ # Load in the optimized coils from stage_two_optimization.py: coils_filename = Path(__file__).parent / "inputs" / "biot_savart_opt.json" -bs = simsopt.load_simsopt(coils_filename) +bs = simsopt.load(coils_filename) surf.to_vtk(OUT_DIR + 'surface') sc_fieldline = SurfaceClassifier(surf, h=0.03, p=2) diff --git a/src/simsopt/__init__.py b/src/simsopt/__init__.py index 28470f55b..6e6b52a95 100644 --- a/src/simsopt/__init__.py +++ b/src/simsopt/__init__.py @@ -10,7 +10,7 @@ # "from xyz import *". If xyz[.py] contains __all__ = ['XYZ'], only XYZ is # imported -from ._core import make_optimizable, load_simsopt, save_simsopt +from ._core import make_optimizable, load, save from .objectives import LeastSquaresProblem from .solve import least_squares_serial_solve diff --git a/src/simsopt/_core/__init__.py b/src/simsopt/_core/__init__.py index 24e159a71..ecea44c3a 100644 --- a/src/simsopt/_core/__init__.py +++ b/src/simsopt/_core/__init__.py @@ -1 +1 @@ -from .optimizable import Optimizable, make_optimizable, load_simsopt, save_simsopt +from .optimizable import Optimizable, make_optimizable, load, save diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 0f29a6899..9989ced70 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -1337,6 +1337,8 @@ def save(self, filename=None, fmt=None, **kwargs): if fmt == "json" or fnmatch(fname.lower(), "*.json"): if "cls" not in kwargs: kwargs["cls"] = MontyEncoder + if "indent" not in kwargs: + kwargs["indent"] = 2 s = json.dumps(self.as_dict(), **kwargs) if filename: with zopen(filename, "wt") as f: @@ -1362,7 +1364,7 @@ def from_file(cls, filename: str): return cls.from_str(contents, fmt="json") -def load_simsopt(filename, *args, **kwargs): +def load(filename, *args, **kwargs): """ Function to load simsopt object from a file. Only JSON format is supported at this time. Support for additional @@ -1383,7 +1385,7 @@ def load_simsopt(filename, *args, **kwargs): return json.load(fp, *args, **kwargs) -def save_simsopt(simsopt_objects, filename, *args, **kwargs): +def save(simsopt_objects, filename, *args, **kwargs): fname = Path(filename).suffix.lower() if (not fname == '.json'): raise ValueError(f"Invalid format: `{str(fname[1:])}`") @@ -1391,6 +1393,8 @@ def save_simsopt(simsopt_objects, filename, *args, **kwargs): with zopen(filename, "wt") as fp: if "cls" not in kwargs: kwargs["cls"] = MontyEncoder + if "indent" not in kwargs: + kwargs["indent"] = 2 return json.dump(simsopt_objects, fp, *args, **kwargs) diff --git a/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index aa0bbe2f1..2ce7e2b16 100644 --- a/src/simsopt/geo/surfacerzfourier.py +++ b/src/simsopt/geo/surfacerzfourier.py @@ -488,7 +488,7 @@ def set_zc(self, m, n, val): def set_zs(self, m, n, val): """ - Set a particular `zs` Parametfollow uper. + Set a particular `zs` Parameter. """ self._validate_mn(m, n) self.zs[m, n + self.ntor] = val diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 72ff78843..56ab7f647 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -7,7 +7,7 @@ from monty.serialization import loadfn, dumpfn from simsopt._core.optimizable import Optimizable, make_optimizable, \ - ScaledOptimizable, OptimizableSum, load_simsopt, save_simsopt + ScaledOptimizable, OptimizableSum, load, save from simsopt.objectives.functions import Identity, Rosenbrock, TestObject1, \ TestObject2, Beale from simsopt.objectives.functions import Adder as FAdder @@ -1245,7 +1245,7 @@ def test_optimizable_sum_serializer(self): opt_sum_regen = json.loads(s, cls=MontyDecoder) self.assertAlmostEqual(opt_sum_regen.J(), adder1.J() + adder2.J()) - def test_load_save_simsopt(self): + def test_load_save(self): import tempfile from pathlib import Path @@ -1254,10 +1254,10 @@ def test_load_save_simsopt(self): adder2 = FAdder(n=2, x0=[10, 11], names=["a", "b"], fixed=[True, False]) with tempfile.TemporaryDirectory() as tmpdir: fpath = Path(tmpdir) / "adders.json" - save_simsopt([adder1, adder2], fpath, indent=2) + save([adder1, adder2], fpath, indent=2) self.assertTrue(fpath.is_file()) - adders = load_simsopt(fpath) + adders = load(fpath) self.assertAlmostEqual(adder1.J(), adders[0].J()) self.assertAlmostEqual(adder2.J(), adders[1].J()) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index 809752ac5..c21ca338c 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -592,16 +592,12 @@ def test_interpolated_field_close_no_sym(self): dB = btotal.GradAbsB() B = btotal.B() dBc = btotal.GradAbsB_cyl() - print('dBc', dBc) Bc = btotal.B_cyl() bsh.set_points_cyl(points) Bh = bsh.B() dBh = bsh.GradAbsB() Bhc = bsh.B_cyl() dBhc = bsh.GradAbsB_cyl() - print('dBhc', dBhc) - import time - time.sleep(5) assert np.allclose(B, Bh, rtol=1e-2) assert np.allclose(dB, dBh, rtol=1e-2) assert np.allclose(Bc, Bhc, rtol=1e-2) From dd64fb50072260a7c5463d2991d1ccf5f5e93501 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 31 May 2022 11:33:25 -0400 Subject: [PATCH 76/95] Include points for BiotSavart serialization --- src/simsopt/field/biotsavart.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/simsopt/field/biotsavart.py b/src/simsopt/field/biotsavart.py index 92fd2259c..bd7bb1cf2 100644 --- a/src/simsopt/field/biotsavart.py +++ b/src/simsopt/field/biotsavart.py @@ -208,9 +208,16 @@ def A_vjp(self, v): return sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]) def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + points = self.get_points_cart() + d["points"] = MSONable.as_dict(points) + return d @classmethod def from_dict(cls, d): - coils = MontyDecoder().process_decoded(d["coils"]) - return cls(coils) + decoder = MontyDecoder() + coils = decoder.process_decoded(d["coils"]) + bs = cls(coils) + xyz = decoder.process_decoded(d["points"]) + bs.set_points_cart(xyz) + return bs From af89f89f744c0cb5d4edbebade542b34fb79cb10 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 31 May 2022 11:34:15 -0400 Subject: [PATCH 77/95] Simplify serialization as_dict --- src/simsopt/field/coil.py | 18 ++++------- src/simsopt/field/magneticfieldclasses.py | 38 ++++++----------------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 5c5a6a173..cce69b64b 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -123,12 +123,7 @@ def get_value(self): return self.scale * self.current_to_scale.get_value() def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["current_to_scale"] = self.current_to_scale.as_dict() - d["scale"] = self.scale - return d + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): @@ -155,15 +150,14 @@ def get_value(self): return self.current_a.get_value() + self.current_b.get_value() def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["current"] = self.get_value() - return d + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): - return cls(Current(d["current"]), 1.0) + decoder = MontyDecoder() + current_a = decoder.process_decoded(d["current_a"]) + current_b = decoder.process_decoded(d["current_b"]) + return cls(current_a, current_b) def apply_symmetries_to_curves(base_curves, nfp, stellsym): diff --git a/src/simsopt/field/magneticfieldclasses.py b/src/simsopt/field/magneticfieldclasses.py index 8e7020ced..a8d5618f0 100644 --- a/src/simsopt/field/magneticfieldclasses.py +++ b/src/simsopt/field/magneticfieldclasses.py @@ -1,8 +1,8 @@ +import logging + import numpy as np +from monty.json import MSONable from scipy.special import ellipk, ellipe -from simsopt.field.magneticfield import MagneticField -import simsoptpp as sopp -import logging try: from sympy.parsing.sympy_parser import parse_expr import sympy as sp @@ -10,6 +10,9 @@ except ImportError: sympy_found = False +from simsopt.field.magneticfield import MagneticField +import simsoptpp as sopp + logger = logging.getLogger(__name__) @@ -107,11 +110,7 @@ def _d2A_by_dXdX_impl(self, ddA): np.zeros((3, 3, len(points)))])).transpose((3, 0, 1, 2)) def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["R0"] = self.R0 - d["B0"] = self.B0 + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): @@ -213,12 +212,7 @@ def _dB_by_dX_impl(self, dB): dB[:] = self.B0/self.R0/self.q*np.array([dB_by_dX1_term1+dB_by_dX1_term2, dB_by_dX2_term1+dB_by_dX2_term2, dB_by_dX3_term1+dB_by_dX3_term2]).T def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["R0"] = self.R0 - d["B0"] = self.B0 - d["q"] = self.q + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): @@ -311,11 +305,7 @@ def _dB_by_dX_impl(self, dB): dB[:, 2, 1] = dBrdz * np.sin(phi) + dBphidz * np.cos(phi) def as_dict(self) -> dict: - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["phi_str"] = self.phi_str - return d + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): @@ -564,15 +554,7 @@ def _dB_by_dX_impl(self, dB): dB[:] = sopp.ReimandB(self.iota0, self.iota1, self.k, self.epsilonk, self.m0, points) def as_dict(self): - d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["iota0"] = self.iota0 - d["iota1"] = self.iota1 - d["k"] = self.k - d["epsilonk"] = self.epsilonk - d["m0"] = self.m0 - return d + return MSONable.as_dict(self) @classmethod def from_dict(cls, d): From b0a38aada29f758bfaec7304a4c16fd34ca9521c Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 31 May 2022 12:47:05 -0400 Subject: [PATCH 78/95] Bug fix in points serializaiton in BiotSavart --- examples/1_Simple/inputs/biot_savart_opt.json | 7287 ++++++++++++++++- src/simsopt/field/biotsavart.py | 6 +- 2 files changed, 7289 insertions(+), 4 deletions(-) diff --git a/examples/1_Simple/inputs/biot_savart_opt.json b/examples/1_Simple/inputs/biot_savart_opt.json index 82c2eadf5..90836f87f 100644 --- a/examples/1_Simple/inputs/biot_savart_opt.json +++ b/examples/1_Simple/inputs/biot_savart_opt.json @@ -1 +1,7286 @@ -{"@module": "simsopt.field.biotsavart", "@class": "BiotSavart", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "coils": [{"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "phi": 0.0, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "phi": 3.141592653589793, "flip": false}, "current": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [1.233501354211465, -0.038632922427851484, 0.5852984454664155, -0.014141858555479416, 0.18039584407641315, 0.008814204085441464, -0.01655682014544031, 0.01167906800531384, -0.02059773216949121, -0.003857402632617432, -0.008133792946578289, 0.22074162480804513, -0.18257913260373707, 0.15418421892144288, 0.10381505192659782, -0.026626338915340903, -0.03170273433399945, 0.005220768470452487, -0.022616404284198456, 0.027286536289195168, 0.01639312917488031, -0.016548905933083044, 0.03950734929169367, 0.7750699787087934, -0.017095392446552244, 0.1532655585251926, -0.04757607711176057, -0.04548883248867588, 0.01493488286567969, -0.015257434864057664, -0.005045215727500458, -0.011375665261119244, -0.003924275171997387]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 100000.0}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.9917340662601396, -0.02454210464007463, 0.4762426761709393, 0.09013725791589669, 0.24742020083316416, 0.020074783520087274, -0.011115490549991116, -0.03291564596484827, -0.03365803313189075, -0.012180721456577021, 0.005903180190303339, 0.5685107171448731, -0.13182674922763118, 0.4693091821808579, 0.05659037839442883, -0.03184293734099, -0.0016836126729319932, 0.013273087175202413, 0.005471473062063553, 0.003730419093484114, -0.0011747529261343062, 0.006660512806318454, 0.10599115060499668, 0.6629651732236223, -0.08413743868752134, 0.11886515434296972, -0.060707839761554756, 0.005524294643784337, 0.0036851899885595963, -0.004949678737125111, 0.02446183016558412, 0.004226197275102375, 0.030400621129195164]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.9999896456}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.6443651648571752, -0.025718392664672997, 0.28730284756119284, 0.19118374005614797, 0.1826875059375389, 0.0068936224142392595, -0.0368990112043021, -0.0484243921887623, 0.020244784612683787, -0.003975454320329839, 0.018450721410475154, 0.8259757661598185, -0.04903858339960579, 0.6204882648910434, 0.08072457657712227, -0.0038772986088020656, -0.018723866025036954, 0.02203999931853583, -0.023501014347280677, -0.0077396428592119895, 0.0003648167281897619, 0.00979014261547663, 0.08756944541659828, 0.6019514346178384, -0.03067584958108913, 0.0946311251395393, -0.0318586914730894, 0.016286889661729108, -0.002499411879394953, 0.014386588376246784, 0.02076466001696873, 0.020518457336937745, 0.007689214027131388]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998918145}, "scale": -1.0}}, {"@module": "simsopt.field.coil", "@class": "Coil", "@version": "0.8.0.post83+g270c7eca.d20220525.dirty", "curve": {"@module": "simsopt.geo.curve", "@class": "RotatedCurve", "curve": {"@module": "simsopt.geo.curvexyzfourier", "@class": "CurveXYZFourier", "quadpoints": [0.0, 0.013333333333333334, 0.02666666666666667, 0.04, 0.05333333333333334, 0.06666666666666667, 0.08, 0.09333333333333334, 0.10666666666666667, 0.12000000000000001, 0.13333333333333333, 0.14666666666666667, 0.16, 0.17333333333333334, 0.18666666666666668, 0.2, 0.21333333333333335, 0.22666666666666668, 0.24000000000000002, 0.25333333333333335, 0.26666666666666666, 0.28, 0.29333333333333333, 0.3066666666666667, 0.32, 0.33333333333333337, 0.3466666666666667, 0.36000000000000004, 0.37333333333333335, 0.3866666666666667, 0.4, 0.4133333333333334, 0.4266666666666667, 0.44, 0.45333333333333337, 0.4666666666666667, 0.48000000000000004, 0.49333333333333335, 0.5066666666666667, 0.52, 0.5333333333333333, 0.5466666666666667, 0.56, 0.5733333333333334, 0.5866666666666667, 0.6000000000000001, 0.6133333333333334, 0.6266666666666667, 0.64, 0.6533333333333333, 0.6666666666666667, 0.68, 0.6933333333333334, 0.7066666666666667, 0.7200000000000001, 0.7333333333333334, 0.7466666666666667, 0.76, 0.7733333333333334, 0.7866666666666667, 0.8, 0.8133333333333334, 0.8266666666666668, 0.8400000000000001, 0.8533333333333334, 0.8666666666666667, 0.88, 0.8933333333333334, 0.9066666666666667, 0.92, 0.9333333333333333, 0.9466666666666668, 0.9600000000000001, 0.9733333333333334, 0.9866666666666667], "order": 5, "x0": [0.21297758003826586, -0.09088121067230866, 0.10460118887242231, 0.207305836391702, 0.05637550997377978, -0.040702317105900394, 2.320883757767555e-05, 0.010313716116116414, 0.002945322632204382, -0.008130951334226673, -0.017703456811351093, 0.9487604103507693, -0.012286957026709655, 0.698291081518799, -0.016578449902086883, 0.013742372082370035, 0.006787144031631597, 0.03319963571186441, 0.009355032878606425, 0.0028038183807400623, 0.009515054863662109, -0.02278982027840732, 0.049136626943231315, 0.5666014516591262, -0.03659144054163859, 0.09123786764441841, -0.0162329545607135, -0.010542577200435253, -0.017558045798423745, -0.007354140983822319, -0.01977395136864924, -0.01943005352056765, -0.0187629395266513]}, "phi": 3.141592653589793, "flip": true}, "current": {"@module": "simsopt.field.coil", "@class": "ScaledCurrent", "current_to_scale": {"@module": "simsopt.field.coil", "@class": "Current", "current": 99999.99998837852}, "scale": -1.0}}]} \ No newline at end of file +{ + "@module": "simsopt.field.biotsavart", + "@class": "BiotSavart", + "@version": "0.8.0.post113+gaf89f89f", + "coils": [ + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 1.231333338914602, + -0.036000002895760615, + 0.5843121785825937, + -0.006558047279664672, + 0.18684795678497607, + 0.016981747017693113, + -0.013027550303064545, + 0.0076396411754258145, + -0.02214300438207308, + -0.010415388614486022, + -0.01072342744189134, + 0.22287519392334273, + -0.19023355960883784, + 0.14732859617645694, + 0.11207368999962526, + -0.017679459735591527, + -0.03331198198105898, + 0.002514418370416436, + -0.019514165261852897, + 0.027214778808227376, + 0.005648731123925406, + -0.01568538649238391, + 0.03155712847635861, + 0.7765102506207593, + -0.01238512882639467, + 0.1481721326709913, + -0.04902211582250189, + -0.03443906027657885, + 0.018353903067686684, + -0.023808888960344006, + -0.0012120229792386506, + -0.007839292668238391, + 0.007907364377761522 + ] + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 100000.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.9888649197289301, + -0.024699252416792952, + 0.4828921361734901, + 0.09418295589170694, + 0.24308757401724743, + 0.008223192695101047, + -0.00967162417669802, + -0.03635199465039026, + -0.029425886829315472, + -0.006044714787005472, + 0.00048135011841470834, + 0.5608520368628516, + -0.10792373931219401, + 0.4792000415252677, + 0.029747425814226954, + -0.0371505124708665, + 0.004296125836873706, + 0.013853784608912654, + 0.0027016726825668936, + 0.002022757457659644, + -0.0011815905659883544, + 0.003997358332249889, + 0.13053358984981672, + 0.6609308602756507, + -0.10720476136626025, + 0.12489708508524522, + -0.0481372091926072, + 0.007988460640890843, + 0.011065593678011966, + -0.0053641671794494765, + 0.022016519032705108, + 0.00835238897366872, + 0.02700502995079344 + ] + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999083346 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.6433593519150813, + -0.02395480964315632, + 0.2875326097352223, + 0.1938048310774301, + 0.17827875052765396, + 0.0040503118079266995, + -0.03648345376643961, + -0.04647899633708688, + 0.02339723803477204, + -0.0032281132666516655, + 0.018709112240639487, + 0.8273977302623874, + -0.0465813232887608, + 0.6176408780210416, + 0.07916413280570891, + -0.006945423612135039, + -0.01741817108740337, + 0.023332730512337065, + -0.022149259906321446, + -0.0037337617206620755, + -2.6798285019319246e-05, + 0.011719364445445097, + 0.08857225380635086, + 0.6055181511917542, + -0.03282666800542105, + 0.09601149787479074, + -0.032187070915820105, + 0.0199882688946272, + -0.0030107981652331512, + 0.018034551096786557, + 0.017977908610212104, + 0.024710197125190347, + 0.005990029878839234 + ] + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999067224 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.21220079633810315, + -0.08728220888182021, + 0.10699637935009113, + 0.2085639360014451, + 0.055833276507716564, + -0.04125816447302624, + 0.0005340162975302547, + 0.008408186659917487, + 0.0018417499658198873, + -0.005726315079698504, + -0.015707197593342624, + 0.9495934539770362, + -0.012923560166990337, + 0.6997936107796279, + -0.01471395562114997, + 0.008600156748759573, + 0.010705833730296786, + 0.03007869154417708, + 0.007942643886291696, + 0.007363623962698928, + 0.007911895743862167, + -0.021269087134627128, + 0.049596343141794834, + 0.5665108710576103, + -0.03731120914252456, + 0.09406777009239954, + -0.01358186317431214, + -0.012919726860693337, + -0.017943303167561618, + -0.0050890133003869235, + -0.0197282466981241, + -0.01761770009111755, + -0.017736550564696047 + ] + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99998999985 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 1.231333338914602, + -0.036000002895760615, + 0.5843121785825937, + -0.006558047279664672, + 0.18684795678497607, + 0.016981747017693113, + -0.013027550303064545, + 0.0076396411754258145, + -0.02214300438207308, + -0.010415388614486022, + -0.01072342744189134, + 0.22287519392334273, + -0.19023355960883784, + 0.14732859617645694, + 0.11207368999962526, + -0.017679459735591527, + -0.03331198198105898, + 0.002514418370416436, + -0.019514165261852897, + 0.027214778808227376, + 0.005648731123925406, + -0.01568538649238391, + 0.03155712847635861, + 0.7765102506207593, + -0.01238512882639467, + 0.1481721326709913, + -0.04902211582250189, + -0.03443906027657885, + 0.018353903067686684, + -0.023808888960344006, + -0.0012120229792386506, + -0.007839292668238391, + 0.007907364377761522 + ] + }, + "phi": 0.0, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 100000.0 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.9888649197289301, + -0.024699252416792952, + 0.4828921361734901, + 0.09418295589170694, + 0.24308757401724743, + 0.008223192695101047, + -0.00967162417669802, + -0.03635199465039026, + -0.029425886829315472, + -0.006044714787005472, + 0.00048135011841470834, + 0.5608520368628516, + -0.10792373931219401, + 0.4792000415252677, + 0.029747425814226954, + -0.0371505124708665, + 0.004296125836873706, + 0.013853784608912654, + 0.0027016726825668936, + 0.002022757457659644, + -0.0011815905659883544, + 0.003997358332249889, + 0.13053358984981672, + 0.6609308602756507, + -0.10720476136626025, + 0.12489708508524522, + -0.0481372091926072, + 0.007988460640890843, + 0.011065593678011966, + -0.0053641671794494765, + 0.022016519032705108, + 0.00835238897366872, + 0.02700502995079344 + ] + }, + "phi": 0.0, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999083346 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.6433593519150813, + -0.02395480964315632, + 0.2875326097352223, + 0.1938048310774301, + 0.17827875052765396, + 0.0040503118079266995, + -0.03648345376643961, + -0.04647899633708688, + 0.02339723803477204, + -0.0032281132666516655, + 0.018709112240639487, + 0.8273977302623874, + -0.0465813232887608, + 0.6176408780210416, + 0.07916413280570891, + -0.006945423612135039, + -0.01741817108740337, + 0.023332730512337065, + -0.022149259906321446, + -0.0037337617206620755, + -2.6798285019319246e-05, + 0.011719364445445097, + 0.08857225380635086, + 0.6055181511917542, + -0.03282666800542105, + 0.09601149787479074, + -0.032187070915820105, + 0.0199882688946272, + -0.0030107981652331512, + 0.018034551096786557, + 0.017977908610212104, + 0.024710197125190347, + 0.005990029878839234 + ] + }, + "phi": 0.0, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999067224 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.21220079633810315, + -0.08728220888182021, + 0.10699637935009113, + 0.2085639360014451, + 0.055833276507716564, + -0.04125816447302624, + 0.0005340162975302547, + 0.008408186659917487, + 0.0018417499658198873, + -0.005726315079698504, + -0.015707197593342624, + 0.9495934539770362, + -0.012923560166990337, + 0.6997936107796279, + -0.01471395562114997, + 0.008600156748759573, + 0.010705833730296786, + 0.03007869154417708, + 0.007942643886291696, + 0.007363623962698928, + 0.007911895743862167, + -0.021269087134627128, + 0.049596343141794834, + 0.5665108710576103, + -0.03731120914252456, + 0.09406777009239954, + -0.01358186317431214, + -0.012919726860693337, + -0.017943303167561618, + -0.0050890133003869235, + -0.0197282466981241, + -0.01761770009111755, + -0.017736550564696047 + ] + }, + "phi": 0.0, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99998999985 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 1.231333338914602, + -0.036000002895760615, + 0.5843121785825937, + -0.006558047279664672, + 0.18684795678497607, + 0.016981747017693113, + -0.013027550303064545, + 0.0076396411754258145, + -0.02214300438207308, + -0.010415388614486022, + -0.01072342744189134, + 0.22287519392334273, + -0.19023355960883784, + 0.14732859617645694, + 0.11207368999962526, + -0.017679459735591527, + -0.03331198198105898, + 0.002514418370416436, + -0.019514165261852897, + 0.027214778808227376, + 0.005648731123925406, + -0.01568538649238391, + 0.03155712847635861, + 0.7765102506207593, + -0.01238512882639467, + 0.1481721326709913, + -0.04902211582250189, + -0.03443906027657885, + 0.018353903067686684, + -0.023808888960344006, + -0.0012120229792386506, + -0.007839292668238391, + 0.007907364377761522 + ] + }, + "phi": 3.141592653589793, + "flip": false + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 100000.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.9888649197289301, + -0.024699252416792952, + 0.4828921361734901, + 0.09418295589170694, + 0.24308757401724743, + 0.008223192695101047, + -0.00967162417669802, + -0.03635199465039026, + -0.029425886829315472, + -0.006044714787005472, + 0.00048135011841470834, + 0.5608520368628516, + -0.10792373931219401, + 0.4792000415252677, + 0.029747425814226954, + -0.0371505124708665, + 0.004296125836873706, + 0.013853784608912654, + 0.0027016726825668936, + 0.002022757457659644, + -0.0011815905659883544, + 0.003997358332249889, + 0.13053358984981672, + 0.6609308602756507, + -0.10720476136626025, + 0.12489708508524522, + -0.0481372091926072, + 0.007988460640890843, + 0.011065593678011966, + -0.0053641671794494765, + 0.022016519032705108, + 0.00835238897366872, + 0.02700502995079344 + ] + }, + "phi": 3.141592653589793, + "flip": false + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999083346 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.6433593519150813, + -0.02395480964315632, + 0.2875326097352223, + 0.1938048310774301, + 0.17827875052765396, + 0.0040503118079266995, + -0.03648345376643961, + -0.04647899633708688, + 0.02339723803477204, + -0.0032281132666516655, + 0.018709112240639487, + 0.8273977302623874, + -0.0465813232887608, + 0.6176408780210416, + 0.07916413280570891, + -0.006945423612135039, + -0.01741817108740337, + 0.023332730512337065, + -0.022149259906321446, + -0.0037337617206620755, + -2.6798285019319246e-05, + 0.011719364445445097, + 0.08857225380635086, + 0.6055181511917542, + -0.03282666800542105, + 0.09601149787479074, + -0.032187070915820105, + 0.0199882688946272, + -0.0030107981652331512, + 0.018034551096786557, + 0.017977908610212104, + 0.024710197125190347, + 0.005990029878839234 + ] + }, + "phi": 3.141592653589793, + "flip": false + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999067224 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.21220079633810315, + -0.08728220888182021, + 0.10699637935009113, + 0.2085639360014451, + 0.055833276507716564, + -0.04125816447302624, + 0.0005340162975302547, + 0.008408186659917487, + 0.0018417499658198873, + -0.005726315079698504, + -0.015707197593342624, + 0.9495934539770362, + -0.012923560166990337, + 0.6997936107796279, + -0.01471395562114997, + 0.008600156748759573, + 0.010705833730296786, + 0.03007869154417708, + 0.007942643886291696, + 0.007363623962698928, + 0.007911895743862167, + -0.021269087134627128, + 0.049596343141794834, + 0.5665108710576103, + -0.03731120914252456, + 0.09406777009239954, + -0.01358186317431214, + -0.012919726860693337, + -0.017943303167561618, + -0.0050890133003869235, + -0.0197282466981241, + -0.01761770009111755, + -0.017736550564696047 + ] + }, + "phi": 3.141592653589793, + "flip": false + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99998999985 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 1.231333338914602, + -0.036000002895760615, + 0.5843121785825937, + -0.006558047279664672, + 0.18684795678497607, + 0.016981747017693113, + -0.013027550303064545, + 0.0076396411754258145, + -0.02214300438207308, + -0.010415388614486022, + -0.01072342744189134, + 0.22287519392334273, + -0.19023355960883784, + 0.14732859617645694, + 0.11207368999962526, + -0.017679459735591527, + -0.03331198198105898, + 0.002514418370416436, + -0.019514165261852897, + 0.027214778808227376, + 0.005648731123925406, + -0.01568538649238391, + 0.03155712847635861, + 0.7765102506207593, + -0.01238512882639467, + 0.1481721326709913, + -0.04902211582250189, + -0.03443906027657885, + 0.018353903067686684, + -0.023808888960344006, + -0.0012120229792386506, + -0.007839292668238391, + 0.007907364377761522 + ] + }, + "phi": 3.141592653589793, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 100000.0 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.9888649197289301, + -0.024699252416792952, + 0.4828921361734901, + 0.09418295589170694, + 0.24308757401724743, + 0.008223192695101047, + -0.00967162417669802, + -0.03635199465039026, + -0.029425886829315472, + -0.006044714787005472, + 0.00048135011841470834, + 0.5608520368628516, + -0.10792373931219401, + 0.4792000415252677, + 0.029747425814226954, + -0.0371505124708665, + 0.004296125836873706, + 0.013853784608912654, + 0.0027016726825668936, + 0.002022757457659644, + -0.0011815905659883544, + 0.003997358332249889, + 0.13053358984981672, + 0.6609308602756507, + -0.10720476136626025, + 0.12489708508524522, + -0.0481372091926072, + 0.007988460640890843, + 0.011065593678011966, + -0.0053641671794494765, + 0.022016519032705108, + 0.00835238897366872, + 0.02700502995079344 + ] + }, + "phi": 3.141592653589793, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999083346 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.6433593519150813, + -0.02395480964315632, + 0.2875326097352223, + 0.1938048310774301, + 0.17827875052765396, + 0.0040503118079266995, + -0.03648345376643961, + -0.04647899633708688, + 0.02339723803477204, + -0.0032281132666516655, + 0.018709112240639487, + 0.8273977302623874, + -0.0465813232887608, + 0.6176408780210416, + 0.07916413280570891, + -0.006945423612135039, + -0.01741817108740337, + 0.023332730512337065, + -0.022149259906321446, + -0.0037337617206620755, + -2.6798285019319246e-05, + 0.011719364445445097, + 0.08857225380635086, + 0.6055181511917542, + -0.03282666800542105, + 0.09601149787479074, + -0.032187070915820105, + 0.0199882688946272, + -0.0030107981652331512, + 0.018034551096786557, + 0.017977908610212104, + 0.024710197125190347, + 0.005990029878839234 + ] + }, + "phi": 3.141592653589793, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99999067224 + }, + "scale": -1.0 + } + }, + { + "@module": "simsopt.field.coil", + "@class": "Coil", + "@version": "0.8.0.post113+gaf89f89f", + "curve": { + "@module": "simsopt.geo.curve", + "@class": "RotatedCurve", + "curve": { + "@module": "simsopt.geo.curvexyzfourier", + "@class": "CurveXYZFourier", + "quadpoints": [ + 0.0, + 0.013333333333333334, + 0.02666666666666667, + 0.04, + 0.05333333333333334, + 0.06666666666666667, + 0.08, + 0.09333333333333334, + 0.10666666666666667, + 0.12000000000000001, + 0.13333333333333333, + 0.14666666666666667, + 0.16, + 0.17333333333333334, + 0.18666666666666668, + 0.2, + 0.21333333333333335, + 0.22666666666666668, + 0.24000000000000002, + 0.25333333333333335, + 0.26666666666666666, + 0.28, + 0.29333333333333333, + 0.3066666666666667, + 0.32, + 0.33333333333333337, + 0.3466666666666667, + 0.36000000000000004, + 0.37333333333333335, + 0.3866666666666667, + 0.4, + 0.4133333333333334, + 0.4266666666666667, + 0.44, + 0.45333333333333337, + 0.4666666666666667, + 0.48000000000000004, + 0.49333333333333335, + 0.5066666666666667, + 0.52, + 0.5333333333333333, + 0.5466666666666667, + 0.56, + 0.5733333333333334, + 0.5866666666666667, + 0.6000000000000001, + 0.6133333333333334, + 0.6266666666666667, + 0.64, + 0.6533333333333333, + 0.6666666666666667, + 0.68, + 0.6933333333333334, + 0.7066666666666667, + 0.7200000000000001, + 0.7333333333333334, + 0.7466666666666667, + 0.76, + 0.7733333333333334, + 0.7866666666666667, + 0.8, + 0.8133333333333334, + 0.8266666666666668, + 0.8400000000000001, + 0.8533333333333334, + 0.8666666666666667, + 0.88, + 0.8933333333333334, + 0.9066666666666667, + 0.92, + 0.9333333333333333, + 0.9466666666666668, + 0.9600000000000001, + 0.9733333333333334, + 0.9866666666666667 + ], + "order": 5, + "x0": [ + 0.21220079633810315, + -0.08728220888182021, + 0.10699637935009113, + 0.2085639360014451, + 0.055833276507716564, + -0.04125816447302624, + 0.0005340162975302547, + 0.008408186659917487, + 0.0018417499658198873, + -0.005726315079698504, + -0.015707197593342624, + 0.9495934539770362, + -0.012923560166990337, + 0.6997936107796279, + -0.01471395562114997, + 0.008600156748759573, + 0.010705833730296786, + 0.03007869154417708, + 0.007942643886291696, + 0.007363623962698928, + 0.007911895743862167, + -0.021269087134627128, + 0.049596343141794834, + 0.5665108710576103, + -0.03731120914252456, + 0.09406777009239954, + -0.01358186317431214, + -0.012919726860693337, + -0.017943303167561618, + -0.0050890133003869235, + -0.0197282466981241, + -0.01761770009111755, + -0.017736550564696047 + ] + }, + "phi": 3.141592653589793, + "flip": true + }, + "current": { + "@module": "simsopt.field.coil", + "@class": "ScaledCurrent", + "@version": "0.8.0.post113+gaf89f89f", + "current_to_scale": { + "@module": "simsopt.field.coil", + "@class": "Current", + "current": 99999.99998999985 + }, + "scale": -1.0 + } + } + ], + "points": { + "@module": "numpy", + "@class": "array", + "dtype": "float64", + "data": [ + [ + 1.2998851293181508, + 0.03191038882464297, + 0.00419373730568936 + ], + [ + 1.2934143196593764, + 0.031751539363590923, + 0.07906585291364857 + ], + [ + 1.2772483726093042, + 0.03135468763842581, + 0.1500844687286942 + ], + [ + 1.2532301744970125, + 0.030765073969229853, + 0.21375473765048508 + ], + [ + 1.2240991682518396, + 0.030049947905264354, + 0.267417972301721 + ], + [ + 1.1930338588651803, + 0.029287337364434403, + 0.30937921695344084 + ], + [ + 1.1631798757799854, + 0.028554463215229697, + 0.33881716589778604 + ], + [ + 1.137263755860731, + 0.02791825818080233, + 0.3555593515394394 + ], + [ + 1.1173387112841948, + 0.027429125790989446, + 0.3598219101005657 + ], + [ + 1.104647032739761, + 0.027117562570474187, + 0.35198912364132035 + ], + [ + 1.0995497962721033, + 0.02699243243862982, + 0.3324768962220117 + ], + [ + 1.101489050552417, + 0.02704003845913036, + 0.3017063435669835 + ], + [ + 1.108999944386114, + 0.027224420553554046, + 0.2602002423816471 + ], + [ + 1.1198398435669006, + 0.02749052514224203, + 0.20878511478071493 + ], + [ + 1.1313025780450887, + 0.027771919479282016, + 0.14883148564250998 + ], + [ + 1.1407206862017063, + 0.02800312105739981, + 0.08242154958829276 + ], + [ + 1.1460552831052282, + 0.028134078060887812, + 0.012336955935431099 + ], + [ + 1.1463936961227106, + 0.02814238563417073, + -0.0581738150984735 + ], + [ + 1.1421754600731353, + 0.028038833751423457, + -0.12578725507575997 + ], + [ + 1.1350591281325875, + 0.027864137607813276, + -0.1875165888004896 + ], + [ + 1.1274812013054765, + 0.02767810994576544, + -0.24099806838959187 + ], + [ + 1.12207023306529, + 0.027545278131193708, + -0.2845739656890896 + ], + [ + 1.1211067262922014, + 0.027521625367521764, + -0.3171912247166272 + ], + [ + 1.1261576951800973, + 0.027645619694034656, + -0.33820369198614086 + ], + [ + 1.1379104636852366, + 0.027934133966801, + -0.34717794234705374 + ], + [ + 1.1561484398963036, + 0.028381851152838058, + -0.34377138848020844 + ], + [ + 1.1797932245134795, + 0.02896229803525204, + -0.3277120147033536 + ], + [ + 1.206977205205065, + 0.029629627304666097, + -0.2988849358250655 + ], + [ + 1.2351674786123799, + 0.030321659673689563, + -0.2575165019909788 + ], + [ + 1.2613899560330977, + 0.030965385362650595, + -0.20442183372392364 + ], + [ + 1.2825813463633227, + 0.03148560479583003, + -0.14124121669783085 + ], + [ + 1.296039684319004, + 0.03181598844851826, + -0.07055846043607464 + ], + [ + 1.2955558900562216, + 0.095565943735558, + 0.012523101295674043 + ], + [ + 1.2865214115405799, + 0.0948995205637499, + 0.08715410296963691 + ], + [ + 1.2677543398893512, + 0.09351517819205596, + 0.15816024575638774 + ], + [ + 1.241040921132145, + 0.09154467804340949, + 0.2219943445199918 + ], + [ + 1.2091311012356003, + 0.08919086831875715, + 0.2759281405265298 + ], + [ + 1.1752913219425813, + 0.08669469623636245, + 0.31821299181562995 + ], + [ + 1.1428158458330837, + 0.08429916120272447, + 0.34802009351046354 + ], + [ + 1.1146070793298881, + 0.08221835757766674, + 0.36523046505512896 + ], + [ + 1.0928892849278893, + 0.08061635681968186, + 0.37016569580207626 + ], + [ + 1.0790559506370094, + 0.0795959487796551, + 0.3633344913815696 + ], + [ + 1.0736042232341279, + 0.0791938052069662, + 0.3452480471726537 + ], + [ + 1.0761083615466145, + 0.07937852154604035, + 0.31634568464588936 + ], + [ + 1.085228445600289, + 0.0800512593616966, + 0.2770600476022481 + ], + [ + 1.098807407334671, + 0.08105290375469838, + 0.22801693297027092 + ], + [ + 1.1141303712485646, + 0.08218319347704575, + 0.17030623473292436 + ], + [ + 1.128375171698887, + 0.0832339530844145, + 0.10570723739062358 + ], + [ + 1.1391859231597616, + 0.08403140202025641, + 0.036746232837751004 + ], + [ + 1.1452082504539618, + 0.08447563556955523, + -0.03347377393191328 + ], + [ + 1.1463984444708517, + 0.08456342955461253, + -0.10162904638850165 + ], + [ + 1.1439870018716176, + 0.084385550862131, + -0.16458957297690174 + ], + [ + 1.140109827000446, + 0.0840995532618459, + -0.219746741387959 + ], + [ + 1.137246919571417, + 0.08388837252284219, + -0.26516363234971085 + ], + [ + 1.1376615268655397, + 0.08391895579419821, + -0.2995450925702124 + ], + [ + 1.1429913966989138, + 0.08431211061254569, + -0.3220925824584325 + ], + [ + 1.1540435170100778, + 0.08512736398441643, + -0.3323285105000728 + ], + [ + 1.1707499560713868, + 0.08635970496453832, + -0.32995662916612173 + ], + [ + 1.1922063044311708, + 0.08794242030384543, + -0.3147987660865256 + ], + [ + 1.216742567209377, + 0.08975232377936525, + -0.28683154721882087 + ], + [ + 1.2420338626228082, + 0.09161792180800334, + -0.24633190219902565 + ], + [ + 1.2652966340614868, + 0.09333388691881411, + -0.19410841971213977 + ], + [ + 1.2836067956404038, + 0.09468452557876714, + -0.13174565214996767 + ], + [ + 1.2943253521256513, + 0.09547517380464267, + -0.06174796498025677 + ], + [ + 1.2869381567106086, + 0.15872868226575226, + 0.020679657483510538 + ], + [ + 1.275471562374837, + 0.15731441274588265, + 0.09471699992071653 + ], + [ + 1.2542806411559355, + 0.15470076190063076, + 0.1653713321947585 + ], + [ + 1.2250765448544783, + 0.15109878017484157, + 0.22905374064511805 + ], + [ + 1.190591939105152, + 0.14684550972784885, + 0.28297246885707333 + ], + [ + 1.1541512355975616, + 0.14235097763364077, + 0.3253261462055248 + ], + [ + 1.119170992127789, + 0.13803657610444536, + 0.3552755922468495 + ], + [ + 1.0887089149475992, + 0.13427943723597954, + 0.37275138148612547 + ], + [ + 1.065144179804422, + 0.13137300436839067, + 0.37817937444995936 + ], + [ + 1.0500076266286007, + 0.12950608859849488, + 0.37219848911995995 + ], + [ + 1.043921486091743, + 0.12875543475979856, + 0.3554314445367096 + ], + [ + 1.0465893454883086, + 0.1290844838320318, + 0.32836327521915426 + ], + [ + 1.056810028614626, + 0.13034508486094432, + 0.2913714909124691 + ], + [ + 1.0725499020196405, + 0.13228641308373434, + 0.2449146090198368 + ], + [ + 1.0911478430383488, + 0.13458025032475693, + 0.18982140640032083 + ], + [ + 1.1097044161648744, + 0.13686898532292502, + 0.12756256650481643 + ], + [ + 1.1256208693440144, + 0.1388320925835931, + 0.060373121911550975 + ], + [ + 1.1371513677778433, + 0.140254243922199, + -0.008851069257851741 + ], + [ + 1.143780838631853, + 0.14107191116385248, + -0.07686036456007529 + ], + [ + 1.146283168255804, + 0.14138054408590328, + -0.14045131806130548 + ], + [ + 1.1464358034654996, + 0.1413993698434395, + -0.1968207888741532 + ], + [ + 1.1465022144549637, + 0.14140756085774014, + -0.243775727768022 + ], + [ + 1.1486692332653603, + 0.14167483713548956, + -0.2797754922137806 + ], + [ + 1.1546064803145344, + 0.14240712671404274, + -0.3038467657606403 + ], + [ + 1.1652234523984084, + 0.14371660532398078, + -0.3154366138972231 + ], + [ + 1.180599338551596, + 0.14561304000115427, + -0.31426444327299125 + ], + [ + 1.2000097382911006, + 0.14800708446773375, + -0.30022185143143937 + ], + [ + 1.2219907559757264, + 0.15071818441744597, + -0.2733618844838803 + ], + [ + 1.244436010242119, + 0.15348654248830343, + -0.2340050475923855 + ], + [ + 1.2647671922798074, + 0.15599415461940686, + -0.18295134071637154 + ], + [ + 1.28021966708272, + 0.1579000356055455, + -0.12172840392254165 + ], + [ + 1.2882408136835253, + 0.15888934967908303, + -0.0527573322985865 + ], + [ + 1.274112162312068, + 0.22107943222297913, + 0.02855319649121034 + ], + [ + 1.260409520225467, + 0.21870179827357614, + 0.10166067899591069 + ], + [ + 1.2370362613348804, + 0.2146461531289959, + 0.17164023900857203 + ], + [ + 1.2056117117995382, + 0.2091934765321966, + 0.23487264877903744 + ], + [ + 1.1688233826898997, + 0.2028100958907057, + 0.2885095546995168 + ], + [ + 1.1300217115456834, + 0.19607736726632813, + 0.3306978858333765 + ], + [ + 1.0927163767505417, + 0.18960427762841753, + 0.36058381612625134 + ], + [ + 1.0600975784146789, + 0.18394437920722806, + 0.3781403140947442 + ], + [ + 1.0346824767096727, + 0.1795344407253318, + 0.3838913789073586 + ], + [ + 1.018125505221121, + 0.1766615336420349, + 0.37860589741204 + ], + [ + 1.0111620389725633, + 0.17545325762829572, + 0.3630283980919579 + ], + [ + 1.013617905490957, + 0.1758793908931385, + 0.33771292461995017 + ], + [ + 1.0244360892663007, + 0.17775652384695692, + 0.30301627918276103 + ], + [ + 1.0417331843029596, + 0.18075785454838605, + 0.2592678110873697 + ], + [ + 1.0629544003291371, + 0.18444008483306912, + 0.2070650654468531 + ], + [ + 1.085196524501935, + 0.1882994594854796, + 0.14757912739980938 + ], + [ + 1.1056974089565088, + 0.19185670038573246, + 0.082733571102542 + ], + [ + 1.1223847400477787, + 0.194752227005838, + 0.015167790511052045 + ], + [ + 1.134304666799667, + 0.19682053050092818, + -0.0520117552777472 + ], + [ + 1.1417669774599764, + 0.19811536423115403, + -0.11560143691542843 + ], + [ + 1.1461488337534804, + 0.19887568842403563, + -0.17266464716773414 + ], + [ + 1.149437158176807, + 0.19944626683775613, + -0.22078948128916773 + ], + [ + 1.153683232938349, + 0.2001830306998596, + -0.2581995737488472 + ], + [ + 1.1605443821714532, + 0.2013735530272654, + -0.28373308596184166 + ], + [ + 1.171008175142017, + 0.20318919334313298, + -0.29673345121310385 + ], + [ + 1.1852926111698143, + 0.20566777811774203, + -0.2969031261077361 + ], + [ + 1.2028539126715412, + 0.2087149529897353, + -0.2841749017308462 + ], + [ + 1.2224382761861845, + 0.2121131624208492, + -0.2586583670922499 + ], + [ + 1.2421639311411519, + 0.21553588824253586, + -0.2207070718953206 + ], + [ + 1.2596680292814681, + 0.21857313738974918, + -0.1711086463356149 + ], + [ + 1.2723615701744195, + 0.2207756756721092, + -0.11133247780896387 + ], + [ + 1.277799103946376, + 0.22171917728409915, + -0.04371331481679094 + ], + [ + 1.2571952105336912, + 0.2823126252028697, + 0.036040714297805455 + ], + [ + 1.2415090369043802, + 0.27879017712195564, + 0.10790562754925394 + ], + [ + 1.2162500263182157, + 0.27311807661691145, + 0.17691023248660312 + ], + [ + 1.182931104523009, + 0.2656360625254477, + 0.2394171947540459 + ], + [ + 1.1441674026511306, + 0.2569313821811869, + 0.2925293766080315 + ], + [ + 1.1033022630284983, + 0.2477548082096319, + 0.3343432673905954 + ], + [ + 1.063907145025307, + 0.23890833863158084, + 0.363984995665856 + ], + [ + 1.0292802330655952, + 0.23113260552656442, + 0.38145997641910817 + ], + [ + 1.0020595346684513, + 0.22501999329262903, + 0.387379907525957 + ], + [ + 0.9840103184542988, + 0.2209669062544526, + 0.38263803266149066 + ], + [ + 0.975969598503462, + 0.21916130220917612, + 0.3681052774699297 + ], + [ + 0.9778757926525191, + 0.21958935241956326, + 0.3444230733988141 + ], + [ + 0.9888143077661616, + 0.2220456781291106, + 0.3119593958729354 + ], + [ + 1.0070675963078586, + 0.22614459114088098, + 0.2709542029966007 + ], + [ + 1.0302277498170973, + 0.231345377528318, + 0.22181091121100513 + ], + [ + 1.055453059599783, + 0.23700991026487092, + 0.16542261807098751 + ], + [ + 1.0798972909492128, + 0.2424990459738706, + 0.1033969499877003 + ], + [ + 1.101236258045153, + 0.24729087127633506, + 0.03808228178798968 + ], + [ + 1.1181275833766224, + 0.2510839452218352, + -0.02761776030711626 + ], + [ + 1.1304305447603729, + 0.253846667587395, + -0.09057132409661883 + ], + [ + 1.1390990762164106, + 0.2557932514206, + -0.1477762331153679 + ], + [ + 1.1457959119163945, + 0.2572970761656944, + -0.1966511103339396 + ], + [ + 1.152381966459115, + 0.25877602417003454, + -0.23520668206125045 + ], + [ + 1.160455638579698, + 0.2605890278724758, + -0.26208891900319997 + ], + [ + 1.171051835128952, + 0.26296848337781104, + -0.27651439724597293 + ], + [ + 1.1845118575761493, + 0.2659910324939063, + -0.27813625377747725 + ], + [ + 1.20046679555763, + 0.2695738336283369, + -0.26689702490258665 + ], + [ + 1.2178716626220076, + 0.2734822272263092, + -0.2429391249610318 + ], + [ + 1.2350706935799411, + 0.27734439878089095, + -0.2066354538863707 + ], + [ + 1.249922109261218, + 0.2806793956961119, + -0.1587557882297956 + ], + [ + 1.2600244864365016, + 0.2829479603527758, + -0.10070968951772243 + ], + [ + 1.2630573156909664, + 0.2836290049363425, + -0.03474323453738655 + ], + [ + 1.236338280169999, + 0.3421408806243402, + 0.04304874213173474 + ], + [ + 1.2189672191540937, + 0.33733366061935605, + 0.11338808598028213 + ], + [ + 1.1921628942753606, + 0.32991590492445866, + 0.1811456028570745 + ], + [ + 1.157319787476779, + 0.32027351866580833, + 0.24267878722257652 + ], + [ + 1.1169548157244353, + 0.30910302657376976, + 0.2950505346728776 + ], + [ + 1.0743707859287193, + 0.2973184384165252, + 0.33630862056805844 + ], + [ + 1.0331681497425962, + 0.2859161333557889, + 0.3655529771519784 + ], + [ + 0.9967273265097136, + 0.27583159941265156, + 0.3828093032695672 + ], + [ + 0.9677901598900585, + 0.2678236069167299, + 0.38876303364763015 + ], + [ + 0.9482215128083238, + 0.26240823294298943, + 0.38442165367231773 + ], + [ + 0.9389499872604921, + 0.25984245627284536, + 0.3707819504906079 + ], + [ + 0.940015949918756, + 0.260137447868965, + 0.3485859604273638 + ], + [ + 0.9506400277747766, + 0.26307752617259933, + 0.31824068043268094 + ], + [ + 0.9692759375499568, + 0.26823477697036574, + 0.27993539317100696 + ], + [ + 0.9936903793464866, + 0.2749911629451394, + 0.23392035108595835 + ], + [ + 1.0211571697058883, + 0.2825922475287193, + 0.18084199207218352 + ], + [ + 1.0488201927833591, + 0.2902476370190099, + 0.12200194883226655 + ], + [ + 1.0741820827105868, + 0.29726621720307256, + 0.05943884034736913 + ], + [ + 1.0955754567635567, + 0.30318656113758424, + -0.00419348919382545 + ], + [ + 1.1124419010474242, + 0.30785413488567714, + -0.06590109054974182 + ], + [ + 1.1253091783595701, + 0.31141499008318, + -0.1226864696519986 + ], + [ + 1.1354838871587558, + 0.31423071122074037, + -0.17185708083424583 + ], + [ + 1.1445901023830585, + 0.3167507403632249, + -0.21124537048422173 + ], + [ + 1.1541214939901814, + 0.31938843165721936, + -0.2393121816714992 + ], + [ + 1.1651250394961055, + 0.3224335228023946, + -0.2551309870270899 + ], + [ + 1.1780429765641676, + 0.3260083974424558, + -0.25827578931603334 + ], + [ + 1.1926687122485844, + 0.33005588360955657, + -0.24866654790637105 + ], + [ + 1.2081590497552097, + 0.3343426373246937, + -0.2264522317754311 + ], + [ + 1.2230807113299607, + 0.3384720172148495, + -0.1920087915922151 + ], + [ + 1.235514003653661, + 0.34191277259136804, + -0.1460810321428688 + ], + [ + 1.2432527235081305, + 0.34405436479827534, + -0.09001678255531473 + ], + [ + 1.2441157271243128, + 0.34429319006313897, + -0.02597184754913205 + ], + [ + 1.2117218926592324, + 0.40029884308583125, + 0.049495179237069364 + ], + [ + 1.1929987345536244, + 0.3941135471247755, + 0.11806078672413252 + ], + [ + 1.1650207357868327, + 0.3848708647848327, + 0.18433119979556922 + ], + [ + 1.1290547380388478, + 0.3729893040272317, + 0.24467225498419598 + ], + [ + 1.087495730567582, + 0.3592600624320057, + 0.29611677033782846 + ], + [ + 1.0435731136411837, + 0.34474998974333476, + 0.336666292004612 + ], + [ + 1.00088282279799, + 0.3306470226028921, + 0.36538815368176664 + ], + [ + 0.9628608954668205, + 0.3180862744520448, + 0.3823146547663819 + ], + [ + 0.9323362001749432, + 0.3080022772205758, + 0.38818835466591595 + ], + [ + 0.911263576801094, + 0.3010408227742729, + 0.3841173103930656 + ], + [ + 0.9006552014458583, + 0.29753628893078365, + 0.37121926730450105 + ], + [ + 0.9006430910100253, + 0.2975322881831842, + 0.35034505078939254 + ], + [ + 0.9105723247386749, + 0.3008124639383523, + 0.32196354771138347 + ], + [ + 0.9290647830683727, + 0.30692154698788404, + 0.28624880075500014 + ], + [ + 0.9540771506457376, + 0.3151845171171796, + 0.2433395340344368 + ], + [ + 0.9830398768120653, + 0.32475250944877737, + 0.19367325142250338 + ], + [ + 1.0131521470411535, + 0.3347002598430024, + 0.13826768307875964 + ], + [ + 1.041820848404695, + 0.3441711194999272, + 0.07884846169315216 + ], + [ + 1.0671251858743636, + 0.3525305434531679, + 0.017787440738490323 + ], + [ + 1.0881376762682624, + 0.3594721326461886, + -0.04211571423688539 + ], + [ + 1.1049762432270918, + 0.3650348437878104, + -0.09793617861310837 + ], + [ + 1.1185769191565609, + 0.3695279002166282, + -0.14693350665001897 + ], + [ + 1.130292103205631, + 0.3733980742638171, + -0.18680605117229834 + ], + [ + 1.141469119362439, + 0.37709046165388316, + -0.21584710600273627 + ], + [ + 1.1531296165184564, + 0.3809425695918817, + -0.2329792085652673 + ], + [ + 1.1657886882595558, + 0.3851245620137417, + -0.23767221944251282 + ], + [ + 1.17938295141261, + 0.38961549994738703, + -0.22979251497314182 + ], + [ + 1.1932581325291556, + 0.3941992406408645, + -0.20946833917522634 + ], + [ + 1.2061950024798578, + 0.3984730051950833, + -0.17706053119377388 + ], + [ + 1.2164924935219057, + 0.4018748367339789, + -0.133280308818623 + ], + [ + 1.2221430062379701, + 0.4037415139944833, + -0.07941141275051622 + ], + [ + 1.221115627919375, + 0.40340211404236836, + -0.01751843588881829 + ], + [ + 1.183551512491497, + 0.45654619774253696, + 0.05531055998291784 + ], + [ + 1.1638302257215811, + 0.4489388579737409, + 0.12189301625670054 + ], + [ + 1.135068001081952, + 0.4378440436295839, + 0.18647129243971133 + ], + [ + 1.098397945332461, + 0.4236988422194702, + 0.24543340145814824 + ], + [ + 1.0560722302216639, + 0.40737201225337094, + 0.2957930513091122 + ], + [ + 1.0112154440358831, + 0.39006884043539286, + 0.3355091291195811 + ], + [ + 0.9673855006337307, + 0.3731617655879286, + 0.3636103257033724 + ], + [ + 0.928046846003085, + 0.35798717199705, + 0.38012114361133176 + ], + [ + 0.8960985921873852, + 0.34566337058230084, + 0.38582292093039355 + ], + [ + 0.8735772796457058, + 0.3369759416866794, + 0.38190799646867624 + ], + [ + 0.8615728179583725, + 0.33234530982870414, + 0.36960660180409294 + ], + [ + 0.8602999133214025, + 0.3318542963274209, + 0.3498818812767418 + ], + [ + 0.8692156450902272, + 0.3352934735801123, + 0.32328206822381655 + ], + [ + 0.8871006567725728, + 0.3421924838842493, + 0.2899965486999012 + ], + [ + 0.912106143370784, + 0.35183816445550137, + 0.2500921054933057 + ], + [ + 0.9418471036340673, + 0.3633105187908277, + 0.2038380739054694 + ], + [ + 0.9736315698303023, + 0.37557114034892114, + 0.15199949987444822 + ], + [ + 1.004842249686248, + 0.38761042809161994, + 0.09599976958024299 + ], + [ + 1.0333792186934379, + 0.3986183517500529, + 0.03791197712866427 + ], + [ + 1.0580058022299272, + 0.40811787328190846, + -0.01970220680320839 + ], + [ + 1.0784632750734284, + 0.41600919135598236, + -0.0740520516542661 + ], + [ + 1.095320336730889, + 0.4225116775795217, + -0.12241313021029392 + ], + [ + 1.1096366795232357, + 0.42803410038785855, + -0.16240039956549177 + ], + [ + 1.1225788966041643, + 0.43302646441789394, + -0.19216661635378357 + ], + [ + 1.1351065572066907, + 0.4378589163680861, + -0.21048475227673105 + ], + [ + 1.147775683996147, + 0.44274593784826516, + -0.2167022632362227 + ], + [ + 1.160641371424461, + 0.4477087811337051, + -0.21060442009128164 + ], + [ + 1.1732199384933932, + 0.4525608698748884, + -0.19227213178346797 + ], + [ + 1.1844930582845195, + 0.45690939203299924, + -0.1620319985333128 + ], + [ + 1.1929705637966892, + 0.4601795267648412, + -0.12055176882220693 + ], + [ + 1.1968427076997419, + 0.46167317749097575, + -0.06904818505599475 + ], + [ + 1.194235994767215, + 0.4606676573548507, + -0.009494247454852599 + ], + [ + 1.152052745776683, + 0.5106698605970657, + 0.06043873290844481 + ], + [ + 1.1316952391026442, + 0.5016459985096345, + 0.12487002783931189 + ], + [ + 1.1025425878734185, + 0.48872351697062577, + 0.1875878363775962 + ], + [ + 1.0655913570736633, + 0.4723441628563491, + 0.24501613424502539 + ], + [ + 1.0229334946438793, + 0.4534352329134754, + 0.2941614780080415 + ], + [ + 0.9775597259893679, + 0.4333224245385936, + 0.3329450673143371 + ], + [ + 0.9329571492215629, + 0.41355146201644877, + 0.36035194865481135 + ], + [ + 0.8925910085004252, + 0.3956583824413398, + 0.3763846139610477 + ], + [ + 0.8594135370600029, + 0.3809518208038158, + 0.38184403407940876 + ], + [ + 0.83553543063428, + 0.3703673841758513, + 0.3779887804955569 + ], + [ + 0.8221203905393196, + 0.36442090587413173, + 0.3661502635455378 + ], + [ + 0.8194587963170978, + 0.36324110229707274, + 0.3474033203006533 + ], + [ + 0.8271072744527815, + 0.36663143948228216, + 0.32238757280136804 + ], + [ + 0.8439919232814279, + 0.3741158895607742, + 0.2913325950738281 + ], + [ + 0.8684543304369486, + 0.38495932888920226, + 0.2542687262913634 + ], + [ + 0.8983096803019216, + 0.3981932953108905, + 0.21133789349780369 + ], + [ + 0.9310155374951626, + 0.4126908047303225, + 0.16308947785007666 + ], + [ + 0.9639926547701713, + 0.4273085554743376, + 0.11066684451338252 + ], + [ + 0.9950339718627916, + 0.44106822501251564, + 0.05584240077723397 + ], + [ + 1.0226596621467603, + 0.45331385131558993, + 0.0009104961301344748 + ], + [ + 1.0462806489122412, + 0.46378431463674724, + -0.051523529306826764 + ], + [ + 1.0661177087487146, + 0.47257747850748477, + -0.09881160618086944 + ], + [ + 1.082931421345222, + 0.48003048471683923, + -0.1385389889227857 + ], + [ + 1.097682874589904, + 0.48656935422579334, + -0.16875250325923946 + ], + [ + 1.1112362969959089, + 0.4925771731872606, + -0.18808616424692537 + ], + [ + 1.1241552661576975, + 0.49830375836757773, + -0.1957549129970456 + ], + [ + 1.1365845646890438, + 0.503813287485572, + -0.19144079587692894 + ], + [ + 1.1481881538805594, + 0.5089568048258845, + -0.17515310984480223 + ], + [ + 1.158131211795763, + 0.5133642592745454, + -0.1471651273982447 + ], + [ + 1.165122798448118, + 0.5164634164912608, + -0.10809032136315867 + ], + [ + 1.1675463311641143, + 0.5175376946601791, + -0.05907491615148518 + ], + [ + 1.163689214322091, + 0.515827952352595, + -0.0020003703352797415 + ], + [ + 1.1174665681429339, + 0.5624854013430385, + 0.06483697612766165 + ], + [ + 1.0968298930256177, + 0.5520977720244808, + 0.12699187388336866 + ], + [ + 1.067672106705401, + 0.5374209757710973, + 0.18771825018452495 + ], + [ + 1.0308538128269746, + 0.5188882040539181, + 0.24348931505908225 + ], + [ + 0.9882934041368636, + 0.4974650946331459, + 0.29131721868354465 + ], + [ + 0.9428219091183132, + 0.4745766674941733, + 0.32909210087694496 + ], + [ + 0.8978242271634896, + 0.45192673780909315, + 0.35575211243166743 + ], + [ + 0.8567385355622364, + 0.43124593859002314, + 0.37126465735193365 + ], + [ + 0.8225522239552497, + 0.41403799541490843, + 0.3764313261211599 + ], + [ + 0.797442480449126, + 0.4013988126811157, + 0.372557874079873 + ], + [ + 0.7826441419473879, + 0.3939499550520294, + 0.36106334909545185 + ], + [ + 0.7785184084461154, + 0.3918732353268441, + 0.34313000211017936 + ], + [ + 0.7847103569415584, + 0.39499000027878595, + 0.3194957737787316 + ], + [ + 0.8002766345291299, + 0.40282540595970323, + 0.2904492741563253 + ], + [ + 0.8237390219027388, + 0.41463537929988303, + 0.25601453854822454 + ], + [ + 0.8531180411368838, + 0.4294235348439415, + 0.21624437826313506 + ], + [ + 0.8860486821898476, + 0.44599942657727754, + 0.17151207245990308 + ], + [ + 0.9200404716965217, + 0.4631094555554188, + 0.12271160579847702 + ], + [ + 0.9528448139554611, + 0.4796217738183073, + 0.07132590406238201 + ], + [ + 0.9828058891840717, + 0.4947029117288944, + 0.019367487188012338 + ], + [ + 1.0090591023954336, + 0.507917668743306, + -0.03078234082822411 + ], + [ + 1.0315100365591832, + 0.5192185193223162, + -0.07660490852279934 + ], + [ + 1.0506278099684296, + 0.5288415977708787, + -0.11570884142817944 + ], + [ + 1.0671542878597833, + 0.5371603276680543, + -0.14607483232082882 + ], + [ + 1.08183114986904, + 0.5445480391693056, + -0.1662170644508907 + ], + [ + 1.0951973559709198, + 0.551276021927791, + -0.17521671873323497 + ], + [ + 1.1074565703992285, + 0.5574467919037708, + -0.17263736738728166 + ], + [ + 1.118394090728505, + 0.5629522769782234, + -0.1583962439916562 + ], + [ + 1.1273376555479486, + 0.5674540891937444, + -0.13269528636040032 + ], + [ + 1.1331803618300638, + 0.5703950603885128, + -0.09608242600746926 + ], + [ + 1.1344904511367389, + 0.5710545039284525, + -0.04962926997530369 + ], + [ + 1.129716077434305, + 0.568651286163613, + 0.004873911871627983 + ], + [ + 1.080044757473638, + 0.6118377977444073, + 0.06847561620487981 + ], + [ + 1.0594694045092168, + 0.6001820042613557, + 0.12827176336721213 + ], + [ + 1.0306716023170535, + 0.5838682508254825, + 0.18691282057904948 + ], + [ + 0.9943799475789729, + 0.5633092823588544, + 0.24093345823431403 + ], + [ + 0.9523305076634694, + 0.5394885688779917, + 0.28736462273291624 + ], + [ + 0.9071728081745789, + 0.5139070480981356, + 0.32407382176360205 + ], + [ + 0.8621602784711763, + 0.48840776498596616, + 0.34995146983692343 + ], + [ + 0.8206760658271951, + 0.4649072487993732, + 0.36491889756513435 + ], + [ + 0.7857233717614134, + 0.4451067921845334, + 0.36976034950874365 + ], + [ + 0.759537119818505, + 0.43027246369114197, + 0.3658093794973708 + ], + [ + 0.7434210524815463, + 0.4211428243132238, + 0.35455735033789776 + ], + [ + 0.7378043599941926, + 0.41796100732064984, + 0.337286424451326 + ], + [ + 0.7424117833793337, + 0.42057108042894836, + 0.3148351483477968 + ], + [ + 0.7564160288487347, + 0.4285043874958822, + 0.2875642619133299 + ], + [ + 0.7785053777217239, + 0.4410178490673, + 0.2555157604843626 + ], + [ + 0.8069026848753946, + 0.4571047248662388, + 0.21868744368452528 + ], + [ + 0.8394365815417464, + 0.4755349496792791, + 0.177315735722942 + ], + [ + 0.8737437789808398, + 0.4949697369718139, + 0.13208103372826108 + ], + [ + 0.907590426304892, + 0.5141436258467712, + 0.08419860199684426 + ], + [ + 0.939208529340998, + 0.5320550599763323, + 0.03539866837620965 + ], + [ + 0.9675165350559249, + 0.548091347135089, + -0.01218619177748322 + ], + [ + 0.9921470850073049, + 0.5620443813359459, + -0.05620958295125282 + ], + [ + 1.013297738367123, + 0.5740260784674579, + -0.0943526140690098 + ], + [ + 1.031488858813954, + 0.5843312209124126, + -0.12457212380411853 + ], + [ + 1.0473203985091417, + 0.5932996773721841, + -0.1452886921115478 + ], + [ + 1.0612782979561968, + 0.6012067297417545, + -0.15545729375932993 + ], + [ + 1.0735945543963996, + 0.608183802834962, + -0.154515500899651 + ], + [ + 1.0841477077621067, + 0.6141620903733752, + -0.14227303449574197 + ], + [ + 1.0924044940653161, + 0.6188395020391831, + -0.11884457706506467 + ], + [ + 1.0974239061546065, + 0.6216829638655863, + -0.08470137199601 + ], + [ + 1.0979473993900686, + 0.6219795191204521, + -0.04083587670323355 + ], + [ + 1.0925802752571692, + 0.6189390808543794, + 0.011052277551534328 + ], + [ + 1.0400456405952692, + 0.6586016384128802, + 0.07133725145220769 + ], + [ + 1.0198454805069614, + 0.6458100281112905, + 0.12873407861802783 + ], + [ + 0.9917426440389835, + 0.6280141031831631, + 0.18523186873489347 + ], + [ + 0.9563408929962102, + 0.60559619157487, + 0.2374373904347435 + ], + [ + 0.9151900934311648, + 0.5795377351401819, + 0.2824136074900652 + ], + [ + 0.8707412122354307, + 0.5513907915460768, + 0.31801561693296876 + ], + [ + 0.8260897561905715, + 0.5231155688435101, + 0.3430882065890736 + ], + [ + 0.7845360132223141, + 0.4968019513128234, + 0.35749863068690285 + ], + [ + 0.7490778115769934, + 0.474348292754708, + 0.3619977450271995 + ], + [ + 0.7219969821751135, + 0.45719954666367146, + 0.35792777125176517 + ], + [ + 0.7046633689545887, + 0.4462231571466509, + 0.3468356119326407 + ], + [ + 0.6975728969250535, + 0.44173316525254536, + 0.3300929350978149 + ], + [ + 0.700524010731422, + 0.4436019374603734, + 0.3086370517508012 + ], + [ + 0.7127929737985951, + 0.45137117263840126, + 0.28290874292359725 + ], + [ + 0.7332197310416109, + 0.46430627400569563, + 0.25298646785546597 + ], + [ + 0.7602208187702808, + 0.4814045242390737, + 0.21884199236226148 + ], + [ + 0.7918248880361831, + 0.5014175803844985, + 0.18061159445281127 + ], + [ + 0.8258222739743983, + 0.5229461875981668, + 0.1387999458027913 + ], + [ + 0.8600391949493721, + 0.5446138138406686, + 0.0943841186608997 + ], + [ + 0.8926524802592092, + 0.5652659489977295, + 0.04882386062821651 + ], + [ + 0.9224214205466269, + 0.5841169225337439, + 0.003992334251603436 + ], + [ + 0.9487532823601866, + 0.6007913901301334, + -0.037967282297190895 + ], + [ + 0.9716034838525666, + 0.6152611206434332, + -0.07485098342104315 + ], + [ + 0.9912793754299303, + 0.6277207415718659, + -0.10463377426819558 + ], + [ + 1.008229195332089, + 0.6384540966503754, + -0.12567401462586877 + ], + [ + 1.0228633974380972, + 0.6477211029313362, + -0.1368159926934666 + ], + [ + 1.0354142510223847, + 0.6556688433106534, + -0.13737163464433783 + ], + [ + 1.0458253679189684, + 0.6622616103760233, + -0.12703345077800518 + ], + [ + 1.0536774034685488, + 0.6672338570505731, + -0.10581591862719154 + ], + [ + 1.058174861229515, + 0.6700818407682586, + -0.07410322936686463 + ], + [ + 1.0582180336682219, + 0.6701091794135611, + -0.03280400453596378 + ], + [ + 1.052562618196331, + 0.6665279270624036, + 0.016472067007889977 + ], + [ + 0.9977301975432347, + 0.7026808856145235, + 0.07341570167491163 + ], + [ + 0.9781844738676184, + 0.6889152338820433, + 0.1284122040589295 + ], + [ + 0.951073563664801, + 0.6698215766608424, + 0.1827428202685485 + ], + [ + 0.9168864591091411, + 0.6457443010958454, + 0.2330949739337836 + ], + [ + 0.8769879688998398, + 0.6176446139219094, + 0.27657637158614984 + ], + [ + 0.8336187755495301, + 0.5871005818110014, + 0.31104153702587917 + ], + [ + 0.7896935343342246, + 0.556164936609519, + 0.33529503862726745 + ], + [ + 0.7484023455109319, + 0.5270844004065737, + 0.34914578334217405 + ], + [ + 0.7127143709608242, + 0.5019500929310585, + 0.35329792255745396 + ], + [ + 0.684944607353665, + 0.48239241878945865, + 0.34908402046171333 + ], + [ + 0.6665245929571696, + 0.4694195809811685, + 0.33808855379899644 + ], + [ + 0.6580166141777634, + 0.46342758627335706, + 0.32175960881388854 + ], + [ + 0.6592897617054148, + 0.46432423792773303, + 0.30112775988066237 + ], + [ + 0.6697143859952692, + 0.47166608670231236, + 0.27671728039014465 + ], + [ + 0.6882680315211616, + 0.48473303817036895, + 0.24865640474195572 + ], + [ + 0.7135490164428769, + 0.502537916601172, + 0.21691449372962981 + ], + [ + 0.7437847728640392, + 0.523832338832315, + 0.1815603868302801 + ], + [ + 0.7769349713941865, + 0.5471793427806964, + 0.14296035739514257 + ], + [ + 0.8109198605428851, + 0.5711142021879, + 0.10188736264531705 + ], + [ + 0.8439089896027263, + 0.5943477682165723, + 0.0595522826092023 + ], + [ + 0.8745558333916071, + 0.6159317108374205, + 0.01757111461211601 + ], + [ + 0.9020910774913623, + 0.6353242177067836, + -0.022134587639175345 + ], + [ + 0.9262636774411964, + 0.6523484833671591, + -0.05750947192588133 + ], + [ + 0.9471856511099439, + 0.6670833997028173, + -0.08658597184663624 + ], + [ + 0.9651529623687809, + 0.6797374079893853, + -0.10769448622193288 + ], + [ + 0.9804855361024855, + 0.6905358247522324, + -0.11959059870458713 + ], + [ + 0.9933922478812437, + 0.6996257567653167, + -0.12146827872583828 + ], + [ + 1.0038552040926514, + 0.7069946019249416, + -0.11289913629797374 + ], + [ + 1.0115435890621685, + 0.7124093735461959, + -0.0937881555567986 + ], + [ + 1.015785629041792, + 0.7153969551760131, + -0.0644235976252046 + ], + [ + 1.0156239484532277, + 0.7152830868582667, + -0.025625814067523137 + ], + [ + 1.009955176749875, + 0.7112906873792624, + 0.02108458107877437 + ], + [ + 0.9533585128492477, + 0.7440082858238454, + 0.07471481220087274 + ], + [ + 0.9347061230858627, + 0.7294518179816489, + 0.1273463248273507 + ], + [ + 0.9088405140255464, + 0.7092660985493637, + 0.1795173263580261 + ], + [ + 0.8761483627146349, + 0.6837528932557627, + 0.2280019917464867 + ], + [ + 0.8378154524747655, + 0.6538375964877415, + 0.2699644626074753 + ], + [ + 0.7958661601480723, + 0.6211000474389278, + 0.30327179513641245 + ], + [ + 0.7530155566322557, + 0.5876591082343903, + 0.326697143226918 + ], + [ + 0.712317268620122, + 0.5558978525349189, + 0.33999106011260527 + ], + [ + 0.6766864216092785, + 0.5280912666076988, + 0.34380109656026636 + ], + [ + 0.6484539473163123, + 0.506058427418485, + 0.33943317536304446 + ], + [ + 0.629106125620228, + 0.490959239169242, + 0.32849045826986173 + ], + [ + 0.6192712482563004, + 0.4832840255426757, + 0.31248185313278937 + ], + [ + 0.6188885595192718, + 0.4829853723210353, + 0.292522415598823 + ], + [ + 0.6274165448139938, + 0.48964067736645245, + 0.26921961907085923 + ], + [ + 0.643958433732432, + 0.5025500941835463, + 0.24276040106266755 + ], + [ + 0.6672812473557314, + 0.5207513965798505, + 0.2131303363000955 + ], + [ + 0.6958046320271465, + 0.5430112644566787, + 0.18035883490808313 + ], + [ + 0.7276643965484012, + 0.5678748687238369, + 0.1447086353093998 + ], + [ + 0.7608980383350751, + 0.5938106573323526, + 0.10678447423952203 + ], + [ + 0.7937055831741707, + 0.619413916619223, + 0.06757694326196272 + ], + [ + 0.824680952131147, + 0.6435873318138543, + 0.02845807810476129 + ], + [ + 0.8529247676243895, + 0.6656290217626334, + -0.008878589177844032 + ], + [ + 0.8780181231393783, + 0.6852120686129503, + -0.042550510099331 + ], + [ + 0.8999023529970382, + 0.7022906892194956, + -0.07068200889453641 + ], + [ + 0.9187293294981977, + 0.7169834058891252, + -0.09161028594695296 + ], + [ + 0.9347214775874285, + 0.7294638007522811, + -0.104028668535597 + ], + [ + 0.9480463663156666, + 0.7398626459798346, + -0.10702702851612217 + ], + [ + 0.9587010599185104, + 0.7481776504787598, + -0.1000581523029801 + ], + [ + 0.9664187765162151, + 0.7542006156266469, + -0.08291234180043472 + ], + [ + 0.9706292195008099, + 0.757486477582361, + -0.05577521976073104 + ], + [ + 0.9704994788246876, + 0.7573852269649464, + -0.019375192308647548 + ], + [ + 0.9650555135887616, + 0.75313671479594, + 0.0248550488764715 + ], + [ + 0.9071865278347814, + 0.7825444805736764, + 0.07524723002829349 + ], + [ + 0.8896226497176878, + 0.7673937751164371, + 0.12558134413143837 + ], + [ + 0.8652089611852211, + 0.7463343825600631, + 0.17562858101384535 + ], + [ + 0.83424399650389, + 0.7196238203338264, + 0.22225329294170054 + ], + [ + 0.7977450108470712, + 0.6881395787849008, + 0.2626862180522914 + ], + [ + 0.7575198617352241, + 0.6534411265351097, + 0.29482082676783133 + ], + [ + 0.7160700862636026, + 0.617686304322579, + 0.3174108829448263 + ], + [ + 0.6762883138360241, + 0.5833703114308623, + 0.3301530936858654 + ], + [ + 0.6410085887329556, + 0.5529378112685683, + 0.33363246366312715 + ], + [ + 0.6125568636084084, + 0.5283951843931959, + 0.32911316314553796 + ], + [ + 0.5924639182627119, + 0.5110628905414876, + 0.3181975650198831 + ], + [ + 0.5814227573159738, + 0.5015387196098299, + 0.30243748652252544 + ], + [ + 0.579444148092549, + 0.49983195955608567, + 0.28302069156641446 + ], + [ + 0.5860722197191952, + 0.5055493734606533, + 0.2606344172258911 + ], + [ + 0.6005269169287211, + 0.5180180810225652, + 0.235529699981063 + ], + [ + 0.6217313120741313, + 0.5363091180649121, + 0.20772263681783237 + ], + [ + 0.6482874848640622, + 0.5592166302515414, + 0.1772264895450155 + ], + [ + 0.6785073539796376, + 0.585284468622659, + 0.14423168472689646 + ], + [ + 0.7105595131727562, + 0.6129328512842805, + 0.10921015860130039 + ], + [ + 0.7427021688113569, + 0.640659296716623, + 0.07296490070314428 + ], + [ + 0.773507462045991, + 0.6672321254057269, + 0.03664557987109784 + ], + [ + 0.8019875968473814, + 0.6917992586368779, + 0.0017218551018835352 + ], + [ + 0.8275943022842278, + 0.7138877546522525, + -0.03011101168354391 + ], + [ + 0.8501272841980981, + 0.7333248385225909, + -0.057097463514346254 + ], + [ + 0.8696097812071838, + 0.7501305560178024, + -0.07761453324314734 + ], + [ + 0.8861675769991983, + 0.7644134088930898, + -0.09032194417926319 + ], + [ + 0.8999154404919464, + 0.7762723974977901, + -0.09422386669286838 + ], + [ + 0.9108459768087427, + 0.7857011429674063, + -0.0886614147687382 + ], + [ + 0.9187339493187049, + 0.7925053548479779, + -0.07330927135376915 + ], + [ + 0.9230888303365664, + 0.7962619010482574, + -0.04824647879089236 + ], + [ + 0.9231838143541127, + 0.7963438348252624, + -0.014107141055182902 + ], + [ + 0.9181611433950826, + 0.7920112490602293, + 0.027762335741339808 + ], + [ + 0.8594630345542866, + 0.8182768325594328, + 0.07503324579758966 + ], + [ + 0.8431379836025316, + 0.8027340919794369, + 0.12316504496528016 + ], + [ + 0.8203352171240031, + 0.7810240535281391, + 0.1711489673537426 + ], + [ + 0.7912802171366193, + 0.7533613939327553, + 0.21594029639398418 + ], + [ + 0.7568359555705316, + 0.720567730772171, + 0.25484460219843513 + ], + [ + 0.7185991446781208, + 0.6841632604851599, + 0.28579583795282326 + ], + [ + 0.6788490548553854, + 0.6463180288854449, + 0.30754316421572386 + ], + [ + 0.6402954160615492, + 0.6096119133603936, + 0.3197383863926496 + ], + [ + 0.6056632676168913, + 0.5766393670207839, + 0.3229022891487254 + ], + [ + 0.577249251433871, + 0.5495869747389076, + 0.31824456927891454 + ], + [ + 0.5566146759635225, + 0.5299412257325624, + 0.3073472105183719 + ], + [ + 0.5445140836250603, + 0.5184205041043836, + 0.2917850016747742 + ], + [ + 0.5410320021949874, + 0.5151052869876966, + 0.2728038921254845 + ], + [ + 0.545798624139857, + 0.519643488341614, + 0.25116473116065846 + ], + [ + 0.5581448103977239, + 0.5313980348190146, + 0.2271852517966337 + ], + [ + 0.5771385466986516, + 0.5494815750690643, + 0.20092291644119384 + ], + [ + 0.6015531256786444, + 0.5727261865913981, + 0.17239385690103487 + ], + [ + 0.6298717861507468, + 0.5996877926893905, + 0.14174330822884348 + ], + [ + 0.6604005105260834, + 0.6287535545425288, + 0.10934369787377957 + ], + [ + 0.691474323261484, + 0.6583382836563932, + 0.07584461089709596 + ], + [ + 0.721672617874289, + 0.6870894502231759, + 0.04220052094253826 + ], + [ + 0.7499544170125295, + 0.7140159614138789, + 0.00966977941049134 + ], + [ + 0.7756780652638777, + 0.7385069104910572, + -0.020245217117331994 + ], + [ + 0.7985325177197906, + 0.7602661580835995, + -0.045930259015929786 + ], + [ + 0.8184330824786639, + 0.7792130707980941, + -0.06583161342924623 + ], + [ + 0.8354165427805017, + 0.795382669190297, + -0.0786039766280895 + ], + [ + 0.8495397340024262, + 0.8088290650375608, + -0.08318685190840416 + ], + [ + 0.8607761264797544, + 0.8195269999993722, + -0.07882086404661298 + ], + [ + 0.8689224704186456, + 0.8272829641854439, + -0.06506825402367106 + ], + [ + 0.8735478065887816, + 0.831686650299512, + -0.04190075470803095 + ], + [ + 0.8740134882539183, + 0.8321300161018896, + -0.00985768307162457 + ], + [ + 0.8695643254040313, + 0.8278940609322357, + 0.029798450923735714 + ], + [ + 0.8104268599200485, + 0.8512179504490567, + 0.07409976090000503 + ], + [ + 0.7954469218512324, + 0.8354840294607403, + 0.12014658632299448 + ], + [ + 0.7743676687793666, + 0.8133437975851572, + 0.16614814230712915 + ], + [ + 0.7473566765103846, + 0.7849732651955144, + 0.2091489459866794 + ], + [ + 0.7151396481068646, + 0.7511346620550549, + 0.2465354703807506 + ], + [ + 0.6791125450351018, + 0.7132942123439134, + 0.2762957809874963 + ], + [ + 0.6413290598000585, + 0.6736089767561286, + 0.29719127968414705 + ], + [ + 0.6042976605895527, + 0.6347136818231136, + 0.3088418337613033 + ], + [ + 0.5706067296031329, + 0.5993269903876766, + 0.3117066744622974 + ], + [ + 0.5424966020187476, + 0.5698020000738037, + 0.30693116838608697 + ], + [ + 0.5215413597278509, + 0.5477920207947602, + 0.29605777749560336 + ], + [ + 0.508551194677388, + 0.5341480237641819, + 0.2806627463324217 + ], + [ + 0.5036863201129788, + 0.5290382862163462, + 0.26203319298791394 + ], + [ + 0.5066653636886784, + 0.5321672735342833, + 0.24099497473341233 + ], + [ + 0.5169271858463296, + 0.5429456024087362, + 0.2179328386562432 + ], + [ + 0.5336756269980713, + 0.5605370402737259, + 0.19295379229289272 + ], + [ + 0.5558438793994562, + 0.5838210839146363, + 0.1660923418877146 + ], + [ + 0.5820788155844595, + 0.6113764991087518, + 0.13747166828464955 + ], + [ + 0.6108245242773841, + 0.6415690611373732, + 0.10739486732045658 + ], + [ + 0.64050401533848, + 0.6727424054585245, + 0.07639170233008682 + ], + [ + 0.6697249390101822, + 0.7034341013883811, + 0.04525146069356884 + ], + [ + 0.6974214797553835, + 0.7325246878602867, + 0.015039698485179658 + ], + [ + 0.7228904215320289, + 0.759275553967268, + -0.012932097954514258 + ], + [ + 0.7457403397243333, + 0.7832755735785389, + -0.03720517379427833 + ], + [ + 0.7658022460062114, + 0.8043472527045813, + -0.05631938789322865 + ], + [ + 0.7830366845200384, + 0.8224491495621551, + -0.06895084855099425 + ], + [ + 0.7974430745815636, + 0.8375806542395184, + -0.07399612364366999 + ], + [ + 0.8089659616681634, + 0.8496835210300874, + -0.07060930505721832 + ], + [ + 0.8174081203594193, + 0.8585505975966827, + -0.05824707724222541 + ], + [ + 0.8223803300646524, + 0.8637730727684817, + -0.03677659247202902 + ], + [ + 0.8233154479737115, + 0.8647552578234736, + -0.006644247688598656 + ], + [ + 0.8195472670119216, + 0.86079741358873, + 0.030967894324909248 + ], + [ + 0.7603042130339408, + 0.8814038770227808, + 0.07247939996088888 + ], + [ + 0.7467340940417083, + 0.8656723378751998, + 0.11657538018912655 + ], + [ + 0.727447454850132, + 0.8433137631536942, + 0.16069163463181438 + ], + [ + 0.7025683242379562, + 0.8144719366813358, + 0.20195819551252905 + ], + [ + 0.6727037556581397, + 0.7798506021147102, + 0.23784629856641165 + ], + [ + 0.6390634764187119, + 0.740852169595914, + 0.2664107172559991 + ], + [ + 0.6034776272666109, + 0.6995982808601832, + 0.28644310661974975 + ], + [ + 0.5682394566412126, + 0.6587474481593129, + 0.2975476417537995 + ], + [ + 0.5357747112522604, + 0.6211117860626956, + 0.3001288000938117 + ], + [ + 0.5082389638106507, + 0.5891902023917244, + 0.2952610130362375 + ], + [ + 0.48719791286637354, + 0.5647977768849466, + 0.28442926687777165 + ], + [ + 0.47350819729940324, + 0.5489275920704793, + 0.2691888057591519 + ], + [ + 0.4674060942962304, + 0.541853558870628, + 0.250848746082265 + ], + [ + 0.46870173946360244, + 0.5433555716889058, + 0.23028904387610896 + ], + [ + 0.47694123593954546, + 0.5529074379210182, + 0.20795977083711917 + ], + [ + 0.4914573809535293, + 0.569735684135378, + 0.184023614257714 + ], + [ + 0.5113327508310614, + 0.5927767613344814, + 0.1585462645916389 + ], + [ + 0.5353688059422063, + 0.6206412290042805, + 0.13164851084179163 + ], + [ + 0.5621448007188433, + 0.6516820481957113, + 0.10359079091043412 + ], + [ + 0.5901773374807702, + 0.6841795487503255, + 0.07481446956709396 + ], + [ + 0.6181167022060295, + 0.7165690370212497, + 0.04597409592617145 + ], + [ + 0.6448940962896724, + 0.7476114783983779, + 0.017964403321921237 + ], + [ + 0.6697715786986443, + 0.7764513941451752, + -0.008086263067361538 + ], + [ + 0.6923053351634378, + 0.8025742801840423, + -0.03088201351797068 + ], + [ + 0.7122663339328548, + 0.8257146250657371, + -0.04907475450955632 + ], + [ + 0.7295547505038374, + 0.8457567044492547, + -0.061384655211305796 + ], + [ + 0.74411756189804, + 0.8626390499672174, + -0.06668600690932501 + ], + [ + 0.7558652000767851, + 0.8762578273711833, + -0.06406177254112053 + ], + [ + 0.7645944684625707, + 0.8863774753580087, + -0.05287305175740696 + ], + [ + 0.769943103202698, + 0.8925780294466344, + -0.032888614355654926 + ], + [ + 0.7714008574348975, + 0.8942679717223451, + -0.004466500945978006 + ], + [ + 0.7683777978199927, + 0.8907634054982341, + 0.031286858163027315 + ], + [ + 0.7093062026749203, + 0.908891902713318, + 0.07020975167507537 + ], + [ + 0.6971726892003917, + 0.8933442420459317, + 0.11250034875912414 + ], + [ + 0.6797084671607724, + 0.8709659096146951, + 0.15483998775764818 + ], + [ + 0.6570068568424393, + 0.8418764843156153, + 0.19443907504304997 + ], + [ + 0.6295752430284985, + 0.8067261196637554, + 0.22885541358771233 + ], + [ + 0.5984545907928867, + 0.7668486891303189, + 0.25622154536547076 + ], + [ + 0.5652584452835648, + 0.7243117597464606, + 0.27537756396868823 + ], + [ + 0.5320559665320546, + 0.6817667150627204, + 0.2859304812366626 + ], + [ + 0.5010874590097093, + 0.6420842399623636, + 0.2882404690866206 + ], + [ + 0.4743953711049869, + 0.6078814901885358, + 0.28330792283510403 + ], + [ + 0.45351328377315886, + 0.5811235681288166, + 0.27254435458465687 + ], + [ + 0.4393315000799025, + 0.5629512917322151, + 0.2574614352676495 + ], + [ + 0.43216005948231584, + 0.5537619398481413, + 0.23936943845337336 + ], + [ + 0.43190298022358653, + 0.553432523221396, + 0.21918932030232385 + ], + [ + 0.43821396057073564, + 0.5615192971902057, + 0.19743284218934395 + ], + [ + 0.45054967640428933, + 0.5773260562358866, + 0.17432282157737042 + ], + [ + 0.46813281737657536, + 0.59985676919696, + 0.1499669563058421 + ], + [ + 0.48991017595456765, + 0.6277618753406503, + 0.12450051049744223 + ], + [ + 0.5145912622641103, + 0.6593877647129908, + 0.09816449879742246 + ], + [ + 0.5407882738990986, + 0.692956132874068, + 0.07134020156892393 + ], + [ + 0.5672036656627785, + 0.7268043293094967, + 0.04457644368745896 + ], + [ + 0.5927820715389869, + 0.759580027445969, + 0.018620348214454803 + ], + [ + 0.6167725605668648, + 0.7903209981823608, + -0.005571100658101282 + ], + [ + 0.6387032304203786, + 0.8184225545705163, + -0.026866410075786887 + ], + [ + 0.6583078246027714, + 0.8435435204399262, + -0.044041793961361535 + ], + [ + 0.6754430590547623, + 0.8655002942363152, + -0.05587922691051291 + ], + [ + 0.6900114397066978, + 0.8841679488546662, + -0.061248886742461886 + ], + [ + 0.7018880881453464, + 0.8993864673965204, + -0.059178212743612464 + ], + [ + 0.7108558836109446, + 0.9108776353196734, + -0.0489450082595655 + ], + [ + 0.7165682175783445, + 0.9181973148445167, + -0.030229098488251102 + ], + [ + 0.718559736122609, + 0.9207492100234385, + -0.0033075893220411373 + ], + [ + 0.7163055631250037, + 0.9178607542660912, + 0.030782278700898634 + ], + [ + 0.657626567067364, + 0.9337579804749978, + 0.06733269187309418 + ], + [ + 0.6469229836762124, + 0.9185600597831112, + 0.1079695178635743 + ], + [ + 0.6312766868031446, + 0.8963440251796743, + 0.1486484312331717 + ], + [ + 0.610761058196697, + 0.867214070741916, + 0.18665435318091336 + ], + [ + 0.5858019615171906, + 0.8317748764072552, + 0.21963174452055695 + ], + [ + 0.5572906963915939, + 0.7912919904083122, + 0.24580008359537167 + ], + [ + 0.526635375572888, + 0.7477647792341385, + 0.2640652598963209 + ], + [ + 0.49567768114491095, + 0.7038082305227042, + 0.2740567587747342 + ], + [ + 0.46645424636008825, + 0.6623141413028817, + 0.2761038095687468 + ], + [ + 0.4408678699796393, + 0.625984278226906, + 0.27113324843837106 + ], + [ + 0.42039492057030525, + 0.596914923592898, + 0.2604698372125654 + ], + [ + 0.4059431521429968, + 0.5763949890635596, + 0.24555995143465909 + ], + [ + 0.39789051154484234, + 0.5649611179291327, + 0.22769316809046197 + ], + [ + 0.396235196355091, + 0.5626107509487893, + 0.20781633039482214 + ], + [ + 0.4007387086457687, + 0.5690052470840964, + 0.1864972404707124 + ], + [ + 0.4109776584122106, + 0.5835434387188002, + 0.16402170895445603 + ], + [ + 0.42630686348429586, + 0.6053092375584496, + 0.1405487484889527 + ], + [ + 0.4458097300969126, + 0.6330011804537626, + 0.11624281765316372 + ], + [ + 0.46832050363781574, + 0.6649640230351066, + 0.09134563788440259 + ], + [ + 0.49254716293715106, + 0.6993632361962834, + 0.06620318801225832 + ], + [ + 0.5172508770557498, + 0.7344398151558003, + 0.041284888229489884 + ], + [ + 0.5414020911130918, + 0.7687319043041588, + 0.01721295650037587 + ], + [ + 0.5642539604786249, + 0.8011790657441145, + -0.005212840451267671 + ], + [ + 0.5853263928890686, + 0.8310996208381977, + -0.0250220971063945 + ], + [ + 0.6043356931204776, + 0.8580907533185635, + -0.04112160620219274 + ], + [ + 0.6211112067394298, + 0.8819101525075549, + -0.05236746444931421 + ], + [ + 0.6355204492927039, + 0.9023697049351889, + -0.057640446057884195 + ], + [ + 0.6474052260953079, + 0.9192447914072639, + -0.05592722931838621 + ], + [ + 0.6565303950087055, + 0.9322015357402441, + -0.04643608996127401 + ], + [ + 0.6625573408067443, + 0.9407591412547315, + -0.02877013538852866 + ], + [ + 0.6650565101574644, + 0.9443076890821102, + -0.0031357655801853286 + ], + [ + 0.6635587833403709, + 0.9421810803985998, + 0.029490720675595623 + ], + [ + 0.6054396868947982, + 0.9560937450986833, + 0.06389372683371497 + ], + [ + 0.5961307777216434, + 0.9413933710948039, + 0.10302986865243764 + ], + [ + 0.5822689839664068, + 0.9195032066538122, + 0.14216701850427318 + ], + [ + 0.5639161396460557, + 0.8905208983590694, + 0.17865876534142683 + ], + [ + 0.5414328801137588, + 0.8550159516673363, + 0.2102350888883286 + ], + [ + 0.5155802016382657, + 0.8141901110105422, + 0.23520949527798565 + ], + [ + 0.4875751687804437, + 0.7699653313566455, + 0.25256928474739787 + ], + [ + 0.4590340878377926, + 0.724894039272079, + 0.26198591669722243 + ], + [ + 0.43177739576771573, + 0.681851018861675, + 0.26377302600569985 + ], + [ + 0.40754528686787506, + 0.6435843372232222, + 0.25878780986744765 + ], + [ + 0.3877319678814389, + 0.612295687399597, + 0.24825839774060088 + ], + [ + 0.3732436007890892, + 0.589416055532821, + 0.2335460350340972 + ], + [ + 0.3645161619247625, + 0.5756339234897264, + 0.21589756800430557 + ], + [ + 0.361639060524842, + 0.5710904838837321, + 0.1962689138546054 + ], + [ + 0.36448035566942266, + 0.5755773792335325, + 0.17527616101742816 + ], + [ + 0.37273284336466, + 0.5886094814192804, + 0.15326927332179918 + ], + [ + 0.3858764581629216, + 0.6093655173525309, + 0.1304665447920232 + ], + [ + 0.40312339785448353, + 0.6366014113947096, + 0.10707465747690162 + ], + [ + 0.4234274879366114, + 0.6686650734697989, + 0.08335348174098942 + ], + [ + 0.44559232245457514, + 0.7036671721139448, + 0.05963492519540656 + ], + [ + 0.46844303050155506, + 0.7397523834202031, + 0.03633197435233425 + ], + [ + 0.49098568890683714, + 0.7753511311827221, + 0.01396297900628085 + ], + [ + 0.5124907519821906, + 0.8093113368635745, + -0.006814318122629638 + ], + [ + 0.5324852656286791, + 0.8408861243238152, + -0.025183530750000984 + ], + [ + 0.5506838248867567, + 0.8696247898805695, + -0.04018291317050738 + ], + [ + 0.5669022402637416, + 0.895236466539688, + -0.0507496156903776 + ], + [ + 0.5809827188886559, + 0.9174719721279029, + -0.055785822300943755 + ], + [ + 0.5927380810359748, + 0.9360357175573866, + -0.054250615184736395 + ], + [ + 0.6019145309980496, + 0.9505269156087547, + -0.045297169441470435 + ], + [ + 0.60817731547263, + 0.960416933050059, + -0.028466262180852724 + ], + [ + 0.6111265341451482, + 0.9650742582746158, + -0.003906357062687633 + ], + [ + 0.6103416284934914, + 0.9638347568010824, + 0.027457076609213565 + ], + [ + 0.5528989693567201, + 0.9760031751352543, + 0.059941295050475914 + ], + [ + 0.5449258913026513, + 0.9619287240552927, + 0.09772735028311426 + ], + [ + 0.5327915952079207, + 0.9405086958532735, + 0.13544113103831895 + ], + [ + 0.5165523211603731, + 0.911842368168238, + 0.17049973345197733 + ], + [ + 0.4965171696378236, + 0.8764753796511947, + 0.20071685271589437 + ], + [ + 0.47333521568502235, + 0.8355535079933446, + 0.224505034778602 + ], + [ + 0.4480489252614098, + 0.7909169629667698, + 0.24094611893337586 + ], + [ + 0.42205642772440893, + 0.7450337880436481, + 0.24977170449025424 + ], + [ + 0.39695583196254414, + 0.7007250399375168, + 0.2512961164321254 + ], + [ + 0.374306861606445, + 0.6607440159055565, + 0.24631392531095053 + ], + [ + 0.35539840188797883, + 0.6273659165692321, + 0.23595063377244413 + ], + [ + 0.34111417820551065, + 0.6021507354220298, + 0.22146542160906307 + ], + [ + 0.3319343283937202, + 0.5859460342738321, + 0.20404115987752022 + ], + [ + 0.32803240977312487, + 0.5790581846414639, + 0.18462483274141758 + ], + [ + 0.32937912006854003, + 0.581435460775369, + 0.16387094903372754 + ], + [ + 0.3357788130007118, + 0.5927324986934491, + 0.14219284688619202 + ], + [ + 0.34682990629635296, + 0.6122404065446986, + 0.1198746150372498 + ], + [ + 0.36186646521186294, + 0.6387836451072896, + 0.09717667017684149 + ], + [ + 0.37995767930987323, + 0.6707191041700061, + 0.0743921252624317 + ], + [ + 0.4000032860896107, + 0.7061045487970767, + 0.051856719949032404 + ], + [ + 0.42089765717515976, + 0.7429882719584286, + 0.02994649767128477 + ], + [ + 0.44169109220815395, + 0.7796938180689286, + 0.009094749217825049 + ], + [ + 0.4616816464297993, + 0.8149820813401363, + -0.010167440456370254 + ], + [ + 0.4804145539599796, + 0.8480502877255509, + -0.027167859533193298 + ], + [ + 0.4976139609244774, + 0.8784114870371069, + -0.04107257055906719 + ], + [ + 0.5130928449866018, + 0.9057355386803229, + -0.05090184227920794 + ], + [ + 0.5266770107086661, + 0.9297149447040377, + -0.05558623603440847 + ], + [ + 0.5381561725418871, + 0.9499784992014356, + -0.05406838132496454 + ], + [ + 0.5472601896460669, + 0.9660493368998732, + -0.0454607047227195 + ], + [ + 0.5536572326190025, + 0.9773416969857055, + -0.029257458726779116 + ], + [ + 0.5569736345186764, + 0.9831959650590796, + -0.0055640200467556855 + ], + [ + 0.5568322607103833, + 0.9829464053864768, + 0.024733075974402736 + ], + [ + 0.5001356929902523, + 0.9935989718438265, + 0.05552598184604132 + ], + [ + 0.4934208812470535, + 0.9802589320552703, + 0.09210695451688984 + ], + [ + 0.48293853123835423, + 0.9594340792460673, + 0.12851222533349269 + ], + [ + 0.4687429768917727, + 0.931232356391243, + 0.1622184647309498 + ], + [ + 0.4511024719691343, + 0.896186692185307, + 0.1911211876658309 + ], + [ + 0.43057057010903493, + 0.8553968088314096, + 0.2137350740844575 + ], + [ + 0.40803244951815143, + 0.8106212533966358, + 0.22924663197199585 + ], + [ + 0.3846795854894139, + 0.7642270807475453, + 0.2374633868949964 + ], + [ + 0.3618881762622678, + 0.7189483272165398, + 0.23871649648837978 + ], + [ + 0.34102581579675706, + 0.6775019353686962, + 0.2337474573016177 + ], + [ + 0.32325630129288385, + 0.6421999731439035, + 0.2235772891783565 + ], + [ + 0.3094196421110923, + 0.6147112525237388, + 0.2093499569809133 + ], + [ + 0.3000228485591821, + 0.596043029994001, + 0.19216494351385335 + ], + [ + 0.295312114106084, + 0.5866844079743588, + 0.17294181019929872 + ], + [ + 0.2953532171587542, + 0.5867660657153382, + 0.15236167651859012 + ], + [ + 0.30005547420007217, + 0.5961078460103311, + 0.13089829074848944 + ], + [ + 0.3091287382767254, + 0.6141333258634675, + 0.10890626186296205 + ], + [ + 0.32202260855830017, + 0.6397490466256928, + 0.08670959972897721 + ], + [ + 0.3379185399560152, + 0.6713288384994502, + 0.0646475588645451 + ], + [ + 0.35581421687988707, + 0.7068814424051966, + 0.0430746067174456 + ], + [ + 0.3746794481704136, + 0.7443602200181123, + 0.022346106425592902 + ], + [ + 0.393617138282821, + 0.7819829485866006, + 0.0028268414465582926 + ], + [ + 0.41196128400201953, + 0.8184265069676742, + -0.015063643504180898 + ], + [ + 0.4292827176609413, + 0.8528382854421633, + -0.03078546853422491 + ], + [ + 0.44532209889587276, + 0.8847030631962871, + -0.04362527347815494 + ], + [ + 0.4598968572201374, + 0.9136581349674672, + -0.05268450025375799 + ], + [ + 0.47282395515791487, + 0.9393398677450365, + -0.05692567314046616 + ], + [ + 0.48387678440420956, + 0.9612980681474578, + -0.055283996349565746 + ], + [ + 0.4927735202464404, + 0.9789728466315819, + -0.04684483964939816 + ], + [ + 0.4991870100501006, + 0.9917142625396281, + -0.031072372348934115 + ], + [ + 0.5027687170461956, + 0.9988298922350181, + -0.008045200547225968 + ], + [ + 0.503181597732899, + 0.999650145281446, + 0.021375623486103213 + ], + [ + 0.4472583901064134, + 1.0089987605520987, + 0.05069963034233863 + ], + [ + 0.4417101238849039, + 0.9964820725154937, + 0.08621276877237356 + ], + [ + 0.4327901600752672, + 0.976358956600437, + 0.12141869400351236 + ], + [ + 0.4205526900151925, + 0.9487516665058433, + 0.15385129341809836 + ], + [ + 0.40523274704887013, + 0.9141904289602035, + 0.18148641917155706 + ], + [ + 0.3873021132085598, + 0.8737395671742856, + 0.20294235139894706 + ], + [ + 0.3675057274385762, + 0.8290796364783578, + 0.21751715042615516 + ], + [ + 0.3468431948613794, + 0.7824657098948933, + 0.22510687213212047 + ], + [ + 0.32647537317076425, + 0.7365166404183269, + 0.22607449137801716 + ], + [ + 0.3075728627952042, + 0.6938732603003475, + 0.22111981074996975 + ], + [ + 0.2911593780613986, + 0.656845032056691, + 0.211161620476732 + ], + [ + 0.2780110550872172, + 0.6271828907134481, + 0.19721997932020602 + ], + [ + 0.26864212538171567, + 0.6060469239665713, + 0.18029442758606726 + ], + [ + 0.26335564928320376, + 0.5941208249842409, + 0.16125902067740824 + ], + [ + 0.2623007011510812, + 0.5917409001325122, + 0.14080812766536308 + ], + [ + 0.26548204398459263, + 0.5989178945655079, + 0.11947060594914401 + ], + [ + 0.2727126244497258, + 0.6152298227233135, + 0.09767406925560874 + ], + [ + 0.2835512806959223, + 0.6396814394182189, + 0.07581392922831659 + ], + [ + 0.2972895618986312, + 0.670674505199168, + 0.05428620914853092 + ], + [ + 0.31302628821786443, + 0.7061759909230605, + 0.03347627963304614 + ], + [ + 0.32981418399689955, + 0.7440488769505178, + 0.013732332600442683 + ], + [ + 0.34681755936822967, + 0.782407877149021, + -0.004634698133258076 + ], + [ + 0.3634135629504477, + 0.8198478613169918, + -0.02130196029604901 + ], + [ + 0.3792032621550044, + 0.8554688519553063, + -0.035848591962895575 + ], + [ + 0.3939471562646573, + 0.8887305441033545, + -0.047671931724352176 + ], + [ + 0.40747126217064106, + 0.9192404381569156, + -0.055949671503018175 + ], + [ + 0.41958975800476545, + 0.9465793266984542, + -0.05967725532226084 + ], + [ + 0.4300669374176073, + 0.9702154647240019, + -0.05778956616006038 + ], + [ + 0.438615714025435, + 0.9895011957294786, + -0.04935754919392447 + ], + [ + 0.44491746303781715, + 1.003717713705343, + -0.03383162177498409 + ], + [ + 0.4486494648643138, + 1.0121369749212532, + -0.011280696686228664 + ], + [ + 0.449512842705623, + 1.0140847241218034, + 0.01744501708609586 + ], + [ + 0.3943528188161184, + 1.022321240376011, + 0.04551436870642923 + ], + [ + 0.38986936725196136, + 1.0106983292529335, + 0.08008795427002666 + ], + [ + 0.382412162328261, + 0.9913662524334438, + 0.11419672577330729 + ], + [ + 0.3720355252613868, + 0.964465832375625, + 0.14543112411542114 + ], + [ + 0.35894608460971433, + 0.9305327334744463, + 0.17184664322159615 + ], + [ + 0.3435446573018786, + 0.890606034544853, + 0.19216536952195923 + ], + [ + 0.3264518014587432, + 0.8462944720217749, + 0.2058005723621238 + ], + [ + 0.3084920738929002, + 0.7997356290621936, + 0.21274575772819565 + ], + [ + 0.29062282799326833, + 0.7534113510019074, + 0.21340867487863888 + ], + [ + 0.27381960185109133, + 0.7098506252448055, + 0.20845982485014303 + ], + [ + 0.2589567959753106, + 0.6713202498717794, + 0.1987218178687583 + ], + [ + 0.2467291970573134, + 0.6396213916508721, + 0.18508696385911078 + ], + [ + 0.23763766734400496, + 0.6160524871320008, + 0.16844208532893312 + ], + [ + 0.23202283157295786, + 0.6014965727424525, + 0.14959905748027832 + ], + [ + 0.23010093559290357, + 0.5965142447649291, + 0.1292512102030555 + ], + [ + 0.2319590690880225, + 0.6013312747159688, + 0.10797489668786085 + ], + [ + 0.23750279836457516, + 0.6157028524501622, + 0.08627052714790762 + ], + [ + 0.24639323561473236, + 0.638750444361262, + 0.06461011117682439 + ], + [ + 0.2580302746352998, + 0.6689183336171973, + 0.04345450661721908 + ], + [ + 0.2716181155556405, + 0.704143486629713, + 0.023229613655687483 + ], + [ + 0.2863010397687543, + 0.7422075363275464, + 0.00428774404890901 + ], + [ + 0.3013141977991037, + 0.7811276850046236, + -0.013100670486749176 + ], + [ + 0.3160846145252268, + 0.8194185505134657, + -0.02869462460731092 + ], + [ + 0.3302464402404443, + 0.8561317031533634, + -0.04217776550473636 + ], + [ + 0.3435807310560986, + 0.8906995522361173, + -0.05304640104924122 + ], + [ + 0.3559237938026491, + 0.9226977391768737, + -0.06054761770106265 + ], + [ + 0.367091774895082, + 0.9516496414789479, + -0.0637090039867316 + ], + [ + 0.37684725441829475, + 0.9769397711566669, + -0.06147071080708629 + ], + [ + 0.3849055145218497, + 0.997830024937711, + -0.05290063451888711 + ], + [ + 0.39096179728000957, + 1.0135303476080983, + -0.037451021347134066 + ], + [ + 0.3947211230635569, + 1.023276033234364, + -0.01519819646613559 + ], + [ + 0.3959218069603817, + 1.0263886891915002, + 0.01300312802983262 + ], + [ + 0.3414825444167465, + 1.0336824154698547, + 0.040021610062946855 + ], + [ + 0.33795580587759255, + 1.023006825541503, + 0.07377463471558486 + ], + [ + 0.3318549826208396, + 1.0045393699614054, + 0.10688107930368282 + ], + [ + 0.32323374712696096, + 0.9784425176468368, + 0.1369888499222131 + ], + [ + 0.31227280346096276, + 0.9452632676097669, + 0.16223337010959243 + ], + [ + 0.2993099306577724, + 0.9060240915824339, + 0.181439867654043 + ], + [ + 0.28485532546674724, + 0.8622693771678447, + 0.19413750735505098 + ], + [ + 0.26957612382021123, + 0.816018573656297, + 0.20042230287319843 + ], + [ + 0.25424203216061253, + 0.7696016157037229, + 0.20075705109905972 + ], + [ + 0.23964168811187897, + 0.7254057434703026, + 0.19579551074584917 + ], + [ + 0.22649720276769672, + 0.6856168184349412, + 0.18627339300474346 + ], + [ + 0.2154085768651505, + 0.6520510687514165, + 0.1729563390031761 + ], + [ + 0.20684337265188488, + 0.6261238255440569, + 0.15661018367967433 + ], + [ + 0.20115811288827226, + 0.608914298611832, + 0.13797038233807388 + ], + [ + 0.19861614850348477, + 0.6012196625952433, + 0.11771482475213933 + ], + [ + 0.19936988253688046, + 0.6035012480788967, + 0.09645767972159963 + ], + [ + 0.20340423468100652, + 0.6157134063205983, + 0.07476891738224221 + ], + [ + 0.2104742013290451, + 0.6371145008172968, + 0.05319912996154387 + ], + [ + 0.2200859494257936, + 0.6662096775751737, + 0.032279083476880105 + ], + [ + 0.23155365385894736, + 0.7009229143486766, + 0.012482307742394378 + ], + [ + 0.2441224024183714, + 0.7389690592622599, + -0.0058252290714171494 + ], + [ + 0.2571080837181305, + 0.778277277594132, + -0.022400934787714186 + ], + [ + 0.26999426208143973, + 0.8172842962384139, + -0.03707039646224396 + ], + [ + 0.2824502264942273, + 0.8549890386673666, + -0.04960614028512001 + ], + [ + 0.29427694184260267, + 0.890789016992067, + -0.059590460143117044 + ], + [ + 0.305321295382348, + 0.9242207523206359, + -0.06633196774181802 + ], + [ + 0.3154053098802208, + 0.9547454998786638, + -0.06888878326150341 + ], + [ + 0.3242972673906918, + 0.9816618394338174, + -0.0662109351860644 + ], + [ + 0.33172316946353364, + 1.0041403658393426, + -0.0573733885858094 + ], + [ + 0.33739838313068204, + 1.0213194828035355, + -0.041844568352390456 + ], + [ + 0.3410583203494992, + 1.0323982708899857, + -0.019724653635577332 + ], + [ + 0.34247802300265173, + 1.036695772158363, + 0.008111650741774973 + ], + [ + 0.2886901158628296, + 1.0431920345111103, + 0.034271110456347446 + ], + [ + 0.28600868125620216, + 1.0335025748829694, + 0.0673137210531973 + ], + [ + 0.28115382266291655, + 1.01595937012843, + 0.0995057265805129 + ], + [ + 0.27417710941551326, + 0.990748767870688, + 0.12855465003564712 + ], + [ + 0.2652340578719483, + 0.9584327320182445, + 0.1526771111607164 + ], + [ + 0.2546048108137065, + 0.9200235685078557, + 0.1708002973788381 + ], + [ + 0.24270105194281635, + 0.8770089110862144, + 0.18256742585253843 + ], + [ + 0.23004983243690477, + 0.8312932779895503, + 0.18817834601500166 + ], + [ + 0.21725166256155595, + 0.7850466344895435, + 0.18815809015447918 + ], + [ + 0.20492164094709525, + 0.7404916614343979, + 0.18315560043186369 + ], + [ + 0.19363280900258842, + 0.6996990644026394, + 0.1738314454489961 + ], + [ + 0.18388196095048426, + 0.664464027043354, + 0.16083036099084166 + ], + [ + 0.1760856413774698, + 0.6362917480833907, + 0.14479389524278863 + ], + [ + 0.17059370110115854, + 0.6164464259353306, + 0.1263702228864451 + ], + [ + 0.16769346612423755, + 0.6059663233620508, + 0.10620821113816928 + ], + [ + 0.167581926568893, + 0.6055632711989362, + 0.08494856652715833 + ], + [ + 0.1703069432811223, + 0.6154102163207358, + 0.0632244263987468 + ], + [ + 0.17570690716549694, + 0.6349231784946949, + 0.04166323918270835 + ], + [ + 0.18339099346479898, + 0.6626898985154684, + 0.020867295295090738 + ], + [ + 0.19278731757480788, + 0.6966438509601149, + 0.0013622169887050615 + ], + [ + 0.20325073223231047, + 0.7344537731733155, + -0.016464289063044514 + ], + [ + 0.21418772659766888, + 0.7739749925587965, + -0.032384983945924775 + ], + [ + 0.22514519895373603, + 0.8135702098943849, + -0.04627597957765791 + ], + [ + 0.23582975266711012, + 0.852179225976508, + -0.05798187819877345 + ], + [ + 0.2460615515965038, + 0.8891521964918352, + -0.06715709720255582 + ], + [ + 0.25569811038008466, + 0.9239742455013212, + -0.07316357948328348 + ], + [ + 0.26457100918583354, + 0.9560367819325248, + -0.07508829203682905 + ], + [ + 0.2724616667107373, + 0.9845499544477414, + -0.0718953402077247 + ], + [ + 0.27911546842795265, + 1.0085937043692437, + -0.06267578257528417 + ], + [ + 0.2842745949502804, + 1.0272363921420546, + -0.04692705293165018 + ], + [ + 0.2877078218747656, + 1.0396424801357071, + -0.024788368128626604 + ], + [ + 0.28922660688724233, + 1.045130663970527, + 0.0028305424213381654 + ], + [ + 0.235998792346151, + 1.0509503470349943, + 0.028310185351052418 + ], + [ + 0.23405037249996052, + 1.0422736394418457, + 0.06074473128843283 + ], + [ + 0.23032915537636572, + 1.0257023070695244, + 0.09210436527980698 + ], + [ + 0.2248827423586249, + 1.0014483285038414, + 0.12015911365317387 + ], + [ + 0.2178410543413226, + 0.9700902677615584, + 0.14320883541214474 + ], + [ + 0.20943001561649627, + 0.932634211402526, + 0.1602812505718092 + ], + [ + 0.1999724488627488, + 0.8905177540971899, + 0.17112980984224027 + ], + [ + 0.18987151878551212, + 0.8455362698085852, + 0.1760561946540208 + ], + [ + 0.17957816082996914, + 0.7996978652642673, + 0.17565164319797957 + ], + [ + 0.1695511491552935, + 0.7550455545703727, + 0.1705708899778763 + ], + [ + 0.1602232782535068, + 0.7135066591214856, + 0.16141272978196777 + ], + [ + 0.15198519308689099, + 0.6768207999325967, + 0.14871092416233384 + ], + [ + 0.14518820711186597, + 0.6465524468693018, + 0.1329845684399181 + ], + [ + 0.1401535814538252, + 0.6241322406898208, + 0.11478783734228859 + ], + [ + 0.13716768646475874, + 0.6108354464826422, + 0.09472875599402024 + ], + [ + 0.13644832919630023, + 0.6076320030945941, + 0.07346234893407427 + ], + [ + 0.13808680155210465, + 0.6149284518341037, + 0.05167550698242819 + ], + [ + 0.14199183111815017, + 0.6323183381840217, + 0.03006681771214805 + ], + [ + 0.1478702760103157, + 0.6584962420539107, + 0.009307875284508913 + ], + [ + 0.15526640663493427, + 0.6914327074035218, + -0.010021970737165153 + ], + [ + 0.16365238713855854, + 0.7287771744361573, + -0.02750655270988233 + ], + [ + 0.17253438652127798, + 0.7683305138445309, + -0.04292139844704342 + ], + [ + 0.181529521220046, + 0.808387667693992, + -0.05617600119973934 + ], + [ + 0.19038473745580678, + 0.8478217363327524, + -0.06716897917561025 + ], + [ + 0.19893983655076788, + 0.8859193226525188, + -0.07561230068826766 + ], + [ + 0.20706392886279537, + 0.9220975486077877, + -0.08091312607418294 + ], + [ + 0.21460228351637237, + 0.9556673663195215, + -0.0821860562657453 + ], + [ + 0.22135698333803921, + 0.9857474105906199, + -0.07841357877521825 + ], + [ + 0.2271014587157732, + 1.0113287211208388, + -0.06871106572223831 + ], + [ + 0.23161143335871417, + 1.0314125502328362, + -0.05261618210819379 + ], + [ + 0.2346920488823988, + 1.045131154135474, + -0.03032065845392485 + ], + [ + 0.2361907877304043, + 1.0518053413072286, + -0.002783232452474369 + ], + [ + 0.1834147516219488, + 1.0570452594308857, + 0.022183182361132606 + ], + [ + 0.18208792048547226, + 1.0493985431745583, + 0.0541056844050169 + ], + [ + 0.1793876988547425, + 1.0338367824713692, + 0.08471083748260513 + ], + [ + 0.17535558884919836, + 1.0105991599291577, + 0.11183418129146896 + ], + [ + 0.17009487974087298, + 0.9802809462902566, + 0.13386126062112075 + ], + [ + 0.1637793270323287, + 0.9438835191901034, + 0.14991880964442475 + ], + [ + 0.1566505785147694, + 0.9027995291646289, + 0.15986530640449273 + ], + [ + 0.1490024440837962, + 0.8587222443646919, + 0.1640995203863567 + ], + [ + 0.14115582485874764, + 0.8135010635106418, + 0.1632797747176778 + ], + [ + 0.13343275238359317, + 0.7689919001205021, + 0.15807538115599523 + ], + [ + 0.12613915909509857, + 0.7269578862713264, + 0.14903746585081576 + ], + [ + 0.1195619633784377, + 0.6890525733607507, + 0.13660218886268163 + ], + [ + 0.11397739325400769, + 0.6568679026961164, + 0.12117300984147923 + ], + [ + 0.10965830204943322, + 0.6319763667510941, + 0.1032080216065447 + ], + [ + 0.1068648713847438, + 0.6158774291490356, + 0.08326519935943122 + ], + [ + 0.10581001735208867, + 0.6097981555641718, + 0.06200149829760917 + ], + [ + 0.10660634640546736, + 0.614387512981595, + 0.040145539416730575 + ], + [ + 0.10921714103981156, + 0.6294338932055229, + 0.01845736924969676 + ], + [ + 0.11343883559072063, + 0.6537641184046481, + -0.0023283540898947535 + ], + [ + 0.11893121000102169, + 0.6854174520764938, + -0.021579939819926862 + ], + [ + 0.12528867244984712, + 0.722056410961285, + -0.038847499381045564 + ], + [ + 0.13212446046530765, + 0.7614520280112733, + -0.05389665736091525 + ], + [ + 0.13913261063712762, + 0.8018409926446697, + -0.06665204455234687 + ], + [ + 0.14610476055244137, + 0.8420224826875999, + -0.07704695197879947 + ], + [ + 0.1529029121371447, + 0.8812011956425685, + -0.08483563019652403 + ], + [ + 0.15941068975645736, + 0.9187064422004014, + -0.08946253978671703 + ], + [ + 0.16549229310827868, + 0.9537555859357542, + -0.09006944576057294 + ], + [ + 0.1709782308107541, + 0.9853718239463553, + -0.08566202192547671 + ], + [ + 0.1756784184533273, + 1.0124596727812092, + -0.07538772290273132 + ], + [ + 0.17940860570895978, + 1.0339572716411034, + -0.05883415443620612 + ], + [ + 0.18201315173588253, + 1.0489676402531274, + -0.03625705021669041 + ], + [ + 0.18337498389362555, + 1.0568160723653377, + -0.008676560636073473 + ], + [ + 0.13092969775862884, + 1.0615499447671184, + 0.015931286436729742 + ], + [ + 0.1301149259632141, + 1.0549439495710184, + 0.0474331495358554 + ], + [ + 0.12832377910492399, + 1.0404217145010732, + 0.07735951679120513 + ], + [ + 0.12558930499922166, + 1.018251184166338, + 0.10361393196660389 + ], + [ + 0.12198687482106621, + 0.9890434519091519, + 0.1246699784211076 + ], + [ + 0.11763934356609788, + 0.9537945997190782, + 0.13975181288871247 + ], + [ + 0.11271331112617645, + 0.9138553838342313, + 0.14881689372117368 + ], + [ + 0.10740589947137408, + 0.8708240269651552, + 0.1523542948710169 + ], + [ + 0.10192647405594142, + 0.8263980193696039, + 0.15108755972351132 + ], + [ + 0.09648081928021096, + 0.7822458168872112, + 0.14570724847305747 + ], + [ + 0.09126459553368932, + 0.7399537920463607, + 0.13673086462977693 + ], + [ + 0.08646811839044974, + 0.7010649827567421, + 0.12451293002254639 + ], + [ + 0.08228735301081189, + 0.6671682325632105, + 0.10935262905965934 + ], + [ + 0.07893018799239418, + 0.6399490576862826, + 0.09161470174926385 + ], + [ + 0.07660663530138462, + 0.6211101901640199, + 0.07180113012468488 + ], + [ + 0.07549849962632864, + 0.6121256634692532, + 0.050559050632959575 + ], + [ + 0.075715887080105, + 0.6138881943808787, + 0.02864484156148236 + ], + [ + 0.07725834879701897, + 0.6263941435919889, + 0.00686674071938929 + ], + [ + 0.08000056371875301, + 0.6486274348050551, + -0.013987689873687837 + ], + [ + 0.08371338476226524, + 0.6787301925532363, + -0.03323893639933224 + ], + [ + 0.08811465567603742, + 0.7144147543860545, + -0.050399844163525095 + ], + [ + 0.09292941398537553, + 0.7534517834546416, + -0.06521369491927141 + ], + [ + 0.09793464824400235, + 0.7940331507208133, + -0.0776011777330571 + ], + [ + 0.10297248649928659, + 0.8348788642082273, + -0.08750973867516196 + ], + [ + 0.10793244402738021, + 0.8750931374413451, + -0.09471987680330893 + ], + [ + 0.11271831192337534, + 0.9138959292267674, + -0.09870549973579863 + ], + [ + 0.1172201568400356, + 0.9503959235324205, + -0.09863579978192599 + ], + [ + 0.12130511312790127, + 0.9835158741318185, + -0.09354515918593355 + ], + [ + 0.12482769760291579, + 1.0120762345305938, + -0.08262078945586716 + ], + [ + 0.12764973252519987, + 1.0349566892109692, + -0.06550867393141913 + ], + [ + 0.12965739416392166, + 1.0512343797439845, + -0.04253795309838056 + ], + [ + 0.13076827715605963, + 1.0602411810971866, + -0.014800588452909741 + ], + [ + 0.07852378153772456, + 1.0645209339656714, + 0.009592696335767347 + ], + [ + 0.0781137736001248, + 1.0589625919688923, + 0.040762514675130625 + ], + [ + 0.07712102817174767, + 1.045504270554526, + 0.07008573227133376 + ], + [ + 0.07556754505096158, + 1.0244442137124516, + 0.0955352674354265 + ], + [ + 0.07349946619783333, + 0.9964079527334885, + 0.11567443895106184 + ], + [ + 0.07098971574576461, + 0.9623840959739485, + 0.12982304350818225 + ], + [ + 0.06813490689575209, + 0.9236823966441883, + 0.13803107190060276 + ], + [ + 0.06504637027740845, + 0.8818121272664098, + 0.14086979996983717 + ], + [ + 0.061838779129188275, + 0.8383278750052915, + 0.1391238991151621 + ], + [ + 0.05862179852858691, + 0.7947163330438746, + 0.13350968123611567 + ], + [ + 0.055499099058912815, + 0.7523829291902335, + 0.12452437865050224 + ], + [ + 0.05257510166626102, + 0.7127432636725523, + 0.11245854501137267 + ], + [ + 0.04996477605107579, + 0.677355942692646, + 0.09752231284282524 + ], + [ + 0.0477985035215991, + 0.6479885025217363, + 0.0799944412004235 + ], + [ + 0.04621482691848688, + 0.6265191226264917, + 0.060318621304860924 + ], + [ + 0.04533928174761143, + 0.6146496463381943, + 0.0391218054219448 + ], + [ + 0.04525518810682674, + 0.6135096166646911, + 0.017173052784797933 + ], + [ + 0.04597817243700818, + 0.6233108760963646, + -0.004687348256824259 + ], + [ + 0.04744652935506883, + 0.6432169051642328, + -0.02563258911168462 + ], + [ + 0.04953335356567557, + 0.6715072907543616, + -0.04494293770293981 + ], + [ + 0.052076476500895794, + 0.7059835672297826, + -0.062092467536568934 + ], + [ + 0.05491389340274198, + 0.7444494896707716, + -0.07679047002253359 + ], + [ + 0.05791023561634683, + 0.7850699099975107, + -0.08893433117059642 + ], + [ + 0.06096512329958325, + 0.8264840118916832, + -0.09846425336385212 + ], + [ + 0.06400383622102794, + 0.8676788378901713, + -0.10517011513354413 + ], + [ + 0.06695917895169991, + 0.9077434417876816, + -0.1085471752599665 + ], + [ + 0.06975618410943, + 0.9456615155801823, + -0.10779278942041974 + ], + [ + 0.07230751679560099, + 0.9802490889338049, + -0.10197630740659755 + ], + [ + 0.07452011187142672, + 1.010244508544032, + -0.0903325738266353 + ], + [ + 0.07630737942847368, + 1.0344739036625048, + -0.07257344354766149 + ], + [ + 0.07759960549025405, + 1.051992184968409, + -0.04910885089173267 + ], + [ + 0.0783481232128969, + 1.062139592156317, + -0.021110675832325478 + ], + [ + 0.026168750037576072, + 1.0659966951082605, + 0.0032031636977222634 + ], + [ + 0.02605815229271042, + 1.061491442455997, + 0.034128505840451 + ], + [ + 0.02575439886011217, + 1.0491178994013008, + 0.06292628432005115 + ], + [ + 0.025265588274676763, + 1.0292059636817932, + 0.08763854834345104 + ], + [ + 0.024607393289809595, + 1.0023940724910507, + 0.10691883008021436 + ], + [ + 0.023803820436425965, + 0.9696601434819977, + 0.12018035529722097 + ], + [ + 0.022885988417600693, + 0.9322718120818593, + 0.12755908558789217 + ], + [ + 0.021888874039402217, + 0.8916538754101327, + 0.1296997363041485 + ], + [ + 0.020847375249941753, + 0.8492279182692717, + 0.1274424065426365 + ], + [ + 0.01979378307241487, + 0.8063093311138723, + 0.12153166961673328 + ], + [ + 0.018758248139631735, + 0.7641263145605058, + 0.11245672468080518 + ], + [ + 0.017772179758030856, + 0.723958341905032, + 0.10046270549925657 + ], + [ + 0.016872557079639885, + 0.6873117767984761, + 0.08568893010659641 + ], + [ + 0.016104020240565658, + 0.6560050567852661, + 0.06833970063746535 + ], + [ + 0.015516173345655799, + 0.6320588290784113, + 0.04880183446160327 + ], + [ + 0.01515571884012016, + 0.6173755403815444, + 0.027673724321446335 + ], + [ + 0.015055826578502313, + 0.6133063807694648, + 0.005721876790038082 + ], + [ + 0.015227009866771583, + 0.6202796148479277, + -0.016199386711423805 + ], + [ + 0.015653589604626105, + 0.6376565468794582, + -0.03724042313555896 + ], + [ + 0.01629751146344332, + 0.6638870153741887, + -0.05665171951871458 + ], + [ + 0.017107974007918927, + 0.6969015992836025, + -0.07386941938475834 + ], + [ + 0.018032762137817546, + 0.7345732912341818, + -0.08855868865027765 + ], + [ + 0.019026708065882757, + 0.7750621595566051, + -0.10057476157045661 + ], + [ + 0.020054503935513827, + 0.8169299216277555, + -0.10982881595297274 + ], + [ + 0.021088095216271006, + 0.8590337625753655, + -0.11610241258498145 + ], + [ + 0.022101421945155613, + 0.9003121171969953, + -0.11890343781224463 + ], + [ + 0.0230660586960575, + 0.939607062005778, + -0.11745817022301307 + ], + [ + 0.023950133165860785, + 0.9756202633121704, + -0.11087773853536975 + ], + [ + 0.024720679454418196, + 1.0070088392223944, + -0.09845287824663745 + ], + [ + 0.025347680922216113, + 1.0325500474016491, + -0.07996822121709414 + ], + [ + 0.025807485784511006, + 1.0512804209539681, + -0.05592007712049288 + ], + [ + 0.026084141437029845, + 1.06255012282527, + -0.027565943460415052 + ] + ] + } +} \ No newline at end of file diff --git a/src/simsopt/field/biotsavart.py b/src/simsopt/field/biotsavart.py index bd7bb1cf2..39460743a 100644 --- a/src/simsopt/field/biotsavart.py +++ b/src/simsopt/field/biotsavart.py @@ -1,5 +1,6 @@ +import json import numpy as np -from monty.json import MSONable, MontyDecoder +from monty.json import MSONable, MontyDecoder, MontyEncoder import simsoptpp as sopp from .magneticfield import MagneticField @@ -209,8 +210,7 @@ def A_vjp(self, v): def as_dict(self) -> dict: d = MSONable.as_dict(self) - points = self.get_points_cart() - d["points"] = MSONable.as_dict(points) + d["points"] = self.get_points_cart() return d @classmethod From acd1c9e19cdd1773ce0ba2bd17724f0995487d49 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Tue, 31 May 2022 14:47:19 -0400 Subject: [PATCH 79/95] Add serialization tests for magnetic field classes --- src/simsopt/field/magneticfield.py | 8 ++- src/simsopt/field/magneticfieldclasses.py | 8 +-- tests/field/test_magneticfields.py | 79 +++++++++++++++++++---- 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/simsopt/field/magneticfield.py b/src/simsopt/field/magneticfield.py index ecfaa3a92..6ef25d6f9 100644 --- a/src/simsopt/field/magneticfield.py +++ b/src/simsopt/field/magneticfield.py @@ -170,9 +170,15 @@ def _d2A_by_dXdX_impl(self, ddA): def B_vjp(self, v): return sum([bf.B_vjp(v) for bf in self.Bfields if np.any(bf.dofs_free_status)]) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + @classmethod def from_dict(cls, d): - Bfields = Optimizable._decode(d) + decoder = MontyDecoder() + Bfields = [] + for field in d["Bfields"]: + Bfields.append(decoder.process_decoded(field)) return cls(Bfields) diff --git a/src/simsopt/field/magneticfieldclasses.py b/src/simsopt/field/magneticfieldclasses.py index a8d5618f0..02d3231bb 100644 --- a/src/simsopt/field/magneticfieldclasses.py +++ b/src/simsopt/field/magneticfieldclasses.py @@ -1,7 +1,7 @@ import logging import numpy as np -from monty.json import MSONable +from monty.json import MSONable, MontyDecoder from scipy.special import ellipk, ellipe try: from sympy.parsing.sympy_parser import parse_expr @@ -512,14 +512,14 @@ def as_dict(self) -> dict: d = {} d["@module"] = self.__class__.__module__ d["@class"] = self.__class__.__name__ - mn = [list(self.m), list(self.n)] - d["mn"] = mn + d["mn"] = np.column_stack((self.m, self.n)) d["coeffs"] = self.coeffs return d @classmethod def from_dict(cls, d): - return cls(d["mn"], d["coeffs"]) + mn = MontyDecoder().process_decoded(d["mn"]) + return cls(mn, d["coeffs"]) class Reiman(MagneticField): diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index c21ca338c..b5ad03b48 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -1,3 +1,14 @@ +import unittest +import json + +import numpy as np +from monty.json import MontyEncoder, MontyDecoder +try: + import pyevtk + pyevtk_found = True +except ImportError: + pyevtk_found = False + from simsopt.field.magneticfieldclasses import ToroidalField, \ ScalarPotentialRZMagneticField, CircularCoil, Dommaschk, \ Reiman, sympy_found, InterpolatedField, PoloidalField @@ -9,15 +20,6 @@ from simsopt.field.coil import coils_via_symmetries, Coil, Current from simsopt.util.zoo import get_ncsx_data -import numpy as np -import unittest - -try: - import pyevtk - pyevtk_found = True -except ImportError: - pyevtk_found = False - class Testing(unittest.TestCase): @@ -33,6 +35,13 @@ def test_toroidal_field(self): Bfield = ToroidalField(R0test, B0test) Bfield.set_points(points) B1 = Bfield.B() + + field_json_str = json.dumps(Bfield, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(points) + B1_regen = Bfield_regen.B() + self.assertTrue(np.allclose(B1, B1_regen)) + dB1_by_dX = Bfield.dB_by_dX() # Bfield analytical B2 = np.array([(B0test*R0test/(point[0]**2+point[1]**2))*np.array([-point[1], point[0], 0.]) for point in points]) @@ -91,8 +100,18 @@ def test_sum_Bfields(self): Btotal1.set_points(points) Btotal2.set_points(points) Btotal3.set_points(points) + + B1 = Btotal1.B() + B2 = Btotal2.B() + + # Verify serialization works + field_json_str = json.dumps(Btotal2, cls=MontyEncoder) + Btotal_regen = json.loads(field_json_str, cls=MontyDecoder) + Btotal_regen.set_points(points) + self.assertTrue(np.allclose(B2, Btotal_regen.B())) + # Verify - assert np.allclose(Btotal1.B(), Btotal2.B()) + assert np.allclose(B1, B2) assert np.allclose(Bhelical.B()+Btoroidal1.B()+Btoroidal2.B(), Btotal1.B()) assert np.allclose(Btotal1.dB_by_dX(), Btotal2.dB_by_dX()) assert np.allclose(Bhelical.dB_by_dX()+Btoroidal1.dB_by_dX()+Btoroidal2.dB_by_dX(), Btotal1.dB_by_dX()) @@ -117,6 +136,12 @@ def test_scalarpotential_Bfield(self): B1 = np.array(Bscalar.B()) dB1_by_dX = np.array(Bscalar.dB_by_dX()) + # Verify serialization works + field_json_str = json.dumps(Bscalar, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(points) + self.assertTrue(np.allclose(B1, np.array(Bfield_regen.B()))) + # Analytical Formula for B rphiz = [[np.sqrt(np.power(point[0], 2) + np.power(point[1], 2)), np.arctan2(point[1], point[0]), point[2]] for point in points] B2 = np.array([[0.2*point[2]+0.8*point[0], (0.1+0.3*point[2])/point[0], 0.2*point[0]+0.3*point[1]+point[2]] for point in rphiz]) @@ -185,6 +210,13 @@ def test_circularcoil_Bfield(self): points = np.array([[1e-10, 0, 0.]]) Bfield.set_points(points) assert np.allclose(Bfield.B(), [[0, 0, current/1e7*2*np.pi/radius]]) + + # Verify serialization works + field_json_str = json.dumps(Bfield, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(points) + self.assertTrue(np.allclose(Bfield.B(), Bfield_regen.B())) + # Verify that divergence is zero dB1_by_dX = Bfield.dB_by_dX() assert np.allclose(dB1_by_dX[:, 0, 0]+dB1_by_dX[:, 1, 1]+dB1_by_dX[:, 2, 2], np.zeros((npoints))) @@ -400,18 +432,31 @@ def test_helicalcoil_Bfield(self): assert np.allclose(Bhelical.B(), field) assert np.allclose(Bhelical.dB_by_dX(), derivative) + # Verify serialization works + field_json_str = json.dumps(Bhelical, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(point) + self.assertTrue(np.allclose(Bhelical.B(), Bfield_regen.B())) + def test_Dommaschk(self): mn = [[10, 2], [15, 3]] coeffs = [[-2.18, -2.18], [25.8, -25.8]] Bfield = Dommaschk(mn=mn, coeffs=coeffs) - Bfield.set_points(np.asarray([[0.9231, 0.8423, -0.1123]])) + point = np.asarray([[0.9231, 0.8423, -0.1123]]) + Bfield.set_points(point) gradB = np.array(Bfield.dB_by_dX()) transpGradB = np.array([dBdx.T for dBdx in gradB]) # Verify B - assert np.allclose(Bfield.B(), [[-1.72696, 3.26173, -2.22013]]) + B = Bfield.B() + assert np.allclose(B, [[-1.72696, 3.26173, -2.22013]]) # Verify gradB is symmetric and its value assert np.allclose(gradB, transpGradB) assert np.allclose(gradB, np.array([[-59.9602, 8.96793, -24.8844], [8.96793, 49.0327, -18.4131], [-24.8844, -18.4131, 10.9275]])) + # Verify serialization works + field_json_str = json.dumps(Bfield, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(point) + self.assertTrue(np.allclose(B, Bfield_regen.B())) def test_BifieldMultiply(self): scalar = 1.2345 @@ -445,6 +490,11 @@ def test_BifieldMultiply(self): assert np.allclose(Bfield2.A(), scalar*np.array(Bfield1.A())) assert np.allclose(Bfield2.dA_by_dX(), scalar*np.array(Bfield1.dA_by_dX())) assert np.allclose(Bfield2.d2A_by_dXdX(), scalar*np.array(Bfield1.d2A_by_dXdX())) + # Verify serialization works + field_json_str = json.dumps(Bfield2, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(points) + self.assertTrue(np.allclose(Bfield2.B(), Bfield_regen.B())) def test_Reiman(self): iota0 = 0.15 @@ -463,6 +513,11 @@ def test_Reiman(self): # Check that div(B)=0 dB1 = Bfield.dB_by_dX() assert np.allclose(dB1[:, 0, 0]+dB1[:, 1, 1]+dB1[:, 2, 2], np.zeros((npoints))) + # Verify serialization works + field_json_str = json.dumps(Bfield, cls=MontyEncoder) + Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) + Bfield_regen.set_points(points) + self.assertTrue(np.allclose(B1, Bfield_regen.B())) # Bfield analytical x = points[:, 0] y = points[:, 1] From 84103465a9d8ea96f1ef6372f93be0a7bf385fa2 Mon Sep 17 00:00:00 2001 From: Florian Wechsung Date: Fri, 3 Jun 2022 14:56:56 +0200 Subject: [PATCH 80/95] fix --- docs/source/optimizable.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index 2f4fdf5c7..1308808e9 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -699,7 +699,7 @@ out the exact class name of the saved object. # or curve = Optimizable.from_file('curve.json') -To save multiple simsopt objects use ``save`` function implemented in simsopt. +To save multiple simsopt objects use the ``save`` function implemented in simsopt. .. code-block:: From 5c0b1558bd8196da8e8dde4b94a9bd80cd5930a3 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 6 Jun 2022 08:24:24 -0400 Subject: [PATCH 81/95] Bug fix in boozer surface --- src/simsopt/geo/boozersurface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index c742d8bf1..af4389a66 100644 --- a/src/simsopt/geo/boozersurface.py +++ b/src/simsopt/geo/boozersurface.py @@ -500,7 +500,7 @@ def solve_residual_equation_exactly_newton(self, tol=1e-10, maxiter=10, iota=0., label = self.label if G is None: - G = 2. * np.pi * np.sum(np.abiotsavart(self.biotsavart.coil_currents)) * (4 * np.pi * 10**(-7) / (2 * np.pi)) + G = 2. * np.pi * np.sum(np.abs(self.biotsavart.coil_currents)) * (4 * np.pi * 10**(-7) / (2 * np.pi)) x = np.concatenate((s.get_dofs(), [iota, G])) i = 0 r, J = boozer_surface_residual(s, iota, G, self.biotsavart, derivatives=1) From a20f0100076cf0ec4f6219b5dd94993cb0574ca6 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 6 Jun 2022 08:44:55 -0400 Subject: [PATCH 82/95] Add points to serialization of magnetic classes --- src/simsopt/field/magneticfield.py | 21 ++++++-- src/simsopt/field/magneticfieldclasses.py | 60 +++++++++++++++++------ 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/simsopt/field/magneticfield.py b/src/simsopt/field/magneticfield.py index 6ef25d6f9..9d8995319 100644 --- a/src/simsopt/field/magneticfield.py +++ b/src/simsopt/field/magneticfield.py @@ -125,12 +125,18 @@ def _d2A_by_dXdX_impl(self, ddA): ddA[:] = self.scalar*self.Bfield.d2A_by_dXdX() def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d @classmethod def from_dict(cls, d): - Bfield = MontyDecoder().process_decoded(d["Bfield"]) - return cls(d["scalar"], Bfield) + decoder = MontyDecoder() + Bfield = decoder.process_decoded(d["Bfield"]) + field = cls(d["scalar"], Bfield) + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class MagneticFieldSum(MagneticField): @@ -171,7 +177,9 @@ def B_vjp(self, v): return sum([bf.B_vjp(v) for bf in self.Bfields if np.any(bf.dofs_free_status)]) def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d @classmethod def from_dict(cls, d): @@ -179,7 +187,10 @@ def from_dict(cls, d): Bfields = [] for field in d["Bfields"]: Bfields.append(decoder.process_decoded(field)) - return cls(Bfields) + field_sum = cls(Bfields) + xyz = decoder.process_decoded(d["points"]) + field_sum.set_points_cart(xyz) + return field_sum diff --git a/src/simsopt/field/magneticfieldclasses.py b/src/simsopt/field/magneticfieldclasses.py index 02d3231bb..cb64ca581 100644 --- a/src/simsopt/field/magneticfieldclasses.py +++ b/src/simsopt/field/magneticfieldclasses.py @@ -110,11 +110,17 @@ def _d2A_by_dXdX_impl(self, ddA): np.zeros((3, 3, len(points)))])).transpose((3, 0, 1, 2)) def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d @classmethod def from_dict(cls, d): - return cls(d["R0"], d["B0"]) + field = cls(d["R0"], d["B0"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class PoloidalField(MagneticField): @@ -212,11 +218,17 @@ def _dB_by_dX_impl(self, dB): dB[:] = self.B0/self.R0/self.q*np.array([dB_by_dX1_term1+dB_by_dX1_term2, dB_by_dX2_term1+dB_by_dX2_term2, dB_by_dX3_term1+dB_by_dX3_term2]).T def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d @classmethod def from_dict(cls, d): - return cls(d["R0"], d["B0"], d["q"]) + field = cls(d["R0"], d["B0"], d["q"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class ScalarPotentialRZMagneticField(MagneticField): @@ -305,11 +317,17 @@ def _dB_by_dX_impl(self, dB): dB[:, 2, 1] = dBrdz * np.sin(phi) + dBphidz * np.cos(phi) def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d @classmethod def from_dict(cls, d): - return cls(d["phi_str"]) + field = cls(d["phi_str"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class CircularCoil(MagneticField): @@ -470,11 +488,16 @@ def as_dict(self): d["center"] = self.center d["I"] = self.Inorm * 25e5 d["normal"] = self.normal + d["points"] = self.get_points_cart() return d @classmethod def from_dict(cls, d): - return cls(d["r0"], d["center"], d["I"], d["normal"]) + field = cls(d["r0"], d["center"], d["I"], d["normal"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class Dommaschk(MagneticField): @@ -514,12 +537,17 @@ def as_dict(self) -> dict: d["@class"] = self.__class__.__name__ d["mn"] = np.column_stack((self.m, self.n)) d["coeffs"] = self.coeffs + d["points"] = self.get_points_cart() return d @classmethod def from_dict(cls, d): - mn = MontyDecoder().process_decoded(d["mn"]) - return cls(mn, d["coeffs"]) + decoder = MontyDecoder() + mn = decoder .process_decoded(d["mn"]) + field = cls(mn, d["coeffs"]) + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class Reiman(MagneticField): @@ -554,15 +582,17 @@ def _dB_by_dX_impl(self, dB): dB[:] = sopp.ReimandB(self.iota0, self.iota1, self.k, self.epsilonk, self.m0, points) def as_dict(self): - return MSONable.as_dict(self) + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d @classmethod def from_dict(cls, d): - return cls(d["iota0"], - d["iota1"], - d["k"], - d["epsilonk"], - d["m0"]) + field = cls(d["iota0"], d["iota1"], d["k"], d["epsilonk"], d["m0"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field class UniformInterpolationRule(sopp.UniformInterpolationRule): From 6f08e5015a82ca6a00e23a18ef6d97abfce19808 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 6 Jun 2022 08:57:38 -0400 Subject: [PATCH 83/95] Linter fixes --- src/simsopt/geo/boozersurface.py | 2 +- src/simsopt/geo/curvehelical.py | 2 +- src/simsopt/geo/qfmsurface.py | 2 +- src/simsopt/objectives/fluxobjective.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index af4389a66..556768e7c 100644 --- a/src/simsopt/geo/boozersurface.py +++ b/src/simsopt/geo/boozersurface.py @@ -542,4 +542,4 @@ def from_dict(cls, d): decoder = MontyDecoder() bs = decoder.process_decoded(d["biotsavart"]) surf = decoder.process_decoded(d["surface"]) - return cls(bs, surf, d["label"], d["targetlabel"]) \ No newline at end of file + return cls(bs, surf, d["label"], d["targetlabel"]) diff --git a/src/simsopt/geo/curvehelical.py b/src/simsopt/geo/curvehelical.py index 22ad0f6d4..3c7bb7a95 100644 --- a/src/simsopt/geo/curvehelical.py +++ b/src/simsopt/geo/curvehelical.py @@ -78,4 +78,4 @@ def from_dict(cls, d): curve = cls(d["quadpoints"], d["order"], d["n0"], d["l0"], d["R0"], d["r0"]) curve.local_full_x = d["x0"] - return curve \ No newline at end of file + return curve diff --git a/src/simsopt/geo/qfmsurface.py b/src/simsopt/geo/qfmsurface.py index 22f820b35..46b8d2488 100644 --- a/src/simsopt/geo/qfmsurface.py +++ b/src/simsopt/geo/qfmsurface.py @@ -210,4 +210,4 @@ def from_dict(cls, d): decoder = MontyDecoder() bs = decoder.process_decoded(d["biotsavart"]) surf = decoder.process_decoded(d["surface"]) - return cls(bs, surf, d["label"], d["targetlabel"]) \ No newline at end of file + return cls(bs, surf, d["label"], d["targetlabel"]) diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index 9d77cb1b8..4f1a72441 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -63,4 +63,4 @@ def from_dict(cls, d): decoder = MontyDecoder() surface = decoder.process_decoded(d["surface"]) field = decoder.process_decoded(d["field"]) - return cls(surface, field, d["target"]) \ No newline at end of file + return cls(surface, field, d["target"]) From e5393969f9d5bfd48b55b2432a3f35f4b2a55e79 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Mon, 6 Jun 2022 08:58:15 -0400 Subject: [PATCH 84/95] set points call for deserialized objects removed in the tests --- tests/field/test_magneticfields.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index b5ad03b48..ac428008d 100644 --- a/tests/field/test_magneticfields.py +++ b/tests/field/test_magneticfields.py @@ -38,7 +38,6 @@ def test_toroidal_field(self): field_json_str = json.dumps(Bfield, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(points) B1_regen = Bfield_regen.B() self.assertTrue(np.allclose(B1, B1_regen)) @@ -107,7 +106,6 @@ def test_sum_Bfields(self): # Verify serialization works field_json_str = json.dumps(Btotal2, cls=MontyEncoder) Btotal_regen = json.loads(field_json_str, cls=MontyDecoder) - Btotal_regen.set_points(points) self.assertTrue(np.allclose(B2, Btotal_regen.B())) # Verify @@ -139,7 +137,6 @@ def test_scalarpotential_Bfield(self): # Verify serialization works field_json_str = json.dumps(Bscalar, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(points) self.assertTrue(np.allclose(B1, np.array(Bfield_regen.B()))) # Analytical Formula for B @@ -214,7 +211,6 @@ def test_circularcoil_Bfield(self): # Verify serialization works field_json_str = json.dumps(Bfield, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(points) self.assertTrue(np.allclose(Bfield.B(), Bfield_regen.B())) # Verify that divergence is zero @@ -435,7 +431,6 @@ def test_helicalcoil_Bfield(self): # Verify serialization works field_json_str = json.dumps(Bhelical, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(point) self.assertTrue(np.allclose(Bhelical.B(), Bfield_regen.B())) def test_Dommaschk(self): @@ -455,7 +450,6 @@ def test_Dommaschk(self): # Verify serialization works field_json_str = json.dumps(Bfield, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(point) self.assertTrue(np.allclose(B, Bfield_regen.B())) def test_BifieldMultiply(self): @@ -493,7 +487,6 @@ def test_BifieldMultiply(self): # Verify serialization works field_json_str = json.dumps(Bfield2, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(points) self.assertTrue(np.allclose(Bfield2.B(), Bfield_regen.B())) def test_Reiman(self): @@ -516,7 +509,6 @@ def test_Reiman(self): # Verify serialization works field_json_str = json.dumps(Bfield, cls=MontyEncoder) Bfield_regen = json.loads(field_json_str, cls=MontyDecoder) - Bfield_regen.set_points(points) self.assertTrue(np.allclose(B1, Bfield_regen.B())) # Bfield analytical x = points[:, 0] From 98f3b4a456615a10dc23acb36e2ee139a27f50e3 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Wed, 8 Jun 2022 15:50:50 -0400 Subject: [PATCH 85/95] Add serialization test for CurrentSum --- tests/field/test_coil.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/field/test_coil.py b/tests/field/test_coil.py index 597cab9da..29b3f4706 100644 --- a/tests/field/test_coil.py +++ b/tests/field/test_coil.py @@ -8,7 +8,7 @@ from simsopt.geo.curverzfourier import CurveRZFourier from simsopt.geo.curvehelical import CurveHelical from simsopt.geo.curve import RotatedCurve -from simsopt.field.coil import Coil, Current, ScaledCurrent +from simsopt.field.coil import Coil, Current, ScaledCurrent, CurrentSum from simsopt.field.biotsavart import BiotSavart @@ -73,7 +73,7 @@ def test_serialization(self): self.subtest_serialization(curvetype, rotated) -class TestCurrent(unittest.TestCase): +class TestCurrentSerialization(unittest.TestCase): def test_current_serialization(self): current = Current(1e4) current_str = json.dumps(current, cls=MontyEncoder) @@ -88,6 +88,15 @@ def test_scaled_current_serialization(self): self.assertAlmostEqual(scaled_current.get_value(), current_regen.get_value()) + def test_current_sum_serialization(self): + current_a = Current(1e4) + current_b = Current(1.5e4) + current = CurrentSum(current_a, current_b) + current_str = json.dumps(current, cls=MontyEncoder) + current_regen = json.loads(current_str, cls=MontyDecoder) + self.assertAlmostEqual(current.get_value(), + current_regen.get_value()) + class ScaledCurrentTesting(unittest.TestCase): From 6396c71965c41996979389abe6c1ee98115ba6ac Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Thu, 9 Jun 2022 16:58:11 -0400 Subject: [PATCH 86/95] Test for scaledsurface class --- src/simsopt/geo/surface.py | 9 ++++----- tests/geo/test_surface.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/simsopt/geo/surface.py b/src/simsopt/geo/surface.py index 4e1730f7a..9c015b07a 100644 --- a/src/simsopt/geo/surface.py +++ b/src/simsopt/geo/surface.py @@ -510,9 +510,6 @@ def interpolate_on_arclength_grid(self, function, theta_evaluate): def as_dict(self) -> dict: d = super().as_dict() - # d = {} - # d["@module"] = self.__class__.__module__ - # d["@class"] = self.__class__.__name__ d["nfp"] = self.nfp d["quadpoints_phi"] = list(self.quadpoints_phi) d["quadpoints_theta"] = list(self.quadpoints_theta) @@ -663,8 +660,10 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d): - surf = MontyDecoder.process_decoded(d["surf"]) - return cls(surf, d["scale_factors"]) + decoder = MontyDecoder() + surf = decoder.process_decoded(d["surf"]) + scale_factors = decoder.process_decoded(d["scale_factors"]) + return cls(surf, scale_factors) def best_nphi_over_ntheta(surf): diff --git a/tests/geo/test_surface.py b/tests/geo/test_surface.py index e6432b789..45e55c44c 100755 --- a/tests/geo/test_surface.py +++ b/tests/geo/test_surface.py @@ -1,9 +1,12 @@ import unittest +import json from pathlib import Path import os import logging import numpy as np +from monty.json import MontyDecoder, MontyEncoder + from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.geo.surfacexyzfourier import SurfaceXYZFourier from simsopt.geo.surfacexyztensorfourier import SurfaceXYZTensorFourier @@ -289,6 +292,20 @@ def test_fixed(self): surf_scaled.update_fixed() np.testing.assert_array_equal(surf1.dofs_free_status, surf_scaled.dofs_free_status) + def test_serialization(self): + surfacetypes = ["SurfaceRZFourier", "SurfaceXYZFourier", + "SurfaceXYZTensorFourier"] + for surfacetype in surfacetypes: + for stellsym in [True, False]: + with self.subTest(surfacetype=surfacetype, stellsym=stellsym): + s = get_surface(surfacetype, stellsym, full=True) + dof_size = len(s.x) + scale_factors = np.random.random_sample(dof_size) + scaled_s = SurfaceScaled(s, scale_factors) + scaled_s_str = json.dumps(scaled_s, cls=MontyEncoder) + regen_s = json.loads(scaled_s_str, cls=MontyDecoder) + + class BestNphiOverNthetaTests(unittest.TestCase): def test_axisymm(self): From 670fcb1740cbb97142e7e2310e5dec99f0412891 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Thu, 9 Jun 2022 18:38:20 -0400 Subject: [PATCH 87/95] Add serialization test for curve objectives --- src/simsopt/geo/curveobjectives.py | 5 +++-- src/simsopt/geo/qfmsurface.py | 7 ------- tests/geo/test_curve_objectives.py | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/simsopt/geo/curveobjectives.py b/src/simsopt/geo/curveobjectives.py index 7a5fd0d12..49eee7e1a 100644 --- a/src/simsopt/geo/curveobjectives.py +++ b/src/simsopt/geo/curveobjectives.py @@ -436,6 +436,7 @@ def __init__(self, curve, nintervals="full"): and thus specify the number of intervals directly. """ super().__init__(depends_on=[curve]) + assert nintervals in ["full", "partial"] \ or (isinstance(nintervals, int) and 0 < nintervals <= curve.gamma().shape[0]) self.curve = curve @@ -448,8 +449,8 @@ def __init__(self, curve, nintervals="full"): nintervals = 2*curve.order else: raise RuntimeError("Please provide a value other than `partial` for `nintervals`. We only have a default for `CurveXYZFourier` and `JaxCurveXYZFourier`.") - else: - self.nintervals = nintervals + + self.nintervals = nintervals indices = np.floor(np.linspace(0, nquadpoints, nintervals+1, endpoint=True)).astype(int) mat = np.zeros((nintervals, nquadpoints)) for i in range(nintervals): diff --git a/src/simsopt/geo/qfmsurface.py b/src/simsopt/geo/qfmsurface.py index 46b8d2488..2ee68fc4c 100644 --- a/src/simsopt/geo/qfmsurface.py +++ b/src/simsopt/geo/qfmsurface.py @@ -204,10 +204,3 @@ def minimize_qfm(self, tol=1e-3, maxiter=1000, method='SLSQP', tol=tol, maxiter=maxiter, constraint_weight=constraint_weight) else: raise ValueError - - @classmethod - def from_dict(cls, d): - decoder = MontyDecoder() - bs = decoder.process_decoded(d["biotsavart"]) - surf = decoder.process_decoded(d["surface"]) - return cls(bs, surf, d["label"], d["targetlabel"]) diff --git a/tests/geo/test_curve_objectives.py b/tests/geo/test_curve_objectives.py index ca2696dc6..5fdbea046 100644 --- a/tests/geo/test_curve_objectives.py +++ b/tests/geo/test_curve_objectives.py @@ -1,6 +1,8 @@ import unittest +import json import numpy as np +from monty.json import MontyDecoder, MontyEncoder from simsopt.geo import parameters from simsopt.geo.curve import RotatedCurve, curves_to_vtk @@ -70,6 +72,9 @@ def subtest_curve_length_taylor_test(self, curve): # print("err_new %s" % (err_new)) assert err_new < 0.55 * err err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) def test_curve_length_taylor_test(self): for curvetype in self.curvetypes: @@ -96,6 +101,9 @@ def subtest_curve_curvature_taylor_test(self, curve): # print("err_new %s" % (err_new)) assert err_new < 0.55 * err err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) def test_curve_curvature_taylor_test(self): for curvetype in self.curvetypes: @@ -122,6 +130,9 @@ def subtest_curve_torsion_taylor_test(self, curve): # print("err_new %s" % (err_new)) assert err_new < 0.55 * err err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) def test_curve_torsion_taylor_test(self): for curvetype in self.curvetypes: @@ -159,6 +170,9 @@ def subtest_curve_minimum_distance_taylor_test(self, curve): # print("err_new %s" % (err_new)) assert err_new < 0.55 * err err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) def test_curve_minimum_distance_taylor_test(self): for curvetype in self.curvetypes: @@ -190,6 +204,9 @@ def subtest_curve_arclengthvariation_taylor_test(self, curve, nintervals): # print("err_new %s" % (err_new)) assert err_new < 0.3 * err err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) def test_curve_arclengthvariation_taylor_test(self): for curvetype in self.curvetypes: @@ -226,6 +243,9 @@ def subtest_curve_meansquaredcurvature_taylor_test(self, curve): # print("err_new %s" % (err_new)) assert err_new < 0.3 * err err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) def test_curve_meansquaredcurvature_taylor_test(self): for curvetype in self.curvetypes: From 0986f65d0e737e50bec05d3c775f87f3651a1373 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 08:11:19 -0400 Subject: [PATCH 88/95] Add serialization test for FluxObjective --- src/simsopt/objectives/fluxobjective.py | 3 +- tests/objectives/test_fluxobjective.py | 47 +++++++++++++++---------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index 4f1a72441..eb6ebc591 100644 --- a/src/simsopt/objectives/fluxobjective.py +++ b/src/simsopt/objectives/fluxobjective.py @@ -63,4 +63,5 @@ def from_dict(cls, d): decoder = MontyDecoder() surface = decoder.process_decoded(d["surface"]) field = decoder.process_decoded(d["field"]) - return cls(surface, field, d["target"]) + target = decoder.process_decoded(d["target"]) + return cls(surface, field, target) diff --git a/tests/objectives/test_fluxobjective.py b/tests/objectives/test_fluxobjective.py index a4a43b91b..e40c9894a 100755 --- a/tests/objectives/test_fluxobjective.py +++ b/tests/objectives/test_fluxobjective.py @@ -1,5 +1,9 @@ import unittest +import json + import numpy as np +from monty.json import MontyDecoder, MontyEncoder + from simsopt.geo.surfacerzfourier import SurfaceRZFourier from simsopt.field.coil import coils_via_symmetries, Current from simsopt.geo.curve import create_equally_spaced_curves @@ -13,29 +17,34 @@ filename = TEST_DIR / 'input.LandremanPaul2021_QA' -def check_taylor_test(J): - dofs = J.x - np.random.seed(1) - h = np.random.uniform(size=dofs.shape) - J0, dJ0 = J.J(), J.dJ() - dJh = sum(dJ0 * h) - err_old = 1e10 - for i in range(11, 17): - eps = 0.5**i - J.x = dofs + eps*h - J1 = J.J() - J.x = dofs - eps*h - J2 = J.J() - err = np.abs((J1-J2)/(2*eps) - dJh) - # print(i, "err", err) - # print(i, "err/err_old", err/err_old) - assert err < 0.6**2 * err_old - err_old = err - class FluxObjectiveTests(unittest.TestCase): def test_flux(self): + + def check_taylor_test(J): + dofs = J.x + np.random.seed(1) + h = np.random.uniform(size=dofs.shape) + J0, dJ0 = J.J(), J.dJ() + dJh = sum(dJ0 * h) + err_old = 1e10 + for i in range(11, 17): + eps = 0.5 ** i + J.x = dofs + eps * h + J1 = J.J() + J.x = dofs - eps * h + J2 = J.J() + err = np.abs((J1 - J2) / (2 * eps) - dJh) + # print(i, "err", err) + # print(i, "err/err_old", err/err_old) + assert err < 0.6 ** 2 * err_old + err_old = err + + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) + s = SurfaceRZFourier.from_vmec_input(filename) ncoils = 4 ALPHA = 1e-5 From 299e22adee229efe64ebb931c8ec9870bc031072 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 08:15:14 -0400 Subject: [PATCH 89/95] Linter fixes --- tests/geo/test_surface.py | 1 - tests/objectives/test_fluxobjective.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/geo/test_surface.py b/tests/geo/test_surface.py index 45e55c44c..69ad8754b 100755 --- a/tests/geo/test_surface.py +++ b/tests/geo/test_surface.py @@ -306,7 +306,6 @@ def test_serialization(self): regen_s = json.loads(scaled_s_str, cls=MontyDecoder) - class BestNphiOverNthetaTests(unittest.TestCase): def test_axisymm(self): """ diff --git a/tests/objectives/test_fluxobjective.py b/tests/objectives/test_fluxobjective.py index e40c9894a..8c729aecf 100755 --- a/tests/objectives/test_fluxobjective.py +++ b/tests/objectives/test_fluxobjective.py @@ -17,7 +17,6 @@ filename = TEST_DIR / 'input.LandremanPaul2021_QA' - class FluxObjectiveTests(unittest.TestCase): def test_flux(self): From 9f2ca788675844cdb143acd059454ce9d3264543 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 09:27:34 -0400 Subject: [PATCH 90/95] Remove @class and @module from as_dict methods --- src/simsopt/field/coil.py | 2 -- src/simsopt/field/magneticfieldclasses.py | 4 ---- src/simsopt/geo/curve.py | 2 -- src/simsopt/geo/curvehelical.py | 2 -- src/simsopt/geo/curvexyzfourier.py | 17 +++++++++-------- src/simsopt/objectives/functions.py | 4 ---- 6 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index cce69b64b..b703e88f5 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -93,8 +93,6 @@ def vjp(self, v_current): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["current"] = self.get_value() return d diff --git a/src/simsopt/field/magneticfieldclasses.py b/src/simsopt/field/magneticfieldclasses.py index cb64ca581..0a1ec6697 100644 --- a/src/simsopt/field/magneticfieldclasses.py +++ b/src/simsopt/field/magneticfieldclasses.py @@ -482,8 +482,6 @@ def _A_impl(self, A): def as_dict(self): d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["r0"] = self.r0 d["center"] = self.center d["I"] = self.Inorm * 25e5 @@ -533,8 +531,6 @@ def _dB_by_dX_impl(self, dB): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["mn"] = np.column_stack((self.m, self.n)) d["coeffs"] = self.coeffs d["points"] = self.get_points_cart() diff --git a/src/simsopt/geo/curve.py b/src/simsopt/geo/curve.py index 0eb2b038c..b93c67afc 100644 --- a/src/simsopt/geo/curve.py +++ b/src/simsopt/geo/curve.py @@ -813,8 +813,6 @@ def dgammadashdashdash_by_dcoeff_vjp(self, v): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["curve"] = self.curve.as_dict() d["phi"] = self._phi d["flip"] = True if self.rotmat[2][2] == -1 else False diff --git a/src/simsopt/geo/curvehelical.py b/src/simsopt/geo/curvehelical.py index 3c7bb7a95..113a1428b 100644 --- a/src/simsopt/geo/curvehelical.py +++ b/src/simsopt/geo/curvehelical.py @@ -62,8 +62,6 @@ def set_dofs_impl(self, dofs): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["n0"] = self.n0 diff --git a/src/simsopt/geo/curvexyzfourier.py b/src/simsopt/geo/curvexyzfourier.py index 6edb101d3..eaf88d3aa 100644 --- a/src/simsopt/geo/curvexyzfourier.py +++ b/src/simsopt/geo/curvexyzfourier.py @@ -3,6 +3,7 @@ import numpy as np import jax.numpy as jnp +from monty.json import MontyDecoder from .curve import Curve, JaxCurve import simsoptpp as sopp @@ -98,17 +99,19 @@ def load_curves_from_file(filename, order=None, ppp=20, delimiter=','): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ - d["quadpoints"] = list(self.quadpoints) + d["quadpoints"] = self.quadpoints d["order"] = self.order - d["x0"] = list(self.local_full_x) + d["x0"] = self.local_full_x return d @classmethod def from_dict(cls, d): - curve = cls(d["quadpoints"], d["order"]) - curve.local_full_x = d["x0"] + decoder = MontyDecoder() + quadpoints = decoder.process_decoded(d["quadpoints"]) + order = decoder.process_decoded(d["order"]) + x0 = decoder.process_decoded(d["x0"]) + curve = cls(quadpoints, order) + curve.local_full_x = x0 return curve @@ -171,8 +174,6 @@ def set_dofs_impl(self, dofs): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["x0"] = list(self.local_full_x) diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index 31906767e..a7ede8ea1 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -202,8 +202,6 @@ def dterms(self): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["b"] = self._sqrtb * self._sqrtb d["x"] = self.get("x") d["y"] = self.get("y") @@ -257,8 +255,6 @@ def dJ(self): def as_dict(self) -> dict: d = {} - d["@module"] = self.__class__.__module__ - d["@class"] = self.__class__.__name__ d["val"] = self.local_full_x[0] d["depends_on"] = [] for opt in self.parents: From 818393df4db4c0616491432b82ab591fedd88cde Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 09:28:10 -0400 Subject: [PATCH 91/95] Add serialization test for quadraticpenalty class --- src/simsopt/objectives/utilities.py | 11 ++++++++--- tests/objectives/test_utilities.py | 6 ++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/simsopt/objectives/utilities.py b/src/simsopt/objectives/utilities.py index f56dea518..6597af28d 100644 --- a/src/simsopt/objectives/utilities.py +++ b/src/simsopt/objectives/utilities.py @@ -86,11 +86,16 @@ def dJ(self): return np.maximum(val-self.threshold, 0)*dval def as_dict(self) -> dict: - return MSONable.as_dict(self) + d = {} + d["obj"] = self.obj + d["threshold"] = np.array(self.threshold) + return d @classmethod def from_dict(cls, d): - obj = MontyDecoder().process_decoded(d["obj"]) - return cls(obj, d["threshold"]) + decoder = MontyDecoder() + obj = decoder.process_decoded(d["obj"]) + threshold = decoder.process_decoded(d["threshold"]) + return cls(obj, threshold) return_fn_map = {'J': J, 'dJ': dJ} diff --git a/tests/objectives/test_utilities.py b/tests/objectives/test_utilities.py index 2276fbb31..8593bc654 100644 --- a/tests/objectives/test_utilities.py +++ b/tests/objectives/test_utilities.py @@ -1,6 +1,8 @@ import unittest +import json import numpy as np +from monty.json import MontyDecoder, MontyEncoder from simsopt.geo.curvexyzfourier import CurveXYZFourier from simsopt.geo.curveobjectives import CurveLength, LpCurveCurvature, LpCurveTorsion @@ -46,6 +48,10 @@ def subtest_quadratic_penalty(self, curve, threshold): assert err_new < 0.55 * err or err_new < 1e-13 err = err_new + J_str = json.dumps(J, cls=MontyEncoder) + J_regen = json.loads(J_str, cls=MontyDecoder) + self.assertAlmostEqual(J.J(), J_regen.J()) + def test_quadratic_penalty(self): curve = self.create_curve() J = CurveLength(curve) From dce026cec0e611b2de7adc493378927df08ed408 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 11:17:04 -0400 Subject: [PATCH 92/95] Revert changes in serialization for curvexyzfourier --- src/simsopt/geo/curvexyzfourier.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/simsopt/geo/curvexyzfourier.py b/src/simsopt/geo/curvexyzfourier.py index eaf88d3aa..c5ee0d5b1 100644 --- a/src/simsopt/geo/curvexyzfourier.py +++ b/src/simsopt/geo/curvexyzfourier.py @@ -99,19 +99,15 @@ def load_curves_from_file(filename, order=None, ppp=20, delimiter=','): def as_dict(self) -> dict: d = {} - d["quadpoints"] = self.quadpoints + d["quadpoints"] = list(self.quadpoints) d["order"] = self.order - d["x0"] = self.local_full_x + d["x0"] = list(self.local_full_x) return d @classmethod def from_dict(cls, d): - decoder = MontyDecoder() - quadpoints = decoder.process_decoded(d["quadpoints"]) - order = decoder.process_decoded(d["order"]) - x0 = decoder.process_decoded(d["x0"]) - curve = cls(quadpoints, order) - curve.local_full_x = x0 + curve = cls(d["quadpoints"], d["order"]) + curve.local_full_x = d["x0"] return curve From e84902cbe6ab39976a6fdeb9a6c14dd488aa9914 Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 12:09:14 -0400 Subject: [PATCH 93/95] Bug fix in curvexyzfourier serialization --- src/simsopt/geo/curvexyzfourier.py | 2 ++ src/simsopt/objectives/utilities.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/simsopt/geo/curvexyzfourier.py b/src/simsopt/geo/curvexyzfourier.py index c5ee0d5b1..c524cca7f 100644 --- a/src/simsopt/geo/curvexyzfourier.py +++ b/src/simsopt/geo/curvexyzfourier.py @@ -99,6 +99,8 @@ def load_curves_from_file(filename, order=None, ppp=20, delimiter=','): def as_dict(self) -> dict: d = {} + d["@class"] = self.__class__.__name__ + d["@module"] = self.__class__.__module__ d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["x0"] = list(self.local_full_x) diff --git a/src/simsopt/objectives/utilities.py b/src/simsopt/objectives/utilities.py index 6597af28d..bd165849f 100644 --- a/src/simsopt/objectives/utilities.py +++ b/src/simsopt/objectives/utilities.py @@ -87,6 +87,8 @@ def dJ(self): def as_dict(self) -> dict: d = {} + d["@class"] = self.__class__.__name__ + d["@module"] = self.__class__.__module__ d["obj"] = self.obj d["threshold"] = np.array(self.threshold) return d From bdfee957939607b9faba284b74dfbd4d29f2c1bf Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 16:21:47 -0400 Subject: [PATCH 94/95] Add back the class and module info in RotatedCurve serialization --- src/simsopt/geo/curve.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/simsopt/geo/curve.py b/src/simsopt/geo/curve.py index b93c67afc..0eb2b038c 100644 --- a/src/simsopt/geo/curve.py +++ b/src/simsopt/geo/curve.py @@ -813,6 +813,8 @@ def dgammadashdashdash_by_dcoeff_vjp(self, v): def as_dict(self) -> dict: d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ d["curve"] = self.curve.as_dict() d["phi"] = self._phi d["flip"] = True if self.rotmat[2][2] == -1 else False From a32cbdade00788ff717bed685540a0cbb491d86b Mon Sep 17 00:00:00 2001 From: Bharat Medasani Date: Fri, 10 Jun 2022 22:10:00 -0400 Subject: [PATCH 95/95] Add back class and module names into serialization of curves --- src/simsopt/field/coil.py | 2 ++ src/simsopt/geo/curvehelical.py | 2 ++ src/simsopt/geo/curvexyzfourier.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index b703e88f5..cce69b64b 100644 --- a/src/simsopt/field/coil.py +++ b/src/simsopt/field/coil.py @@ -93,6 +93,8 @@ def vjp(self, v_current): def as_dict(self) -> dict: d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ d["current"] = self.get_value() return d diff --git a/src/simsopt/geo/curvehelical.py b/src/simsopt/geo/curvehelical.py index 113a1428b..3c7bb7a95 100644 --- a/src/simsopt/geo/curvehelical.py +++ b/src/simsopt/geo/curvehelical.py @@ -62,6 +62,8 @@ def set_dofs_impl(self, dofs): def as_dict(self) -> dict: d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["n0"] = self.n0 diff --git a/src/simsopt/geo/curvexyzfourier.py b/src/simsopt/geo/curvexyzfourier.py index c524cca7f..8b554a9b1 100644 --- a/src/simsopt/geo/curvexyzfourier.py +++ b/src/simsopt/geo/curvexyzfourier.py @@ -172,6 +172,8 @@ def set_dofs_impl(self, dofs): def as_dict(self) -> dict: d = {} + d["@module"] = self.__class__.__module__ + d["@class"] = self.__class__.__name__ d["quadpoints"] = list(self.quadpoints) d["order"] = self.order d["x0"] = list(self.local_full_x)