diff --git a/docs/source/optimizable.rst b/docs/source/optimizable.rst index b0f1cbaf3..1308808e9 100644 --- a/docs/source/optimizable.rst +++ b/docs/source/optimizable.rst @@ -167,6 +167,8 @@ 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.local_fix_all()`, +:obj:`~simsopt._core.optimizable.Optimizable.local_unfix_all()`, :obj:`~simsopt._core.optimizable.Optimizable.fix_all()`, and :obj:`~simsopt._core.optimizable.Optimizable.unfix_all()`:: @@ -671,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 the ``save`` function implemented in simsopt. + +.. code-block:: + + from simsopt import save + + curves = [CurveRZFourier(...), CurveXYZFourier(...), CurveHelical(...), ...] + save(curves, 'curves.json') + +To load the geometric objects from the saved json file, use the ``load`` function. + +.. code-block:: + + from simsopt import load + + curves = load('curves.json') 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..90836f87f --- /dev/null +++ b/examples/1_Simple/inputs/biot_savart_opt.json @@ -0,0 +1,7286 @@ +{ + "@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/examples/1_Simple/surf_vol_area.py b/examples/1_Simple/surf_vol_area.py index 31215747c..f96a4011d 100755 --- a/examples/1_Simple/surf_vol_area.py +++ b/examples/1_Simple/surf_vol_area.py @@ -3,9 +3,10 @@ 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, save """ 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/surf_vol_area.py") @@ -23,36 +24,43 @@ # 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", indent=2) + +# Load the saved surface, and redo optimization using central difference scheme +surf1 = load("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()) +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_fieldline.py b/examples/1_Simple/tracing_fieldlines_NCSX.py similarity index 88% rename from examples/1_Simple/tracing_fieldline.py rename to examples/1_Simple/tracing_fieldlines_NCSX.py index 9edc0ccf5..1f9a08819 100755 --- a/examples/1_Simple/tracing_fieldline.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..519c19caa --- /dev/null +++ b/examples/1_Simple/tracing_fieldlines_QA.py @@ -0,0 +1,137 @@ +#!/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 the optimized coils from stage_two_optimization.py: +coils_filename = Path(__file__).parent / "inputs" / "biot_savart_opt.json" +bs = simsopt.load(coils_filename) + +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/2_Intermediate/stage_two_optimization.py b/examples/2_Intermediate/stage_two_optimization.py index 3a0c44708..3eeb2bb0a 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") 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 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 diff --git a/src/simsopt/__init__.py b/src/simsopt/__init__.py index c2b5e5cee..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 +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 583c418af..ecea44c3a 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, save diff --git a/src/simsopt/_core/optimizable.py b/src/simsopt/_core/optimizable.py index 6fd6f85f6..9989ced70 100644 --- a/src/simsopt/_core/optimizable.py +++ b/src/simsopt/_core/optimizable.py @@ -9,18 +9,20 @@ 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 +from typing import Union, Tuple, Dict, Callable, Sequence, List 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, MontyEncoder +from monty.io import zopen from ..util.dev import SimsoptRequires from ..util.types import RealArray, StrArray, BoolArray, Key @@ -308,6 +310,10 @@ def lower_bounds(self) -> RealArray: """ return self._lb[self._free] + @property + def full_lower_bounds(self) -> RealArray: + return self._lb + @lower_bounds.setter def lower_bounds(self, lower_bounds: RealArray) -> None: """ @@ -330,6 +336,10 @@ def upper_bounds(self) -> RealArray: """ return self._ub[self._free] + @property + def full_upper_bounds(self) -> RealArray: + return self._ub + @upper_bounds.setter def upper_bounds(self, upper_bounds: RealArray) -> None: """ @@ -352,6 +362,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 @@ -410,7 +424,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. @@ -532,7 +546,7 @@ def __init__(self, self._id = ImmutableId(next(self.__class__._ids)) self.name = self.__class__.__name__ + str(self._id.id) hash_str = hashlib.sha256(self.name.encode('utf-8')).hexdigest() - self.hash = int(hash_str, 16) % 10**32 # 32 digit int as hash + self._hash = int(hash_str, 16) % 10**32 # 32 digit int as hash self._children = set() # This gets populated when the object is passed # as argument to another Optimizable object self.return_fns = WeakKeyDefaultDict(list) # Store return fn's required by each child @@ -579,13 +593,14 @@ 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 def __hash__(self) -> int: - return self.hash + return self._hash def __eq__(self, other: Optimizable) -> bool: """ @@ -1053,6 +1068,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: """ @@ -1070,6 +1089,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 @@ -1273,6 +1296,107 @@ 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 + + @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): + filename = filename or "" + fmt = "" if fmt is None else fmt.lower() + fname = Path(filename).name + + 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: + f.write(s) + return s + else: + raise ValueError(f"Invalid format: `{str(fmt)}`") + + @classmethod + def from_str(cls, input_str: str, fmt="json"): + fmt_low = fmt.lower() + if fmt_low == "json": + return 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 load(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 save(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 + if "indent" not in kwargs: + kwargs["indent"] = 2 + return json.dump(simsopt_objects, fp, *args, **kwargs) + def make_optimizable(func, *args, dof_indicators=None, **kwargs): """ @@ -1442,6 +1566,19 @@ def dJ(self): # Next line uses __rmul__ function for the Derivative class return float(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.as_dict() + return d + + @classmethod + def from_dict(cls, d): + opt = MontyDecoder().process_decoded(d["opt"]) + return cls(d["factor"], opt) + class OptimizableSum(Optimizable): """ @@ -1467,3 +1604,21 @@ 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"] = [] + for opt in self.opts: + d["opts"].append(opt.as_dict()) + return d + + @classmethod + def from_dict(cls, d): + opts = [] + decoder = MontyDecoder() + for odict in d["opts"]: + opts.append(decoder.process_decoded(odict)) + return cls(opts) + diff --git a/src/simsopt/field/biotsavart.py b/src/simsopt/field/biotsavart.py index a8235f036..39460743a 100644 --- a/src/simsopt/field/biotsavart.py +++ b/src/simsopt/field/biotsavart.py @@ -1,4 +1,7 @@ +import json import numpy as np +from monty.json import MSONable, MontyDecoder, MontyEncoder + import simsoptpp as sopp from .magneticfield import MagneticField @@ -19,14 +22,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 +39,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 +49,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 +65,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 +103,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 +120,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 +130,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 +140,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 +156,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 +194,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 +207,17 @@ 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: + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + coils = decoder.process_decoded(d["coils"]) + bs = cls(coils) + xyz = decoder.process_decoded(d["points"]) + bs.set_points_cart(xyz) + return bs diff --git a/src/simsopt/field/coil.py b/src/simsopt/field/coil.py index 383c19a57..cce69b64b 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, MSONable class Coil(sopp.Coil, Optimizable): @@ -15,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]) @@ -34,6 +35,16 @@ def plot(self, **kwargs): """ return self.curve.plot(**kwargs) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + current = decoder.process_decoded(d["current"]) + curve = decoder.process_decoded(d["curve"]) + return cls(curve, current) + class CurrentBase(Optimizable): @@ -72,14 +83,25 @@ 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}) + 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(d["current"]) + class ScaledCurrent(sopp.CurrentBase, CurrentBase): """ @@ -87,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) @@ -99,6 +122,15 @@ def vjp(self, v_current): def get_value(self): return self.scale * self.current_to_scale.get_value() + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @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): """ @@ -117,6 +149,16 @@ def vjp(self, v_current): def get_value(self): return self.current_a.get_value() + self.current_b.get_value() + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + 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/magneticfield.py b/src/simsopt/field/magneticfield.py index fa2756178..9d8995319 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,20 @@ 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: + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + 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): """ @@ -160,3 +175,22 @@ 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: + d = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + Bfields = [] + for field in d["Bfields"]: + Bfields.append(decoder.process_decoded(field)) + 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 b33ebad0c..0a1ec6697 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, MontyDecoder 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__) @@ -106,6 +109,19 @@ 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 = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + field = cls(d["R0"], d["B0"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field + class PoloidalField(MagneticField): ''' @@ -201,6 +217,19 @@ 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 = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + 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): """ @@ -287,6 +316,19 @@ 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 = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + field = cls(d["phi_str"]) + decoder = MontyDecoder() + xyz = decoder.process_decoded(d["points"]) + field.set_points_cart(xyz) + return field + class CircularCoil(MagneticField): ''' @@ -438,6 +480,23 @@ 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["r0"] = self.r0 + 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): + 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): """ @@ -470,6 +529,22 @@ 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["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): + 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): ''' @@ -502,6 +577,19 @@ 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 = MSONable.as_dict(self) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d): + 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): pass diff --git a/src/simsopt/geo/boozersurface.py b/src/simsopt/geo/boozersurface.py index 3bfe64b5b..556768e7c 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.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.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 = decoder.process_decoded(d["surface"]) + return cls(bs, surf, d["label"], d["targetlabel"]) 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): """ diff --git a/src/simsopt/geo/curvehelical.py b/src/simsopt/geo/curvehelical.py index 99e662896..3c7bb7a95 100644 --- a/src/simsopt/geo/curvehelical.py +++ b/src/simsopt/geo/curvehelical.py @@ -59,3 +59,23 @@ 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"] = 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): + curve = cls(d["quadpoints"], d["order"], d["n0"], d["l0"], d["R0"], + d["r0"]) + curve.local_full_x = d["x0"] + return curve diff --git a/src/simsopt/geo/curveobjectives.py b/src/simsopt/geo/curveobjectives.py index a0159bfa1..49eee7e1a 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} @@ -388,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 @@ -400,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): @@ -420,6 +469,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 +518,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/geo/curveperturbed.py b/src/simsopt/geo/curveperturbed.py index 743f9d34a..9c0b9ac0e 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 @@ -89,25 +93,28 @@ class PerturbationSample(): 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 - self.resample() + self.randomgen = randomgen # If not None, most likely fail with serialization + 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): @@ -186,3 +193,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..6cbb2cce8 100644 --- a/src/simsopt/geo/curverzfourier.py +++ b/src/simsopt/geo/curverzfourier.py @@ -47,3 +47,23 @@ def set_dofs(self, dofs): """ self.local_x = dofs sopp.CurveRZFourier.set_dofs(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["nfp"] = self.nfp + d["stellsym"] = self.stellsym + d["x0"] = list(self.local_full_x) + return d + + @classmethod + def from_dict(cls, d): + 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 3fda885db..8b554a9b1 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 @@ -96,6 +97,21 @@ def load_curves_from_file(filename, order=None, ppp=20, delimiter=','): coils[ic].local_x = np.concatenate(dofs) return coils + 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) + return d + + @classmethod + def from_dict(cls, d): + curve = cls(d["quadpoints"], d["order"]) + curve.local_full_x = d["x0"] + return curve + def jaxfouriercurve_pure(dofs, quadpoints, order): k = len(dofs)//3 @@ -153,3 +169,18 @@ def set_dofs_impl(self, dofs): counter += 1 self.coefficients[i][2*j] = dofs[counter] counter += 1 + + 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) + return d + + @classmethod + def from_dict(cls, d): + curve = cls(d["quadpoints"], d["order"]) + curve.local_full_x = d["x0"] + return curve diff --git a/src/simsopt/geo/qfmsurface.py b/src/simsopt/geo/qfmsurface.py index 61b2ea9ab..2ee68fc4c 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 diff --git a/src/simsopt/geo/surface.py b/src/simsopt/geo/surface.py index 8ce67eb09..9c015b07a 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 @@ -508,6 +508,13 @@ def interpolate_on_arclength_grid(self, function, theta_evaluate): return function_interpolated + def as_dict(self) -> dict: + d = super().as_dict() + 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): """ @@ -648,6 +655,16 @@ def update_fixed(self): else: self.fix(j) + def as_dict(self) -> dict: + return MSONable.as_dict(self) + + @classmethod + def from_dict(cls, d): + 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/src/simsopt/geo/surfacegarabedian.py b/src/simsopt/geo/surfacegarabedian.py index 223bf2d6a..9d130c718 100644 --- a/src/simsopt/geo/surfacegarabedian.py +++ b/src/simsopt/geo/surfacegarabedian.py @@ -242,6 +242,23 @@ def volume(self): self.area_volume() return self._volume + def as_dict(self) -> dict: + d = super().as_dict() + d["mmax"] = self.mmax + d["mmin"] = self.mmin + d["nmax"] = self.nmax + d["nmin"] = self.nmin + return d + + @classmethod + def from_dict(cls, d): + 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, 'aspect-ratio': Surface.aspect_ratio} diff --git a/src/simsopt/geo/surfacehenneberg.py b/src/simsopt/geo/surfacehenneberg.py index 863733c53..d3e667192 100644 --- a/src/simsopt/geo/surfacehenneberg.py +++ b/src/simsopt/geo/surfacehenneberg.py @@ -740,4 +740,19 @@ 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 = super().as_dict() + d["alpha_fac"] = self.alpha_fac + d["mmax"] = self.mmax + d["nmax"] = self.nmax + return d + + @classmethod + def from_dict(cls, d): + 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/src/simsopt/geo/surfacerzfourier.py b/src/simsopt/geo/surfacerzfourier.py index f693b6449..2ce7e2b16 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 @@ -579,6 +580,22 @@ def write_nml(self, filename: str): with open(filename, 'w') as f: f.write(self.get_nml()) + def as_dict(self) -> dict: + d = super().as_dict() + d["stellsym"] = self.stellsym + d["mpol"] = self.mpol + d["ntor"] = self.ntor + return d + + @classmethod + def from_dict(cls, d): + 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.local_full_x = d["x0"] + return surf + return_fn_map = {'area': sopp.SurfaceRZFourier.area, 'volume': sopp.SurfaceRZFourier.volume, 'aspect-ratio': Surface.aspect_ratio} @@ -826,3 +843,13 @@ def change_resolution(self, mpol, ntor): r_shift=self.r_shift, a_scale=self.a_scale) return surf3 + + 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"]) + surf.local_full_x = d["x0"] + return surf diff --git a/src/simsopt/geo/surfacexyzfourier.py b/src/simsopt/geo/surfacexyzfourier.py index bcc040c20..4e2837c39 100644 --- a/src/simsopt/geo/surfacexyzfourier.py +++ b/src/simsopt/geo/surfacexyzfourier.py @@ -112,6 +112,22 @@ def to_RZFourier(self): surf.least_squares_fit(gamma) return surf + def as_dict(self) -> dict: + d = super().as_dict() + d["mpol"] = self.mpol + d["ntor"] = self.ntor + d["stellsym"] = self.stellsym + return d + + @classmethod + def from_dict(cls, d): + 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, 'aspect-ratio': Surface.aspect_ratio} diff --git a/src/simsopt/geo/surfacexyztensorfourier.py b/src/simsopt/geo/surfacexyztensorfourier.py index 90b2f4e9a..b097ec05e 100644 --- a/src/simsopt/geo/surfacexyztensorfourier.py +++ b/src/simsopt/geo/surfacexyztensorfourier.py @@ -160,3 +160,22 @@ 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 = super().as_dict() + 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): + 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 + diff --git a/src/simsopt/objectives/fluxobjective.py b/src/simsopt/objectives/fluxobjective.py index 8225cff44..eb6ebc591 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,14 @@ 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) + + @classmethod + def from_dict(cls, d): + decoder = MontyDecoder() + surface = decoder.process_decoded(d["surface"]) + field = decoder.process_decoded(d["field"]) + target = decoder.process_decoded(d["target"]) + return cls(surface, field, target) diff --git a/src/simsopt/objectives/functions.py b/src/simsopt/objectives/functions.py index 5991a9f0e..a7ede8ea1 100644 --- a/src/simsopt/objectives/functions.py +++ b/src/simsopt/objectives/functions.py @@ -20,7 +20,7 @@ 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. @@ -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): """ @@ -83,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) @@ -93,6 +109,16 @@ def df(self): """ return self.dJ() + def as_dict(self) -> dict: + d = super().as_dict() + 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} @@ -174,6 +200,17 @@ 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["b"] = self._sqrtb * self._sqrtb + d["x"] = self.get("x") + d["y"] = self.get("y") + return d + + @classmethod + def from_dict(cls, d): + return cls(d["b"], d["x"], d["y"]) + class TestObject1(Optimizable): """ @@ -188,16 +225,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} @@ -214,6 +253,14 @@ 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["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): """ @@ -328,9 +375,9 @@ class Beale(Optimizable): https://en.wikipedia.org/wiki/Test_functions_for_optimization """ - def __init__(self): - x = np.zeros(2) - super().__init__(x0=x) + def __init__(self, x0=None, **kwargs): + x = np.zeros(2) if not x0 else x0 + super().__init__(x0=x, **kwargs) def J(self): x = self.local_full_x[0] @@ -338,3 +385,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]) + diff --git a/src/simsopt/objectives/least_squares.py b/src/simsopt/objectives/least_squares.py index 0ceaf723e..0cd7988ff 100644 --- a/src/simsopt/objectives/least_squares.py +++ b/src/simsopt/objectives/least_squares.py @@ -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 49fa535d0..bd165849f 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,19 @@ def dJ(self): dval = self.obj.dJ(partials=True) return np.maximum(val-self.threshold, 0)*dval + 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 + + @classmethod + def from_dict(cls, d): + 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/src/simsopt/solve/graph_mpi.py b/src/simsopt/solve/graph_mpi.py index bc432de34..d603975c2 100644 --- a/src/simsopt/solve/graph_mpi.py +++ b/src/simsopt/solve/graph_mpi.py @@ -1,6 +1,5 @@ from .mpi import * - import warnings warnings.warn("Importing of simsopt.solve.graph_mpi is deprecated. " diff --git a/src/simsoptpp/python_surfaces.cpp b/src/simsoptpp/python_surfaces.cpp index 7d996190d..ead1069e8 100644 --- a/src/simsoptpp/python_surfaces.cpp +++ b/src/simsoptpp/python_surfaces.cpp @@ -200,6 +200,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); } 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: diff --git a/tests/core/test_graph_dofs.py b/tests/core/test_graph_dofs.py deleted file mode 100755 index 4c5528d7a..000000000 --- a/tests/core/test_graph_dofs.py +++ /dev/null @@ -1,303 +0,0 @@ -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.util import DofLengthMismatchError - - -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.rosenbrock_dofs = Rosenbrock()._dofs - - def tearDown(self) -> None: - self.identity_dofs = None - self.adder_dofs = None - self.rosenbrock_dofs = None - - def test_init(self): - # Create an empty dof and check all the methods - empty_dof = DOFs() - empty_dof.fix_all() - empty_dof.unfix_all() - self.assertFalse(empty_dof.any_free()) - self.assertFalse(empty_dof.any_fixed()) - self.assertTrue(empty_dof.all_free()) - self.assertTrue(empty_dof.all_fixed()) - empty_dof.x = [] # This statement working is what is desired - self.assertTrue(len(empty_dof) == 0) - self.assertTrue(empty_dof.reduced_len == 0) - - def test_fix(self): - # TODO: This test uses too many methods of DOFs. - self.adder_dofs.fix("x") - self.assertTrue(self.adder_dofs.any_fixed()) - self.assertFalse(self.adder_dofs.all_free()) - self.assertEqual(self.adder_dofs.reduced_len, 2) - with self.assertRaises(DofLengthMismatchError): - self.adder_dofs.x = np.array([4, 5, 6]) - - self.adder_dofs.fix("x") - self.assertEqual(self.adder_dofs.reduced_len, 2) - - self.adder_dofs.fix("y") - self.assertEqual(self.adder_dofs.reduced_len, 1) - - def test_fix_all(self): - self.identity_dofs.fix_all() - self.adder_dofs.fix_all() - self.rosenbrock_dofs.fix_all() - - self.assertEqual(self.identity_dofs.reduced_len, 0) - self.assertEqual(self.adder_dofs.reduced_len, 0) - self.assertEqual(self.rosenbrock_dofs.reduced_len, 0) - - def test_unfix(self): - self.adder_dofs.fix("x") - self.adder_dofs.fix("y") - self.rosenbrock_dofs.fix("x") - self.assertEqual(self.adder_dofs.reduced_len, 1) - self.assertEqual(self.rosenbrock_dofs.reduced_len, 1) - - self.adder_dofs.unfix("x") - self.assertEqual(self.adder_dofs.reduced_len, 2) - self.adder_dofs.unfix("x") - self.assertEqual(self.adder_dofs.reduced_len, 2) - self.rosenbrock_dofs.unfix("y") - self.assertEqual(self.rosenbrock_dofs.reduced_len, 1) - - def test_unfix_all(self): - self.adder_dofs.fix("x") - self.rosenbrock_dofs.fix("x") - - self.adder_dofs.unfix_all() - self.rosenbrock_dofs.unfix_all() - - self.assertEqual(self.identity_dofs.reduced_len, 1) - self.assertEqual(self.adder_dofs.reduced_len, 3) - self.assertEqual(self.rosenbrock_dofs.reduced_len, 2) - - def test_any_free(self): - fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([False, False, False])) - self.assertFalse(fixed_dofs.any_free()) - one_fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertTrue(one_fixed_dofs.any_free()) - - def test_any_fixed(self): - free_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, True])) - self.assertFalse(free_dofs.any_fixed()) - one_fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertTrue(one_fixed_dofs.any_fixed()) - - def test_all_free(self): - free_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, True])) - self.assertTrue(free_dofs.all_free()) - one_fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertFalse(one_fixed_dofs.all_free()) - - def test_all_fixed(self): - fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([False, False, False])) - self.assertTrue(fixed_dofs.all_fixed()) - one_fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertFalse(one_fixed_dofs.all_fixed()) - - def test_x(self): - # Test the getter - fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([False, False, False])) - free_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, True])) - one_fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertTrue(np.allclose(fixed_dofs.x, np.array([]))) - self.assertTrue(np.allclose(free_dofs.x, np.array([1, 2, 3]))) - self.assertTrue(np.allclose(one_fixed_dofs.x, np.array([1, 2]))) - - # Test the setter - - # Use full array size - with self.assertRaises(DofLengthMismatchError): - fixed_dofs.x = np.array([4, 5, 6]) - with self.assertRaises(DofLengthMismatchError): - one_fixed_dofs.x = np.array([4, 5, 6]) - - free_dofs.x = np.array([4, 5, 6]) - self.assertTrue(np.allclose(free_dofs.x, np.array([4, 5, 6]))) - one_fixed_dofs.x = np.array([4, 5]) - self.assertTrue(np.allclose(one_fixed_dofs.full_x, np.array([4, 5, 3]))) - - def test_full_x(self): - fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([False, False, False])) - free_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, True])) - one_fixed_dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - output = np.array([1, 2, 3]) - self.assertTrue(np.allclose(fixed_dofs.full_x, output)) - self.assertTrue(np.allclose(free_dofs.full_x, output)) - self.assertTrue(np.allclose(one_fixed_dofs.full_x, output)) - - def test_lower_bounds(self): - dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertTrue(np.allclose(dofs.lower_bounds, - np.array([np.NINF, np.NINF]))) - - with self.assertRaises(DofLengthMismatchError): - dofs.lower_bounds = np.array([-1000.0, -1001.0, -1002.0]) - with self.assertRaises(DofLengthMismatchError): - dofs.lower_bounds = np.array([-1000.0]) - dofs.lower_bounds = np.array([-1000.0, -1001.0]) - self.assertTrue(np.allclose(dofs.lower_bounds, - np.array([-1000.0, -1001.0]))) - - dofs.unfix_all() - self.assertTrue(np.allclose(dofs.lower_bounds, - np.array([-1000.0, -1001.0, np.NINF]))) - - dofs.lower_bounds = np.array([-1000.0, -1001.0, -1002.]) - self.assertTrue(np.allclose(dofs.lower_bounds, - np.array([-1000.0, -1001.0, -1002.0]))) - - with self.assertRaises(DofLengthMismatchError): - dofs.lower_bounds = np.array([-1000.0]) - with self.assertRaises(DofLengthMismatchError): - dofs.lower_bounds = np.array([-1000.0, -1001.0]) - - def test_upper_bounds(self): - dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False])) - self.assertTrue(np.allclose(dofs.upper_bounds, - np.array([np.inf, np.inf]))) - - with self.assertRaises(DofLengthMismatchError): - dofs.upper_bounds = np.array([1000.0, 1001.0, 1002.0]) - with self.assertRaises(DofLengthMismatchError): - dofs.upper_bounds = np.array([1000.0]) - dofs.upper_bounds = np.array([1000.0, 1001.0]) - self.assertTrue(np.allclose(dofs.upper_bounds, - np.array([1000.0, 1001.0]))) - - dofs.unfix_all() - self.assertTrue(np.allclose(dofs.upper_bounds, - np.array([1000.0, 1001.0, np.inf]))) - - dofs.upper_bounds = np.array([1000.0, 1001.0, 1002.]) - self.assertTrue(np.allclose(dofs.upper_bounds, - np.array([1000.0, 1001.0, 1002.0]))) - - with self.assertRaises(DofLengthMismatchError): - dofs.upper_bounds = np.array([1000.0]) - with self.assertRaises(DofLengthMismatchError): - dofs.upper_bounds = np.array([1000.0, 1001.0]) - - def test_bounds(self): - dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False]), - lower_bounds=np.array([-100.0, -101.0, -102.0]), - upper_bounds=np.array([100.0, 101.0, 102.0])) - bounds = dofs.bounds - self.assertTrue(np.allclose(bounds[0], - np.array([-100.0, -101.0])) - and np.allclose(bounds[1], - np.array([100.0, 101.0]))) - - def test_update_upper_bound(self): - dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False]), - lower_bounds=np.array([-100.0, -101.0, -102.0]), - upper_bounds=np.array([100.0, 101.0, 102.0])) - dofs.update_upper_bound("x", 200) - self.assertTrue(np.allclose(dofs.upper_bounds, - np.array([200.0, 101.0]))) - - # Test with integer keys - - def test_update_lower_bound(self): - dofs = DOFs(x=np.array([1, 2, 3]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False]), - lower_bounds=np.array([-100.0, -101.0, -102.0]), - upper_bounds=np.array([100.0, 101.0, 102.0])) - dofs.update_lower_bound("x", -200) - self.assertTrue(np.allclose(dofs.lower_bounds, - np.array([-200.0, -101.0]))) - # Test with integer keys - - def test_update_bounds(self): - pass - - def test_names(self): - dofs = DOFs(x=np.array([1.0, 2.0, 3.0]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False]), - lower_bounds=np.array([-100.0, -101.0, -102.0]), - upper_bounds=np.array([100.0, 101.0, 102.0])) - self.assertTrue('x' in dofs.names) - self.assertTrue('y' in dofs.names) - self.assertFalse('z' in dofs.names) - dofs.fix_all() - self.assertTrue(len(dofs.names) == 0) - dofs.unfix_all() - self.assertTrue('x' in dofs.names) - self.assertTrue('y' in dofs.names) - self.assertTrue('z' in dofs.names) - dofs.fix('y') - self.assertTrue('x' in dofs.names) - self.assertFalse('y' in dofs.names) - self.assertTrue('z' in dofs.names) - - def test_full_names(self): - dofs = DOFs(x=np.array([1.0, 2.0, 3.0]), - names=np.array(['x', 'y', 'z']), - free=np.array([True, True, False]), - lower_bounds=np.array([-100.0, -101.0, -102.0]), - upper_bounds=np.array([100.0, 101.0, 102.0])) - self.assertTrue('x' in dofs.full_names) - self.assertTrue('y' in dofs.full_names) - self.assertTrue('z' in dofs.full_names) - dofs.fix_all() - self.assertTrue(len(dofs.full_names) == 3) - self.assertTrue('x' in dofs.full_names) - self.assertTrue('y' in dofs.full_names) - self.assertTrue('z' in dofs.full_names) - dofs.unfix_all() - self.assertTrue(len(dofs.full_names) == 3) - dofs.fix('y') - self.assertTrue('x' in dofs.full_names) - self.assertTrue('y' in dofs.full_names) - self.assertTrue('z' in dofs.full_names) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/core/test_graph_optimizable.py b/tests/core/test_graph_optimizable.py index 03a65b2a4..da8e91f44 100755 --- a/tests/core/test_graph_optimizable.py +++ b/tests/core/test_graph_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.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): @@ -23,6 +27,22 @@ 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 +56,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 +1101,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() diff --git a/tests/core/test_optimizable.py b/tests/core/test_optimizable.py index 82fca16ee..56ab7f647 100755 --- a/tests/core/test_optimizable.py +++ b/tests/core/test_optimizable.py @@ -1,10 +1,16 @@ 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._core.optimizable import Optimizable, make_optimizable, \ + ScaledOptimizable, OptimizableSum, load, save +from simsopt.objectives.functions import Identity, Rosenbrock, TestObject1, \ + TestObject2, Beale +from simsopt.objectives.functions import Adder as FAdder class Adder(Optimizable): @@ -23,6 +29,22 @@ 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 +58,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 @@ -1152,5 +1185,96 @@ 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_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) + 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()) + + 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()) + + def test_load_save(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([adder1, adder2], fpath, indent=2) + self.assertTrue(fpath.is_file()) + + adders = load(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 = Optimizable.from_str(adder_str1) + self.assertAlmostEqual(adder1.J(), adder1_str_regen1.J()) + + if __name__ == "__main__": unittest.main() diff --git a/tests/field/test_coil.py b/tests/field/test_coil.py index ceb352774..29b3f4706 100644 --- a/tests/field/test_coil.py +++ b/tests/field/test_coil.py @@ -1,9 +1,104 @@ import unittest +import json + import numpy as np -from simsopt.field.coil import Current, ScaledCurrent +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, CurrentSum +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 TestCurrentSerialization(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()) + + 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 CoilTesting(unittest.TestCase): +class ScaledCurrentTesting(unittest.TestCase): def test_scaled_current(self): one = np.asarray([1.]) diff --git a/tests/field/test_magneticfields.py b/tests/field/test_magneticfields.py index 2f7687f67..ac428008d 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,12 @@ 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) + 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 +99,17 @@ 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) + 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 +134,11 @@ 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) + 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 +207,12 @@ 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) + 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 +428,29 @@ 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) + 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) + self.assertTrue(np.allclose(B, Bfield_regen.B())) def test_BifieldMultiply(self): scalar = 1.2345 @@ -445,6 +484,10 @@ 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) + self.assertTrue(np.allclose(Bfield2.B(), Bfield_regen.B())) def test_Reiman(self): iota0 = 0.15 @@ -463,6 +506,10 @@ 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) + self.assertTrue(np.allclose(B1, Bfield_regen.B())) # Bfield analytical x = points[:, 0] y = points[:, 1] @@ -601,7 +648,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 diff --git a/tests/geo/test_curve.py b/tests/geo/test_curve.py index 1f878b09f..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 @@ -442,6 +445,21 @@ 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): + 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: + 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_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: diff --git a/tests/geo/test_curveperturbed.py b/tests/geo/test_curveperturbed.py index d0afa841a..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 @@ -133,3 +137,24 @@ 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): + 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())) diff --git a/tests/geo/test_surface.py b/tests/geo/test_surface.py index e6432b789..69ad8754b 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,19 @@ 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): 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() diff --git a/tests/geo/test_surfacehenneberg.py b/tests/geo/test_surfacehenneberg.py index 883d4abb3..e702c798d 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 @@ -112,7 +115,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.local_fix_all() surf.fixed_range(0, 1, False) np.testing.assert_equal(surf.local_dofs_free_status, [True, True, True, False, False, True, @@ -267,6 +270,26 @@ 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() diff --git a/tests/objectives/test_fluxobjective.py b/tests/objectives/test_fluxobjective.py index a4a43b91b..8c729aecf 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,33 @@ 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 diff --git a/tests/objectives/test_graph_least_squares.py b/tests/objectives/test_graph_least_squares.py old mode 100755 new mode 100644 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)