# Advanced performance evaluator demo

# Import definitions
from definitions import *
# Import PerformanceEvaluator class
from pyopus.evaluator.performance import PerformanceEvaluator
# Import MPI support
# If MPI is imported an application not using MPI will behave correctly
# (i.e. only slot 0 will run the program) even when started with mpirun/mpiexec
from pyopus.parallel.mpi import MPI
# Import cooperative multitasking OS which will also take care of job distribution
from pyopus.parallel.cooperative import cOS
# Plotting support
from pyopus.plotter import interface as pyopl
# pprint for pretty printing data structures
from pprint import pprint
# pickle for loading results
import pickle
# NumPy
import numpy as np

if __name__=='__main__':
	# Prepare statistical parameters dictionary with nominal values
	nominalStat={ name: desc['dist'].mean() for name, desc in statParams.items() }
	
	# Prepare operating parameters dictionary with nominal values
	nominalOp={ name: desc['init'] for name, desc in opParams.items() }
	
	# Prepare initial design parameters dictionary
	initialDesign={ name: desc['init'] for name, desc in designParams.items() }
	
	# Prepare one corner
	#  defined for all simulator setups
	#  module 'tm' definines the typical MOS models
	#  operating parameters are set to nominal values
	corners={
		'nom': {
			'params': nominalOp, 
			'modules': ['tm']
		}
	}
	
	# Prepare parallel evaluation environment, cOS will use MPI as its backend
	# Remote tasks will be started in their own local folders. Before a task 
	# is started all files in the current folder of the machine that spawns a 
	# the remote task will be copied to that task's local folder (this is 
	# specified by the mirrorMap argument to MPI constructor). 
	cOS.setVM(MPI(mirrorMap={'*':'.'}))
	
	# In definitions.py measures have no corners listed, 
	# Therefore they will be evaluated across all specified corners. 
	# The storeResults argument makes the evaluator store the waveforms
	# in temporary result files in the system TMP folder (because resultsFolder
	# is set to None) with prefix 'restemp_'
	pe=PerformanceEvaluator(
		heads, analyses, measures, corners, variables=variables, 
		storeResults=True, resultsFolder=None, resultsPrefix="restemp.", 
		debug=2
	)
	
	# Evaluate, pass initial design parameters and nominal statistical parameters
	# to the netlist. 
	results, anCount = pe([initialDesign, nominalStat])
	
	# Print formatted results (values of performance measures)
	print(pe.formatResults(nMeasureName=10, nCornerName=15))
	
	# Print the list of temporary pickled result files across hosts where 
	# evaluations took place. This is a dictionary with (host, (corner, analysis))
	# for key. None for host means the results are stored on the local machine
	# (i.e. the one that invoked the evaluator). 
	print("\nResult files across hosts")
	pprint(pe.resFiles)
	
	# Extract and print the set of hosts where evaluations took place
	s=set()
	for h, k in pe.resFiles.keys():
		s.add(h)
	print("\nSet of hosts where results are stored (None is localhost):", s)
	
	# To access all result files we must first move them to the host where 
	# pe() was called. The files will be moved to the current directory 
	# (destination='.'). The collected files will have prefix 'res_'. Files 
	# will be moved (i.e. deleted from remote machines). 
	files={}
	files=pe.collectResultFiles(destination=".", prefix="res.", move=True)
	
	# Print names of pickled result files after they were moved here. 
	# This is a dictionary with (corner, analysis) for key. 
	print("\nCollected result files")
	pprint(files)
	
	# Result file with key ('nom', 'blank') in the above mentioned dictionary 
	# holds the environment where dependent measurements without were 
	# evaluated for the 'nom' corner. Load one and print the environment. 
	with open(files[('nom', 'blank')], 'rb') as f:
		noneRes=pickle.load(f)
	
	print("\n'blank' analysis measure evaluation environment")
	pprint(noneRes.evalEnvironment())
	
	# Load the ac analysis results and plot gain in dB vs frequency
	with open(files[('nom', 'ac')], 'rb') as f:
		acRes=pickle.load(f)
	
	# Extract frequency scale and gain. 
	freq=np.real(acRes.scale())
	gain=20*np.log10(np.abs(acRes.v('out')/acRes.v('inp', 'inn')))
	
	# Plot gain. 
	f1=pyopl.figure(windowTitle="AC analysis", figpx=(600,400), dpi=100)	
	pyopl.lock(True)
	if pyopl.alive(f1):
		ax1=f1.add_axes((0.12,0.12,0.76,0.76))
		ax1.semilogx(freq, gain, '-', label='gain [dB]', color=(1,0,0))
		ax1.set_xlabel('f [Hz]')
		ax1.set_ylabel('gain [dB]')
		ax1.set_title('Gain vs frequency')
		ax1.grid(True)
		pyopl.draw(f1)
	pyopl.lock(False)
	
	# Wait for the user to close the plot environment control window. 
	pyopl.join()

	# Delete collected result files
	pe.deleteResultFiles()
	
	# Cleanup temporary files generated by the evaluator
	pe.finalize()
	
	# Finalize cOS parallel environment
	cOS.finalize()
	
