#!/usr/bin/env python2.7
# -*- coding: UTF-8 -*-
#
# Copyright (C) 2010-2011 Yung-Yu Chen <yyc@solvcon.net>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from solvcon.hook import BlockHook
from solvcon.kerpak import gasdyn

"""
Calculate Mach reflection.  Parameters are non-dimensionalized.  This script
can be run directly by ./go .
"""

class ExactMovingShockHook(BlockHook):
    def __init__(self, cse, **kw):
        self.ms = kw['ms']
        self.pressure = kw['pressure']
        self.density = kw['density']
        super(ExactMovingShockHook, self).__init__(cse, **kw)
    def preloop(self):
        tem = self.ms.calc_temperature(self.pressure, self.density)
        self.info('Properties behind shock (exact):\n')
        self.info('  rho1, rho2       = %g, %g (kg/m^3)\n' % (
            self.density, self.density*self.ms.ratio_rho))
        self.info('  p1, p2           = %g, %g (Pa)\n' % (
            self.pressure, self.pressure*self.ms.ratio_p))
        self.info('  T1, T2           = %g, %g (K)\n' % (
            tem, tem*self.ms.ratio_T))
        self.info('  Ms (M1), M2, M2p = %g, %g, %g\n' % (
            self.ms.Ms, self.ms.M2, self.ms.M2p))
        self.info('  vs, v            = %g, %g (m/s)\n' % tuple(
            self.ms.calc_speeds(self.pressure, self.density)))
    postloop = preloop

def mesher(cse):
    """
    Generate a cube according to journaling file cube.tmpl.
    """
    import os
    from solvcon.helper import Cubit
    itv = float(cse.io.meshfn)/1000
    cmds = open(os.path.join(os.path.dirname(__file__),
        'mrefl.tmpl')).read() % itv
    cmds = [cmd.strip() for cmd in cmds.strip().split('\n')]
    gn = Cubit(cmds, 2)()
    return gn.toblock(bcname_mapper=cse.condition.bcmap)

def mrefl_base(casename=None, meshfn=None, gamma=None, gasconst=None,
    density=None, pressure=None, Ms=None,
    psteps=None, ssteps=None, **kw
):
    """
    Fundamental configuration of the simulation and return the case object.

    @return: the created Case object.
    @rtype: solvcon.case.BlockCase
    """
    import os
    from numpy import pi, array, sin, cos, sqrt
    from solvcon.conf import env
    from solvcon.boundcond import bctregy
    from solvcon.solver import ALMOST_ZERO
    from solvcon import hook, anchor
    from solvcon.kerpak import cuse
    # set flow properties (fp).
    ms = gasdyn.MovingShock(ga=gamma, Ms=Ms, gasconst=gasconst)
    vs, v = ms.calc_speeds(pressure, density)
    fpi = {
        'gamma': gamma, 'rho': density, 'v2': 0.0, 'v3': 0.0, 'p': pressure,
    }
    fpi['v1'] = 0.0
    fpb = fpi.copy()
    fpb['p'] = pressure*ms.ratio_p
    fpb['rho'] = density*ms.ratio_rho
    fpb['v1'] = v
    # set up BCs.
    bcmap = {
        'upper': (bctregy.GasdynWall, {},),
        'left': (bctregy.GasdynInlet, fpb,),
        'right': (bctregy.CuseNonrefl, {},),
        'lower': (bctregy.GasdynWall, {},),
        'ramp': (bctregy.GasdynWall, {},),
    }
    # set up case.
    basedir = os.path.abspath(os.path.join(os.getcwd(), 'result'))
    cse = gasdyn.GasdynCase(basedir=basedir, rootdir=env.projdir,
        basefn=casename, mesher=mesher, meshfn=meshfn, bcmap=bcmap, **kw)
    # informative.
    cse.runhooks.append(hook.ProgressHook,
        psteps=psteps, linewidth=ssteps/psteps,
    )
    cse.runhooks.append(cuse.CflHook, fullstop=False, psteps=ssteps,
        cflmax=10.0, linewidth=ssteps/psteps,
    )
    cse.runhooks.append(cuse.ConvergeHook, psteps=ssteps)
    cse.runhooks.append(hook.SplitMarker)
    cse.runhooks.append(hook.GroupMarker)
    cse.runhooks.append(hook.BlockInfoHook, psteps=ssteps, show_bclist=True)
    # initializer.
    cse.runhooks.append(anchor.FillAnchor, keys=('soln',), value=ALMOST_ZERO)
    cse.runhooks.append(anchor.FillAnchor, keys=('dsoln',), value=0)
    cse.runhooks.append(gasdyn.UniformIAnchor, **fpi)
    # post processing.
    ## collect variables.
    varlist = list()
    for var in ['soln', 'dsoln']:
        varlist.append((var, {'inder': False, 'consider_ghost': True}))
    for var in ['rho', 'p', 'T', 'ke', 'M', 'sch', 'v']:
        varlist.append((var, {'inder': True, 'consider_ghost': True}))
    cse.runhooks.append(hook.CollectHook, psteps=ssteps, varlist=varlist)
    ## execution order is reversed for postloop.
    cse.runhooks.append(ExactMovingShockHook,
        ms=ms, density=density, pressure=pressure)
    cse.runhooks.append(gasdyn.GasdynOAnchor, gasconst=gasconst, rsteps=ssteps)
    ## output.
    cse.runhooks.append(hook.MarchSave,
        psteps=ssteps, binary=True, cache_grid=True)
    return cse

if __name__ == '__main__':
    import sys
    if len(sys.argv) == 2 and sys.argv[1] == 'fine':
        cse = mrefl_base('mreflf', meshfn='5', gamma=1.4, gasconst=286.9,
            density=1.205, pressure=101325., Ms=1.5,
            time_increment=2.75e-6, steps_run=1000, ssteps=100, psteps=2)
        cse.init()
        cse.run()
        cse.cleanup()
    cse = mrefl_base('mrefl', meshfn='10', gamma=1.4, gasconst=286.9,
        density=1.205, pressure=101325., Ms=1.5,
        time_increment=6.e-6, steps_run=500, ssteps=50, psteps=1)
    cse.init()
    cse.run()
    cse.cleanup()
    cse = mrefl_base('mrefls', meshfn='10', gamma=1.4, gasconst=286.9,
        density=1.205, pressure=101325., Ms=8.0,
        time_increment=1.e-6, steps_run=500, ssteps=50, psteps=1)
    cse.init()
    cse.run()
    cse.cleanup()
