@@ -592,6 +592,8 @@ class VmecWOut(BaseModelWithNumpy):
592592 "DGeod" ,
593593 "raxis_cc" ,
594594 "zaxis_cs" ,
595+ "raxis_cs" ,
596+ "zaxis_cc" ,
595597 "version_" ,
596598 "bvco" ,
597599 "buco" ,
@@ -617,6 +619,15 @@ class VmecWOut(BaseModelWithNumpy):
617619 "bsupumnc" ,
618620 "bsupvmnc" ,
619621 "gmnc" ,
622+ "rmns" ,
623+ "zmnc" ,
624+ "gmns" ,
625+ "bmns" ,
626+ "bsubumns" ,
627+ "bsubvmns" ,
628+ "bsubsmnc" ,
629+ "bsupumns" ,
630+ "bsupvmns" ,
620631 "restart_reason_timetrace" ,
621632 ]
622633 """If quantities are not exactly the same in C++ WoutFileContents and this class,
@@ -661,7 +672,10 @@ class VmecWOut(BaseModelWithNumpy):
661672 ),
662673 pydantic .Field (alias = "lasym__logical__" ),
663674 ]
664- """Flag indicating non-stellarator-symmetry."""
675+ """Flag indicating non-stellarator-symmetry.
676+
677+ Non-stellarator symmetric fields are only populated if this is True.
678+ """
665679
666680 lfreeb : typing .Annotated [
667681 bool ,
@@ -859,76 +873,101 @@ class VmecWOut(BaseModelWithNumpy):
859873 grid."""
860874
861875 bmnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
862- """Fourier coefficients of the magnetic field strength ``|B|`` on the half-grid."""
876+ """Fourier coefficients (cos) of the magnetic field strength ``|B|`` on the half-
877+ grid."""
863878
864879 gmnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
865- r"""Fourier coefficients of the Jacobian :math:`\sqrt{g}` on the half-grid."""
880+ r"""Fourier coefficients (cos) of the Jacobian :math:`\sqrt{g}` on the half-grid."""
866881
867882 bsubumnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
868- r"""Fourier coefficients of the covariant magnetic field component
883+ r"""Fourier coefficients (cos) of the covariant magnetic field component
869884 :math:`B_{\theta}` on the half-grid."""
870885
871886 bsubvmnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
872- r"""Fourier coefficients of the covariant magnetic field component :math:`B_{\phi}`
873- on the half-grid."""
887+ r"""Fourier coefficients (cos) of the covariant magnetic field component
888+ :math:`B_{\phi}` on the half-grid."""
874889
875890 bsubsmns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
876- """Fourier coefficients of the covariant magnetic field component :math:`B_{s}` on
877- the full- grid."""
891+ """Fourier coefficients (sin) of the covariant magnetic field component
892+ :math:`B_{s}` on the full- grid."""
878893
879894 bsupumnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
880- r"""Fourier coefficients of the contravariant magnetic field component
895+ r"""Fourier coefficients (cos) of the contravariant magnetic field component
881896 :math:`B^{\theta}` on the half-grid."""
882897
883898 bsupvmnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ]
884- r"""Fourier coefficients of the contravariant magnetic field component
899+ r"""Fourier coefficients (cos) of the contravariant magnetic field component
885900 :math:`B^{\phi}` on the half-grid."""
886901
887902 rmnc : jt .Float [np .ndarray , "mn_mode n_surfaces" ]
888- """Fourier coefficients for ``R`` of the geometry of the flux surfaces on the full-
889- grid."""
903+ """Fourier coefficients (cos) for ``R`` of the geometry of the flux surfaces on the
904+ full- grid."""
890905
891906 zmns : jt .Float [np .ndarray , "mn_mode n_surfaces" ]
892- """Fourier coefficients for ``Z`` of the geometry of the flux surfaces on the full-
893- grid."""
907+ """Fourier coefficients (sin) for ``Z`` of the geometry of the flux surfaces on the
908+ full- grid."""
894909
895910 lmns : jt .Float [np .ndarray , "mn_mode n_surfaces" ]
896- """Fourier coefficients for ``lambda`` stream function on the half-grid."""
911+ """Fourier coefficients (sin) for ``lambda`` stream function on the half-grid."""
897912
898913 lmns_full : jt .Float [np .ndarray , "mn_mode n_surfaces" ]
899- """Fourier coefficients for ``lambda`` stream function on the full-grid.
914+ """Fourier coefficients (sin) for ``lambda`` stream function on the full-grid.
900915
901916 This quantity is VMEC++ specific and required for hot-restart to work properly. We
902917 store it with the Fortran convention for the order of the dimensions for consistency
903918 with lmns.
904919 """
905920
906921 rmns : jt .Float [np .ndarray , "mn_mode n_surfaces" ] | None = None
907- """Fourier coefficients for `R` ~ sin(m*theta - n*zeta) of the geometry of the flux surfaces on the full-grid.
908-
909- Only populated when lasym=True (non-stellarator-symmetric configurations).
910- """
922+ """Fourier coefficients (sin) for `R` of the geometry of the flux surfaces on the
923+ full-grid; non-stellarator-symmetric."""
911924
912925 zmnc : jt .Float [np .ndarray , "mn_mode n_surfaces" ] | None = None
913- """Fourier coefficients for `Z` ~ cos(m*theta - n*zeta) of the geometry of the flux surfaces on the full-grid.
914-
915- Only populated when lasym=True (non-stellarator-symmetric configurations).
916- """
926+ """Fourier coefficients (cos) for `Z` of the geometry of the flux surfaces on the
927+ full-grid; non-stellarator-symmetric."""
917928
918929 lmnc : jt .Float [np .ndarray , "mn_mode n_surfaces" ] | None = None
919- """Fourier coefficients for `lambda` ~ cos(m*theta - n*zeta) stream function on the half-grid.
920-
921- Only populated when lasym=True (non-stellarator-symmetric configurations).
922- """
930+ """Fourier coefficients (cos) for `lambda` stream function on the half-grid; non-
931+ stellarator-symmetric."""
923932
924933 lmnc_full : jt .Float [np .ndarray , "mn_mode n_surfaces" ] | None = None
925- """Fourier coefficients for `lambda` ~ cos(m*theta - n*zeta) stream function on the full-grid.
934+ """Fourier coefficients (cos) for `lambda` stream function on the full-grid; non-
935+ stellarator-symmetric.
926936
927937 This quantity is VMEC++ specific and required for hot-restart to work properly. We
928938 store it with the Fortran convention for the order of the dimensions for consistency
929- with lmnc. Only populated when lasym=True (non-stellarator-symmetric configurations).
939+ with lmnc. Only populated when lasym=True (non-stellarator-symmetric
940+ configurations).
930941 """
931942
943+ gmns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
944+ r"""Fourier coefficients (sin) of the Jacobian :math:`\sqrt{g}` on the half-grid;
945+ non-stellarator-symmetric."""
946+
947+ bmns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
948+ """Fourier coefficients (sin) of the magnetic field strength ``|B|`` on the half-
949+ grid; non-stellarator-symmetric."""
950+
951+ bsubumns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
952+ r"""Fourier coefficients (sin) of the covariant magnetic field component
953+ :math:`B_{\theta}` on the half-grid; non-stellarator-symmetric."""
954+
955+ bsubvmns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
956+ r"""Fourier coefficients (sin) of the covariant magnetic field component
957+ :math:`B_{\phi}` on the half-grid; non-stellarator-symmetric."""
958+
959+ bsubsmnc : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
960+ """Fourier coefficients (cos) of the covariant magnetic field component
961+ :math:`B_{s}` on the full- grid; non-stellarator-symmetric."""
962+
963+ bsupumns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
964+ r"""Fourier coefficients (sin) of the contravariant magnetic field component
965+ :math:`B^{\theta}` on the half-grid; non-stellarator-symmetric."""
966+
967+ bsupvmns : jt .Float [np .ndarray , "mn_mode_nyq n_surfaces" ] | None = None
968+ r"""Fourier coefficients (sin) of the contravariant magnetic field component
969+ :math:`B^{\phi}` on the half-grid; non-stellarator-symmetric."""
970+
932971 pcurr_type : ProfileType
933972 """Parametrization of toroidal current profile (copied from input)."""
934973
@@ -1017,6 +1056,16 @@ class VmecWOut(BaseModelWithNumpy):
10171056 zaxis_cs : jt .Float [np .ndarray , "ntor_plus_1" ]
10181057 """Fourier coefficients of :math:`Z(phi)` of the magnetic axis geometry."""
10191058
1059+ # In the C++ WOutFileContents this is called raxis_s.
1060+ raxis_cs : jt .Float [np .ndarray , "ntor_plus_1" ] | None = None
1061+ """Fourier coefficients of :math:`R(phi)` of the magnetic axis geometry; non-
1062+ stellarator-symmetric."""
1063+
1064+ # In the C++ WOutFileContents this is called zaxis_c.
1065+ zaxis_cc : jt .Float [np .ndarray , "ntor_plus_1" ] | None = None
1066+ """Fourier coefficients of :math:`Z(phi)` of the magnetic axis geometry; non-
1067+ stellarator-symmetric."""
1068+
10201069 # In the C++ WOutFileContents this is called dVds.
10211070 vp : jt .Float [np .ndarray , "n_surfaces" ]
10221071 r"""Differential volume :math:`V' = \frac{\partial V}{\partial s}` on half-grid.
@@ -1329,24 +1378,37 @@ def _from_cpp_wout(cpp_wout: _vmecpp.VmecppWOut) -> VmecWOut:
13291378 attrs ["lmns_full" ] = cpp_wout .lmns_full .T
13301379
13311380 # Asymmetric attributes are transposed and only populated when lasym=True
1381+ # All of them are defaulted to None when lasym=False
13321382 if cpp_wout .lasym :
1383+ attrs ["raxis_cs" ] = cpp_wout .raxis_s
1384+ attrs ["zaxis_cc" ] = cpp_wout .zaxis_c
1385+
1386+ attrs ["bsubsmnc" ] = cpp_wout .bsubsmnc .T
13331387 attrs ["rmns" ] = cpp_wout .rmns .T
13341388 attrs ["zmnc" ] = cpp_wout .zmnc .T
13351389 attrs ["lmnc_full" ] = cpp_wout .lmnc_full .T
1336- else :
1337- attrs ["rmns" ] = None
1338- attrs ["zmnc" ] = None
1339- attrs ["lmnc_full" ] = None
1390+
1391+ attrs ["lmnc" ] = _pad_and_transpose (cpp_wout .lmnc , attrs ["mnmax" ])
1392+
1393+ attrs ["bmns" ] = _pad_and_transpose (cpp_wout .bmns , attrs ["mnmax_nyq" ])
1394+ attrs ["bsubumns" ] = _pad_and_transpose (
1395+ cpp_wout .bsubumns , attrs ["mnmax_nyq" ]
1396+ )
1397+ attrs ["bsubvmns" ] = _pad_and_transpose (
1398+ cpp_wout .bsubvmns , attrs ["mnmax_nyq" ]
1399+ )
1400+ attrs ["bsupumns" ] = _pad_and_transpose (
1401+ cpp_wout .bsupumns , attrs ["mnmax_nyq" ]
1402+ )
1403+ attrs ["bsupvmns" ] = _pad_and_transpose (
1404+ cpp_wout .bsupvmns , attrs ["mnmax_nyq" ]
1405+ )
1406+ attrs ["gmns" ] = _pad_and_transpose (cpp_wout .gmns , attrs ["mnmax_nyq" ])
13401407
13411408 # These attributes have one column less and their elements are transposed
13421409 # in VMEC++ with respect to SIMSOPT/VMEC2000
13431410 attrs ["lmns" ] = _pad_and_transpose (cpp_wout .lmns , attrs ["mnmax" ])
13441411
1345- # Asymmetric lambda coefficients on half-grid (only when lasym=True)
1346- if cpp_wout .lasym :
1347- attrs ["lmnc" ] = _pad_and_transpose (cpp_wout .lmnc , attrs ["mnmax" ])
1348- else :
1349- attrs ["lmnc" ] = None
13501412 attrs ["bmnc" ] = _pad_and_transpose (cpp_wout .bmnc , attrs ["mnmax_nyq" ])
13511413 attrs ["bsubumnc" ] = _pad_and_transpose (cpp_wout .bsubumnc , attrs ["mnmax_nyq" ])
13521414 attrs ["bsubvmnc" ] = _pad_and_transpose (cpp_wout .bsubvmnc , attrs ["mnmax_nyq" ])
@@ -1453,30 +1515,49 @@ def _to_cpp_wout(self) -> _vmecpp.WOutFileContents:
14531515 # stored in a wout file for consistency with lmns.
14541516 cpp_wout .lmns_full = self .lmns_full .T
14551517
1518+ if self .lasym :
1519+ cpp_wout .raxis_s = self .raxis_cs
1520+ cpp_wout .zaxis_c = self .zaxis_cc
14561521 # Asymmetric attributes are transposed and only set when lasym=True
1457- if self .lasym and self .rmns is not None :
1458- cpp_wout .rmns = self .rmns .T
1459- if self .lasym and self .zmnc is not None :
1460- cpp_wout .zmnc = self .zmnc .T
1461- if self .lasym and self .lmnc_full is not None :
1462- cpp_wout .lmnc_full = self .lmnc_full .T
1522+ for field in [
1523+ "bsubsmnc" ,
1524+ "rmns" ,
1525+ "zmnc" ,
1526+ "lmnc_full" ,
1527+ "lmnc" ,
1528+ "bmns" ,
1529+ ]:
1530+ value = getattr (self , field )
1531+ if value is not None :
1532+ setattr (cpp_wout , field , value .T )
14631533
14641534 # This is a VMEC++ only quantity
14651535 cpp_wout .restart_reasons = self .restart_reason_timetrace
14661536
1537+ # coefficients on half-grid
14671538 # These attributes have one column less and their elements are transposed
14681539 # in VMEC++ with respect to SIMSOPT/VMEC2000
1469- cpp_wout .lmns = self .lmns .T [1 :, :]
1470-
1471- # Asymmetric lambda coefficients on half-grid (only when lasym=True)
1472- if self .lasym and self .lmnc is not None :
1473- cpp_wout .lmnc = self .lmnc .T [1 :, :]
1474- cpp_wout .bmnc = self .bmnc .T [1 :, :]
1475- cpp_wout .bsubumnc = self .bsubumnc .T [1 :, :]
1476- cpp_wout .bsubvmnc = self .bsubvmnc .T [1 :, :]
1477- cpp_wout .bsupumnc = self .bsupumnc .T [1 :, :]
1478- cpp_wout .bsupvmnc = self .bsupvmnc .T [1 :, :]
1479- cpp_wout .gmnc = self .gmnc .T [1 :, :]
1540+ for field in [
1541+ "lmns" ,
1542+ "gmnc" ,
1543+ "bmnc" ,
1544+ "bsubumnc" ,
1545+ "bsubvmnc" ,
1546+ "bsupumnc" ,
1547+ "bsupvmnc" ,
1548+ # Asymmetric coefficients (only when lasym=True)
1549+ "lmnc" ,
1550+ "gmns" ,
1551+ "bmns" ,
1552+ "bsubumns" ,
1553+ "bsubvmns" ,
1554+ "bsupumns" ,
1555+ "bsupvmns" ,
1556+ ]:
1557+ value = getattr (self , field )
1558+ # Asymmetric coefficients may be None when lasym=False
1559+ if value is not None :
1560+ setattr (cpp_wout , field , value .T [1 :, :])
14801561
14811562 return cpp_wout
14821563
@@ -1511,10 +1592,12 @@ def from_wout_file(wout_filename: str | Path) -> VmecWOut:
15111592
15121593 # Special handling for variables only present in VMEC++
15131594 # For now, only special case for lambda coefficients: lambda = 0 is a physically meaningful fall-back value
1514- if "lmns_full" not in attrs :
1595+ if "lmns_full" not in attrs or "lmnc_full" not in attrs :
15151596 mnmax = attrs ["mnmax" ]
15161597 ns = attrs ["ns" ]
15171598 attrs ["lmns_full" ] = np .zeros ([mnmax , ns ])
1599+ if attrs ["lasym__logical__" ]:
1600+ attrs ["lmnc_full" ] = np .zeros ([mnmax , ns ])
15181601
15191602 return VmecWOut .model_validate (attrs , by_alias = True )
15201603
@@ -1928,8 +2011,10 @@ def ensure_vmec2000_input(input_path: Path) -> Generator[Path, None, None]:
19282011
19292012
19302013def _pad_and_transpose (
1931- arr : jt .Float [np .ndarray , "ns_minus_one mn" ], mnsize : int
1932- ) -> jt .Float [np .ndarray , "mn ns" ]:
2014+ arr : jt .Float [np .ndarray , "ns_minus_one mn" ] | None , mnsize : int
2015+ ) -> jt .Float [np .ndarray , "mn ns" ] | None :
2016+ if arr is None :
2017+ return None
19332018 stacked = np .vstack ((np .zeros (mnsize ), arr )).T
19342019 assert stacked .shape [1 ] == arr .shape [0 ] + 1
19352020 assert stacked .shape [0 ] == arr .shape [1 ]
0 commit comments