Exemplo n.º 1
0
 def __init__(self):
     self.res = Resources()
     self.main = BrowserWidget()
     self.main.set_html(self.res.getStartScreen())
     self.main.set_form_handler(self._create_snap_form_handler())
     self.main.show()
     self.snapRunning = "inactive"
     self.lastOutputDir = ""
     self.lastQDict = {}
Exemplo n.º 2
0
 def __init__(self):
     self.res = Resources()
     self.lastOutputDir = os.path.join(
         self.res.getSnapOutputDir(),
         "{0}_{1}".format("backtrack", strftime("%Y-%m-%dT%H%M%S",
                                                gmtime())))
     self.main = BrowserWidget()
     self.main.set_html(self.res.getStartScreenInverse().replace(
         "OUTPUTDIR", self.lastOutputDir))
     self.main.set_form_handler(self._create_snap_form_handler())
     self.main.show()
     self.snapRunning = "inactive"
     self.lastQDict = {}
Exemplo n.º 3
0
 def getGlobalMeteoResources():
     '''retrieve the GlobalMeteoResources from internal resources'''
     ecres = Resources()
     res = Snappy.MeteorologyCalculator.GlobalMeteoResource()
     res.indirs = ecres.MET_GLOBAL_INPUTDIRS[MetModel.NrpaEC0p1Global]
     res.pathglob = "ec_atmo_0_1deg_????????T??????Z_3h.nc"
     res.pathptime = "ec_atmo_0_1deg_%Y%m%dT%H%M%SZ_3h.nc"
     res.outputdir = ecres.getSnapOutputDir()
     res.output_filename_pattern = ecres.EC_FILENAME_PATTERN
     res.domainHeight = ecres.ecDomainHeight
     res.domainWidth = ecres.ecDomainWidth
     res.domainDeltaX = ecres.ecDomainRes
     res.domainDeltaY = ecres.ecDomainRes
     res.timeoffset = 3  # required offset between reference-time and first useful startup-time
     return res
Exemplo n.º 4
0
 def getGlobalMeteoResources():
     '''retrieve the GlobalMeteoResources from internal resources'''
     gres = Resources()
     res = Snappy.MeteorologyCalculator.GlobalMeteoResource()
     res.indirs = gres.MET_GLOBAL_INPUTDIRS[MetModel.Icon0p25Global]
     # icon_20200427T00Z.nc
     res.pathglob = "icon_????????T??Z.nc"
     res.pathptime = "icon_%Y%m%dT%HZ.nc"
     res.outputdir = gres.getSnapOutputDir()
     res.output_filename_pattern = gres.MET_FILENAME_PATTERN[
         MetModel.Icon0p25Global]  # keeping filename for extracted data
     res.domainHeight = gres.ecDomainHeight  # reuse domainHeight/Width
     res.domainWidth = gres.ecDomainWidth
     res.domainDeltaX = 0.25
     res.domainDeltaY = 0.25
     res.timeoffset = 0  # required offset between reference-time and first useful startup-time
     return res
Exemplo n.º 5
0
    def calc(self, proc=None):
        '''run the calculation of ec-data if required.
        
        Args:
           proc -- A QProcess, which will be used to run a longer process in the background.
                  STDERR/STDOUT and signal-handler should be set. If proc is None, the
                  subprocess will be run in the current-process. If proc is set, the caller
                  needs to wait for the proc to finish before calling other methods of this object
'''
        if (not self.must_calc()):
            return

        precommand = '''#! /bin/bash
. /usr/share/modules/init/bash
module load fimex/1.4.2
export OMP_NUM_THREADS=1

cd {outputdir} || exit 1
umask 000
touch running
cp {resdir}/icon_fimex.cfg .
cp {resdir}/icon_sigma_hybrid.ncml .
tmpfile=out$$.nc4
echo "Preprocessing 7days icon meteorology, please wait ca. 3min"
fimex -c icon_fimex.cfg \
      --input.file={globalfile} \
      --interpolate.xAxisValues={xAxisValues} \
      --interpolate.yAxisValues={yAxisValues} \
      --output.file=$tmpfile \
      --output.type=nc4 \
      && mv $tmpfile {outputfile}
rm {outputdir}/running
'''
        command = precommand.format(resdir=Resources().directory,
                                    xAxisValues="{},{},...,{}".format(
                                        self.lon0,
                                        self.lon0 + self.res.domainDeltaX,
                                        self.lon0 + self.res.domainWidth),
                                    yAxisValues="{},{},...,{}".format(
                                        self.lat0,
                                        self.lat0 + self.res.domainDeltaY,
                                        self.lat0 + self.res.domainHeight),
                                    globalfile=self.globalfile,
                                    outputfile=self.files[0],
                                    outputdir=self.outputdir)
        scriptFile = os.path.join(self.outputdir, "command.sh")
        with open(scriptFile, 'w') as script:
            script.write(command)

        if proc is None:
            subprocess.call(['/bin/bash', scriptFile])
        else:
            self.proc = proc  # make sure proc lives long enough
            proc.start('/bin/bash', [scriptFile])

        return
Exemplo n.º 6
0
def convert_to_grib(snapNc, basedir, ident, isotopes):
    config = Resources().getGribWriterConfig(isotopes)
    xmlFile = "cdmGribWriterConfig.xml"
    basexmlFile = os.path.join(basedir, xmlFile)
    with open(basexmlFile, 'w') as xh:
        xh.write(config['xml'])
    errlog = open(os.path.join(basedir, "fimex.errlog"), "w")
    outlog = open(os.path.join(basedir, "fimex.outlog"), "w")
    tempfile = 'tmp.grib'
    basetempfile = os.path.join(basedir, tempfile)
    # fimex works in basedir, so it does not need the basefiles
    for appendix, params in config['extracts'].items():
        outFile = os.path.join(
            basedir, "{ident}_{type}".format(ident=ident, type=appendix))
        with open(outFile, 'wb') as gh:
            for param in params:
                if (os.path.exists(basetempfile)):
                    os.remove(basetempfile)
                procOptions = [
                    'fimex',
                    '--input.file={}'.format(snapNc),
                    '--input.config={}'.format(config['ncml']),
                    # avoid problem with lat/lon variables
                    # in fimex grib-writer< 0.64
                    # '--extract.removeVariable=longitude',
                    # '--extract.removeVariable=latitude',
                    '--output.file={}'.format(tempfile),
                    '--output.type=grib',
                    '--output.config={}'.format(xmlFile)
                ]
                procOptions.append(
                    '--extract.selectVariables={}'.format(param))
                print(" ".join(procOptions))
                proc = subprocess.Popen(procOptions,
                                        cwd=basedir,
                                        stderr=errlog,
                                        stdout=outlog)
                if (proc.wait() != 0):
                    errlog.write("'{fimex}' in {dir} failed".format(
                        fimex=' '.join(procOptions), dir=basedir))
                else:
                    # append tmp-file to final grib-file
                    with (open(basetempfile, 'rb')) as th:
                        while True:
                            data = th.read(16 * 1024 *
                                           1024)  # read max 16M blocks
                            if data:
                                gh.write(data)
                            else:
                                break
                if (os.path.exists(basetempfile)):
                    os.remove(basetempfile)

    errlog.close()
    outlog.close()
Exemplo n.º 7
0
    def add_expected_files(self, date):
        self.files = []
        self.optFiles = []

        # only one file expected
        self.files.append(
            os.path.join(
                self.outputdir,
                self.res.output_filename_pattern.format(
                    year=date.year,
                    month=date.month,
                    day=date.day,
                    UTC=date.hour,
                    resdir=Resources().directory)))
        return
Exemplo n.º 8
0
class SnapControllerInverse:
    def __init__(self):
        self.res = Resources()
        self.lastOutputDir = os.path.join(
            self.res.getSnapOutputDir(),
            "{0}_{1}".format("backtrack", strftime("%Y-%m-%dT%H%M%S",
                                                   gmtime())))
        self.main = BrowserWidget()
        self.main.set_html(self.res.getStartScreenInverse().replace(
            "OUTPUTDIR", self.lastOutputDir))
        self.main.set_form_handler(self._create_snap_form_handler())
        self.main.show()
        self.snapRunning = "inactive"
        self.lastQDict = {}

    def write_log(self, txt: str):
        debug(txt)
        self.main.evaluate_javaScript('updateSnapLog({0});'.format(
            json.dumps(txt)))

    def _snap_finished(self):
        debug("finished")
        self.snapRunning = "finished"
        #self.plot_results()
        with open(os.path.join(self.lastOutputDir, "snap.log.out"),
                  "a") as logFile:
            logFile.write(
                "All work finished. Please open 'vgl-launch diana -s {dir}/diana.setup' to see results.\n"
                .format(dir=self.lastOutputDir))
        self.update_log()

    def _defaultDomainCheck(self, lonf, latf):
        if (latf <= self.res.ecDefaultDomainStartY or latf >=
            (self.res.ecDefaultDomainStartY + self.res.ecDomainHeight)
                or lonf <= self.res.ecDefaultDomainStartX or lonf >=
                self.res.ecDefaultDomainStartX + self.res.ecDomainWidth):
            self.write_log(
                "(lat,lon) = ({lat},{lon}) outside domain.\nTry global EC meteorology under advanced."
                .format(lat=latf, lon=lonf))
            return False
        return True

    def run_snap_query(self, qDict):
        # make sure all files are rw for everybody (for later deletion)
        os.umask(0)
        debug("run_snap_query inverse")
        for key, value in qDict.items():
            print(str.format("{0} => {1}", key, value))
        runTime = -96
        if "runTime" in qDict:
            runTime = int(qDict["runTime"])
        if "outputDir" in qDict:
            self.lastOutputDir = qDict["outputDir"]

        errors = ""
        self.measurements = []
        # measurementX, latX, lonX, startX, encX
        for i in range(1, 100):
            if 'measurement{}'.format(i) not in qDict:
                continue
            name = qDict['measurement{}'.format(i)]
            for tag in ('lat{}', 'lon{}', 'start{}', 'end{}'):
                if tag.format(i) not in qDict:
                    errors += "No tag " + tag.format(i) + " for " + name + "\n"
            if len(errors) > 0:
                continue
            match = re.search(r'(\d{4})-(\d{2})-(\d{2})[\+\s]+(\d{1,2})',
                              qDict['start{}'.format(i)])
            if match:
                startDT = datetime.datetime(
                    *tuple(map(int, list(match.group(1, 2, 3, 4)))))
            else:
                errors += "Cannot interprete startTime: {0}\n".format(
                    qDict['start{}'.format(i)])
            match = re.search(
                r'(\d{4})-(\d{2})-(\d{2})[\+\s]+(\d{1,2}):(\d{1,2})',
                qDict['end{}'.format(i)])
            if match:
                endDT = datetime.datetime(
                    *tuple(map(int, list(match.group(1, 2, 3, 4)))))
                if int(match.group(5)) > 0:
                    endDT = endDT + datetime.timedelta(hours=1)
            else:
                errors += "Cannot interprete endTime: {0}\n".format(
                    qDict['end{}'.format(i)])
            if startDT >= endDT:
                errors += "Start must be before end for {}\n".format(name)
            lat = qDict['lat{}'.format(i)]
            lon = qDict['lon{}'.format(i)]
            try:
                latf = Snappy.Utils.parseLat(lat)
                lonf = Snappy.Utils.parseLon(lon)
            except ValueError as ve:
                latf = 0.
                lonf = 0.
                errors += "Cannot interprete latitude/longitude: {lat}/{lon}: {ex}\n".format(
                    lat=lat, lon=lon, ex=ve)
            if len(errors) == 0:
                self.measurements.append(
                    Measurement(i, name, lonf, latf, startDT, endDT))

        debug("output directory: {}".format(self.lastOutputDir))
        if not os.path.isdir(self.lastOutputDir):
            try:
                os.mkdir(self.lastOutputDir)
            except:
                errors += "cannot create directory: {}".format(
                    self.lastOutputDir)
        else:
            errors += "cowardly refusing to write into existing directory: {}".format(
                self.lastOutputDir)

        if (len(errors) > 0):
            debug('updateSnapLog("{0}");'.format(
                json.dumps("ERRORS:\n\n" + errors)))
            self.write_log("ERRORS:\n\n{0}".format(errors))
            return

        curtime = gmtime()
        self.lastQDict = qDict
        self.write_log("working with {number} measurements in {dir}".format(
            number=len(self.measurements), dir=self.lastOutputDir))

        # write snap.input files
        for mes in self.measurements:
            print("{id} {name}".format(id=mes.id, name=mes.name))
            releaseDT = mes.end - mes.start
            releaseH = releaseDT.days * 24 + math.ceil(
                releaseDT.seconds / 3600)
            sourceTerm = """
SIMULATION.START.DATE={simStart}
SET_RELEASE.POS= P=   {lat},   {lon}
TIME.START= {startTime}
TIME.RUN = {runTime}h
MAX.PARTICLES.PER.RELEASE= 4000
TIME.RELEASE.PROFILE.STEPS
STEP.HOUR.OUTPUT.FIELDS= 1
RELEASE.HOUR= 0, {releaseTime}
RELEASE.RADIUS.M= {radius}, {radius}
RELEASE.LOWER.M= {lowerHeight}, {lowerHeight}
RELEASE.UPPER.M= {upperHeight}, {upperHeight}
RELEASE.BQ/SEC.COMP= 1e12, 1e12, 'Cs137'
"""
            self.lastSourceTerm = sourceTerm.format(
                simStart=strftime("%Y-%m-%d_%H:%M:%S", curtime),
                lat=mes.lat,
                lon=mes.lon,
                startTime=mes.end.strftime("%Y %m %d %H"),
                runTime=runTime,
                releaseTime=releaseH,
                radius=500,
                lowerHeight=0,
                upperHeight=250)

            with open(
                    os.path.join(self.lastOutputDir,
                                 "snap.input{}".format(mes.id)), 'w') as fh:
                fh.write(self.lastSourceTerm)
                # add Cs137 definition
                fh.write(self.res.isotopes2snapinput([169]))

            metmodel = 'nrpa_ec_0p1'
            if (metmodel == 'nrpa_ec_0p1'):
                if ('metpattern' in qDict):
                    files = self.res.getECMeteorologyFiles(
                        startDT, runTime, pattern=qDict['metpattern'])
                    if (len(files) == 0):
                        self.write_log(
                            "no EC met-files found for {}, runtime {} with pattern {}"
                            .format(startDT, runTime, qDict['metpattern']))
                        return
                else:
                    files = self.res.getECMeteorologyFiles(startDT, runTime)
                    if (len(files) == 0):
                        self.write_log(
                            "no EC met-files found for {}, runtime {}".format(
                                startDT, runTime))
                        return
                if (not self._defaultDomainCheck(lonf, latf)):
                    return
                snapIn = self.res.getSnapInputMetDefinitions(metmodel, files)
                snapIn = snapIn.replace(
                    "snap.", "snap{}.".format(mes.id)
                )  # replace snap.nc and snap.log to snap1.nc snap1.log
                with open(
                        os.path.join(self.lastOutputDir,
                                     "snap.input{}".format(mes.id)),
                        'a') as fh:
                    fh.write(snapIn)

        snapscript = os.path.join(self.lastOutputDir, "snap.sh")
        with open(snapscript, 'a') as fh:
            fh.write("#! /bin/bash\n")
            fh.write("cd {}\n".format(self.lastOutputDir))
            ids = " ".join([str(x.id) for x in self.measurements])
            fh.write(
                r'parallel -i -j 4 /usr/bin/bsnap_naccident snap.input{} -- ' +
                ids + "\n")
            joinIds = " ".join(
                ["-i snap{}.nc".format(x.id) for x in self.measurements])
            fh.write(
                "snapCombineInverse -I Cs137 -o snapCombined.nc {}\n".format(
                    joinIds))

        # create diana.setup
        with open(os.path.join(self.lastOutputDir, "diana.setup"), 'w') as fh:
            fh.write('''
%include /etc/diana/setup/diana.setup-COMMON
<FIELD_PLOT>
field=source_probability
  colour=off minvalue=0.1 line.interval=1. palettecolours=gray_rev
  plot=FILL_CELL(Cs137_probability)
end.field

field=acc_source_probability
  colour=off minvalue=0.1 line.interval=3. palettecolours=who_uvi
  plot=FILL_CELL(Cs137_acc_probability)
end.field

field=Cs137_avg_in_boundary_layer
  colour=off minvalue=0.1 line.values=0,1 palettecolours=gray_rev
  plot=FILL_CELL(Cs137_concentration_bl)
end.field

</FIELD_PLOT>

<FIELD_FILES>
filegroup=snapBacktrack
m=combined t=fimex format=netcdf f={dir}/snapCombined.nc
            '''.format(dir=self.lastOutputDir))
            for m in self.measurements:
                fh.write(
                    "m={name} t=fimex format=netcdf f={dir}/snap{id}.nc\n".
                    format(name=m.name, dir=self.lastOutputDir, id=m.id))
            fh.write("</FIELD_FILES>\n")
        os.chmod(snapscript, 0o755)
        self._snap_model_run(snapscript)

    def _snap_model_run(self, snapscript):
        self.snap_run = SnapRunInverse(self)
        self.snap_run.proc.finished.connect(self._snap_finished)
        self.snap_run.start(snapscript)

        self.snap_update = SnapUpdateThread(self)
        self.snap_update.update_log_signal.connect(self.update_log)
        self.snap_update.start(QThread.LowPriority)

    def update_log_query(self, qDict):
        #MainBrowserWindow._default_form_handler(qDict)
        self.write_log("updating...")
        if os.path.isfile(os.path.join(self.lastOutputDir, "snap.log.out")):
            lfh = open(os.path.join(self.lastOutputDir, "snap.log.out"))
            debug(tail(os.path.join(self.lastOutputDir, "snap.log.out"), 30))
            self.write_log(
                tail(os.path.join(self.lastOutputDir, "snap.log.out"), 30))
            lfh.close()

    def update_log(self):
        self.update_log_query({})

    def _create_snap_form_handler(self):
        def handler(queryDict):
            """a form-handler with closure for self"""
            options = {
                'Run': self.run_snap_query,
                'Update': self.update_log_query
            }
            # mapping from QList<QPair> to simple dictionary
            qDict = dict()
            for key, value in queryDict:
                qDict[key] = value
            # calling the correct handler depending on the module
            try:
                options[qDict['action']](qDict)
            except TypeError as ex:
                self.write_log("type-error: {}".format(ex))
            except ValueError as ex:
                self.write_log("value-error: {}".format(ex))
            except:
                self.write_log("Unexpected error on {0}: {1}".format(
                    qDict['action'],
                    sys.exc_info()[0]))
                raise

        return handler
Exemplo n.º 9
0
class SnapController():
    def __init__(self):
        self.res = Resources()
        self.main = BrowserWidget()
        self.main.set_html(self.res.getStartScreen())
        self.main.set_form_handler(self._create_snap_form_handler())
        self.main.show()
        self.snapRunning = "inactive"
        self.lastOutputDir = ""
        self.lastQDict = {}

    def write_log(self, txt:str):
        debug(txt)
        self.main.evaluate_javaScript('updateSnapLog({0});'.format(json.dumps(txt)))

    def _snap_finished(self):
        debug("finished")
        self.snapRunning = "finished"
        self.results_add_toa()
        self.plot_results()
        with open(os.path.join(self.lastOutputDir,"snap.log.out"), "a") as logFile:
            logFile.write("All work finished. Please open diana to see results.\nResults in {}\n".format(self.lastOutputDir))
        self.update_log()

    def _met_calculate_and_run(self):
        if self.metcalc.must_calc():
            proc = SnapRun.getQProcess(self)
            proc.finished.connect(self._met_finished_run_snap)
            self.metcalc.calc(proc)
            if (proc.waitForStarted(3000)) :
                self.snapRunning = "running"
            # start background logging thread
            self.snap_update = SnapUpdateThread(self)
            self.snap_update.update_log_signal.connect(self.update_log)
            self.snap_update.start(QThread.LowPriority)
        else:
            self._met_finished_run_snap()
        

    def _met_finished_run_snap(self):
        debug("ec_finished")
        self.snapRunning = "finished" # quit update-thread
        if (self.metcalc.must_calc()):
            with open(os.path.join(self.lastOutputDir,"snap.log.out"), "a") as logFile:
                logFile.write("Meteorology not generated.\nErrors in {}\n".format(self.lastOutputDir))
            self.update_log()
            return

        metdefs = self.res.getDefaultMetDefinitions(self.lastQDict['metmodel'])
        (metdefs["startX"], metdefs["startY"]) = self.metcalc.get_grid_startX_Y()
        with open(os.path.join(self.lastOutputDir, "snap.input"),'a') as fh:
            fh.write(self.res.getSnapInputMetDefinitions(self.lastQDict['metmodel'],
                                self.metcalc.get_meteorology_files(), **metdefs))
        self._snap_model_run()

    def results_add_toa(self):
        proc = SnapRun.getQProcess(self)
        proc.start("snapAddToa", ['snap.nc'])
        proc.waitForFinished(-1)


    def plot_results(self):
        match = re.search(r'(\d*\.\d+)', self.lastQDict['dianaversion'])
        if match:
            diVersion = "-{}".format(match.group(1))
        else :
            diVersion = ""

        if 'region' in self.lastQDict and self.lastQDict['region']:
            prod_dir = os.path.join(self.lastOutputDir, "prod")
            os.mkdir(prod_dir)
            with open(os.path.join(prod_dir, "diana.setup"),'wt') as fh:
                di_setup = """
%include /etc/diana/setup/diana.setup-COMMON
<FIELD_FILES>
filegroup=SNAP
m=SNAP.current t=fimex format=netcdf f={}
</FIELD_FILES>
"""
                fh.write(di_setup.format(os.path.join(self.lastOutputDir, "snap.nc")))

            hours = ",".join(["{x} | {y}".format(x=x, y=x+3) for x in range(0,abs(int(self.lastQDict['runTime'])),3)])
            component = 'Cs137'
            if 'isBomb' in self.lastQDict:
                component = 'Aerosol'
            dianaIn = os.path.join(prod_dir, "diana.in")
            with open(self.res.getBSnapInputFile(), 'rt') as fh:
                dianaInTmpl = fh.read()
            with open(dianaIn, 'wt') as fh:
                fh.write(dianaInTmpl.format(hours=hours, component=component))

            # plots
            proc = QProcess()
            proc.setWorkingDirectory(os.path.join(prod_dir))
            proc.setStandardOutputFile(os.path.join(self.lastOutputDir,"snap.log.out"), QIODevice.Append)
            proc.setStandardErrorFile(os.path.join(self.lastOutputDir,"snap.log.out"), QIODevice.Append)
            proc.start("bdiana{}".format(diVersion), ['-i', dianaIn, '-s', 'diana.setup', 'p={}'.format(self.lastQDict['region'])])
            proc.waitForFinished(-1)
            with open(os.path.join(self.lastOutputDir,"snap.log.out"), 'a') as lfh:
                lfh.write("plotting finished\n")

            sendPngsFromDir("SNAP calculation: {}".format(self.lastTag),
                            "Finished in {wdir}. See attached file(s).\n SourceTerm: \n{sourceTerm}".format(wdir=self.lastOutputDir, sourceTerm=self.lastSourceTerm),
                            prod_dir)


    def _defaultDomainCheck(self, lonf, latf):
        if (latf <= self.res.ecDefaultDomainStartY or
            latf >= (self.res.ecDefaultDomainStartY + self.res.ecDomainHeight) or
            lonf <= self.res.ecDefaultDomainStartX or
            lonf >= self.res.ecDefaultDomainStartX + self.res.ecDomainWidth):
            self.write_log("(lat,lon) = ({lat},{lon}) outside domain.\nTry global EC meteorology under advanced.".format(lat=latf, lon=lonf))
            return False
        return True

    def _meps25DomainCheck(self, lonf, latf):
        if (latf <= 53. or
            latf >= 72. or
            lonf <= 0. or
            lonf >= 36.):
            self.write_log("(lat,lon) = ({lat},{lon}) outside domain for meps.\n".format(lat=latf, lon=lonf))
            return False
        return True

    def get_bomb_yield(self, yld):
        if yld == 1:
            lower=500.0
            upper=1500.0
            radius=600.0
            activity=2.0e19
        elif (yld == 3):
            lower=1400.0
            upper=3100.0
            radius=1000.0
            activity=6.0e19
        elif (yld == 10):
            lower=2250.0
            upper=4750.0
            radius=1400.0
            activity=2.0e20
        elif (yld == 30):
            lower=4100.0
            upper=8400.0
            radius=2300.0
            activity=6.0e20
        elif (yld == 100):
            lower=5950.0
            upper=12050.0
            radius=3200.0
            activity=2.0e21
        elif (yld == 300):
            lower=8000.0
            upper=18500.0
            radius=5800.0
            activity=6.0e21
        elif (yld == 1000):
            lower=10000.0
            upper=25000.0
            radius=8500.0
            activity=2.0e22
        elif(yld == 3000):
            lower=12000.0
            upper=32000.0
            radius=11100.0
            activity=6.0e22
        else: 
            lower=0.0
            upper=0.0
            radius=0.0
            activity=0  
        return (lower, upper, radius, activity)
    
    def get_bomb_release(self, qDict):
        source_tmpl = '''
MAX.PARTICLES.PER.RELEASE= 1000000

* PARTICLE CLASSES
COMPONENT= Aerosol_2.2mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 2.2
*DENSITY.G/CM3=2.95
GRAVITY.FIXED.CM/S= 0.2
FIELD.IDENTIFICATION=01
*
COMPONENT= Aerosol_4.4mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 4.4
GRAVITY.FIXED.CM/S= 0.7
FIELD.IDENTIFICATION=2
*
COMPONENT= Aerosol_8.6mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 8.6
GRAVITY.FIXED.CM/S= 2.5
FIELD.IDENTIFICATION=3
*
COMPONENT= Aerosol_14.6mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 14.6
GRAVITY.FIXED.CM/S= 6.9
FIELD.IDENTIFICATION=4
*
COMPONENT= Aerosol_22.8mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 22.8
GRAVITY.FIXED.CM/S= 15.9
FIELD.IDENTIFICATION=5
*
COMPONENT= Aerosol_36.1mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 36.1
GRAVITY.FIXED.CM/S= 35.6
FIELD.IDENTIFICATION=6
*
COMPONENT= Aerosol_56.5mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 56.5
GRAVITY.FIXED.CM/S= 71.2
FIELD.IDENTIFICATION=7
*
COMPONENT= Aerosol_92.3mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 92.3
GRAVITY.FIXED.CM/S= 137.0
FIELD.IDENTIFICATION=8
*
COMPONENT= Aerosol_173.2mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER=173.2
GRAVITY.FIXED.CM/S= 277.3
FIELD.IDENTIFICATION=9
*
COMPONENT= Aerosol_250.0mym
DRY.DEP.ON
WET.DEP.ON
RADIOACTIVE.DECAY.OFF
RADIUS.MICROMETER= 250.0
GRAVITY.FIXED.CM/S= 10000.0
FIELD.IDENTIFICATION=10

** Explosive yield = {yld}kt
TIME.RELEASE.PROFILE.BOMB
RELEASE.SECOND=    0
RELEASE.RADIUS.M={radius}
RELEASE.LOWER.M= {lowerHeight}
RELEASE.UPPER.M= {upperHeight}

'''
        errors = ""
        try:
            yld = int(qDict['yield'])
            (lowerHeight, upperHeight, radius, activity) = self.get_bomb_yield(yld)
            if (activity == 0.):
                raise Exception()
        except:
            errors += "unknown yield\n"
  
        source_term = source_tmpl.format(radius=radius, lowerHeight=lowerHeight, upperHeight=upperHeight, yld=yld)
  
        release = []
        tags = ('2.2', '4.4', '8.6', '14.6', '22.8', '36.1', '56.5', '92.3', '173.2', '250.0')
        for tag in tags:
            release.append("RELEASE.BQ/STEP.COMP= {amount:.3E} 'Aerosol_{tag}mym'\n".format(tag=tag, amount=activity/len(tags)))

        source_term += "".join(release)
        
        return (source_term, errors)

    def get_isotope_release(self, qDict):
        errors = ""
        for tag in ('releaseTime', 'radius', 'lowerHeight', 'upperHeight'):
            if not re.search('\d+', qDict[tag]):
                errors += "Cannot interprete {}: {}".format(tag, qDict[tag])

        source_tmpl = '''
MAX.PARTICLES.PER.RELEASE= 2000
TIME.RELEASE.PROFILE.STEPS
RELEASE.HOUR= 0, {releaseTime}
RELEASE.RADIUS.M= {radius}, {radius}
RELEASE.LOWER.M= {lowerHeight}, {lowerHeight}
RELEASE.UPPER.M= {upperHeight}, {upperHeight}
'''
        source_term = source_tmpl.format(releaseTime=qDict['releaseTime'],
                                         radius=qDict['radius'],
                                         lowerHeight=qDict['lowerHeight'], upperHeight=qDict['upperHeight'])

        isotopes = {'relI131': 'I131',
                    'relXE133': 'Xe133',
                    'relCS137': 'Cs137'}
        for rel, iso in isotopes.items():
            emis = 0.
            try:
                emis = float(qDict[rel])
            except:
                pass
            if (emis > 0.):
                source_term += "RELEASE.BQ/SEC.COMP= {rel}, {rel}, '{iso}'\n".format(rel=qDict[rel], iso=iso)

        # add Cs137, I131 and Xe133
        source_term += self.res.isotopes2snapinput([169, 158, 148])

        return (source_term, errors)

    def run_snap_query(self, qDict):
        # make sure all files are rw for everybody (for later deletion)
        os.umask(0)
        debug("run_snap_query")
        for key, value in qDict.items():
            print(str.format("{0} => {1}", key, value))
        errors = ""
        match = re.search(r'(\d{4})-(\d{2})-(\d{2})[\+\s]+(\d{1,2})', qDict['startTime'])
        if match:
            startTime = "{0} {1} {2} {3}".format(*match.group(1,2,3,4))
            startDT = datetime.datetime(*tuple(map(int, list(match.group(1,2,3,4)))))
        else:
            errors += "Cannot interprete startTime: {0}\n".format(qDict['startTime'])

        if not re.search('-?\d+(.\d+)?', qDict['runTime']):
            errors += "Cannot interprete runTime: {}\n".format(qDict['runTime'])


        lat = qDict['latitude']
        lon = qDict['longitude']
        tag = "latlon"
        nPPs = self.res.readNPPs()
        if (qDict['npp'] and nPPs[qDict['npp']]):
            tag = qDict['npp']
            npp = nPPs[qDict['npp']]['site']
            lat = nPPs[qDict['npp']]['lat']
            lon = nPPs[qDict['npp']]['lon']
            debug("NPP: {0} {1} {2}".format(npp, lat, lon))
        self.lastTag = "{0} {1}".format(tag, startTime)

        try:
            latf = Snappy.Utils.parseLat(lat)
            lonf = Snappy.Utils.parseLon(lon)
        except ValueError as ve:
            latf = 0.
            lonf = 0.
            errors += "Cannot interprete latitude/longitude: {lat}/{lon}: {ex}\n".format(lat=lat,lon=lon,ex=ve)

        if (len(errors) > 0):
            debug('updateSnapLog("{0}");'.format(json.dumps("ERRORS:\n\n"+errors)))
            self.write_log("ERRORS:\n\n{0}".format(errors))
            return
        self.write_log("working with lat/lon=({0}/{1}) starting at {2}".format(latf, lonf, startTime))

        curtime = gmtime()
        self.lastOutputDir = os.path.join(self.res.getSnapOutputDir(), "{0}_{1}".format(tag, strftime("%Y-%m-%dT%H%M%S", curtime)))
        self.lastQDict = qDict
        sourceTerm = """
TITLE={tag}
SIMULATION.START.DATE={simStart}
SET_RELEASE.POS= P=   {lat},   {lon}
TIME.START= {startTime}
TIME.RUN = {runTime}h
STEP.HOUR.OUTPUT.FIELDS= 3
"""
        self.lastSourceTerm = sourceTerm.format(tag=self.lastTag,
                                                simStart=strftime("%Y-%m-%d_%H:%M:%S",curtime),
                                                lat=latf, lon=lonf, startTime=startTime,
                                                runTime=qDict['runTime'])

        if 'isBomb' in qDict:
            (term, errors) = self.get_bomb_release(qDict)
        else:
            (term, errors) = self.get_isotope_release(qDict)
        if (len(errors) > 0):
            debug('updateSnapLog("{0}");'.format(json.dumps("ERRORS:\n\n"+errors)))
            self.write_log("ERRORS:\n\n{0}".format(errors))
            return
        self.lastSourceTerm += term

        debug("output directory: {}".format(self.lastOutputDir))
        os.mkdir(self.lastOutputDir)

        with open(os.path.join(self.lastOutputDir, "snap.input"),'w') as fh:
            fh.write(self.lastSourceTerm)
        if (qDict['metmodel'] == 'nrpa_ec_0p1'):
            files = self.res.getECMeteorologyFiles(startDT, int(qDict['runTime']), qDict['ecmodelrun'])
            if (len(files) == 0):
                self.write_log("no EC met-files found for {}, runtime {}".format(startDT, qDict['runTime']))
                return
            if (not self._defaultDomainCheck(lonf,latf)):
                return
            with open(os.path.join(self.lastOutputDir, "snap.input"),'a') as fh:
                fh.write(self.res.getSnapInputMetDefinitions(qDict['metmodel'], files))
            self._snap_model_run()
        elif qDict['metmodel'] == MetModel.NrpaEC0p1Global:
            try:
                self.write_log("extracting meteorology from EC for domain")
                self.metcalc = EcMeteorologyCalculator(EcMeteorologyCalculator.getGlobalMeteoResources(), startDT, lonf, latf)
                self._met_calculate_and_run()
            except MeteoDataNotAvailableException as e:
                self.write_log("problems creating EC-met: {}".format(e.args[0]))
        elif qDict['metmodel'] == MetModel.Icon0p25Global:
            try:
                self.write_log("extracting meteorology from ICON for domain")
                self.metcalc = ICONMeteorologyCalculator(ICONMeteorologyCalculator.getGlobalMeteoResources(), startDT, lonf, latf)
                self._met_calculate_and_run()
            except MeteoDataNotAvailableException as e:
                self.write_log("problems creating ICON-met: {}".format(e.args[0]))
        elif qDict['metmodel'] == 'meps_2_5km':
            files = self.res.getMEPS25MeteorologyFiles(startDT, int(qDict['runTime']), "best")
            if (len(files) == 0):
                self.write_log("no MEPS2_5 met-files found for {}, runtime {}".format(startDT, qDict['runTime']))
                return
            if (not self._meps25DomainCheck(lonf,latf)):
                return
            with open(os.path.join(self.lastOutputDir, "snap.input"),'a') as fh:
                fh.write(self.res.getSnapInputMetDefinitions(qDict['metmodel'], files))
            self._snap_model_run()
        else:
            # hirlam
            if (not self._defaultDomainCheck(lonf,latf)):
                return
            with open(os.path.join(self.lastOutputDir, "snap.input"),'a') as fh:
                fh.write(self.res.getSnapInputMetDefinitions(qDict['metmodel'], []))
            self._snap_model_run()

    def _snap_model_run(self):
        self.snap_run = SnapRun(self)
        self.snap_run.proc.finished.connect(self._snap_finished)
        self.snap_run.start()

        self.snap_update = SnapUpdateThread(self)
        self.snap_update.update_log_signal.connect(self.update_log)
        self.snap_update.start(QThread.LowPriority)


    def update_log_query(self, qDict):
        #MainBrowserWindow._default_form_handler(qDict)
        self.write_log("updating...")
        if os.path.isfile(os.path.join(self.lastOutputDir,"snap.log.out")) :
            lfh = open(os.path.join(self.lastOutputDir,"snap.log.out"))
            debug(tail(os.path.join(self.lastOutputDir,"snap.log.out"),30))
            self.write_log(tail(os.path.join(self.lastOutputDir,"snap.log.out"), 30))
            lfh.close()

    def update_log(self):
        self.update_log_query({})

    def _create_snap_form_handler(self):
        def handler(queryDict):
            """a form-handler with closure for self"""
            options = { 'Run' : self.run_snap_query,
                        'Update' : self.update_log_query
            }
            # mapping from QList<QPair> to simple dictionary
            qDict = dict()
            for key, value in queryDict:
                qDict[key] = value
            # calling the correct handler depending on the module
            try:
                options[qDict['action']](qDict)
            except TypeError as ex:
                self.write_log("type-error: {}".format(ex))
            except ValueError as ex:
                self.write_log("value-error: {}".format(ex))
            except:
                self.write_log("Unexpected error on {0}: {1}".format(qDict['action'],sys.exc_info()[0]))
                raise
        return handler