-
Notifications
You must be signed in to change notification settings - Fork 49
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
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): | ||
|
@@ -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") | ||
|
||
id_str = f"{ID:07d}" | ||
# string to 7 digits | ||
url = f'https://quasr.flatironinstitute.org/simsopt_serials/{id_str[0:4]}/serial{id_str}.json' | ||
Comment on lines
+223
to
+225
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
else: | ||
raise ValueError(f"Download of ID {ID:07d} failed. Status code: {r.status_code}\n Check if the confituration exists") | ||
|
||
if return_style == 'simsopt-style': | ||
nfp = surfaces[0].nfp | ||
nc_per_hp = len(coils) // nfp // (1 + surfaces[0].stellsym) | ||
coils = coils[:nc_per_hp] | ||
|
||
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 | ||
else: | ||
raise ValueError #should not be reached as we check before download to avoid clobbering the database. | ||
|
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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() |
There was a problem hiding this comment.
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?