"""
.. module:: grid
:synopsis: Simple helper routines to generate small |prodimo| model grids.
.. moduleauthor:: Ch. Rab
"""
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals
import numpy
import math
import os
import shutil
import collections
import glob
[docs]def chgriddir(gridname):
"""
Changes the current working directory to the grid directory.
Verifies if I am already in the grid directory.
Parameters
----------
gridname : str
The name of the grid (the directory with the models).
"""
if not os.getcwd().endswith("/"+gridname):
os.chdir(gridname)
[docs]def genparamentry(name,value):
if name == "fcarbon":
fsil=1.0-(0.25+value)
entry = "3 ! NDUST \n"
entry = entry+" "+str(fsil)+" Mg0.7Fe0.3SiO3[s] \n"
entry = entry+" "+str(value)+" amC-Zubko[s] \n"
entry = entry+" 0.25 vacuum[s] \n"
else:
entry=str(value)+" ! "+name +" \n"
return entry
[docs]def genvalues(param):
"""
Generates the values for the given parameter.
Currently only lineare and logarithmic spacing is possible.
.. todo::
allow boolean type of parameters
.. todo::
allow for the dust composition parameters
"""
start=param[1]
end=param[2]
steps=param[3]
linlog=param[4]
if linlog=="lin":
return numpy.linspace(start,end,steps)
elif linlog=="log":
return numpy.logspace(math.log10(start),math.log10(end),steps,base=10.0)
else:
print("Unknown spacing type")
return None
[docs]def run_grid(gridname,modeldirs,runProDiMo):
"""
Runs the grid.
Changes to the grid directory and runs each model by either calling
a passed python function or a system command string (see parameters).
Parameters
----------
gridname : str
The name of the grid (the directory with the models).
modeldirs : list
a list of all models in the grid (also the directory name of each model)
runProDiMo : str or method object
if it is a `str` the string is interpreted as a system command.
Any accurence of `$MODELNAME$` in the given string is replaced by
the actual model name (model directory).
if the parameter is a python function. This funcion is called with
the modeldir as a parameter.
"""
chgriddir(gridname)
for modeldir in modeldirs:
if isinstance(runProDiMo, collections.Callable):
print("run "+modeldir+", exec. function: "+runProDiMo.__name__)
runProDiMo(modeldir)
else:
runProDiMoCMD=runProDiMo.replace("$MODELNAME$",modeldir)
os.chdir(modeldir)
print("run "+modeldir+", exec. command: "+runProDiMoCMD)
os.system(runProDiMoCMD)
os.chdir("..")
# go back to the original working directory
os.chdir("..")
[docs]def get_modeldirs(modeldirs):
"""
Returns all grid models of the current grid.
If modeldirs is not `
Assumes that I am already in the grid directory.
.. todo ::
also pass the gridname as optional parameter.
Returns
-------
list
If `modeldirs` is not `None` `modeldirs` is returned.
Otherwise all directory names staring with `model*/` are returned.
"""
if modeldirs is None:
modeldirs=glob.glob("model*/")
return modeldirs
[docs]def check_grid(gridname,modeldirs=None):
"""
Checks if all models look okay.
This routine checks if finished.out exists for all models of the grid.
Parameters
----------
gridname : str
The name of the grid (the directory with the models).
modeldirs : list
a list of all models in the grid (directory name of each model).
If `modeldirs` is `None` all directories with names starting with `model` are
considered as potential grid models.
"""
chgriddir(gridname)
# guess the model directories
modeldirs = get_modeldirs(modeldirs)
for modeldir in modeldirs:
if not os.path.isfile(modeldir+"/finished.out"):
print("Model "+modeldir+" failed:")
[docs]def sel_lowest_chisquare(gridname,modeldirs=None,tolerance=None):
"""
Selects the model(s) with the lowest chi squared. If a tolerance is
given all models within this tolerance, measured relative to the minimum
chi square are selected.
Parameters
----------
gridname : str
The name of the grid (the directory with the models).
modeldirs : list
a list of all models in the grid (directory name of each model).
If `modeldirs` is `None` all directories with names starting with `model` are
considered as potential grid models.
"""
chgriddir(gridname)
modeldirs=get_modeldirs(modeldirs)
nmodels=len(modeldirs)
chisquares=numpy.ndarray(shape=(nmodels))
chisquares[:]=1.e100
# go through all models and read the chi square
for i in range(nmodels):
modeldir=modeldirs[i]
if os.path.isfile(modeldir+"/finished.out"):
ffin=open(modeldir+"/finished.out")
for line in ffin:
if "total chi" in line:
val=float(line.split("=")[1])
chisquares[i]=val
minchi=numpy.min(chisquares)
if tolerance is not None:
tol=minchi*tolerance
idx=numpy.where(numpy.abs(chisquares-minchi)<tol)[0]
else:
idx=numpy.argmin(chisquares)
# need to convert to numpy array, otherwise selection of multiple indices
# does not work
return numpy.array(modeldirs)[idx],chisquares[idx]
[docs]def sel_param_val(gridname,param,value,modeldirs=None):
"""
Very primitiv and inefficient method to select models having a certain
value of a parameter.
"""
chgriddir(gridname)
modeldirs=get_modeldirs(modeldirs)
nmodels=len(modeldirs)
selmodels=list()
for i in range(nmodels):
modeldir=modeldirs[i]
if os.path.isfile(modeldir+"/ParameterGrid.in"):
ffin=open(modeldir+"/ParameterGrid.in")
for line in ffin:
if param in line:
val=float(line.split("!")[0])
if val == value:
selmodels.append(modeldir)
return selmodels
[docs]def make_grid(gridname,params,indir=None):
"""
Produces a grid of prodimo models.
A directory with the name gridname is created. Within this directory for
each parameter combination a directory is created. This directory is initially
copied from the directory given by indir and additionally the new parameters
are added to a ParameterGrid.in file. If ParameterGrid.in does not exist
it will be created.
.. todo::
deal with dust composition properties (e.g. carbon fraction)
.. todo::
deal with boolean values
.. todo::
deal with something like stellar particles and CR spectra (e.g. on/off)
Parameters
----------
gridname : str
The name of the grid (the directory with the models).
params : list
the list of parameters.
Each entry contains the parameter name, its start and end value, the number
of steps and if either linear (lin) or logarithmic (log) spacing should be
used.
indir : str
an input or starting directorz containing the usual |prodimo| input
files. This directory is copied into a new model.
If it is `None` only the ParameterGrid.in are created
Returns
-------
list
the list of modelnames (directory names)
"""
# make the list for the meshgrid
values=list()
for param in params:
values.append(genvalues(param))
grid=numpy.meshgrid(*values)
# create the directory for the grid
os.mkdir(gridname)
# create an iterator that loops over all indices of the firt grid array (all combinations)
it = numpy.nditer(grid[0], flags=['multi_index'])
imodel=0
modelnames=list()
while not it.finished:
# create the model directory
modelname="model"+"{:07d}".format(imodel)
modeldir=gridname+"/"+modelname
if indir is not None:
shutil.copytree(indir,modeldir)
else:
os.mkdir(modeldir)
fparam=open(modeldir+"/ParameterGrid.in","a+")
for iparam in range(len(params)):
#fparam.write(str(grid[iparam][it.multi_index])+" ! "+params[iparam][0] +" \n")
fparam.write(genparamentry(params[iparam][0], grid[iparam][it.multi_index]))
fparam.close()
imodel=imodel+1
modelnames.append(modelname)
it.iternext()
return modelnames