Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dark-Elektron committed Aug 16, 2024
1 parent e24ed93 commit b773e3e
Show file tree
Hide file tree
Showing 22 changed files with 2,454 additions and 2,996 deletions.
95 changes: 73 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,28 @@ cavs['TESLA'].plot_fields(mode=1, which='H')
> Meshes and fields are properties of a Cavity object and not a Cavities object. Therefore, to visualise the mesh
> and field profiles, use the `Cavity` object `name` or corresponding index.
---------------

## Configuration dictionaries

Configuration dictionaries are used to specify simulation inputs. Each simulation has its specific configuration dictonary
format and content. The configuration files are written so that it is logical. For example, a simple eigenmode simulation
config file is shown below:


We will talk about uncertainty quantification (UQ) later but if we were to equip the eigenmode analysis with UQ,
we only need include a uq_config dictioanry as an entry into the eigenmode_config dictionary. For example:


The same goes for wakefield analysis and tuning. For optimisation control, consider that in an optimisation, we
want to optimise for a particular frequency so for any parameter set generated, we want to first tunr. Therefore,
an optimisation_config dictionary will contain a tune_config. The depending on the objectives the optimisation_config
then includes an eigenmode_config and or a wakefield_config field. It also follows that the eigenmode_config and
wakefield_config can also contain uq_config fields.

> [!NOTE]
> For eigenmode and wakefield analysis, if a configuration dictionary is not entered, default values are used.
---------------
## Cavity Tuning

Expand Down Expand Up @@ -308,7 +330,14 @@ op_points = {
"Nb [1e11]": 2.76 # <- Bunch population
}
}
cavs.run_wakefield(operating_points=op_points)
wakefield_config = {
'bunch_length': 25,
'wakelength': 50,
'processes': 2,
'rerun': True,
'operating_points': op_points,
}
cavs.run_wakefield(wakefield_config)
pp.pprint(cavs.abci_qois)
```

Expand All @@ -333,18 +362,27 @@ are the fundamental `freq [MHz]`, `Epk/Eacc []`, `Bpk/Eacc [mT/MV/m]`, `R/Q [Ohm
analysis The algorithm currently implemented is genetic algorithm. The optimisation settings are controlled
using a configuration dictionary. The most important parameters for the algorithm are

- `cell type`: The options are `mid-cell`, `end-cell` and `end-end-cell` depending on the parameterisation of the cavity
- `cell_type`: The options are `mid-cell`, `end-cell` and `end-end-cell` depending on the parameterisation of the cavity
geometry. See Fig []. Default is `mid-cell`.
```
'cell type': 'mid-cell'
'cell_type': 'mid-cell'
```
- `tune variable`: Target operating frequency of the cavity.
- `freqs`: Target operating frequency of the cavity.
```
'tune variable': 'Req'
'parameters': 'Req'
```
- 'tune freq.': Target operating frequency of the cavity.
```
`tune freq.`: 1300
`freqs`: 1300
```

The preceeding parameters belong to the tune_config dictionary and so are entered this way in the optimisation_config
```
'tune_config': {
'freqs': 801.58,
'parameters': 'Req',
'cell_types': cell_type
}
```
- `bounds`: This defines the optimisation search space. All geometric variables must be entered.
Note that variables excluded from optimisation should have identical upper and lower bounds..
Expand Down Expand Up @@ -372,31 +410,34 @@ using a configuration dictionary. The most important parameters for the algorith
```
The third parameter for the impedances `ZL`, `ZT` define the frequency interval for which to evaluate the peak impedance.
The algorithm specific entries include
- `initial points`: The number of initial points to be genereated.
- `initial_points`: The number of initial points to be genereated.
- `method`: Method of generating the initial points. Defaults to latin hypercube sampling (LHS).
- `no. of generations`: The number of generations to be analysed. Defaults to 20.
- `crossover factor`: The number of crossovers to create offsprings.
- `elites for crossover`: The number of elites allowed to produce offsprings.
- `mutation factor`: The number of mutations to create offsprings.
- `chaos factor`: The number of new random geometries included to improve diversity.
- `no_of_generations`: The number of generations to be analysed. Defaults to 20.
- `crossover_factor`: The number of crossovers to create offsprings.
- `elites_for_crossover`: The number of elites allowed to produce offsprings.
- `mutation_factor`: The number of mutations to create offsprings.
- `chaos_factor`: The number of new random geometries included to improve diversity.

```
'initial points': 5,
'initial_points': 5,
'method': {
'LHS': {'seed': 5},
},
'no. of generations': 5,
'crossover factor': 5,
'elites for crossover': 2,
'mutation factor': 5,
'chaos factor': 5,
'no_of_generations': 5,
'crossover_factor': 5,
'elites_for_crossover': 2,
'mutation_factor': 5,
'chaos_factor': 5,
```
Putting it all together, we get
```python
optimisation_config = {
'cell type': 'mid-cell',
'tune variable': 'Req',
'tune freq.': 1300,
'tune_config': {
'freqs': 1300,
'parameters': 'Req',
'cell_types': 'mid-cell',
'processes': 1
},
'bounds': {'A': [20.0, 80.0],
'B': [20.0, 80.0],
'a': [10.0, 60.0],
Expand Down Expand Up @@ -428,7 +469,7 @@ cavs = Cavities()
# must first save cavities
cavs.save('D:\Dropbox\CavityDesignHub\MuCol_Study\SimulationData\ConsoleTest')

cavs.run_optimisation(config=optimisation_config)
cavs.run_optimisation(optimisation_config)
```

## Uncertainty Quantification Capabilities
Expand Down Expand Up @@ -469,6 +510,12 @@ uq_config = {
'cell type': 'mid-cell',
'cell complexity': 'simplecell'
}
eigenmode_config = {
'processes': 3,
'rerun': True,
'boundary_conditions': 'mm',
'uq_config': uq_config
}

cavs.run_eigenmode(uq_config=uq_config)
pp.pprint(cavs.eigenmode_qois)
Expand Down Expand Up @@ -498,4 +545,8 @@ Simulations using `cavsim2d` are easily parallelised by specifying a value for t
specifying the amount of processes the analysis should use. If UQ is enabled, an extra level of parallelisation
can be achieved by passing `processes` also in the uq configuration dictionary. The number of processes defaults to 1.

## Understanding the geometry types



## Folder structure
33 changes: 16 additions & 17 deletions cavsim2d/analysis/tune/pyTuner.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ def __init__(self):
self.plot = None

def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc,
sim_folder, parentDir, projectDir, iter_set, proc=0, conv_list=None):

sim_folder, parentDir, projectDir, proc=0):
convergence_list = []
# tv => tune variable
indx = VAR_TO_INDEX_DICT[tune_var]

Expand Down Expand Up @@ -73,12 +73,12 @@ def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc
beampipes = 'both'

res = ngsolve_mevp.cavity(1, 1, mid, left, right,
n_modes=1, fid=fid, f_shift=0, bc=bc, beampipes=beampipes,
sim_folder=sim_folder, parentDir=parentDir, projectDir=projectDir)
n_modes=1, fid=fid, f_shift=0, bc=bc, beampipes=beampipes,
sim_folder=sim_folder, parentDir=parentDir, projectDir=projectDir)
if not res:
# make functionality later for restart for the tune variable
error('\tCannot continue with the tuning geometry -> Skipping degenerate geometry')
return 0, 0, 0
return 0, 0, [], []

tv = tuned_cell[indx]

Expand All @@ -90,19 +90,19 @@ def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc
tv_list.append(tv)

# first shot
tv = tv + TUNE_VAR_STEP_DIRECTION_DICT[tune_var]*0.05*tuned_cell[indx]
tv = tv + TUNE_VAR_STEP_DIRECTION_DICT[tune_var] * 0.05 * tuned_cell[indx]
tuned_cell[indx] = tv

enforce_Req_continuity(mid, left, right, cell_type)

# run
res = ngsolve_mevp.cavity(1, 1, mid, left, right,
n_modes=1, fid=fid, f_shift=0, bc=bc, beampipes=beampipes,
sim_folder=sim_folder, parentDir=parentDir, projectDir=projectDir)
n_modes=1, fid=fid, f_shift=0, bc=bc, beampipes=beampipes,
sim_folder=sim_folder, parentDir=parentDir, projectDir=projectDir)
if not res:
# make functionality later for restart for the tune variable
error('Cannot continue with the tuning geometry -> Skipping degenerate geometry')
return 0, 0, 0
return 0, 0, [], []

# get results and compare with set value
with open(fr"{projectDir}/SimulationData/{sim_folder}/{fid}/monopole/qois.json") as json_file:
Expand All @@ -112,7 +112,6 @@ def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc
freq_list.append(freq)
tv_list.append(tv)

tol = iter_set[1]
tol = 1e-2 # for testing purposes to reduce tuning time.
# max_iter = iter_set[2]

Expand All @@ -125,7 +124,7 @@ def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc
# solve for coefficients
coeffs = np.linalg.solve(mat, np.array(tv_list)[-2:])

max_step = 0.2*tuned_cell[indx] # control order of convergence/stability with maximum step
max_step = 0.2 * tuned_cell[indx] # control order of convergence/stability with maximum step
# bound_factor = 0.1
# bound the maximum step
if coeffs[0] * target_freq - (tv - coeffs[1]) > max_step:
Expand All @@ -145,8 +144,8 @@ def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc

# run
res = ngsolve_mevp.cavity(1, 1, mid, left, right,
n_modes=1, fid=fid, f_shift=0, bc=bc, beampipes=beampipes,
sim_folder=sim_folder, parentDir=parentDir, projectDir=projectDir)
n_modes=1, fid=fid, f_shift=0, bc=bc, beampipes=beampipes,
sim_folder=sim_folder, parentDir=parentDir, projectDir=projectDir)
if not res:
# make functionality later for restart for the tune variable
error('Cannot continue with the tuning of this geometry -> Skipping degenerate geometry')
Expand Down Expand Up @@ -188,10 +187,11 @@ def tune(self, par_mid, par_end, tune_var, target_freq, cell_type, beampipes, bc
# plt.scatter(tv_list, freq_list)
# plt.show()
# update convergence list
if conv_list is not None:
conv_list.extend([tv_list, freq_list])
convergence_list.extend([tv_list, freq_list])

return tv_list[key], freq_list[key], abs_err_list
# save convergence information
conv_dict = {f'{tune_var}': convergence_list[0], 'freq [MHz]': convergence_list[1]}
return tv_list[key], freq_list[key], conv_dict, abs_err_list

@staticmethod
def all_equal(iterable):
Expand All @@ -204,4 +204,3 @@ def write_output(tv_list, freq_list, fid, projectDir):

with open(fr"{projectDir}\SimulationData\SLANS_opt\{fid}\convergence_output.json", "w") as outfile:
json.dump(dd, outfile, indent=4, separators=(',', ': '))

50 changes: 18 additions & 32 deletions cavsim2d/analysis/tune/tuner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ def __init__(self):
pass

def tune_ngsolve(self, pseudo_shape_space, bc, parentDir, projectDir, filename, resume="No",
proc=0, sim_folder='NGSolveMEVP', tune_variable='Req', iter_set=None, cell_type='Mid Cell',
progress_list=None, convergence_list=None, save_last=True, n_cell_last_run=1):
proc=0, sim_folder='NGSolveMEVP', tune_variable='Req', cell_type='Mid Cell',
save_last=True, n_cell_last_run=1):

# tuner
abs_err_list, conv_dict = [], []
pytune_ngsolve = PyTuneNGSolve()

start = time.time()
Expand All @@ -35,13 +36,12 @@ def tune_ngsolve(self, pseudo_shape_space, bc, parentDir, projectDir, filename,

existing_keys = list(population.keys())

progress = 0
error_msg1 = 1
error_msg2 = 1

for key, pseudo_shape in pseudo_shape_space.items():
A_i, B_i, a_i, b_i, Ri_i, L_i, Req = pseudo_shape['IC'][:7]
A_o, B_o, a_o, b_o, Ri_o, L_o, Req_o = pseudo_shape['OC'][:7] # Req here is none but required
A_i, B_i, a_i, b_i, Ri_i, L_i, Req = np.array(pseudo_shape['IC'])[:7]
A_o, B_o, a_o, b_o, Ri_o, L_o, Req_o = np.array(pseudo_shape['OC'])[:7] # Req here is none but required

beampipes = pseudo_shape['BP']
target_freq = pseudo_shape['FREQ']
Expand All @@ -68,14 +68,16 @@ def tune_ngsolve(self, pseudo_shape_space, bc, parentDir, projectDir, filename,
# edit to check for key later
if key not in existing_keys:
try:
tune_var, freq, abs_err_list = pytune_ngsolve.tune(inner_cell, outer_cell, tune_variable,
target_freq,
cell_type, beampipes, bc, sim_folder,
parentDir, projectDir, iter_set=iter_set,
proc=proc,
conv_list=convergence_list)
tune_var, freq, conv_dict, abs_err_list = pytune_ngsolve.tune(inner_cell, outer_cell,
tune_variable,
target_freq,
cell_type, beampipes, bc,
sim_folder,
parentDir, projectDir,
proc=proc)
except FileNotFoundError:
tune_var, freq = 0, 0

if tune_var != 0 and freq != 0:
if cell_type.lower() == 'mid cell' or cell_type.lower() == 'mid-cell' or cell_type.lower() == 'mid_cell':
tuned_mid_cell = pseudo_shape['IC'][:7]
Expand Down Expand Up @@ -146,14 +148,12 @@ def tune_ngsolve(self, pseudo_shape_space, bc, parentDir, projectDir, filename,
d_tune_res = {'IC': list(tuned_mid_cell), 'OC': list(tuned_end_cell),
'OC_R': list(tuned_end_cell),
'TUNED VARIABLE': tune_variable, 'CELL TYPE': cell_type, 'FREQ': freq}
self.save_tune_result(d_tune_res, 'tune_res.json', projectDir, key, sim_folder)
save_tune_result(d_tune_res, 'tune_res.json', projectDir, key, sim_folder)

# save convergence information
if convergence_list:
conv_dict = {f'{tune_variable}': convergence_list[0], 'freq [MHz]': convergence_list[1]}
abs_err_dict = {'abs_err': abs_err_list}
self.save_tune_result(conv_dict, 'convergence.json', projectDir, key, sim_folder)
self.save_tune_result(abs_err_dict, 'absolute_error.json', projectDir, key, sim_folder)
abs_err_dict = {'abs_err': abs_err_list}
save_tune_result(conv_dict, 'convergence.json', projectDir, key, sim_folder)
save_tune_result(abs_err_dict, 'absolute_error.json', projectDir, key, sim_folder)

done(f'Done Tuning Cavity {key}: {result}')

Expand All @@ -168,24 +168,10 @@ def tune_ngsolve(self, pseudo_shape_space, bc, parentDir, projectDir, filename,
except FileNotFoundError:
continue

# update progress
progress_list.append((progress + 1) / total_no_of_shapes)

# Update progressbar
progress += 1

# print("Saving Dictionary", f"shape_space{proc}.json")◙
# print("Done saving")

end = time.time()

runtime = end - start
info(f'\tProcessor {proc} runtime: {runtime} s')

@staticmethod
def save_tune_result(d, filename, projectDir, key, sim_folder='SLAN_Opt'):
with open(fr"{projectDir}\SimulationData\{sim_folder}\{key}\{filename}", 'w') as file:
file.write(json.dumps(d, indent=4, separators=(',', ': ')))
info(f'\tProcessor {proc} runtime: {runtime}s')

# if __name__ == '__main__':
# #
Expand Down
3 changes: 2 additions & 1 deletion cavsim2d/analysis/uq/dakota_scripts/generate_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
import numpy as np
import pandas as pd

from cavsim2d.utils.shared_functions import *

# Get current path
sCurPath = os.path.abspath(".")

if len(sys.argv) != 3:
print("Usage: python myscript.py param_file output_file partitions")
error("Usage: python myscript.py param_file output_file partitions")
sys.exit(1)

param_file = sys.argv[1]
Expand Down
4 changes: 0 additions & 4 deletions cavsim2d/analysis/uq/dakota_scripts/py_dakota.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ def read_output_from_cst_sweep(sim_folder, folders, requests):
d = pd.DataFrame()
for folder in folders:
d = pd.concat([d, pd.read_csv(fr"{sim_folder}\{folder}\Export\{request}.txt", sep="\t", header=None)])
# print(d)

df_[request] = d.loc[:, 1]

Expand Down Expand Up @@ -53,12 +52,9 @@ def read_output_from_cst_sweep(sim_folder, folders, requests):
df_data = pd.read_excel(fr"{filename}", 'Sheet1', engine='openpyxl')

for indx, row in df_data.iterrows():
# print(indx, row.tolist(), pars_in.tolist())
# match input parameters to output
# print(np.around(row.tolist()[:num_in_vars], 3), np.around(pars_in.tolist(), 3))
tolerance = 1e-3
if np.allclose(np.around(row.tolist()[:num_in_vars], 3), np.around(pars_in.tolist(), 3), rtol=tolerance, atol=tolerance):
print("Got here")
# write output
out = row.tolist()[num_in_vars:]
with open(output_file, 'w') as f:
Expand Down
Loading

0 comments on commit b773e3e

Please sign in to comment.