From d59e3a880378c86d238b8bfb586297ad51e0a56a Mon Sep 17 00:00:00 2001 From: RemDelaporteMathurin Date: Wed, 20 Sep 2023 14:22:14 -0400 Subject: [PATCH 01/24] added blablabla to description --- fusion_toolbox/shot_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fusion_toolbox/shot_analysis.py b/fusion_toolbox/shot_analysis.py index 92f20fb..d963179 100644 --- a/fusion_toolbox/shot_analysis.py +++ b/fusion_toolbox/shot_analysis.py @@ -2,7 +2,7 @@ def smooth(data): """Smooth the data to remove noise Args: - data (_type_): _description_ + data (_type_): blablabla """ pass From 2dbd08526e74dbf603dc84be4be929c94a7b7a65 Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 09:34:41 -0400 Subject: [PATCH 02/24] adding CI --- .github/workflows/main.yaml | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/main.yaml diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..244cd30 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,43 @@ +name: CI + +# We can specify which Github events will trigger a CI build +on: push + +# now define a single job 'build' (but could define more) +jobs: + + build: + + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.9", "3.10"] + + runs-on: ${{ matrix.os }} + + # a job is a seq of steps + steps: + + # Next we need to checkout out repository, and set up Python + # A 'name' is just an optional label shown in the log - helpful to clarify progress - and can be anything + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + pip3 install -r requirements.txt + + + - name: Test with PyTest + run: | + python -m pytest --cov=fusion_toolbox tests/ + + - name: Check style with Pylint + run: | + python3 -m pylint --fail-under=0 --reports=y fusiontoolbox \ No newline at end of file From 8cc26f34eba17a5c6822608cb719e95e5bb1b639 Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 09:46:32 -0400 Subject: [PATCH 03/24] adding requirements.txt file --- requirements.txt | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4bc7b28 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,51 @@ +contourpy==1.0.7 +cycler==0.11.0 +et-xmlfile==1.1.0 +fonttools==4.39.3 +h5py==3.8.0 +imageio==2.31.3 +imagepy==0.22 +iniconfig==2.0.0 +kiwisolver==1.4.4 +lazy_loader==0.3 +llvmlite==0.40.1 +Markdown==3.4.4 +matplotlib==3.7.1 +networkx==3.1 +numba==0.57.1 +numpy==1.24.2 +numpy-stl==3.0.1 +openpyxl==3.1.2 +packaging==23.1 +pandas==2.1.0 +Pillow==9.5.0 +pluggy==1.3.0 +pydicom==2.4.3 +Pygments==2.14.0 +PyNE==0.1.0 +pyparsing==3.0.9 +Pypubsub==4.0.3 +PyQt6==6.5.2 +PyQt6-Qt6==6.5.2 +PyQt6-sip==13.5.2 +pyqtgraph==0.13.3 +pystackreg==0.2.7 +pytest==7.4.2 +python-dateutil==2.8.2 +python-utils==3.7.0 +pytz==2023.3.post1 +PyWavelets==1.4.1 +read-roi==1.6.0 +scikit-image==0.21.0 +scipy==1.11.2 +sdf==1.0 +shapely==2.0.1 +six==1.16.0 +tifffile==2023.8.30 +tqdm==4.66.1 +typing_extensions==4.7.1 +tzdata==2023.3 +viscid==1.0.0 +wxPython==4.2.1 +xlrd==2.0.1 +xlwt==1.3.0 From 2d3602581656f865f7b94740ca444ccfde128b9c Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 09:53:17 -0400 Subject: [PATCH 04/24] cleaned up requirements.txt --- requirements.txt | 49 +----------------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4bc7b28..ec478d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,51 +1,4 @@ -contourpy==1.0.7 -cycler==0.11.0 -et-xmlfile==1.1.0 -fonttools==4.39.3 -h5py==3.8.0 -imageio==2.31.3 -imagepy==0.22 -iniconfig==2.0.0 -kiwisolver==1.4.4 -lazy_loader==0.3 -llvmlite==0.40.1 -Markdown==3.4.4 matplotlib==3.7.1 -networkx==3.1 -numba==0.57.1 numpy==1.24.2 -numpy-stl==3.0.1 -openpyxl==3.1.2 -packaging==23.1 -pandas==2.1.0 -Pillow==9.5.0 -pluggy==1.3.0 -pydicom==2.4.3 -Pygments==2.14.0 -PyNE==0.1.0 -pyparsing==3.0.9 -Pypubsub==4.0.3 -PyQt6==6.5.2 -PyQt6-Qt6==6.5.2 -PyQt6-sip==13.5.2 -pyqtgraph==0.13.3 -pystackreg==0.2.7 pytest==7.4.2 -python-dateutil==2.8.2 -python-utils==3.7.0 -pytz==2023.3.post1 -PyWavelets==1.4.1 -read-roi==1.6.0 -scikit-image==0.21.0 -scipy==1.11.2 -sdf==1.0 -shapely==2.0.1 -six==1.16.0 -tifffile==2023.8.30 -tqdm==4.66.1 -typing_extensions==4.7.1 -tzdata==2023.3 -viscid==1.0.0 -wxPython==4.2.1 -xlrd==2.0.1 -xlwt==1.3.0 + From 99384687a93530d1b781f09dfd50e9b0ed3d6742 Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 10:03:11 -0400 Subject: [PATCH 05/24] added pytest-cov package to requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ec478d5..7ff17a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ matplotlib==3.7.1 numpy==1.24.2 pytest==7.4.2 - +pytest-cov==4.1.0 \ No newline at end of file From 8717b992160fad6001936da4dd584b1ec34a0f36 Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 10:06:14 -0400 Subject: [PATCH 06/24] renamed test folder to tests --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From 53eabeaa39b0ea35d25a42601711b7bf49fa7b4a Mon Sep 17 00:00:00 2001 From: Yousef Lawrence Date: Thu, 21 Sep 2023 10:08:10 -0400 Subject: [PATCH 07/24] Delete test directory --- test/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/__init__.py diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29..0000000 From ca945203cada6ea881ac38cb0c4f4710bdc0d16b Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 10:11:46 -0400 Subject: [PATCH 08/24] removing --cov on pytest for now --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 244cd30..6d11f9b 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -36,7 +36,7 @@ jobs: - name: Test with PyTest run: | - python -m pytest --cov=fusion_toolbox tests/ + python -m pytest tests/ - name: Check style with Pylint run: | From a459cf416b91f581e43d4c0aae420c500b866d69 Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 10:15:50 -0400 Subject: [PATCH 09/24] added empty test --- tests/test_empty.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/test_empty.py diff --git a/tests/test_empty.py b/tests/test_empty.py new file mode 100644 index 0000000..f9d032b --- /dev/null +++ b/tests/test_empty.py @@ -0,0 +1,4 @@ + + +def test_empty(): + pass \ No newline at end of file From 7f8d649c1db226a25c9a64430242663ec789db6b Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 10:18:09 -0400 Subject: [PATCH 10/24] added pylint package and added --cov back to testing --- .github/workflows/main.yaml | 2 +- requirements.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 6d11f9b..244cd30 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -36,7 +36,7 @@ jobs: - name: Test with PyTest run: | - python -m pytest tests/ + python -m pytest --cov=fusion_toolbox tests/ - name: Check style with Pylint run: | diff --git a/requirements.txt b/requirements.txt index 7ff17a9..fc231fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ matplotlib==3.7.1 numpy==1.24.2 pytest==7.4.2 -pytest-cov==4.1.0 \ No newline at end of file +pytest-cov==4.1.0 +pylint==2.17.5 \ No newline at end of file From c65e6895dda42fdd2265367285944f9a8591b1a8 Mon Sep 17 00:00:00 2001 From: yousef Date: Thu, 21 Sep 2023 10:19:45 -0400 Subject: [PATCH 11/24] fixed typo in main.yaml --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 244cd30..4731f82 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -40,4 +40,4 @@ jobs: - name: Check style with Pylint run: | - python3 -m pylint --fail-under=0 --reports=y fusiontoolbox \ No newline at end of file + python3 -m pylint --fail-under=0 --reports=y fusion_toolbox \ No newline at end of file From 500ca18382935b4727ad9d3f956c13f4659cf596 Mon Sep 17 00:00:00 2001 From: josephwj Date: Wed, 20 Sep 2023 22:32:11 -0400 Subject: [PATCH 12/24] Added Coil class to represent 1D filamentous coils, which can calculate the magnetic field due to the coil at any points in 3D --- fusion_toolbox/Bfield.py | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 fusion_toolbox/Bfield.py diff --git a/fusion_toolbox/Bfield.py b/fusion_toolbox/Bfield.py new file mode 100644 index 0000000..8615eb4 --- /dev/null +++ b/fusion_toolbox/Bfield.py @@ -0,0 +1,82 @@ +import numpy as np +import matplotlib.pyplot as plt + +class Coil: + def __init__(self,pts,I,closed=True): + """ + Coil Class. + + Parameters + ---------- + pts : np.ndarray of shape (N,3) + List of x,y,z triplets which define a closed current loop. The last entry should be + equal to the first. + I : float + Coil current, in Amps + """ + if not np.allclose(pts[0],pts[-1]): #Given loop is not properly closed, repeat first pt + pts = np.append(pts,pts[0][None,:],axis=0) + self.pts = pts + self.I = I + + def B(self,xyz): + """ + Returns the magnetic field + + Parameters + ---------- + pts : np.ndarray with last dimension of size 3 + + Returns + ------- + np.ndarray of 3D vectors at each point of xyz. Shape is (*xyz.shape,3) + """ + B_out = np.zeros((len(xyz),3)) #Return array of B vectors in same shape as input + + drs = self.pts[1:]-self.pts[:-1] + + for i,pt in enumerate(self.pts[:-1]): #Skip last point because it repeats + B_out += self.BGreen(xyz,pt,drs[i]) + + return B_out + + def BGreen(self, xyz_samples, xyz_center, uvw): + """Evaluates the B field at all sample locations due to a wire segment. Uses the formula: + dB = mu_0/4pi * I * dl x (r-r_0)/|r-r_0|^3 + + Parameters + ---------- + xyz_samples : np.ndarray of shape (N,3) + Locations to evaluate B at + + xyz_center : np.ndarray of shape (3) + Location of wire segment + + uvw : np.ndarray of shape (3) + Vector for wire segment. Should have magnitude equal to length of wire segment. + + """ + _r = xyz_samples - xyz_center[None,:] + return 1e-7 * self.I * np.cross(uvw,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 #mu_0/4pi = 1e-7 H/m + + def plot(self): + """ + Displays a 3D plot of the coil + """ + ax = plt.figure().add_subplot(projection='3d') + x,y,z = self.pts.T + ax.plot(x,y,z) + bbox_val = np.max( (np.max(np.abs(self.pts.T[0])), + np.max(np.abs(self.pts.T[1])), + np.max(np.abs(self.pts.T[2]))) + ) + ax.set_xlabel("x") + ax.set_ylabel("y") + ax.set_zlabel("z") + ax.set_xlim(-bbox_val,bbox_val) + ax.set_ylim(-bbox_val,bbox_val) + ax.set_zlim(-bbox_val,bbox_val) + limits = np.array([getattr(ax, f'get_{axis}lim')() for axis in 'xyz']) + ax.set_box_aspect(np.ptp(limits, axis = 1)) + + plt.show() From 5edf4f717961ea330dc8d437a64d4ac6f1208736 Mon Sep 17 00:00:00 2001 From: josephwj Date: Thu, 21 Sep 2023 09:27:59 -0400 Subject: [PATCH 13/24] newline linting? --- fusion_toolbox/Bfield.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fusion_toolbox/Bfield.py b/fusion_toolbox/Bfield.py index 8615eb4..9647b47 100644 --- a/fusion_toolbox/Bfield.py +++ b/fusion_toolbox/Bfield.py @@ -58,7 +58,6 @@ def BGreen(self, xyz_samples, xyz_center, uvw): """ _r = xyz_samples - xyz_center[None,:] return 1e-7 * self.I * np.cross(uvw,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 #mu_0/4pi = 1e-7 H/m - def plot(self): """ Displays a 3D plot of the coil From b2c0a287e9a1cd02e36391cd01338358a7ee1672 Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 09:49:58 -0400 Subject: [PATCH 14/24] Packages requirements documented --- requirements.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index fc231fa..3333752 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,11 @@ -matplotlib==3.7.1 -numpy==1.24.2 -pytest==7.4.2 -pytest-cov==4.1.0 -pylint==2.17.5 \ No newline at end of file +contourpy==1.1.1 +cycler==0.11.0 +fonttools==4.42.1 +kiwisolver==1.4.5 +matplotlib==3.8.0 +numpy==1.26.0 +packaging==23.1 +Pillow==10.0.1 +pyparsing==3.1.1 +python-dateutil==2.8.2 +six==1.16.0 From ec6074a9752e3a84e66c73cc38390c5a658b15fe Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:33:17 -0400 Subject: [PATCH 15/24] generate pf coil xyz for testing --- requirements.txt | 16 ++++++++++++++++ test/test_magnetics.py | 11 +++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test/test_magnetics.py diff --git a/requirements.txt b/requirements.txt index 3333752..8c2385b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,27 @@ +asttokens==2.4.0 +backcall==0.2.0 +colorama==0.4.6 contourpy==1.1.1 cycler==0.11.0 +decorator==5.1.1 +executing==1.2.0 fonttools==4.42.1 +ipython==8.15.0 +jedi==0.19.0 kiwisolver==1.4.5 matplotlib==3.8.0 +matplotlib-inline==0.1.6 numpy==1.26.0 packaging==23.1 +parso==0.8.3 +pickleshare==0.7.5 Pillow==10.0.1 +prompt-toolkit==3.0.39 +pure-eval==0.2.2 +Pygments==2.16.1 pyparsing==3.1.1 python-dateutil==2.8.2 six==1.16.0 +stack-data==0.6.2 +traitlets==5.10.0 +wcwidth==0.2.6 diff --git a/test/test_magnetics.py b/test/test_magnetics.py new file mode 100644 index 0000000..15ad6f2 --- /dev/null +++ b/test/test_magnetics.py @@ -0,0 +1,11 @@ +import numpy as np +import numpy.testing as npt +from math import pi + +def generate_pf_coil_xyz(R, N = 100, z = 0): + thetas = np.linspace(0, 2*pi, N) + xyz = np.zeros((N,3)) + xyz[:,0] = R * np.cos(thetas) + xyz[:,1] = R * np.sin(thetas) + xyz[:,2] = z + return xyz From c89f1c067a7407742a861d93477e3c274dbe6928 Mon Sep 17 00:00:00 2001 From: josephwj Date: Thu, 21 Sep 2023 09:38:03 -0400 Subject: [PATCH 16/24] Minor formatting fixes and removing unused variables --- fusion_toolbox/Bfield.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fusion_toolbox/Bfield.py b/fusion_toolbox/Bfield.py index 9647b47..56937b0 100644 --- a/fusion_toolbox/Bfield.py +++ b/fusion_toolbox/Bfield.py @@ -2,7 +2,7 @@ import matplotlib.pyplot as plt class Coil: - def __init__(self,pts,I,closed=True): + def __init__(self,pts,I): """ Coil Class. @@ -14,7 +14,7 @@ def __init__(self,pts,I,closed=True): I : float Coil current, in Amps """ - if not np.allclose(pts[0],pts[-1]): #Given loop is not properly closed, repeat first pt + if not np.allclose(pts[0],pts[-1]): #If loop is not properly closed, connect first/last pts pts = np.append(pts,pts[0][None,:],axis=0) self.pts = pts self.I = I @@ -57,7 +57,8 @@ def BGreen(self, xyz_samples, xyz_center, uvw): """ _r = xyz_samples - xyz_center[None,:] - return 1e-7 * self.I * np.cross(uvw,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 #mu_0/4pi = 1e-7 H/m + Bvecs = np.cross(uvw,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 + return 1e-7 * self.I * Bvecs #mu_0/4pi = 1e-7 H/m def plot(self): """ Displays a 3D plot of the coil From e7913652664fe35671c92093105f71534e586850 Mon Sep 17 00:00:00 2001 From: josephwj Date: Thu, 21 Sep 2023 09:51:58 -0400 Subject: [PATCH 17/24] Renamed variable for clarity and agreement with commented formula --- fusion_toolbox/Bfield.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fusion_toolbox/Bfield.py b/fusion_toolbox/Bfield.py index 56937b0..1e6bad3 100644 --- a/fusion_toolbox/Bfield.py +++ b/fusion_toolbox/Bfield.py @@ -40,7 +40,7 @@ def B(self,xyz): return B_out - def BGreen(self, xyz_samples, xyz_center, uvw): + def BGreen(self, xyz_samples, xyz_center, dl): """Evaluates the B field at all sample locations due to a wire segment. Uses the formula: dB = mu_0/4pi * I * dl x (r-r_0)/|r-r_0|^3 @@ -52,12 +52,12 @@ def BGreen(self, xyz_samples, xyz_center, uvw): xyz_center : np.ndarray of shape (3) Location of wire segment - uvw : np.ndarray of shape (3) + dl : np.ndarray of shape (3) Vector for wire segment. Should have magnitude equal to length of wire segment. """ _r = xyz_samples - xyz_center[None,:] - Bvecs = np.cross(uvw,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 + Bvecs = np.cross(dl,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 return 1e-7 * self.I * Bvecs #mu_0/4pi = 1e-7 H/m def plot(self): """ From 01ab9dd3a0ad342047db7b520bb4b7682d3df7d1 Mon Sep 17 00:00:00 2001 From: josephwj Date: Thu, 21 Sep 2023 09:58:06 -0400 Subject: [PATCH 18/24] debug stuff added in comment lines --- fusion_toolbox/Bfield.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fusion_toolbox/Bfield.py b/fusion_toolbox/Bfield.py index 1e6bad3..8d396ef 100644 --- a/fusion_toolbox/Bfield.py +++ b/fusion_toolbox/Bfield.py @@ -80,3 +80,15 @@ def plot(self): ax.set_box_aspect(np.ptp(limits, axis = 1)) plt.show() + +""" +thetas = np.linspace(0,2*np.pi,100) +X = 1*np.cos(thetas) +Y = 1*np.sin(thetas) +Z = 0*X +XYZ = np.vstack((X,Y,Z)).T + +a = Coil(XYZ,1) +field = a.B(np.array([[0,0,0],[0,1,2]])) +print(field) +""" From eaec2e9bd9fb8155b1f9a29c10a5b9737a572d0d Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:09:16 -0400 Subject: [PATCH 19/24] Test magnetic field at the center of ciruclar poloidal field coil --- test/test_magnetics.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/test_magnetics.py b/test/test_magnetics.py index 15ad6f2..ab49be9 100644 --- a/test/test_magnetics.py +++ b/test/test_magnetics.py @@ -1,6 +1,10 @@ import numpy as np import numpy.testing as npt from math import pi +from fusion_toolbox.Bfield import * +from IPython import embed + +MU_0 = 4*pi*1E-7 # T * m / A def generate_pf_coil_xyz(R, N = 100, z = 0): thetas = np.linspace(0, 2*pi, N) @@ -9,3 +13,20 @@ def generate_pf_coil_xyz(R, N = 100, z = 0): xyz[:,1] = R * np.sin(thetas) xyz[:,2] = z return xyz + +def analytic_B_center_of_pf_coil(I, R): + return np.array([0,0,MU_0 * I / (2 * R)]) + +def check_B_center_of_pf_circular_coil(I, R): + ''' + Checks the calculated field at the center of a circular coil against the analytic solution + ''' + + # Generate test coil and calculate the field at the center + xyz = generate_pf_coil_xyz(R, N = int(1E3)) + test_coil = Coil(xyz, I) + B_center = test_coil.B([np.array([0,0,0])]) + + # Compare to analytic calculation + B_analytic = [analytic_B_center_of_pf_coil(I,R)] + npt.assert_almost_equal(B_center, B_analytic, decimal = 4) From 7fd542ddd5f48f4863a618cea16574f9dae43ad8 Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:27:01 -0400 Subject: [PATCH 20/24] Parameterize testing --- requirements.txt | 3 +++ test/test_magnetics.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/requirements.txt b/requirements.txt index 8c2385b..e3dcde1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ cycler==0.11.0 decorator==5.1.1 executing==1.2.0 fonttools==4.42.1 +iniconfig==2.0.0 ipython==8.15.0 jedi==0.19.0 kiwisolver==1.4.5 @@ -16,10 +17,12 @@ packaging==23.1 parso==0.8.3 pickleshare==0.7.5 Pillow==10.0.1 +pluggy==1.3.0 prompt-toolkit==3.0.39 pure-eval==0.2.2 Pygments==2.16.1 pyparsing==3.1.1 +pytest==7.4.2 python-dateutil==2.8.2 six==1.16.0 stack-data==0.6.2 diff --git a/test/test_magnetics.py b/test/test_magnetics.py index ab49be9..4e7c05c 100644 --- a/test/test_magnetics.py +++ b/test/test_magnetics.py @@ -3,10 +3,18 @@ from math import pi from fusion_toolbox.Bfield import * from IPython import embed +import pytest MU_0 = 4*pi*1E-7 # T * m / A def generate_pf_coil_xyz(R, N = 100, z = 0): + ''' + Get xyz coordinates of points along a pf coil + R: radius [m] + N: number of points + z: pf coil height [m] + Returns: xyz - np.ndarray [N,3] + ''' thetas = np.linspace(0, 2*pi, N) xyz = np.zeros((N,3)) xyz[:,0] = R * np.cos(thetas) @@ -17,8 +25,14 @@ def generate_pf_coil_xyz(R, N = 100, z = 0): def analytic_B_center_of_pf_coil(I, R): return np.array([0,0,MU_0 * I / (2 * R)]) +@pytest.mark.parametrize( + "I, R", + [[1E3,0.3] + ]) def check_B_center_of_pf_circular_coil(I, R): ''' + I: current [A] + R: coil radius [m] Checks the calculated field at the center of a circular coil against the analytic solution ''' @@ -30,3 +44,4 @@ def check_B_center_of_pf_circular_coil(I, R): # Compare to analytic calculation B_analytic = [analytic_B_center_of_pf_coil(I,R)] npt.assert_almost_equal(B_center, B_analytic, decimal = 4) + print('all good!') From cb82e1facfe4a0c98ba3524290c8aa918ea6909b Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:36:31 -0400 Subject: [PATCH 21/24] magnetic tests automatically run --- {test => tests}/test_magnetics.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename {test => tests}/test_magnetics.py (88%) diff --git a/test/test_magnetics.py b/tests/test_magnetics.py similarity index 88% rename from test/test_magnetics.py rename to tests/test_magnetics.py index 4e7c05c..efec05e 100644 --- a/test/test_magnetics.py +++ b/tests/test_magnetics.py @@ -27,9 +27,9 @@ def analytic_B_center_of_pf_coil(I, R): @pytest.mark.parametrize( "I, R", - [[1E3,0.3] + [[1E3,0.3], [1E5, 0.02], [1E7, 0.004], [1E2, 1] ]) -def check_B_center_of_pf_circular_coil(I, R): +def test_B_center_of_pf_circular_coil(I, R): ''' I: current [A] R: coil radius [m] @@ -37,11 +37,10 @@ def check_B_center_of_pf_circular_coil(I, R): ''' # Generate test coil and calculate the field at the center - xyz = generate_pf_coil_xyz(R, N = int(1E3)) + xyz = generate_pf_coil_xyz(R, N = int(1E4)) test_coil = Coil(xyz, I) B_center = test_coil.B([np.array([0,0,0])]) # Compare to analytic calculation B_analytic = [analytic_B_center_of_pf_coil(I,R)] npt.assert_almost_equal(B_center, B_analytic, decimal = 4) - print('all good!') From 40f39cfd5f5fe69c0e98b87a729f95085f0a92ca Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:49:49 -0400 Subject: [PATCH 22/24] Remove IPython dependence --- requirements.txt | 35 +++++------------------------------ tests/test_magnetics.py | 1 - 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/requirements.txt b/requirements.txt index e3dcde1..a43e5db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,5 @@ -asttokens==2.4.0 -backcall==0.2.0 -colorama==0.4.6 -contourpy==1.1.1 -cycler==0.11.0 -decorator==5.1.1 -executing==1.2.0 -fonttools==4.42.1 -iniconfig==2.0.0 -ipython==8.15.0 -jedi==0.19.0 -kiwisolver==1.4.5 -matplotlib==3.8.0 -matplotlib-inline==0.1.6 -numpy==1.26.0 -packaging==23.1 -parso==0.8.3 -pickleshare==0.7.5 -Pillow==10.0.1 -pluggy==1.3.0 -prompt-toolkit==3.0.39 -pure-eval==0.2.2 -Pygments==2.16.1 -pyparsing==3.1.1 -pytest==7.4.2 -python-dateutil==2.8.2 -six==1.16.0 -stack-data==0.6.2 -traitlets==5.10.0 -wcwidth==0.2.6 +matplotlib>=3.0.0 +numpy>=1.0.0 +pytest>=7.0.0 +pytest-cov>=4.0.0 +pylint>=2.0.0 \ No newline at end of file diff --git a/tests/test_magnetics.py b/tests/test_magnetics.py index efec05e..2e4c317 100644 --- a/tests/test_magnetics.py +++ b/tests/test_magnetics.py @@ -2,7 +2,6 @@ import numpy.testing as npt from math import pi from fusion_toolbox.Bfield import * -from IPython import embed import pytest MU_0 = 4*pi*1E-7 # T * m / A From 601f4915ec59d2f92a875a9c1112d69839e9cd4b Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:16:23 -0400 Subject: [PATCH 23/24] Merge branch 'main' of github.com:DeIonizedPlasma/fusion-toolbox into DeIonizedPlasma-main --- fusion_toolbox/Bfield.py | 73 ++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/fusion_toolbox/Bfield.py b/fusion_toolbox/Bfield.py index 8d396ef..0e40a4f 100644 --- a/fusion_toolbox/Bfield.py +++ b/fusion_toolbox/Bfield.py @@ -38,11 +38,12 @@ def B(self,xyz): for i,pt in enumerate(self.pts[:-1]): #Skip last point because it repeats B_out += self.BGreen(xyz,pt,drs[i]) - return B_out + return B_out*self.I def BGreen(self, xyz_samples, xyz_center, dl): """Evaluates the B field at all sample locations due to a wire segment. Uses the formula: dB = mu_0/4pi * I * dl x (r-r_0)/|r-r_0|^3 + The current I Parameters ---------- @@ -58,8 +59,9 @@ def BGreen(self, xyz_samples, xyz_center, dl): """ _r = xyz_samples - xyz_center[None,:] Bvecs = np.cross(dl,_r)/np.linalg.norm(_r,axis=1)[:,None]**3 - return 1e-7 * self.I * Bvecs #mu_0/4pi = 1e-7 H/m - def plot(self): + return 1e-7 * Bvecs #mu_0/4pi = 1e-7 H/m + + def plot_contour(self): """ Displays a 3D plot of the coil """ @@ -80,15 +82,58 @@ def plot(self): ax.set_box_aspect(np.ptp(limits, axis = 1)) plt.show() + def plot_B_slice(self,axis,r,w1,w2,n1,n2): + ax = plt.figure().add_subplot() + #ax = plt.figure().add_subplot(projection='3d') + X,Y,Z = gen_plane_pts(axis,r,w1,w2,n1,n2) + if axis==0: + C1,C2 = Y,Z + elif axis==1: + C1,C2 = X,Z + elif axis==2: + C1,C2 = X,Y + Bpts = np.vstack((X.ravel(),Y.ravel(),Z.ravel())).T + B_samples = self.B(Bpts).T + B_clean = np.delete(B_samples,axis,axis=0).T + B_2D = np.zeros((n2,n1,2)) + for i,B in enumerate(B_clean): + B_2D[(i//n1)%n2,i%n1] = B + ax.streamplot(C1,C2,B_2D[:,:,0],B_2D[:,:,1],density=1.5) + ax.set_aspect(1) + plt.show() -""" -thetas = np.linspace(0,2*np.pi,100) -X = 1*np.cos(thetas) -Y = 1*np.sin(thetas) -Z = 0*X -XYZ = np.vstack((X,Y,Z)).T - -a = Coil(XYZ,1) -field = a.B(np.array([[0,0,0],[0,1,2]])) -print(field) -""" +def gen_plane_pts(axis,r,w1,w2,n1,n2): + """ + Generates a 2D grid of points on a plane in 3D which is normal to one of the three axes. + + Parameters + ---------- + axis : int + Which axis the plane should be normal to. Valid values are 0, 1, or 2 for x, y, or z. + r : np.ndarray of shape (3) + Displacement from origin of the center of the plane + w1 : float + Physical width of first dimension of grid + w2 : float + Physical width of second dimension of grid + n1 : int + Number of grid points along side 1 of the grid. + n2 : int + Number of grid points along side 2 of the grid. + """ + if axis==0: + yspan = np.linspace(r[1]-w1/2,r[1]+w1/2,n1) + zspan = np.linspace(r[2]-w2/2,r[2]+w2/2,n2) + Y,Z = np.meshgrid(yspan,zspan) + X = np.zeros_like(Y) + elif axis==1: + xspan = np.linspace(r[1]-w1/2,r[1]+w1/2,n1) + zspan = np.linspace(r[2]-w2/2,r[2]+w2/2,n2) + X,Z = np.meshgrid(xspan,zspan) + Y = np.zeros_like(Z) + elif axis==2: + xspan = np.linspace(r[1]-w1/2,r[1]+w1/2,n1) + yspan = np.linspace(r[2]-w2/2,r[2]+w2/2,n2) + X,Y = np.meshgrid(xspan,yspan) + Z = np.zeros_like(X) + return X,Y,Z From e72c9cd732de49bd241f826796a71a3e668ce87a Mon Sep 17 00:00:00 2001 From: Audrey Saltzman <37987951+AudreySaltzman@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:40:29 -0400 Subject: [PATCH 24/24] Linting --- tests/test_Bfield.py | 53 +++++++++++++++++++++++++++++++++++++++++ tests/test_magnetics.py | 45 ---------------------------------- 2 files changed, 53 insertions(+), 45 deletions(-) create mode 100644 tests/test_Bfield.py delete mode 100644 tests/test_magnetics.py diff --git a/tests/test_Bfield.py b/tests/test_Bfield.py new file mode 100644 index 0000000..03e5521 --- /dev/null +++ b/tests/test_Bfield.py @@ -0,0 +1,53 @@ +''' Module to test Bfield.py calculations''' +from math import pi +import numpy as np +import numpy.testing as npt +import pytest +from fusion_toolbox.Bfield import Coil + + +MU_0 = 4*pi*1E-7 # T * m / A + +def generate_pf_coil_xyz(coil_radius, num_coil_pts = 100, coil_height = 0): + ''' + Get xyz coordinates of points along a pf coil + coil radius: [m] + num_coil_points: Number of points used to define the coil (int) + coil_height: pf coil height [m] + Returns: xyz - np.ndarray [N,3] + ''' + thetas = np.linspace(0, 2*pi, num_coil_pts) + xyz = np.zeros((num_coil_pts,3)) + xyz[:,0] = coil_radius * np.cos(thetas) + xyz[:,1] = coil_radius * np.sin(thetas) + xyz[:,2] = coil_height + return xyz + +def analytic_B_center_of_pf_coil(current, coil_radius): + ''' + Analytic calculation of magnetic field at the center of a circular pf coil + current: [A] + coil radius: [m] + returns: B-field [T] + ''' + return np.array([0,0,MU_0 * current / (2 * coil_radius)]) + +@pytest.mark.parametrize( + "current, coil_radius", + [[1E3,0.3], [1E5, 0.02], [1E7, 0.004], [1E2, 1] + ]) +def test_B_center_of_pf_circular_coil(current, coil_radius): + ''' + current: [A] + coil radius: [m] + Checks the calculated field at the center of a circular coil against the analytic solution + ''' + + # Generate test coil and calculate the field at the center + xyz = generate_pf_coil_xyz(coil_radius, num_coil_pts= int(1E4)) + test_coil = Coil(xyz, current) + B_center = test_coil.B([np.array([0,0,0])]) + + # Compare to analytic calculation + B_analytic = [analytic_B_center_of_pf_coil(current,coil_radius)] + npt.assert_almost_equal(B_center, B_analytic, decimal = 4) diff --git a/tests/test_magnetics.py b/tests/test_magnetics.py deleted file mode 100644 index 2e4c317..0000000 --- a/tests/test_magnetics.py +++ /dev/null @@ -1,45 +0,0 @@ -import numpy as np -import numpy.testing as npt -from math import pi -from fusion_toolbox.Bfield import * -import pytest - -MU_0 = 4*pi*1E-7 # T * m / A - -def generate_pf_coil_xyz(R, N = 100, z = 0): - ''' - Get xyz coordinates of points along a pf coil - R: radius [m] - N: number of points - z: pf coil height [m] - Returns: xyz - np.ndarray [N,3] - ''' - thetas = np.linspace(0, 2*pi, N) - xyz = np.zeros((N,3)) - xyz[:,0] = R * np.cos(thetas) - xyz[:,1] = R * np.sin(thetas) - xyz[:,2] = z - return xyz - -def analytic_B_center_of_pf_coil(I, R): - return np.array([0,0,MU_0 * I / (2 * R)]) - -@pytest.mark.parametrize( - "I, R", - [[1E3,0.3], [1E5, 0.02], [1E7, 0.004], [1E2, 1] - ]) -def test_B_center_of_pf_circular_coil(I, R): - ''' - I: current [A] - R: coil radius [m] - Checks the calculated field at the center of a circular coil against the analytic solution - ''' - - # Generate test coil and calculate the field at the center - xyz = generate_pf_coil_xyz(R, N = int(1E4)) - test_coil = Coil(xyz, I) - B_center = test_coil.B([np.array([0,0,0])]) - - # Compare to analytic calculation - B_analytic = [analytic_B_center_of_pf_coil(I,R)] - npt.assert_almost_equal(B_center, B_analytic, decimal = 4)