<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Parameter Variation and Optimization
Create a machine with 4 poles
Step1:  Use a pm_sym_fast calculation at a rotor speed of 5000 1/min
Step2:  Define the variation parameters with their ranges and number of steps
Step3:  Define the objective parameters
Step4:  Combine the objective and variation parameters and set the population size which is in this case the upper limit of the bucket size (ie. the number of parallel calculations)
Step5:  Use logging to get a feedback during the calculation
Step6:  Setup up an engine the drives the calculation. Here we chose a multi-core calculation
Step7:  Define the working directory
Step8:  Start the parameter variation with the total 125 FE calculations (duration on a Linux i7 laptop with 8 cores
Step9:  Print the results in table form
Step10:  The next step will be the creation of the pareto sets. Before doing that we must define the optimization criteria. We want all values be minimize with the exception of the torque
Step11:  Now we are ready to create the pareto sets
Step12:  Show the results of the pareto front
Step13:  Display a 3D scatter plot with all results and the pareto front
Step14:  With multi objective optimization
<ASSISTANT_TASK:>
Python Code:
machine = dict(
    name="PM-4-130",
    lfe=0.1,
    poles=4,
    outer_diam=0.13,
    bore_diam=0.07,
    inner_diam=0.015,
    airgap=0.0015,
     
    stator=dict(
        num_slots=12,
        rlength=1.0,
        statorRotor3=dict(
            slot_height=0.02,
            slot_h1=0.002,
            slot_h2=0.004,
            slot_r1=0.0,
            slot_r2=0.0,
            wedge_width1=0.0,
            wedge_width2=0.0,
            middle_line=0,
            tooth_width=0.009,
            slot_top_sh=0,
            slot_width=0.003)
    ),
    magnet=dict(
        magnetSector=dict(
            magn_num=1,
            magn_width_pct=0.6,
            magn_height=0.005,
            magn_shape=0.02,
            bridge_height=0,
            magn_type=2,
            condshaft_r=0.02,
            magn_ori=1,
            magn_rfe=0.0,
            bridge_width=0,
            magn_len=1)
    ),
    windings=dict(
        num_phases=3,
        num_wires=20,
        coil_span=3.0,
        num_layers=1)
)
Explanation: Parameter Variation and Optimization
Create a machine with 4 poles:
End of explanation
simulation = dict(
    speed=5000.0 / 60,
    calculationMode="pm_sym_fast",
    magn_temp=20.0,
    wind_temp=60,
    current=28.284,
    period_frac=6,
    angl_i_up=0.0)
Explanation: Use a pm_sym_fast calculation at a rotor speed of 5000 1/min:
End of explanation
decision_vars = [
        {"steps": 5, "bounds": [3e-3, 8e-3],
         "name": "stator.statorRotor3.slot_width",
         "label": "Slot Width/m"},
        {"steps": 5, "bounds": [0.72, 0.85],
         "name": "magnet.magnetSector.magn_width_pct",
         "label": "Rel. Magnet Width"},
        {"steps": 5, "bounds": [0.024, 0.0335],
         "name": "magnet.magnetSector.magn_shape",
         "label": "Magnet Shape/m"}
    ]
Explanation: Define the variation parameters with their ranges and number of steps:
End of explanation
objective_vars = [
        {"name": "machine.torque",
         "label": "Load Torque/Nm"},
        {"name": "torque[0].ripple",
         "label": "Cogging Torque/Nm"},
        {"name": "torque[-1].ripple",
         "label": "Torque Ripple/Nm"}
    ]
Explanation: Define the objective parameters:
End of explanation
parvardef = {
    "objective_vars": objective_vars,
    "population_size": 20,
    "decision_vars": decision_vars
}
Explanation: Combine the objective and variation parameters and set the population size which is in this case the upper limit of the bucket size (ie. the number of parallel calculations):
End of explanation
import logging
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(message)s')
Explanation: Use logging to get a feedback during the calculation:
End of explanation
from femagtools.multiproc import Engine
engine = Engine()
Explanation: Setup up an engine the drives the calculation. Here we chose a multi-core calculation:
End of explanation
import pathlib
workdir = pathlib.Path.home() / 'parvar2'
workdir.mkdir(parents=True, exist_ok=True)
Explanation: Define the working directory:
End of explanation
import femagtools.parstudy
parvar = femagtools.parstudy.Grid(workdir)
results = parvar(parvardef, machine, simulation, engine)
Explanation: Start the parameter variation with the total 125 FE calculations (duration on a Linux i7 laptop with 8 cores: 4min):
End of explanation
import numpy as np
x = results['x']
f = results['f']
# print header
print(' '.join(['{:15}'.format(s)
                for s in [d['label']
                          for d in parvardef['decision_vars']] +
                [o['label']
                 for o in parvardef['objective_vars']]]))
print()
# print values in table format
for l in np.vstack((x, f)).T:
    print(' '.join(['{:15.4f}'.format(x) for x in l]))
Explanation: Print the results in table form:
End of explanation
parvardef['objective_vars'][0]['sign']=-1
Explanation: The next step will be the creation of the pareto sets. Before doing that we must define the optimization criteria. We want all values be minimize with the exception of the torque:
End of explanation
import femagtools.moproblem
import femagtools.moo
size = np.shape(f)[1]
prob = femagtools.moproblem.FemagMoProblem(parvardef['decision_vars'],
                                           parvardef['objective_vars'])
pop = femagtools.moo.Population(prob, size)
signs = [o.get('sign', 1)
         for o in parvardef['objective_vars']]
pop.populate(np.array(x).T, np.array(f), signs)
                               
px = pop.get_ranked_decisions()
po = pop.get_ranked_objectives(signs)
#
fp = dict()
xp = dict()
for k in po:
    #print("k {} len {}".format(k, len(pareto[k])))
    fp[k] = np.array(po[k]).T
    xp[k] = np.array(px[k]).T
for k in xp:
    xp[k] = [xp[k][0]*1e3, xp[k][1], xp[k][2]*1e3]
Explanation: Now we are ready to create the pareto sets:
End of explanation
np.concatenate((np.array(xp[0]), fp[0])).T
Explanation: Show the results of the pareto front
End of explanation
import matplotlib.pyplot as pl
import matplotlib.colors
import matplotlib.cm
import mpl_toolkits.mplot3d as mpl
cm = pl.get_cmap('jet')
cNorm = matplotlib.colors.Normalize(vmin=0, vmax=max(fp.keys()))
scalarMap = matplotlib.cm.ScalarMappable(norm=cNorm, cmap=cm)
fig = pl.figure()
ax = fig.add_subplot(111, projection='3d')
for k in fp:
    ax.scatter(fp[k][0], fp[k][1], fp[k][2], color=scalarMap.to_rgba(k))
ax.plot(fp[0][0], fp[0][1], fp[0][2],
        color='red', linewidth=3, label='Pareto Front')
ax.set_xlabel(parvardef['objective_vars'][0]['label'])
ax.set_ylabel(parvardef['objective_vars'][1]['label'])
ax.set_zlabel(parvardef['objective_vars'][2]['label'])
pl.legend()
pl.show()
pl.plot(fp[0][0], fp[0][2], 'o')
pl.grid()
Explanation: Display a 3D scatter plot with all results and the pareto front:
End of explanation
import femagtools.opt
import femagtools.docker
workdir = pathlib.Path.home() / 'opti'
parvardef['population_size'] = 48
parvardef['decision_vars'][0]['desc'] = 'Slot width/mm' 
parvardef['decision_vars'][1]['desc'] = 'rel. Magn. width' 
parvardef['decision_vars'][2]['desc'] = 'Magn. Shape/mm' 
parvardef['objective_vars'][0]['desc'] = 'Load Torque/Nm' 
parvardef['objective_vars'][1]['desc'] = 'Cogging Torque/Nm' 
parvardef['objective_vars'][2]['desc'] = 'Torque Ripple/Nm' 
engine = femagtools.docker.Engine(num_threads=9, port=5555)
opt = femagtools.opt.Optimizer(workdir, magnetizingCurves=dict(), magnetMat=dict())
num_generations = 6
results = opt.optimize(num_generations, parvardef, machine, simulation, engine)
pl.plot([t for t in results['f'][0] if t>0], 
        [p for p in results['f'][2] if p>0], 'o')
pl.xlabel(parvardef['objective_vars'][0]['desc'])
pl.ylabel(parvardef['objective_vars'][2]['desc'])
pl.grid()
Explanation: With multi objective optimization
End of explanation
<END_TASK>