Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a QUASR-downloader #425

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
59 changes: 58 additions & 1 deletion src/simsopt/configs/zoo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@
from simsopt.geo.curverzfourier import CurveRZFourier
from simsopt.geo.curvexyzfourier import CurveXYZFourier
from simsopt.field.coil import Current
from simsopt._core.dev import SimsoptRequires
from simsopt._core.json import GSONDecoder
import json

try:
import requests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "requests" already installed in the CI, or does it need to be added so the test will run?

except ImportError:
requests = None

from pathlib import Path
THIS_DIR = (Path(__file__).parent).resolve()

__all__ = ['get_ncsx_data', 'get_hsx_data', 'get_giuliani_data', "get_w7x_data"]
__all__ = ['get_ncsx_data', 'get_hsx_data', 'get_giuliani_data', "get_w7x_data", 'get_QUASR_data']


def get_ncsx_data(Nt_coils=25, Nt_ma=10, ppp=10):
Expand Down Expand Up @@ -187,3 +195,52 @@
ma.zs[:] = sZ[0:Nt_ma]
ma.x = ma.get_dofs()
return (curves, currents, ma)


@SimsoptRequires(requests is not None, "You need to install the requests library to use this function. Run 'pip install requests'")
def get_QUASR_data(ID, return_style='quasr-style'):
"""
Download a configuration from the QUASR database.

Args:
ID: the ID of the configuration to download. The database is navigatable at https://quasr.flatironinstitute.org/
Alternatively, you can download the latest full set of devices from https://zenodo.org/doi/10.5281/zenodo.10050655

return_style: 'simsopt-style' or 'quasr-style'. '.
simsopt-style: [coils_1fp, surface], similar to get_ncsx_data(), gives the curves and currenst for one field period,
which you can copy and rotate using simsopt methods (allows finer control over degrees-of-freedom).
NOTE: this does not return the magnetic axis.
quasr-style: [coils_all, surfaces], returns all coils and all surfaces in the object.

returns: depending on return_style:
simsopt-style: [list of simsopt.geo.Coil objects, list of simsopt.field.Current objects]
quasr-style: [list of simsopt.geo.SurfaceXYZTensorFourier objects, list of simsopt.field.COIL objects]
"""

if return_style not in ['simsopt-style', 'quasr-style']:
raise ValueError(f"invalid return_style: {return_style}, must be either simsopt-style or quasr-style")

Check warning on line 221 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L220-L221

Added lines #L220 - L221 were not covered by tests

id_str = f"{ID:07d}"

Check warning on line 223 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L223

Added line #L223 was not covered by tests
# string to 7 digits
url = f'https://quasr.flatironinstitute.org/simsopt_serials/{id_str[0:4]}/serial{id_str}.json'

Check warning on line 225 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L225

Added line #L225 was not covered by tests
Comment on lines +223 to +225
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, but note that we reserve the right to change the ID structure in new versions of the database, which would break this code. (Fair warning!)

I might be able to set up an endpoint on QUASR for converting an ID (arbitrary length) to the correct location, but I wouldn't have a chance to do that for at least a few weeks.


with requests.get(url) as r:
if r.status_code == 200:
print(f"Configuration with ID {ID:07} downloaded successfully")
surfaces, coils = json.loads(r.content, cls=GSONDecoder)

Check warning on line 230 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L227-L230

Added lines #L227 - L230 were not covered by tests
else:
raise ValueError(f"Download of ID {ID:07d} failed. Status code: {r.status_code}\n Check if the confituration exists")

Check warning on line 232 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L232

Added line #L232 was not covered by tests

if return_style == 'simsopt-style':
nfp = surfaces[0].nfp
nc_per_hp = len(coils) // nfp // (1 + surfaces[0].stellsym)
coils = coils[:nc_per_hp]

Check warning on line 237 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L234-L237

Added lines #L234 - L237 were not covered by tests

curves = [coil.curve for coil in coils]
currents = [coil.current for coil in coils]
return curves, currents
elif return_style == 'quasr-style':
return surfaces, coils

Check warning on line 243 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L239-L243

Added lines #L239 - L243 were not covered by tests
else:
raise ValueError #should not be reached as we check before download to avoid clobbering the database.

Check warning on line 245 in src/simsopt/configs/zoo.py

View check run for this annotation

Codecov / codecov/patch

src/simsopt/configs/zoo.py#L245

Added line #L245 was not covered by tests

16 changes: 16 additions & 0 deletions tests/configs/test_zoo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import unittest
from simsopt.configs import get_QUASR_data

class ZooTests(unittest.TestCase):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting that, as you're hitting an external resource (QUASR website), this isn't strictly a unit test but an integration test. Now, an integration test may be what you actually care about, but there's always the risk that it will fail (or start failing) due to factors that don't indicate an error in your code.
I would consider mocking the requests.get call for regular testing, and have a separate integration test that's run less frequently.

def test_QUASR_downloader(self):
curves, currents, ma = get_QUASR_data(952)
coils, ma, surfaces = get_QUASR_data(952, return_style='json')
Comment on lines +6 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might also be useful to have assertions verifying (from the returned data) that these are the same device record.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about also covering the case of a success with return_style="default".

with self.assertRaises(Exception):
curves, currents, ma = get_QUASR_data(0)

with self.assertRaises(Exception):
curves, currents, ma = get_QUASR_data(952, return_style='')

if __name__ == "__main__":
unittest.main()
Loading