Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ v0.17.1

Bug Fixes

- Fixes the possible permission error in `from_input_file` method of `Equilibrium`, `FourierRZToroidalSurface` and `FourierRZCurve` classes when used with a VMEC input file. Now the automatically generated DESC input file is written to a temporary file under `./tmp/`.

v0.17.1
-------

This patch release updates package dependencies to resolve critical incompatibilities between jax-related libraries. DESC versions 0.16.0 and 0.17.0 contain a known bug when used alongside jax-finufft v1.3.0, and we recommend that users upgrade to DESC version 0.17.1 to ensure environment stability.

If an immediate upgrade is not feasible, the issue can be mitigated by manually downgrading jax-finufft to version 1.2.0.

Bug Fixes

- Fixes incorrect units in the documentation of some curvature variables.


Expand Down
19 changes: 14 additions & 5 deletions desc/input_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,13 @@
isVMEC = re.search(r"&INDATA", line, re.IGNORECASE)
if isVMEC:
print("Converting VMEC input to DESC input")
path = self.input_path + "_desc"
InputReader.vmec_to_desc_input(self.input_path, path)
print("Generated DESC input file {}:".format(path))
return self.parse_inputs(path)
# use a buffer here to avoid having to read/write a file
# unnecessarily
buffer = io.StringIO()
InputReader.vmec_to_desc_input(self.input_path, buffer)
buffer.seek(0)
desc_file = buffer
return self.parse_inputs(desc_file)

# extract numbers & words
match = re.search(r"[!#]", line)
Expand Down Expand Up @@ -680,9 +683,11 @@

"""
# open the file, unless its already open
opened_here = False
if not isinstance(filename, io.IOBase):
filename = os.path.expanduser(filename)
f = open(filename, "w+")
opened_here = True

Check warning on line 690 in desc/input_reader.py

View check run for this annotation

Codecov / codecov/patch

desc/input_reader.py#L690

Added line #L690 was not covered by tests
else:
f = filename
f.seek(0)
Expand Down Expand Up @@ -783,7 +788,11 @@
for n, R0, Z0 in inputs[0]["axis"]:
f.write("n: {:3d}\tR0 = {:16.8E}\tZ0 = {:16.8E}\n".format(int(n), R0, Z0))

f.close()
# only close the file if we opened it here, as we also
# can use this function to write to an already open file
# or to an in-memory buffer
if opened_here:
f.close()

Check warning on line 795 in desc/input_reader.py

View check run for this annotation

Codecov / codecov/patch

desc/input_reader.py#L795

Added line #L795 was not covered by tests

@staticmethod
def desc_output_to_input( # noqa: C901 - fxn too complex
Expand Down
38 changes: 25 additions & 13 deletions tests/test_input_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ def test_vmec_input(tmpdir_factory):
with pytest.warns(UserWarning):
ir = InputReader(cl_args=[str(tmp_path)])
vmec_inputs = ir.inputs
# ir makes a VMEC file automatically
path_converted_file = tmpdir.join("input.DSHAPE_desc")
# also test making a DESC file from the ir.inputs manually
path = tmpdir.join("desc_from_vmec")
ir.write_desc_input(path, ir.inputs)
Expand All @@ -60,6 +58,13 @@ def test_vmec_input(tmpdir_factory):
correct_file_path = ".//tests//inputs//input.DSHAPE_desc"

# check DESC input file matches known correct one line-by-line
# above InputReader(cl_args=[str(tmp_path)]) uses a temp buffer
# instead of the old behavior of writing an intermediate file (#2148)
# so we re-create the input file here at a specific
# location, so that we can compare
path_converted_file = tmpdir.join("input.DSHAPE_converted")
with pytest.warns(UserWarning):
InputReader.vmec_to_desc_input(input_path, str(path_converted_file))
with open(correct_file_path) as f:
lines_correct = f.readlines()
with open(path) as f:
Expand Down Expand Up @@ -238,20 +243,30 @@ def test_near_axis_input_files():
np.testing.assert_allclose(
inputs_desc[arg], inputs_vmec[arg], rtol=1e-6, atol=1e-8
)
if os.path.exists(".//tests//inputs//input.QSC_r2_5.5_vmec_desc"):
os.remove(".//tests//inputs//input.QSC_r2_5.5_vmec_desc")


@pytest.mark.unit
def test_from_input_file_equilibrium_desc_vmec_DSHAPE():
"""Test that from_input_file works for DESC input files."""
vmec_path = ".//tests//inputs//input.DSHAPE"
def test_from_input_file_equilibrium_desc_vmec_DSHAPE(tmp_path):
"""Test that from_input_file works for DESC and VMEC input files."""
desc_path = ".//tests//inputs//input.DSHAPE_desc"
kwargs = {"spectral_indexing": "fringe"}
with pytest.warns(UserWarning, match="Left handed"):
eq = Equilibrium.from_input_file(desc_path, **kwargs)
with pytest.warns(UserWarning):
eq_VMEC = Equilibrium.from_input_file(vmec_path, **kwargs)

# load VMEC input from a read-only directory to ensure temp buffer works
# Related to issue #2139
vmec_src = ".//tests//inputs//input.DSHAPE"
locked_dir = tmp_path / "locked"
locked_dir.mkdir()
shutil.copy(vmec_src, locked_dir / "input.DSHAPE")
os.chmod(locked_dir, 0o555)
try:
with pytest.warns(UserWarning):
eq_VMEC = Equilibrium.from_input_file(
str(locked_dir / "input.DSHAPE"), **kwargs
)
finally:
os.chmod(locked_dir, 0o755)

# make sure the loaded eqs are equivalent
np.testing.assert_allclose(eq.R_lmn, eq_VMEC.R_lmn)
Expand Down Expand Up @@ -304,9 +319,6 @@ def test_from_input_file_equilibrium_desc_vmec():
assert eq_VMEC.iota is None
assert eq.sym == eq_VMEC.sym

if os.path.exists(".//tests//inputs//input.QSC_r2_5.5_vmec_desc"):
os.remove(".//tests//inputs//input.QSC_r2_5.5_vmec_desc")


@pytest.mark.unit
def test_vmec_input_surface_threshold():
Expand Down Expand Up @@ -814,7 +826,7 @@ def test_io_OmnigenousField(tmpdir_factory):

@pytest.mark.unit
def test_io_file_like_object(tmpdir_factory):
"""Test loading an equilibrium from a file-like object (BytesIO)"""
"""Test loading an equilibrium from a file-like object (BytesIO)."""
file_path = "./tests/inputs/iotest_HELIOTRON.h5"
with open(file_path, "rb") as f:
file_like_obj = BytesIO(f.read())
Expand Down
Loading