#######################################################################
#
# $Id: Simulation.py,v 1.3 2008-03-28 06:25:37 lorenz Exp $
# 
# Copyright (C) 2004-2008 Petr Lorenz
# All rights reserved.
# 
# ##### BEGIN LICENSE BLOCK #####
#
# Version: MPL 1.1 / GPL 2.0 / LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
# 
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
# License for the specific language governing rights and limitations
# under the License.
# 
# The Original Code is Petr Lorenz emGine Graphical User Interface (GUI).
# 
# The Initial Developer of the Original Code is Petr Lorenz.
# Portions created by the Initial Developer are Copyright (C) 2004-2008.
# All Rights Reserved.
# 
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the  "GPL"), or the
# GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
# case the provisions of the GPL or the LGPL are applicable instead of those
# above.  If you wish to allow use of your version of this file only under the
# terms of either the GPL or the LGPL and not to allow others to use your
# version of this file under the MPL, indicate your decision by deleting the
# provisions above and replace them with the notice and other provisions
# required by the GPL or LGPL.  If you do not delete the provisions above, a
# recipient may use your version of this file under the terms of any one of the
# MPL License, the GPL or the LGPL.
#
# ##### END LICENSE BLOCK #####
#
#######################################################################
# 
# $Log: Simulation.py,v $
# Revision 1.3  2008-03-28 06:25:37  lorenz
# - Perspective/Orthogonal view
# - Top/Bottom/Front/Back/Left/Right views
# - Boundary conditions setup
# - Platform Info
#
# Revision 1.2  2008-02-07 05:45:25  lorenz
# * Fixed 'Simulation Boundary View' check problem
#   when closing a project.
#
# * Fixed Bounding Box problems when moving/editing a geometry.
#
# Revision 1.1.1.1  2008-01-28 06:15:35  lorenz
# Import emGine GUI v. 0.7.1
#
# Revision 1.5  2007-07-02 06:37:18  lorenz
# Clean up of old code.
#
# Revision 1.4  2007-06-22 04:07:19  lorenz
# Calculation of dt (delta t) from first column of TDSignal.
#
# Small changes in the computation of S-Parameters.
#
# Revision 1.3  2007-06-18 03:55:36  lorenz
# Status bar extended
#
# Revision 1.2  2007-05-14 04:53:51  lorenz
# New code for multiport time-domain signals and S-parameters
#
# Revision 1.1.1.1  2007-05-08 06:22:35  lorenz
# Initial Import
#
#######################################################################

#
# June 2006 - November 2007
#
# Simulation class
#

#import os
import platform

from emGineGUI.run import runMesher
from emGineGUI.run import runYatsim

from emGineGUI.run import runTLMGine

from ResultTDSignal import ResultTDSignal
from ResultImpedance import ResultImpedance
from ResultSParameters import ResultSParameters

class Simulation:
  def __init__(self, log_device, output_file_suffix = ''):
    self.log_device = log_device
    self.output_file_suffix = output_file_suffix

    self.platform = {}
    self.timesteps = 500
    self.fmin = 1e8
    self.fmax = 3e9
    self.cells_per_lambda = 10
    self.dl = 3e7/self.fmax		# uniform spatial discretization in [m]
    self.bdist = 0                  	# distance from the boundary in cells
    self.simulator = None
    self.mesher = None
    self.yatsim = None
    self.tlmGine = None
    self.results = {}
    self.number_of_ports = 1		# number of ports to excite (default = 1)
    self.ports_to_excite = [1]		# list of ports which will be excited (default = 1)
    self.have_results = False		# flag if results are available
    self.deembedding = [False]		# list of flags holding deembedding status for ports
    #self.state = 

    self.platform["name"] = 'unknown'
    self.platform["execsuffix"] = ''
    self.platform["pathquote"] = ''

    plt_sys = platform.system()
    if (platform.platform().find("Windows") != -1):
      plt_sys = "Windows"

    if plt_sys == 'Linux':
      self.platform["name"] = 'LINUX'

    elif ((plt_sys == 'Windows') or (plt_sys == 'Microsoft')):
      self.platform["name"] = 'WIN32'
      self.platform["execsuffix"] = '.exe'
      self.platform["pathquote"] = '\"'

    elif plt_sys == 'Darwin':
      self.platform["name"] = 'DARWIN'

    #print "Identified platform: ", self.platform

    self.mesher = runMesher.runMesher(self.platform)	# the mesher
    self.yatsim = runYatsim.runYatsim(self.platform)	# the yatsim TLM simulator
    self.tlmGine = runTLMGine.runTLMGine(self.platform, self.log_device)

    self.simulator = self.tlmGine

  def set_boundary_distance(self, bdist_):
    self.bdist = int(bdist_)
    
  def set_fmin_fmax(self, fmin_, fmax_):
    self.fmin = float(fmin_)
    self.fmax = float(fmax_)

  def set_spatial_resolution(self, ratio_):
    self.dl = 3e8/(self.fmax*ratio_)
    self.cells_per_lambda = ratio_

  def set_number_of_ports(self, nports_):
    self.number_of_ports = nports_
    self.deembedding_turn_off()

  def deembedding_turn_on(self, deemb_ports = None):
    if not deemb_ports:
      self.deembedding = list()
      for i in range(self.number_of_ports):
	self.deembedding.append(True)
    else:
      # FIXME: finish
      None

  def deembedding_turn_off(self, deemb_ports = None):
    if not deemb_ports:
      self.deembedding = list()
      for i in range(self.number_of_ports):
	self.deembedding.append(False)
    else:
      # FIXME: finish
      None

  def deembedding_on(self, port_nr = None):
    deemb_on = False

    if not port_nr:
      deemb_on = True
      for state in self.deembedding:
	deemb_on = (deem_on and state)
    else:
      deemb_on = self.deembedding[port_nr]

    return deemb_on

  ###-------------------------------
  ### start the actual simulation
  ###-------------------------------
  def simulate(self, emm_file):
    #self.log_device.clear()

    print "------------------------------------------------------------------"
    self.print_info()

    if self.simulator.get_id_name() == "tlmGine":
      print "------------------------------------------------------------------"
      print "*** Simulating file: ", emm_file
      sim_results = self.simulator.run(emm_file, self.output_file_suffix)

    else:
      print "------------------------------------------------------------------"
      print "*** Meshing file: ", emm_file
      I3Dfname = self.mesher.run(emm_file)
      self.results["I3Dfname"] = I3Dfname

      print "------------------------------------------------------------------"
      print "*** Simulating file: ", I3Dfname
      self.yatsim.run(I3Dfname)
      print "------------------------------------------------------------------"
      td_fname = I3Dfname.replace("I3D", "EH")
      #self.results["files"].append(I3Dfname.replace("I3D", "EX"))
      #self.results["files"].append(I3Dfname.replace("I3D", "EH"))

    # recalculate new results
    print "Simulation results: ", sim_results

    #self.recalculate_results(sim_results)
    return sim_results

  def print_info(self):
    print "*** Simulation::print_info()"
    print "\ttimesteps =", self.timesteps
    print "\tfmin = ", self.fmin
    print "\tfmax = ", self.fmax
    print "\tcells_per_lambda =", self.cells_per_lambda
    print "\tdl =", self.dl
    print "\tbdist = ", self.bdist
    print "\tnumber_of_ports = ", self.number_of_ports

  def get_platform_info(self):
    info = "Name: " + str(self.platform["name"]) + "\n"
    info += "Exec suffix: " + str(self.platform["execsuffix"]) + "\n"
    info += "Path quote: " + str(self.platform["pathquote"]) + "\n"
    return info

  def set_solver_setup(self, setup):
    self.timesteps = int(setup[0])
    self.fmin = float(setup[1])
    self.fmax = float(setup[2])
    self.set_spatial_resolution(float(setup[3]))
    self.bdist = int(setup[4])

  def get_solver_setup(self):
    fmin_str = "%g" %self.fmin
    fmax_str = "%g" %self.fmax
    return [self.timesteps, fmin_str, fmax_str, self.cells_per_lambda, self.bdist]

  def get_results(self):
    return self.results

  def update_results(self):
    if self.have_results:
      self.recalculate_results()

  def recalculate_results(self, new_results = None, is_reference = False, reference_results = None):
    print "Simulation::recalculate_results recalculating results with:"
    print "\tnew_results : " , new_results
    print "\tis_reference : " , is_reference
    print "\treference_results : " , reference_results

    # TODO: the code is Q&D, use GroupResult
    #
    # time-domain signals
    #
    if new_results:
      self.results["files"] = list()	# erase the list of files
      self.results["resobjs"] = list()	# just for testing !!!
      self.results["tdsignals"] = list()	# list of time-domain signals
      self.results["sparams"] = list()	# list of S-parameters
      self.results["zparams"] = list()	# list of impedances

      tds_fname = new_results["tds_file"][0]
      ind_col = 2			# column index in the result time-domain file
      res_tdsignal = ResultTDSignal(tds_fname, ind_col, 'TDSignal1')
      self.results["tdsignals"].append(res_tdsignal)

      if self.deembedding[0]:	# deembedding on for port 1 ?
	ind_col += 1
	res_tdsignal1_2 = ResultTDSignal(tds_fname, ind_col, 'TDSignal1_2')
	self.results["tdsignals"].append(res_tdsignal1_2)

      if (self.number_of_ports > 1):
	ind_col += 1
	res_tdsignal2 = ResultTDSignal(tds_fname, ind_col, 'TDSignal2')	# third column for signal on port 2 
	self.results["tdsignals"].append(res_tdsignal2)

	if self.deembedding[1]:		# deembedding on for port 2 ?
	  ind_col += 1
	  res_tdsignal2_2 = ResultTDSignal(tds_fname, ind_col, 'TDSignal2_2')
	  self.results["tdsignals"].append(res_tdsignal2_2)

      if not is_reference:
	#
	# S-parameters
	#
	if ( (self.number_of_ports == 2) and (len(self.ports_to_excite) == 1) ):
	  print "Computing 2 Port S-Parameters with one port excited"

	  tdsignals = list()
	  for res in self.results["tdsignals"]:
	    tdsignals.append(res.get_data())

	  ref_tdsignals = None
	  if reference_results:
	    ref_tdsignals = list()
	    for res in reference_results["tdsignals"]:
	      ref_tdsignals.append(res.get_data())

	  ## tdsignals.append(res_tdsignal.get_data())
	  ## tdsignals.append(res_tdsignal2.get_data())

	  res_spar = ResultSParameters(tdsignals, reference_data_ = ref_tdsignals)
	  res_spar.set_fmin_fmax(self.fmin, self.fmax)
	  self.results["sparams"].append(res_spar)
	else:
	  res_spar = ResultSParameters(res_tdsignal.get_data())
	  res_spar.set_fmin_fmax(self.fmin, self.fmax)
	  self.results["sparams"].append(res_spar)

	#
	# Impedances - Z-parameters
	#
	res_imp = ResultImpedance(res_spar)
	self.results["zparams"].append(res_imp)

    else:
      ### res_tdsignal = self.results["tdsignals"][0]
      ### if (self.number_of_ports > 1):
      ###   res_tdsignal2 = self.results["tdsignals"][1]

      if not is_reference:
	for res_spar in self.results["sparams"]:
	  res_spar.set_fmin_fmax(self.fmin, self.fmax)


    #############################################
    # Update all results
    #############################################

    # update time-domain signals
    #print "self.results[tdsignals] : ", self.results["tdsignals"]
    for res in self.results["tdsignals"]:
      res.update()

    if not is_reference:
      # computing dt from TDSignal (assumed to be in first column !)
      #
      time_signal = self.results["tdsignals"][0].get_data().get_xarray()
      dt = time_signal[1] - time_signal[0]
      res_spar.set_dt(dt)

      # update S-parameter results
      for res in self.results["sparams"]:
	res.update()

      # update Z-parameters (impedances)
      for res in self.results["zparams"]:
	res.update()

    # If all was OK, then we can assume that results are available
    self.have_results = True

    return self.results
