|
7 | 7 | import numpy as np |
8 | 8 | import pyvista as pv |
9 | 9 | import matplotlib.pyplot as plt |
| 10 | +from mpl_toolkits.axes_grid1 import make_axes_locatable |
| 11 | + |
| 12 | +import sys |
| 13 | +sys.path.append('../') |
10 | 14 |
|
11 | 15 | from wakis import SolverFIT3D |
12 | 16 | from wakis import GridFIT3D |
|
47 | 51 | Nz = 141 |
48 | 52 |
|
49 | 53 | # Adjust for MPI & ompute local Z-slice range |
50 | | -Nz += Nz%(size-1) |
| 54 | +Nz += Nz%(size) #if rank 0 is common, then size-1 |
51 | 55 | dz = (zmax - zmin) / Nz |
52 | 56 | z = np.linspace(zmin, zmax, Nz+1) |
53 | 57 |
|
54 | | -Nz_mpi = Nz // (size-1) |
55 | | -zmin_mpi = (rank-1) * Nz_mpi * dz |
56 | | -zmax_mpi= rank * Nz_mpi * dz |
| 58 | +# Allocate mpi node cells |
| 59 | +Nz_mpi = Nz // (size) |
| 60 | +zmin_mpi = rank * Nz_mpi * dz + zmin |
| 61 | +zmax_mpi= (rank+1) * Nz_mpi * dz + zmin |
57 | 62 |
|
58 | 63 | print(f"Process {rank}: Handling Z range {zmin_mpi} to {zmax_mpi}") |
59 | 64 |
|
60 | | -# set grid and geometry |
61 | | -if rank == 0: |
62 | | - grid = GridFIT3D(xmin, xmax, ymin, ymax, zmin, zmax, |
63 | | - Nx, Ny, Nz, |
64 | | - stl_solids=stl_solids, |
65 | | - stl_materials=stl_materials, |
66 | | - stl_scale=1.0, |
67 | | - stl_rotate=[0,0,0], |
68 | | - stl_translate=[0,0,0], |
69 | | - verbose=1) |
70 | | -else: |
71 | | - grid = GridFIT3D(xmin, xmax, ymin, ymax, |
72 | | - zmin_mpi, zmax_mpi, |
73 | | - Nx, Ny, Nz_mpi, |
74 | | - stl_solids=stl_solids, |
75 | | - stl_materials=stl_materials, |
76 | | - stl_scale=1.0, |
77 | | - stl_rotate=[0,0,0], |
78 | | - stl_translate=[0,0,0], |
79 | | - verbose=1) |
80 | | -# BONUS: Visualize grid - Uncomment for plotting! |
81 | | -# grid.inspect(add_stl=[solid_1, solid_2], stl_opacity=1.0) |
82 | | - |
83 | | -# BONUS: Visualize imported solids - Uncomment for plotting! |
84 | | -#grid.plot_solids() |
| 65 | +grid = GridFIT3D(xmin, xmax, ymin, ymax, |
| 66 | + zmin_mpi, zmax_mpi, |
| 67 | + Nx, Ny, Nz_mpi, |
| 68 | + stl_solids=stl_solids, |
| 69 | + stl_materials=stl_materials, |
| 70 | + stl_scale=1.0, |
| 71 | + stl_rotate=[0,0,0], |
| 72 | + stl_translate=[0,0,0], |
| 73 | + verbose=1) |
85 | 74 |
|
| 75 | +# ------------ Beam source & Wake ---------------- |
86 | 76 | # ------------ Beam source & Wake ---------------- |
87 | 77 | # Beam parameters |
88 | 78 | sigmaz = 10e-2 #[m] -> 2 GHz |
|
98 | 88 | wakelength = 10. # [m] |
99 | 89 | add_space = 10 # no. cells to skip from boundaries - removes BC artifacts |
100 | 90 |
|
| 91 | +from wakis.sources import Beam |
| 92 | +from scipy.constants import c |
| 93 | +beam = Beam(q=q, sigmaz=sigmaz, beta=beta, |
| 94 | + xsource=xs, ysource=ys, ti=3*sigmaz/c) |
| 95 | + |
| 96 | +results_folder = f'001_results_n{rank}/' |
| 97 | + |
| 98 | +''' |
101 | 99 | wake = WakeSolver(q=q, |
102 | 100 | sigmaz=sigmaz, |
103 | 101 | beta=beta, |
104 | 102 | xsource=xs, ysource=ys, |
105 | 103 | xtest=xt, ytest=yt, |
106 | 104 | add_space=add_space, |
107 | | - results_folder='001_results/', |
108 | | - Ez_file='001_results/001_Ez.h5') |
| 105 | + results_folder=results_folder, |
| 106 | + Ez_file=results_folder+'001_Ez.h5') |
| 107 | +''' |
109 | 108 |
|
110 | 109 | # ----------- Solver & Simulation ---------- |
111 | | -# boundary conditions`` |
| 110 | +# boundary conditions |
112 | 111 | bc_low=['pec', 'pec', 'pec'] |
113 | 112 | bc_high=['pec', 'pec', 'pec'] |
114 | 113 |
|
115 | | -# on-the-fly plotting parameters |
116 | | -if not os.path.exists('001_img/'): |
117 | | - os.mkdir('001_img/') |
| 114 | +if rank > 0: |
| 115 | + bc_low=['pec', 'pec', 'periodic'] |
118 | 116 |
|
119 | | -plotkw2D = {'title':'001_img/Ez', |
120 | | - 'add_patch':'cavity', 'patch_alpha':1.0, |
121 | | - 'patch_reverse' : True, # patch logical_not('cavity') |
122 | | - 'vmin':-1e3, 'vmax':1e3, # colormap limits |
123 | | - 'cmap': 'rainbow', |
124 | | - 'plane': [int(Nx/2), # x |
125 | | - slice(0, Ny), # y |
126 | | - slice(add_space, -add_space)]} # z |
| 117 | +if rank < size - 1: |
| 118 | + bc_high=['pec', 'pec', 'periodic'] |
127 | 119 |
|
128 | 120 | # Solver setup |
129 | | -solver = SolverFIT3D(grid, wake, |
| 121 | +solver = SolverFIT3D(grid, |
130 | 122 | bc_low=bc_low, |
131 | 123 | bc_high=bc_high, |
132 | 124 | use_stl=True, |
133 | 125 | bg='pec' # Background material |
134 | 126 | ) |
135 | | -# [TODO]: domain should be split after the tensors are built |
136 | | -# to avoid issues with boundary conditions / geometry |
137 | | -# Maybe need to add >1 ghost cells |
| 127 | +# Communication between ghost cells |
| 128 | +def communicate_ghost_cells(): |
| 129 | + if rank > 0: |
| 130 | + for d in ['x','y','z']: |
| 131 | + comm.Sendrecv(solver.E[:, :, 1,d], |
| 132 | + recvbuf=solver.E[:, :, 0,d], |
| 133 | + dest=rank-1, sendtag=0, |
| 134 | + source=rank-1, recvtag=1) |
| 135 | + |
| 136 | + comm.Sendrecv(solver.H[:, :, 1,d], |
| 137 | + recvbuf=solver.H[:, :, 0,d], |
| 138 | + dest=rank-1, sendtag=0, |
| 139 | + source=rank-1, recvtag=1) |
| 140 | + |
| 141 | + comm.Sendrecv(solver.J[:, :, 1,d], |
| 142 | + recvbuf=solver.J[:, :, 0,d], |
| 143 | + dest=rank-1, sendtag=0, |
| 144 | + source=rank-1, recvtag=1) |
| 145 | + |
| 146 | + if rank < size - 1: |
| 147 | + for d in ['x','y','z']: |
| 148 | + comm.Sendrecv(solver.E[:, :, -2,d], |
| 149 | + recvbuf=solver.E[:, :, -1, d], |
| 150 | + dest=rank+1, sendtag=1, |
| 151 | + source=rank+1, recvtag=0) |
| 152 | + |
| 153 | + comm.Sendrecv(solver.H[:, :, -2,d], |
| 154 | + recvbuf=solver.H[:, :, -1, d], |
| 155 | + dest=rank+1, sendtag=1, |
| 156 | + source=rank+1, recvtag=0) |
| 157 | + |
| 158 | + comm.Sendrecv(solver.J[:, :, -2,d], |
| 159 | + recvbuf=solver.J[:, :, -1, d], |
| 160 | + dest=rank+1, sendtag=1, |
| 161 | + source=rank+1, recvtag=0) |
| 162 | + |
| 163 | +def compose_field(field, d='z'): |
| 164 | + |
| 165 | + if field == 'E': |
| 166 | + local = solver.E[Nx//2, :, :,d].ravel() |
| 167 | + elif field == 'H': |
| 168 | + local = solver.H[Nx//2, :, :,d].ravel() |
| 169 | + elif field == 'J': |
| 170 | + local = solver.J[Nx//2, :, :,d].ravel() |
| 171 | + |
| 172 | + buffer = comm.gather(local, root=0) |
| 173 | + field = None |
| 174 | + |
| 175 | + if rank == 0: |
| 176 | + field = np.zeros((Ny, Nz)) # Reinitialize global array |
| 177 | + for r in range(size): |
| 178 | + field[:, r*Nz_mpi:(r+1)*Nz_mpi] = np.reshape(buffer[r], (Ny, Nz_mpi)) |
| 179 | + |
| 180 | + return field |
| 181 | + |
| 182 | +def plot_field(field, name='E', n=None, results_folder='img/'): |
| 183 | + extent = (zmin, zmax, ymin, ymax) |
| 184 | + fig, ax = plt.subplots() |
| 185 | + im = ax.imshow(field, cmap='bwr', extent=extent, vmin=-500, vmax=500) |
| 186 | + fig.colorbar(im, cax=make_axes_locatable(ax).append_axes('right', size='5%', pad=0.05)) |
| 187 | + ax.set_title(f'{name} at timestep={n}') |
| 188 | + ax.set_xlabel('z [m]') |
| 189 | + ax.set_ylabel('y [m]') |
| 190 | + |
| 191 | + fig.tight_layout(h_pad=0.3) |
| 192 | + fig.savefig(results_folder+name+str(n).zfill(4)+'.png') |
| 193 | + |
| 194 | + plt.clf() |
| 195 | + plt.close(fig) |
138 | 196 |
|
139 | | -# Solver run |
140 | | -''' |
141 | | -solver.wakesolve(wakelength=wakelength, |
142 | | - add_space=add_space, |
143 | | - plot=True, # turn False for speedup |
144 | | - plot_every=30, plot_until=3000, **plotkw2D |
145 | | - ) |
146 | | -''' |
147 | | -from wakis.sources import Beam |
148 | | -beam = Beam(q=q, sigmaz=sigmaz, beta=beta, |
149 | | - xsource=xs, ysource=ys) |
150 | | - |
151 | | -Nt = 1/solver.dt * (wake.wakelength + wake.ti*wake.v \ |
152 | | - + (solver.z.max()-solver.z.min()))/wake.v |
| 197 | +if rank == 0: |
| 198 | + img_folder = '003_img/' |
| 199 | + if not os.path.exists(img_folder): |
| 200 | + os.mkdir(img_folder) |
153 | 201 |
|
| 202 | +Nt = 3000 |
154 | 203 | for n in tqdm(range(Nt)): |
155 | 204 |
|
156 | | - beam.update_mpi(solver, n*solver.dt, zmin, z) |
| 205 | + beam.update_mpi(solver, n*solver.dt, zmin) |
157 | 206 |
|
158 | 207 | solver.one_step() |
159 | 208 |
|
160 | | - #Communicate slices [TODO] |
161 | | - |
162 | | - |
163 | | -# ----------- 1d plot results -------------------- |
164 | | -# Plot longitudinal wake potential and impedance |
165 | | -fig1, ax = plt.subplots(1,2, figsize=[12,4], dpi=150) |
166 | | -ax[0].plot(wake.s*1e2, wake.WP, c='r', lw=1.5, label='Wakis') |
167 | | -ax[0].set_xlabel('s [cm]') |
168 | | -ax[0].set_ylabel('Longitudinal wake potential [V/pC]', color='r') |
169 | | -ax[0].legend() |
170 | | -ax[0].set_xlim(xmax=wakelength*1e2) |
171 | | - |
172 | | -ax[1].plot(wake.f*1e-9, np.abs(wake.Z), c='b', lw=1.5, label='Wakis') |
173 | | -ax[1].set_xlabel('f [GHz]') |
174 | | -ax[1].set_ylabel('Longitudinal impedance [Abs][$\Omega$]', color='b') |
175 | | -ax[1].legend() |
176 | | - |
177 | | -fig1.tight_layout() |
178 | | -fig1.savefig('001_results/001_longitudinal.png') |
179 | | -#plt.show() |
180 | | - |
181 | | -# Plot transverse x wake potential and impedance |
182 | | -fig2, ax = plt.subplots(1,2, figsize=[12,4], dpi=150) |
183 | | -ax[0].plot(wake.s*1e2, wake.WPx, c='r', lw=1.5, label='Wakis') |
184 | | -ax[0].set_xlabel('s [cm]') |
185 | | -ax[0].set_ylabel('Transverse wake potential X [V/pC]', color='r') |
186 | | -ax[0].legend() |
187 | | -ax[0].set_xlim(xmax=wakelength*1e2) |
188 | | - |
189 | | -ax[1].plot(wake.f*1e-9, np.abs(wake.Zx), c='b', lw=1.5, label='Wakis') |
190 | | -ax[1].set_xlabel('f [GHz]') |
191 | | -ax[1].set_ylabel('Transverse impedance X [Abs][$\Omega$]', color='b') |
192 | | -ax[1].legend() |
193 | | - |
194 | | -fig2.tight_layout() |
195 | | -fig2.savefig('001_results/001_transverse_x.png') |
196 | | -#plt.show() |
197 | | - |
198 | | -# Plot transverse y wake potential and impedance |
199 | | -fig3, ax = plt.subplots(1,2, figsize=[12,4], dpi=150) |
200 | | -ax[0].plot(wake.s*1e2, wake.WPy, c='r', lw=1.5, label='Wakis') |
201 | | -ax[0].set_xlabel('s [cm]') |
202 | | -ax[0].set_ylabel('Transverse wake potential Y [V/pC]', color='r') |
203 | | -ax[0].legend() |
204 | | -ax[0].set_xlim(xmax=wakelength*1e2) |
205 | | - |
206 | | -ax[1].plot(wake.f*1e-9, np.abs(wake.Zy), c='b', lw=1.5, label='Wakis') |
207 | | -ax[1].set_xlabel('f [GHz]') |
208 | | -ax[1].set_ylabel('Transverse impedance Y [Abs][$\Omega$]', color='b') |
209 | | -ax[1].legend() |
210 | | - |
211 | | -fig3.tight_layout() |
212 | | -fig3.savefig('001_results/001_transverse_y.png') |
213 | | -#plt.show() |
214 | | - |
215 | | -# Plot Electric field component in 2D using imshow |
216 | | -solver.plot1D(field='E', component='z', |
217 | | - line='z', pos=0.5, xscale='linear', yscale='linear', |
218 | | - off_screen=True, title='001_img/Ez1d') |
219 | | -#plt.show() |
220 | | - |
221 | | -# ----------- 2d plots results -------------------- |
222 | | -from matplotlib.colors import LinearSegmentedColormap |
223 | | -cmap = LinearSegmentedColormap.from_list('name', plt.cm.jet(np.linspace(0.1, 0.9))) # CST's colormap |
224 | | - |
225 | | -# Plot Electric field component in 2D using imshow |
226 | | -solver.plot2D(field='E', component='z', |
227 | | - plane='XY', pos=0.5, |
228 | | - cmap=cmap, vmin=-500, vmax=500., interpolation='hanning', |
229 | | - add_patch='cavity', patch_reverse=True, patch_alpha=0.8, |
230 | | - off_screen=True, title='001_img/Ez2d') |
| 209 | + #Communicate slices |
| 210 | + communicate_ghost_cells() |
| 211 | + |
| 212 | + if n%20 == 0: |
| 213 | + Ez = compose_field('E') |
| 214 | + if rank == 0: |
| 215 | + plot_field(Ez, 'Ez', n=n, results_folder=img_folder) |
| 216 | + |
| 217 | + |
| 218 | +# Run with: |
| 219 | +# mpiexec -n 4 python 003_MPI_wakefield_simulation.py |
0 commit comments