Beispiel #1
0
    def parse_params(self, params: Parameters):
        """Loop all Parameters in a Parameters object and set the values of this component accordingly.

        Args:
            params: Parameters objects, usually return from a lmfit optimization
        """

        # length of prefix
        n = len(self.prefix)

        # loop parameters
        for name, param in params.items():
            # does prefix match?
            if n > 0 and name.startswith(self.prefix):
                # remove prefix
                name = name[n:]

                # does it exist?
                if name in self.parameters:
                    # de-normalize?
                    if self.normalize:
                        value, stderr = self.denorm_param(
                            name, param.value, param.stderr)
                    else:
                        value, stderr = param.value, param.stderr

                    # set it
                    self.set(name, value=value, stderr=stderr)
Beispiel #2
0
    def fit_parameters(self, error_func):
        gamma1 = (1 / 10 + 1 / 5) / 2
        gamma2 = 0.02
        gamma3 = 0.02
        beta = 0.14

        num_cumulative_positiv = np.sum(self._observations[:, 0])

        print(f"num_cumulative_positiv {num_cumulative_positiv}")
        print(self._observations[:, 0])

        tau = 28 / num_cumulative_positiv

        delta = 5 / 18

        params = Parameters()
        params.add('gamma1', value=gamma1, min=1 / 10, max=1 / 5)
        params.add('gamma2', value=gamma2, min=0, max=5)
        params.add('gamma3', value=gamma3, min=0, max=5)
        params.add('beta', value=beta, min=0.01, max=0.5)
        params.add('tau', value=tau, min=tau * 0.8, max=tau * 1.2)
        params.add('delta', value=delta, min=0.6 * delta, max=1.4 * delta)

        # result = minimize(self._plumb,
        #                   params,
        #                   args=(len(self._observations), error_func),
        #                   method='leastsq')

        # report_fit(result)

        # self._fit_params = result.params

        print(params.items())

        bounds = np.array([(p.min, p.max) for p_name, p in params.items()])
        self._error_func = error_func

        gamodel = ga(function=self._plumb,
                     dimension=len(bounds),
                     variable_type='real',
                     variable_boundaries=bounds)
        gamodel.run()

        self._fit_params = self._params_array_to_dict(
            gamodel.output_dict['variable'])
Beispiel #3
0
def test_minizers():
    """
    test scale minimizers except newton-cg (needs jacobian) and
    anneal (doesn't work out of the box).
    """
    methods = [
        'Nelder-Mead', 'Powell', 'CG', 'BFGS', 'L-BFGS-B', 'TNC', 'COBYLA',
        'SLSQP'
    ]
    p_true = Parameters()
    p_true.add('amp', value=14.0)
    p_true.add('period', value=5.33)
    p_true.add('shift', value=0.123)
    p_true.add('decay', value=0.010)

    def residual(pars, x, data=None):
        amp = pars['amp'].value
        per = pars['period'].value
        shift = pars['shift'].value
        decay = pars['decay'].value

        if abs(shift) > pi / 2:
            shift = shift - np.sign(shift) * pi
        model = amp * np.sin(shift + x / per) * np.exp(-x * x * decay * decay)
        if data is None:
            return model
        return (model - data)

    n = 2500
    xmin = 0.
    xmax = 250.0
    noise = np.random.normal(scale=0.7215, size=n)
    x = np.linspace(xmin, xmax, n)
    data = residual(p_true, x) + noise

    fit_params = Parameters()
    fit_params.add('amp', value=11.0, min=5, max=20)
    fit_params.add('period', value=5., min=1., max=7)
    fit_params.add('shift', value=.10, min=0.0, max=0.2)
    fit_params.add('decay', value=6.e-3, min=0, max=0.1)

    init = residual(fit_params, x)
    mini = Minimizer(residual, fit_params, [x, data])
    for m in methods:
        print(m)
        mini.scalar_minimize(m, x)

        fit = residual(fit_params, x)

        for name, par in fit_params.items():
            nout = "%s:%s" % (name, ' ' * (20 - len(name)))
            print("%s: %s (%s) " % (nout, par.value, p_true[name].value))

        for para, true_para in zip(fit_params.values(), p_true.values()):
            check_wo_stderr(para, true_para.value)
Beispiel #4
0
def test_minizers():
    """
    test scale minimizers except newton-cg (needs jacobian) and
    anneal (doesn't work out of the box).
    """
    methods = ['Nelder-Mead', 'Powell', 'CG', 'BFGS',
               'L-BFGS-B', 'TNC', 'COBYLA', 'SLSQP']
    p_true = Parameters()
    p_true.add('amp', value=14.0)
    p_true.add('period', value=5.33)
    p_true.add('shift', value=0.123)
    p_true.add('decay', value=0.010)

    def residual(pars, x, data=None):
        amp = pars['amp'].value
        per = pars['period'].value
        shift = pars['shift'].value
        decay = pars['decay'].value

        if abs(shift) > pi/2:
            shift = shift - np.sign(shift)*pi
        model = amp*np.sin(shift + x/per) * np.exp(-x*x*decay*decay)
        if data is None:
            return model
        return (model - data)

    n = 2500
    xmin = 0.
    xmax = 250.0
    noise = np.random.normal(scale=0.7215, size=n)
    x     = np.linspace(xmin, xmax, n)
    data  = residual(p_true, x) + noise

    fit_params = Parameters()
    fit_params.add('amp', value=11.0, min=5, max=20)
    fit_params.add('period', value=5., min=1., max=7)
    fit_params.add('shift', value=.10,  min=0.0, max=0.2)
    fit_params.add('decay', value=6.e-3, min=0, max=0.1)

    init = residual(fit_params, x)
    mini = Minimizer(residual, fit_params, [x, data])
    for m in methods:
        print(m)
        mini.scalar_minimize(m, x)

        fit = residual(fit_params, x)

        for name, par in fit_params.items():
            nout = "%s:%s" % (name, ' '*(20-len(name)))
            print("%s: %s (%s) " % (nout, par.value, p_true[name].value))

        for para, true_para in zip(fit_params.values(), p_true.values()):
            check_wo_stderr(para, true_para.value)
Beispiel #5
0
def test_lbfgsb():
    p_true = Parameters()
    p_true.add('amp', value=14.0)
    p_true.add('period', value=5.33)
    p_true.add('shift', value=0.123)
    p_true.add('decay', value=0.010)

    def residual(pars, x, data=None):
        amp = pars['amp'].value
        per = pars['period'].value
        shift = pars['shift'].value
        decay = pars['decay'].value

        if abs(shift) > pi / 2:
            shift = shift - np.sign(shift) * pi
        model = amp * np.sin(shift + x / per) * np.exp(-x * x * decay * decay)
        if data is None:
            return model
        return (model - data)

    n = 2500
    xmin = 0.
    xmax = 250.0
    noise = np.random.normal(scale=0.7215, size=n)
    x = np.linspace(xmin, xmax, n)
    data = residual(p_true, x) + noise

    fit_params = Parameters()
    fit_params.add('amp', value=11.0, min=5, max=20)
    fit_params.add('period', value=5., min=1., max=7)
    fit_params.add('shift', value=.10, min=0.0, max=0.2)
    fit_params.add('decay', value=6.e-3, min=0, max=0.1)

    init = residual(fit_params, x)

    out = minimize(residual,
                   fit_params,
                   method='lbfgsb',
                   args=(x, ),
                   kws={'data': data})

    fit = residual(fit_params, x)

    for name, par in fit_params.items():
        nout = "%s:%s" % (name, ' ' * (20 - len(name)))
        print("%s: %s (%s) " % (nout, par.value, p_true[name].value))

    for para, true_para in zip(fit_params.values(), p_true.values()):
        check_wo_stderr(para, true_para.value)
Beispiel #6
0
def test_lbfgsb():
    p_true = Parameters()
    p_true.add('amp', value=14.0)
    p_true.add('period', value=5.33)
    p_true.add('shift', value=0.123)
    p_true.add('decay', value=0.010)

    def residual(pars, x, data=None):
        amp = pars['amp'].value
        per = pars['period'].value
        shift = pars['shift'].value
        decay = pars['decay'].value

        if abs(shift) > pi/2:
            shift = shift - np.sign(shift)*pi
        model = amp*np.sin(shift + x/per) * np.exp(-x*x*decay*decay)
        if data is None:
            return model
        return (model - data)

    n = 2500
    xmin = 0.
    xmax = 250.0
    noise = np.random.normal(scale=0.7215, size=n)
    x     = np.linspace(xmin, xmax, n)
    data  = residual(p_true, x) + noise

    fit_params = Parameters()
    fit_params.add('amp', value=11.0, min=5, max=20)
    fit_params.add('period', value=5., min=1., max=7)
    fit_params.add('shift', value=.10,  min=0.0, max=0.2)
    fit_params.add('decay', value=6.e-3, min=0, max=0.1)

    init = residual(fit_params, x)

    out = minimize(residual, fit_params, method='lbfgsb', args=(x,), kws={'data':data})

    fit = residual(fit_params, x)

    for name, par in fit_params.items():
        nout = "%s:%s" % (name, ' '*(20-len(name)))
        print("%s: %s (%s) " % (nout, par.value, p_true[name].value))

    for para, true_para in zip(fit_params.values(), p_true.values()):
        check_wo_stderr(para, true_para.value)
    def test_params_prints(self):
        params = Parameters()
        params.add("a", value=1.0, vary=True)
        params.add("b", value=8.5, min=0, vary=True)
        params.add("c", expr='a + sqrt(b)')

        repr_full = params.pretty_repr()
        repr_one = params.pretty_repr(oneline=True)

        out = []
        for key, val in params.items():
            out.append("%s: %s" % (key, repr(val)))
        out = '\n'.join(out)

        assert repr_full.count('\n') > 4
        assert repr_one.count('\n') < 2
        assert len(repr_full) > 150
        assert len(repr_one) > 150
        assert len(out) > 150
Beispiel #8
0
    def from_parameter_dict(cls, parameter: Parameters):
        """Creates a :class:`ParameterGroup` from an lmfit.Parameters dictionary

        Parameters
        ----------
        parameter :
            A lmfit.Parameters dictionary
        """

        root = cls(None)
        for lbl, param in parameter.items():
            lbl = lbl.split("_")
            if len(lbl) == 2:
                # it is a root param
                param = Parameter.from_parameter(lbl.pop(), param)
                root.add_parameter(param)
                continue

            # remove root
            lbl.pop(0)

            top = root
            while len(lbl) != 0:
                group = lbl.pop(0)
                if group in top:
                    if len(lbl) == 1:
                        param = Parameter.from_parameter(lbl.pop(), param)
                        top[group].add_parameter(param)
                    else:
                        top = top[group]
                else:
                    group = ParameterGroup(group)
                    top.add_group(group)
                    if len(lbl) == 1:
                        param = Parameter.from_parameter(lbl.pop(), param)
                        group.add_parameter(param)
                    else:
                        top = group
        return root
Beispiel #9
0
    def from_parameter_dict(cls, parameter: Parameters):
        """Creates a :class:`ParameterGroup` from an lmfit.Parameters dictionary

        Parameters
        ----------
        parameter :
            A lmfit.Parameters dictionary
        """

        root = cls(None)
        for lbl, param in parameter.items():
            lbl = lbl.split("_")
            if len(lbl) == 2:
                # it is a root param
                param = Parameter.from_parameter(lbl.pop(), param)
                root.add_parameter(param)
                continue

            # remove root
            lbl.pop(0)

            top = root
            while len(lbl) != 0:
                group = lbl.pop(0)
                if group in top:
                    if len(lbl) == 1:
                        param = Parameter.from_parameter(lbl.pop(), param)
                        top[group].add_parameter(param)
                    else:
                        top = top[group]
                else:
                    group = ParameterGroup(group)
                    top.add_group(group)
                    if len(lbl) == 1:
                        param = Parameter.from_parameter(lbl.pop(), param)
                        group.add_parameter(param)
                    else:
                        top = group
        return root
Beispiel #10
0
def params_to_dict(params: lmfit.Parameters, kind: str = "values") -> dict:
    """
    Converts fit result parameters to a dict

    Parameters
    ----------
    params : lmfit.Parameters
        fit result parameters
    kind : str, optional
        ["values", "stderr"], by default "values"

    Returns
    -------
    dict
        Dict containing the parameternames as key and the values or stderr as values
    """
    result_dict = {}
    for name, param in params.items():
        if kind == "values":
            result_dict[name] = param.value
        elif kind == "stderr":
            result_dict[name] = param.stderr
    return result_dict
Beispiel #11
0
        def residual(params: Parameters):
            ### if result exist in database, ignore calculation
            result = self.db.session.query(Result).filter(Result.task == task) \
                .filter(Result.parameter == str(params)).first()
            if result is not None:
                R = result.residual
                if R is not None:
                    return json.loads(R)
            ###

            ### save ppf file and run NPT
            ppf = PPF(string=task.ppf)
            paras = OrderedDict()
            for k, v in params.items():
                print(v)
                paras[restore_para_name(k)] = v.value
            ppf.set_nb_paras(paras)

            # TODO Fit several torsions one by one
            if torsions is not None and len(torsions) > 0:
                from config import Config
                print('Fit torsion based on new non-bonded parameters')
                for n, torsion in enumerate(torsions):
                    print(torsion)
                    ppf.fit_torsion(Config.DFF_ROOT,
                                    torsion[0],
                                    torsion[1],
                                    torsion[2],
                                    torsion[3],
                                    dfi_name='fit_torsion-%i-%i' %
                                    (task.iteration + 1, n))
            if modify_torsions is not None:
                for torsion in modify_torsions:
                    ppf.modify_torsion(torsion[0], torsion[1], torsion[2])

            ### new iteration
            task.iteration += 1
            self.db.session.commit()

            ppf_out = os.path.join(self.CWD,
                                   '%s-%i.ppf' % (task.name, task.iteration))
            ppf.write(ppf_out)

            if not task.npt_started():
                ### save gtx_dirs and gtx_cmds for running jobs on gtx queue
                gtx_dirs = []
                gtx_cmds = []
                ###
                for target in task.targets:
                    if not target.need_npt:
                        continue
                    if target.npt_started():
                        continue
                    cmds = target.run_npt(ppf_out,
                                          paras,
                                          drde_dict=self.drde_dict)
                    ### save gtx_dirs and gtx_cmds for running jobs on gtx queue
                    if cmds != []:
                        gtx_dirs.append(target.dir_npt)
                        gtx_cmds = cmds

                os.chdir(self.CWD)

                if gtx_dirs != []:
                    from .models import npt, jobmanager
                    commands_list = npt.gmx.generate_gpu_multidir_cmds(
                        gtx_dirs,
                        gtx_cmds,
                        n_parallel=self.n_parallel,
                        n_gpu=jobmanager.ngpu,
                        n_procs=jobmanager.nprocs)
                    for i, commands in enumerate(commands_list):
                        sh = os.path.join(task.dir, '_job.npt-%i.sh' % i)
                        jobmanager.generate_sh(task.dir,
                                               commands,
                                               name='%s-%i-%i' %
                                               (task.name, task.iteration, i),
                                               sh=sh)
                        jobmanager.submit(sh)

            if not task.vacuum_started():
                gtx_dirs = []
                gtx_cmds = []
                for target in task.targets:
                    if not target.need_vacuum:
                        continue
                    if target.vacuum_started():
                        continue
                    cmds = target.run_vacuum(ppf_out,
                                             paras,
                                             drde_dict=self.drde_dict)
                    ### save gtx_dirs and gtx_cmds for running jobs on gtx queue
                    if cmds != []:
                        gtx_dirs.append(target.dir_vacuum)
                        gtx_cmds = cmds

                os.chdir(self.CWD)

                if gtx_dirs != []:
                    from .models import vacuum, jobmanager
                    commands_list = vacuum.gmx.generate_gpu_multidir_cmds(
                        gtx_dirs,
                        gtx_cmds,
                        n_parallel=self.n_parallel,
                        n_gpu=jobmanager.ngpu,
                        n_procs=jobmanager.nprocs)
                    for i, commands in enumerate(commands_list):
                        sh = os.path.join(task.dir, '_job.vacuum-%i.sh' % i)
                        jobmanager.generate_sh(task.dir,
                                               commands,
                                               name='%s-%i-VAC%i' %
                                               (task.name, task.iteration, i),
                                               sh=sh)
                        jobmanager.submit(sh)

            while True:
                if task.npt_finished() and task.vacuum_finished():
                    break
                else:
                    current_time = time.strftime('%m-%d %H:%M')
                    print(current_time + ' Job still running. Wait ...')
                    time.sleep(60)

            Dens = []
            Hvap = []
            R_dens = []
            R_hvap = []
            targets = task.targets.all()
            for target in targets:
                if target.wDens > 1E-4:
                    dens = target.get_density()
                    R_dens.append((dens - target.density) / target.density *
                                  100 * target.wDens)  # deviation  percent
                    Dens.append(dens)
                if target.wHvap > 1E-4:
                    hvap = target.get_hvap()
                    R_hvap.append((hvap - target.hvap) / target.hvap * 100 *
                                  target.wHvap)  # deviation percent
                    Hvap.append(hvap)
            R = R_dens + R_hvap
            os.chdir(self.CWD)

            ### expansivity
            if weight_expansivity != 0:
                R_expa = []
                for i_mol in range(len(targets) // 2):
                    target_T1 = targets[2 * i_mol]
                    target_T2 = targets[2 * i_mol + 1]
                    res_Kt = ((target_T1.sim_dens - target_T2.sim_dens) / (target_T1.density - target_T2.density) - 1) \
                             * 100 * weight_expansivity
                    R_expa.append(res_Kt)

                R += R_expa

            # parameter penalty
            R_pena = []
            for k, v in params.items():
                if k.endswith('r0') or k.endswith('e0'):
                    res = (v.value - adj_nb_paras[restore_para_name(k)]
                           ) / adj_nb_paras[restore_para_name(k)]
                elif k.endswith('bi'):
                    res = v.value - adj_nb_paras[restore_para_name(k)]
                else:
                    res = v.value
                penalty = get_penalty_for_para(k)
                R_pena.append(res * penalty * np.sqrt(len(R_dens)))
            R += R_pena

            ### save result to database
            result = Result(task=task)
            result.iteration = task.iteration
            result.ppf = str(ppf)
            result.parameter = str(params)
            result.residual = json.dumps(R)
            self.db.session.add(result)
            self.db.session.commit()
            ###

            ### write current parameters and residual to log
            txt = '\nITERATION %i, RSQ %.2f\n' % (
                task.iteration, np.sum(list(map(lambda x: x**2, R))))
            txt += '\nPARAMETERS:\n'
            for k, v in self.drde_dict.items():
                txt += '%10.5f  %-12s  Fixed\n' % (v, k)
            for k, v in params.items():
                txt += '%10.5f  %-12s  %10.5f\n' % (
                    v.value, restore_para_name(k), init_params[k])
            txt += '\n%8s %8s %10s %8s %8s %8s %3s %3s %s %s\n' % (
                'RESIDUAL', 'Property', 'Deviation', 'Expt.', 'Simu.',
                'Weight', 'T', 'P', 'Molecule', 'SMILES')

            targets_dens = task.targets.filter(Target.wDens > 1E-4).all()
            for i, r in enumerate(R_dens):
                target = targets_dens[i]
                prop = 'density'
                weight = target.wDens
                txt += '%8.2f %8s %8.2f %% %8.3f %8.3f %8.2f %3i %3i %s %s\n' % (
                    r, prop, r / weight, target.density, Dens[i], weight,
                    target.T, target.P, target.name, target.smiles)

            targets_hvap = task.targets.filter(Target.wHvap > 1E-4).all()
            for i, r in enumerate(R_hvap):
                target = targets_hvap[i]
                prop = 'hvap'
                weight = target.wHvap
                txt += '%8.2f %8s %8.2f %% %8.1f %8.1f %8.2f %3i %3i %s %s\n' % (
                    r, prop, r / weight, target.hvap, Hvap[i], weight,
                    target.T, target.P, target.name, target.smiles)

            if weight_expansivity != 0:
                for i, r in enumerate(R_expa):
                    target = targets[i * 2]
                    prop = 'expan'
                    weight = weight_expansivity
                    txt += '%8.2f %8s %8.2f %% %8s %8s %8.2f %3s %3s %s %s\n' % (
                        r, prop, r / weight, '', '', weight, '', '',
                        target.name, target.smiles)

            for i, r in enumerate(R_pena):
                prop = 'penalty'
                k = list(params.keys())[i]
                txt += '%8.2f %8s %10s %8s %8s %8.2f\n' % (
                    r, prop, k, '', '', get_penalty_for_para(k))

            print(txt)
            with open(LOG, 'a') as log:
                log.write(txt)
            ###

            return R
Beispiel #12
0
def grid_fit_mk(params_in, data_in):

# Expand input parmeter dictionary:
    n_alpha = params_in['n_alpha']
    n_lambda = params_in['n_lambda']
    n_T0 = params_in['n_T0']
    alpha_min = params_in['alpha_lim'][0]
    alpha_max = params_in['alpha_lim'][1]
    lambda_min = params_in['lambda_lim'][0]
    lambda_max = params_in['lambda_lim'][1]
    T0_min = params_in['T0_lim'][0]
    T0_max = params_in['T0_lim'][1]
    rho_min = params_in['rho_lim'][0]
    rho_max = params_in['rho_lim'][1]

    mjd = data_in['mjd']
    y = data_in['y']
    y_err = data_in['y_err']
    incl = data_in['incl']
    prec_period = data_in['prec_period']


# Now get arrays of grid steps for loops, and for later plotting.
# Shift each *_samples array by half the step size.  This will avoid having 
# zero values, which when run through the function, can give divisions by zero.
    alpha_samples, alpha_step = np.linspace(alpha_min, alpha_max, n_alpha, \
                                                endpoint=False, retstep=True) 
    alpha_samples = alpha_samples + alpha_step/2.
    lambda_samples, lambda_step = np.linspace(lambda_min, lambda_max, n_lambda, \
                                                endpoint=False, retstep=True) 
    lambda_samples = lambda_samples + lambda_step/2.
    T0_samples, T0_step = np.linspace(T0_min, T0_max, n_T0, \
                                                endpoint=False, retstep=True) 
    T0_samples = T0_samples + T0_step/2.

# Set up parameters with arbitrary values, and min/max boundary values.
    params=Parameters()
    params.add('alpha', value=np.radians(152.3), min=alpha_min, max=alpha_max)
    params.add('lambda', value=np.radians(12.4), min=lambda_min, max=lambda_max)
    params.add('rho', value=np.radians(9.), min=rho_min, max=rho_max)
    params.add('T0', value=yeartomjd(1990.), min=T0_min, max=T0_max)

# rho will vary in each fit: 
    params['rho'].vary = True
# The other parameters will be fixed at each grid point value:
    params['alpha'].vary = False
    params['lambda'].vary = False
    params['T0'].vary = False

# Set up 3-d array of ones to use for inputting resulting chi2's for each fit
    n_dims = (n_alpha, n_lambda, n_T0)

# Initialize array to keep fitted rho values for later histogram-ation
    rho_val = np.array([])
    rho_redchi2 = np.array([])

# Now run grid.  Opting not to run a list comprehension-type thing, for clarity.

    for i_incl in np.arange(n_incl):
# Just to be paranoid, reset chi2 array
        chi2 = np.ones(n_dims, dtype=float)
        redchi2 = np.ones(n_dims, dtype=float)

# alpha and lambda each will run from 0. to pi:
        for i_alpha in np.arange(n_alpha):
        # Set alpha value for current iteration of fit:
            params['alpha'].value = alpha_samples[i_alpha]

            for i_lambda in np.arange(n_lambda):
            # Set lambda value for current iteration of fit:
                params['lambda'].value = lambda_samples[i_lambda]

# T0 will run from mjd_mid-prec_period/2. to mjd_mid+prec_period/2.
                for i_T0 in np.arange(n_T0):
                # Set T0 value for current iteration of fit:
                    params['T0'].value = T0_samples[i_T0]

                # Do fit twice: one for inclination as is, and once for 180-incl
                    if(i_incl==0):
                        fit = minimize(residual, params, args=(mjd, y, y_err, \
                                                                   incl, prec_period) )
                    else:
                        fit = minimize(residual, params, args=(mjd, y, y_err, \
                                                               (np.pi-incl, prec_period)) )
                    
                    #chi2[i_alpha, i_lambda, i_T0] = \
                    #    np.sum(residual(params, mjd, y, y_err, incl)**2.)


                    chi2[i_alpha, i_lambda, i_T0] = fit.chisqr
                    redchi2[i_alpha, i_lambda, i_T0] = fit.redchi
 
# Append the fit value of rho to existing array
                    rho_val = np.append(rho_val, params['rho'].value)
                    rho_redchi2 = np.append(rho_redchi2, fit.redchi)
                   

                    # print to screen every 8 iterations of alpha (= 128*128*8)
                    if (np.fmod(i_alpha, 4)==0. and i_lambda==0 and i_T0==0):
                    #    chi2 = chisq(func_rl, mjd, y, popt, sigma=y_err)
                        print "Iter ", i_alpha * n_lambda * n_T0
                        print "Chisq = ", fit.chisqr
                        print "Reduced Chisq = ", fit.redchi
                        print 'Best-Fit Values:'
                        for name, par in params.items():
                            if (name=='T0'):
                                print '  %s = %.4f +/- %.4f ' % (name, par.value, par.stderr)
                            else:
                                print '  %s = %.4f +/- %.4f ' % (name, par.value*180./np.pi, par.stderr*180./np.pi)

# Append the fit value of rho to existing array


# Now have grid done.  Will need to now:
#     (1) Convert chi2 grid to an exp(-(chi2 - chi2_min)) grid.  Normalize to make
#         sum(3-d grid values) = 1

# Find minimum chi2 value:
        chi2_min = np.amin(chi2)
        redchi2_min = np.amin(redchi2)

        print "Min chisq = ", chi2_min
        print "Min reduced chisq = ", redchi2_min
        indices = np.where(chi2 == chi2_min)
        print "indices = ", indices
        print "min_alpha = ", alpha_samples[indices[0]]
        print "min_lambda = ", lambda_samples[indices[1]]
        print "min_T0    = ", T0_samples[indices[2]]

# create likelihood and normalize:
        if(i_incl==0):
            likelihood = np.exp(-(chi2 - chi2_min)/2.0)
        else:  # multiply together and combine probs from both inclination cases
            likelihood = likelihood*np.exp(-(chi2 - chi2_min)/2.0)

    norm_like = likelihood/np.sum(likelihood)

    print "Number of elements in likelihood array = ", np.size(likelihood)
    print "Sum of normalized array = ", np.sum(norm_like)

# Make up output dictionary to pass back to wrapping routine:
    p_out = {'norm_like':norm_like, \
                 'alpha':alpha_samples, 'lambda':lambda_samples, \
                 'T0':T0_samples, 'rho':rho_val, 'rho_redchi2':rho_redchi2}



    return p_out
Beispiel #13
0
class H2ExcitationFit(ExcitationFit):
    r"""Tool for fitting temperatures, column densities, and ortho-to-para ratio(`OPR`) from an :math:`H_2` excitation diagram. It takes as input a set of :math:`H_2` rovibrational line observations with errors represented as :class:`~pdrtpy.measurement.Measurement`.   

Often, excitation diagrams show evidence of both "hot" and "cold" gas components, where the cold gas dominates the intensity in the low `J` transitions and the hot gas dominates in the high `J` transitions. Given data over several transitions, one can fit for :math:`T_{cold}, T_{hot}, N_{total} = N_{cold}+ N_{hot}`, and optionally `OPR`. One needs at least 5 points to fit the temperatures and column densities (slope and intercept :math:`\times 2`), though one could compute (not fit) them with only 4 points. To additionally fit `OPR`, one should have 6 points (5 degrees of freedom).

Once the fit is done, :class:`~pdrtpy.plot.ExcitationPlot` can be used to view the results.

:param measurements: Input :math:`H_2` measurements to be fit.  
:type measurements: list of :class:`~pdrtpy.measurement.Measurement`. 
    """
    def __init__(self,
                 measurements=None,
                 constantsfile="atomic_constants.tab"):
        super().__init__(measurements, constantsfile)
        self._canonical_opr = 3.0
        self._opr = Measurement(data=[self._canonical_opr], uncertainty=None)
        self._init_params()
        self._init_model()
        self._fitresult = None
        self._temperature = None
        self._total_colden = None
        # position and size that was used for averaging/fit
        self._position = None
        self._size = None

    def _init_params(self):
        #fit input parameters
        self._params = Parameters()
        # we have to have opr max be greater than 3 so that fitting will work.
        # the fit algorithm does not like when the initial value is pinned at one
        # of the limits
        self._params.add('opr', value=3.0, min=1.0, max=3.5, vary=False)
        self._params.add('m1', value=0, min=-1, max=0)
        self._params.add('n1', value=15, min=10, max=30)
        self._params.add('m2', value=0, min=-1, max=0)
        self._params.add('n2', value=15, min=10, max=30)

    def _residual(self, params, x, data, error, idx):
        # We assume that the column densities passed in have been normalized
        # using the canonical OPR=3. Therefore what we are actually fitting is
        # the ratio of the actual OPR to the canonical OPR.
        # For odd J, input x = Nu/(3*(2J+1) where 3=canonical OPR.
        #
        # We want the model-data residual to be small, but if the opr
        # is different from the  canonical value of 3, then data[idx] will
        # be low by a factor of 3/opr.
        # So we must LOWER model[idx] artificially by dividing it by
        # 3/opr, i.e. multiplying by opr/3.  This is equivalent to addition in log-space.
        p = params.valuesdict()
        y1 = 10**(x * p['m1'] + p['n1'])
        y2 = 10**(x * p['m2'] + p['n2'])
        model = np.log10(y1 + y2)
        if params['opr'].vary:
            model += np.log10(p['opr'] / self._canonical_opr)
        return (model - data) / error

    def _modelfunc(self, x, m1, n1, m2, n2, opr, idx=[], fit_opr=False):
        '''Function for fitting the excitation curve as sum of two linear functions 
           and allowing ortho-to-para ratio to vary.  Para is even J, ortho is odd J.
           :param x: independent axis array
           :param m1: slope of first line
           :type m1: float
           :param n1: intercept of first line
           :type n1: float
           :param m2: slope of second line
           :type m2: float
           :param n2: intercept of second line
           :type n2: float
           :param opr: ortho-to-para ratio
           :type opr: float
           :type idx: np.ndarray
           :param idx: list of indices that may have variable opr (odd J transitions)
           :param fit_opr: indicate whether opr will be fit, default False (opr fixed)
           :type fit_opr: False
           :return: Sum of lines in log space:log10(10**(x*m1+n1) + 10**(x*m2+n2)) + log10(opr/3.0)
           :rtype: :class:`numpy.ndarray` 
        '''
        y1 = 10**(x * m1 + n1)
        y2 = 10**(x * m2 + n2)

        model = np.log10(y1 + y2)
        # We assume that the column densities passed in have been normalized
        # using the canonical OPR=3. Therefore what we are actually fitting is
        # the ratio of the actual OPR to the canonical OPR.
        # For odd J, input x = Nu/(3*(2J+1) where 3=canonical OPR.
        #
        # We want the model-data residual to be small, but if the opr
        # is different from the  canonical value of 3, then data[idx] will
        # be low by a factor of 3/opr.
        # So we must LOWER model[idx] artificially by dividing it by
        # 3/opr, i.e. multiplying by opr/3.  This is equivalent to addition in log-space.
        if fit_opr:
            model[idx] += np.log10(opr / self._canonical_opr)
        return model

    def _init_model(self):
        #@todo make a separate class that subclasses Model.
        # potentially allow users to change it.
        self._model = Model(self._modelfunc)
        for p, q in self._params.items():
            self._model.set_param_hint(p,
                                       value=q.value,
                                       min=q.min,
                                       max=q.max,
                                       vary=q.vary)
            self._model.make_params()

    def _compute_quantities(self, fitmap):
        """Compute the temperatures and column densities for the hot and cold gas components.  This method will set class variables `_temperature` and `_colden`.
        
        :param params: The fit parameters returned from fit_excitation.
        :type params: :class:`lmfit.Parameters`
        """
        self._temperature = dict()
        # N(J=0) column density = intercept on y axis
        self._j0_colden = dict()
        # total column density = N(J=0)*Z(T) where Z(T) is partition function
        self._total_colden = dict()
        size = fitmap.data.size
        # create default arrays in which calculated values will be stored.
        # Use nan as fill value because there may be nans in fitmapdata, in which
        # case nothing need be done to arrays.
        # tc, th = cold and hot temperatures
        # utc, utc = uncertainties in cold and hot temperatures
        # nc, nh = cold and hot column densities
        # unc, unh = uncertainties in cold and hot temperatures
        # opr = ortho to para ratio
        # uopr = uncertainty in OPR
        tc = np.full(shape=size, fill_value=np.nan, dtype=float)
        th = np.full(shape=size, fill_value=np.nan, dtype=float)
        utc = np.full(shape=size, fill_value=np.nan, dtype=float)
        uth = np.full(shape=size, fill_value=np.nan, dtype=float)
        nc = np.full(shape=size, fill_value=np.nan, dtype=float)
        nh = np.full(shape=size, fill_value=np.nan, dtype=float)
        unh = np.full(shape=size, fill_value=np.nan, dtype=float)
        unc = np.full(shape=size, fill_value=np.nan, dtype=float)
        opr = np.full(shape=size, fill_value=np.nan, dtype=float)
        uopr = np.full(shape=size, fill_value=np.nan, dtype=float)
        ff = fitmap.data.flatten()
        ffmask = fitmap.mask.flatten()
        for i in range(size):
            if ffmask[i]:
                continue
            params = ff[i].params
            for p in params:
                if params[p].stderr is None:
                    print("AT pixel i [mask]", i, ffmask[i])
                    params.pretty_print()
                    raise Exception(
                        "Something went wrong with the fit and it was unable to calculate errors on the fitted parameters. It's likely that a two-temperature model is not appropriate for your data. Check the fit_result report and plot."
                    )

            if params['m2'] < params['m1']:
                cold = '2'
                hot = '1'
            else:
                cold = '1'
                hot = '2'
            mcold = 'm' + cold
            mhot = 'm' + hot
            ncold = 'n' + cold
            nhot = 'n' + hot
            # cold and hot temperatures
            utc[i] = params[mcold].stderr / params[mcold]
            tc[i] = -utils.LOGE / params[mcold]
            uth[i] = params[mhot].stderr / params[mhot]
            th[i] = -utils.LOGE / params[mhot]
            nc[i] = 10**params[ncold]
            unc[i] = utils.LN10 * params[ncold].stderr * nc[i]
            nh[i] = 10**params[nhot]
            unh[i] = utils.LN10 * params[nhot].stderr * nh[i]
            opr[i] = params['opr'].value
            uopr[i] = params['opr'].stderr

        # now reshape them all back to map shape
        tc = tc.reshape(fitmap.data.shape)
        th = th.reshape(fitmap.data.shape)
        utc = utc.reshape(fitmap.data.shape)
        uth = uth.reshape(fitmap.data.shape)
        nc = nc.reshape(fitmap.data.shape)
        nh = nh.reshape(fitmap.data.shape)
        unh = unh.reshape(fitmap.data.shape)
        unc = unc.reshape(fitmap.data.shape)
        opr = opr.reshape(fitmap.data.shape)
        uopr = uopr.reshape(fitmap.data.shape)

        mask = fitmap.mask | np.logical_not(np.isfinite(tc))
        ucc = StdDevUncertainty(np.abs(tc * utc))
        self._temperature["cold"] = Measurement(data=tc,
                                                unit=self._t_units,
                                                uncertainty=ucc,
                                                wcs=fitmap.wcs,
                                                mask=mask)
        mask = fitmap.mask | np.logical_not(np.isfinite(th))
        uch = StdDevUncertainty(np.abs(th * uth))
        self._temperature["hot"] = Measurement(data=th,
                                               unit=self._t_units,
                                               uncertainty=uch,
                                               wcs=fitmap.wcs,
                                               mask=mask)
        # cold and hot total column density
        ucn = StdDevUncertainty(np.abs(unc))
        mask = fitmap.mask | np.logical_not(np.isfinite(nc))
        self._j0_colden["cold"] = Measurement(nc,
                                              unit=self._cd_units,
                                              uncertainty=ucn,
                                              wcs=fitmap.wcs,
                                              mask=mask)
        mask = fitmap.mask | np.logical_not(np.isfinite(nh))
        uhn = StdDevUncertainty(np.abs(unh))
        self._j0_colden["hot"] = Measurement(nh,
                                             unit=self._cd_units,
                                             uncertainty=uhn,
                                             wcs=fitmap.wcs,
                                             mask=mask)
        #
        self._total_colden["cold"] = self._j0_colden[
            "cold"] * self._partition_function(self.tcold)
        self._total_colden["hot"] = self._j0_colden[
            "hot"] * self._partition_function(self.thot)
        mask = fitmap.mask | np.logical_not(np.isfinite(opr))
        self._opr = Measurement(opr,
                                unit=u.dimensionless_unscaled,
                                uncertainty=StdDevUncertainty(uopr),
                                wcs=fitmap.wcs,
                                mask=mask)

    @property
    def fit_result(self):
        '''The result of the fitting procedure which includes fit statistics, variable values and uncertainties, and correlations between variables.
        
        :rtype:  :class:`lmfit.model.ModelResult`      
        '''
        return self._fitresult

    @property
    def opr_fitted(self):
        '''Was the ortho-to-para ratio fitted?
        
        :returns: True if OPR was fitted, False if canonical LTE value was used
        :rtype: bool
        '''
        if self._fitresult is None:
            return False
        return self._params['opr'].vary

    @property
    def opr(self):
        '''The ortho-to-para ratio (OPR)
        
        :returns: The fitted OPR is it was determined in the fit, otherwise the canonical LTE OPR
        :rtype: :class:`~pdrtpy.measurement.Measurement`
        '''
        return self._opr

    @property
    def intensities(self):
        '''The stored intensities. See :meth:`add_measurement`
         
           :rtype: list of :class:`~pdrtpy.measurement.Measurement`
        '''
        return self._measurements

    def colden(self, component):  #,log=False):
        '''The column density of hot or cold gas component, or total column density.
        
        :param component: 'hot', 'cold', or 'total
        :type component: str
        
        :rtype: :class:`~pdrtpy.measurement.Measurement`
        '''
        #:param log: take the log10 of the column density
        cl = component.lower()
        if cl not in self._valid_components:
            raise KeyError(
                f"{cl} not a valid component. Must be one of {self._valid_components}"
            )
        #print(f'returning {cl}')
        if cl == 'total':
            return self.total_colden
        else:
            return self._total_colden[cl]

    @property
    def total_colden(self):
        '''The fitted total column density
        
        :rtype: :class:`~pdrtpy.measurement.Measurement` 
        '''
        return self._total_colden["hot"] + self._total_colden["cold"]

    @property
    def hot_colden(self):
        '''The fitted hot gas total column density
        
        :rtype: :class:`~pdrtpy.measurement.Measurement`         
        '''
        return self._total_colden["hot"]

    @property
    def cold_colden(self):
        '''The fitted cold gas total column density
        
        :rtype: :class:`~pdrtpy.measurement.Measurement`         
        '''
        return self._total_colden["cold"]

    @property
    def tcold(self):
        '''The fitted cold gas excitation temperature
        
        :rtype: :class:`~pdrtpy.measurement.Measurement` 
        '''
        return self._temperature['cold']  #self._fitparams.tcold

    @property
    def thot(self):
        '''The fitted hot gas excitation temperature
        
        :rtype: :class:`~pdrtpy.measurement.Measurement` 
        '''
        return self._temperature['hot']  #self._fitparams.thot

    @property
    def temperature(self):
        '''The fitted gas temperatures, returned in a dictionary with keys 'hot' and 'cold'.
        :rtype: dict
        '''
        return self._temperature

    def column_densities(self, norm=False, unit=utils._CM2, line=False):
        r'''The computed upper state column densities of stored intensities

           :param norm: if True, normalize the column densities by the 
                       statistical weight of the upper state, :math:`g_u`.  
                       Default: False
           :type norm: bool
           :param unit: The units in which to return the column density. Default: :math:`{\\rm }cm^{-2}`
           :type unit: str or :class:`astropy.units.Unit`
           :param line: if True, the dictionary index is the Line name, 
                     otherwise it is the upper state :math:`J` number.  Default: False
           :type line: bool

           :returns: dictionary of column densities indexed by upper state :math:`J` number or Line name. Default: False means return indexed by :math:`J`.
           :rtype: dict
        '''
        # Compute column densities if needed.
        # Note: this has a gotcha - if user changes an existing intensity
        # Measurement in place, rather than replaceMeasurement(), the colden
        # won't get recomputed. But we warned them!
        #if not self._column_density or (len(self._column_density) != len(self._measurements)):

        # screw it. just always compute them.  Note to self: change this if it becomes computationally intensive
        self._compute_column_densities(unit=unit, line=line)
        if norm:
            cdnorm = dict()
            for cd in self._column_density:
                if line:
                    denom = self._ac.loc[cd]["g_u"]
                else:
                    denom = self._ac.loc['J_u', cd]["g_u"]
                # This fails with complaints about units:
                #self._column_density[cd] /= self._ac.loc[cd]["g_u"]
                #gu = Measurement(self._ac.loc[cd]["g_u"],unit=u.dimensionless_unscaled)
                cdnorm[cd] = self._column_density[cd] / denom
            #return #self._column_density
            return cdnorm
        else:
            return self._column_density

    def energies(self, line=False):
        '''Upper state energies of stored intensities, in K. 

           :param line: if True, the dictionary index is the Line name, 
                     otherwise it is the upper state :math:`J` number.  Default: False
           :type line: bool
           :returns: dictionary indexed by upper state :math:`J` number or Line name. Default: False means return indexed by :math:`J`.
           :rtype: dict
        '''
        t = dict()
        if line:
            for m in self._measurements:
                t[m] = self._ac.loc[m]["E_upper/k"]
        else:
            for m in self._measurements:
                t[self._ac.loc[m]["J_u"]] = self._ac.loc[m]["E_upper/k"]
        return t

    def run(self, position=None, size=None, fit_opr=False, **kwargs):
        r'''Fit the :math:`log N_u-E` diagram with two excitation temperatures,
        a ``hot`` :math:`T_{ex}` and a ``cold`` :math:`T_{ex}`. 
        
        If ``position`` and ``size`` are given, the data will be averaged over a spatial box before fitting.  The box is created using :class:`astropy.nddata.utils.Cutout2D`.  If position or size is None, the data are averaged over all pixels.  If the Measurements are single values, these arguments are ignored.

        :param position: The position of the cutout array's center with respect to the data array. The position can be specified either as a `(x, y)` tuple of pixel coordinates.
        :type position: tuple 
        :param size: The size of the cutout array along each axis in pixels. If size is a scalar number or a scalar :class:`~astropy.units.Quantity`, then a square cutout of size will be created. If `size` has two elements, they should be in `(nx, ny)` order [*this is the opposite of Cutout2D signature*]. Scalar numbers in size are assumed to be in units of pixels.  Default value of None means use all pixels (position is ignored)
        :type size: int, array_like`
        :param fit_opr: Whether to fit the ortho-to-para ratio or not. If True, the OPR will be varied to determine the best value. If False, the OPR is fixed at the canonical LTE value of 3.
        :type fit_opr: bool
        '''
        kwargs_opts = {
            'mask': None,
            'method': 'leastsq',
            'nan_policy': 'raise',
            'test': False,
            'profile': False,
        }
        kwargs_opts.update(kwargs)
        return self._fit_excitation(position, size, fit_opr, **kwargs_opts)

    def intensity(self, colden):
        '''Given an upper state column density :math:`N_u`, compute the intensity :math:`I`.  

           .. math::
                 I = {A \Delta E~N_u \over 4\pi}     
              
        where :math:`A` is the Einstein A coefficient and :math:`\Delta E` is the energy of the transition.     
        
        :param colden: upper state column density
        :type colden: :class:`~pdrtpy.measurement.Measurement`
        :returns: optically thin intensity 
        :rtype: :class:`~pdrtpy.measurement.Measurement`
        '''
        # colden is N_upper
        dE = self._ac.loc[
            colden.id]["dE/k"] * constants.k_B.cgs * self._ac["dE/k"].unit
        A = self._ac.loc[colden.id]["A"] * self._ac["A"].unit
        v = A * dE / (4.0 * math.pi * u.sr)
        val = Measurement(data=v.value, unit=v.unit, identifier=colden.id)
        intensity = val * colden  # error will get propagated
        i = intensity.convert_unit_to(self._intensity_units)
        i._identifier = val.id
        return i

    def upper_colden(self, intensity, unit):
        '''Compute the column density in upper state :math:`N_u`, given an 
           intensity :math:`I` and assuming optically thin emission.  
           Units of :math:`I` need to be equivalent to 
           :math:`{\\rm erg~cm^{-2}~s^{-1}~sr^{-1}}`.

           .. math::
                 I &= {A \Delta E~N_u \over 4\pi}

                 N_u &= 4\pi {I\over A\Delta E}

           where :math:`A` is the Einstein A coefficient and :math:`\Delta E` is the energy of the transition.

           :param intensity: A :class:`~pdrtpy.measurement.Measurement` instance containing intensity in units equivalent to :math:`{\\rm erg~cm^{-2}~s^{-1}~sr^{-1}}`
           :type intensity: :class:`~pdrtpy.measurement.Measurement`
           :param unit: The units in which to return the column density. Default: :math:`{\\rm }cm^{-2}`
           :type unit: str or :class:`astropy.units.Unit`
           :returns: a :class:`~pdrtpy.measurement.Measurement` of the column density.
           :rtype: :class:`~pdrtpy.measurement.Measurement` 
        '''

        dE = self._ac.loc[
            intensity.id]["dE/k"] * constants.k_B.cgs * self._ac["dE/k"].unit
        A = self._ac.loc[intensity.id]["A"] * self._ac["A"].unit
        v = 4.0 * math.pi * u.sr / (A * dE)
        val = Measurement(data=v.value, unit=v.unit)
        N_upper = intensity * val  # error will get propagated
        return N_upper.convert_unit_to(unit)

    def _compute_column_densities(self, unit=utils._CM2, line=False):
        r'''Compute all upper level column densities for stored intensity measurements and puts them in a dictionary
           :param unit: The units in which to return the column density. Default: :math:`{\\rm }cm^{-2}`
           :type unit: str or :class:`astropy.units.Unit`
           :param line: if True, the dictionary index is the Line name, 
                     otherwise it is the upper state :math:`J` number.  Default: False
           :type line: bool
        
            # should we reutrn something here or just compute them and never store.
            # I'm beginning to think there is no reason to store them.
           #:returns: dictionary of column densities as:class:`~pdrtpy.measurement.Measurement  indexed by upper state :math:`J` number or Line name. Default: False means return indexed by :math:`J`.
           #:returns: a :class:`~pdrtpy.measurement.Measurement` of the column density.
        '''
        self._column_density = dict()
        for m in self._measurements:
            if line:
                index = m
            else:
                index = self._ac.loc[m]["J_u"]
            self._column_density[index] = self.upper_colden(
                self._measurements[m], unit)

    def gu(self, id, opr):
        r'''Get the upper state statistical weight $g_u$ for the given transition identifer, and, if the transition is odd-$J$, scale the result by the given ortho-to-para ratio.  If the transition is even-$J$, the LTE value is returned.
        
           :param id: the measurement identifier
           :type id: str
           :param opr:
           :type opr: float
           :raises KeyError: if id not in existing Measurements 
           :rtype: float
        '''
        if utils.is_even(self._ac.loc[id]["J_u"]):
            return self._ac.loc[id]["g_u"]
        else:
            #print("Ju=%d scaling by [%.2f/%.2f]=%.2f"%(self._ac.loc[id]["J_u"],opr,self._canonical_opr,opr/self._canonical_opr))
            return self._ac.loc[id]["g_u"] * opr / self._canonical_opr

    def average_column_density(self,
                               position=None,
                               size=None,
                               norm=True,
                               unit=utils._CM2,
                               line=False,
                               clip=-1E40 * u.Unit("cm-2")):
        r'''Compute the average column density over a spatial box.  The box is created using :class:`astropy.nddata.utils.Cutout2D`.

        :param position: The position of the cutout array's center with respect to the data array. The position can be specified either as a `(x, y)` tuple of pixel coordinates.
        :type position: tuple 
        :param size: The size of the cutout array along each axis. If size is a scalar number or a scalar :class:`~astropy.units.Quantity`, then a square cutout of size will be created. If `size` has two elements, they should be in `(nx,ny)` order [*this is the opposite of Cutout2D signature*]. Scalar numbers in size are assumed to be in units of pixels.  Default value of None means use all pixels (position is ignored)
        :type size: int, array_like`
        :param norm: if True, normalize the column densities by the 
                       statistical weight of the upper state, :math:`g_u`.  For ortho-$H_2$ $g_u = OPR \times (2J+1)$, for para-$H_2$ $g_u=2J+1$. In LTE, $OPR = 3$.
        :type norm: bool
        :param unit: The units in which to return the column density. Default: :math:`{\rm cm}^{-2}` 
        :type unit: str or :class:`astropy.units.Unit`
        :param line: if True, the returned dictionary index is the Line name, otherwise it is the upper state :math:`J` number.  
        :type line: bool
        :returns: dictionary of column density Measurements, with keys as :math:`J` number or Line name
        :rtype:  dict
        :param clip: Column density value at which to clip pixels. Pixels with column densities below this value will not be used in the average. Default: a large negative number, which translates to no clipping.  
        :type clip: :class:`astropy.units.Quantity`
        '''
        #@todo
        # - should default clip = None?

        # Set norm=False because we normalize below if necessary.
        if position is not None and size is None:
            print("WARNING: ignoring position keyword since no size given")
        if position is None and size is not None:
            raise Exception(
                "You must supply a position in addition to size for cutout")
        if size is not None:
            if np.isscalar(size):
                size = np.array([size, size])
            else:
                #Cutout2D wants (ny,nx)
                size = np.array([size[1], size[0]])

        clip = clip.to("cm-2")
        cdnorm = self.column_densities(norm=norm, unit=unit, line=line)
        cdmeas = dict()
        for cd in cdnorm:
            ca = cdnorm[cd]
            if size is not None:
                if len(size) != len(ca.shape):
                    raise Exception(
                        f"Size dimensions [{len(size)}] don't match measurements [{len(ca.shape)}]"
                    )

                #if size[0] > ca.shape[0] or size[1] > ca.shape[1]:
                #    raise Exception(f"Requested cutout size {size} exceeds measurement size {ca.shape}")
                cutout = Cutout2D(ca.data,
                                  position,
                                  size,
                                  ca.wcs,
                                  mode='trim',
                                  fill_value=np.nan)
                w = Cutout2D(ca.uncertainty.array,
                             position,
                             size,
                             ca.wcs,
                             mode='trim',
                             fill_value=np.nan)
                cddata = np.ma.masked_array(cutout.data,
                                            mask=np.ma.mask_or(
                                                np.isnan(cutout.data),
                                                cutout.data < clip.value))
                weights = np.ma.masked_array(w.data, np.isnan(w.data))
                if False:
                    # save cutout as a test that we have the x,y correct in size param
                    t = Measurement(cddata,
                                    unit=ca.unit,
                                    uncertainty=StdDevUncertainty(weights),
                                    identifier=ca.id)
                    #t.write("cutout.fits",overwrite=True)

            else:
                cddata = ca.data
                # handle corner case of measurment.data is shape = (1,)
                # and StdDevUncertainty.array is shape = ().
                # They both have only one value but StdDevUncertainty stores
                # its data in a peculiar way.
                # alternative: check that type(ca.uncertainty.array) == np.ndarray would also work.
                if np.shape(ca.data) == (1, ) and np.shape(
                        ca.uncertainty.array) == ():
                    weights = np.array([ca.uncertainty.array])
                else:
                    weights = ca.uncertainty.array
            cdavg = np.average(cddata, weights=weights)
            error = np.nanmean(ca.error) / np.sqrt(ca.error.size)  #-1
            cdmeas[cd] = Measurement(data=cdavg,
                                     uncertainty=StdDevUncertainty(error),
                                     unit=ca.unit,
                                     identifier=cd)
        return cdmeas

    def _get_ortho_indices(self, ids):
        """Given a list of J values, return the indices of those that are ortho
        transitions (odd J)
        
        :param ids:
        :type ids: list of str 
        :returns: The array indices of the odd J values.
        :rtype: list of int
        """
        return np.where(self._ac.loc[ids]["J_u"] % 2 != 0)[0]

    def _get_para_indices(self, ids):
        """Given a list of J values, return the indices of those that are para
        transitions (even J)
        
        :param ids:
        :type ids: list of str 
        :returns: The array indices of the even J values.
        :rtype: list of int
        """
        return np.where(self._ac.loc[ids]["J_u"] % 2 == 0)[0]

    # currently unused.  in future may allow users to give first guesses at the temperatures, though not clear these will be better than _firstguess().  plus this does nothing for the intercepts
    def _slopesfromguess(self, guess):
        """given a guess of two temperatures, compute slopes from them"""
        if guess[0] < guess[1]:
            thot = guess[1]
            tcold = guess[2]
        else:
            tcold = guess[2]
            thot = guess[1]
        slope = []
        slope[0] = -utils.LOGE / tcold
        slope[1] = -utils.LOGE / thot
        return slope

    def _first_guess(self, x, y):
        r"""The first guess at the fit parameters is done by finding the line between the first two (lowest energy) points to determine $T_{cold}and between the last two (highest energy) points to determine $T_{hot}. The first guess is needed to ensure the final fit converges.  The guess doesn't need to be perfect, just in the ballpark.
        
        :param x: array of energies, $E/k$
        :type x: numpy array
        :param y: array of normalized column densities $N_u/g_u$
        :type y: numpy array
        """
        slopecold = (y[1] - y[0]) / (x[1] - x[0])
        slopehot = (y[-1] - y[-2]) / (x[-1] - x[-2])
        intcold = y[1] - slopecold * x[1]
        inthot = y[-1] - slopehot * x[-1]
        #print("FG ",type(slopecold),type(slopehot),type(intcold),type(inthot))
        return np.array([slopecold, intcold, slopehot, inthot])

    def _fit_excitation(self, position, size, fit_opr=False, **kwargs):
        """Fit the :math:`log N_u-E` diagram with two excitation temperatures,
        a ``hot`` :math:`T_{ex}` and a ``cold`` :math:`T_{ex}`.  A first
        pass guess is initially made using data partitioning and two
        linear fits. 

        If ``position`` and ``size`` are given, the data will be averaged over a spatial box before fitting.  The box is created using :class:`astropy.nddata.utils.Cutout2D`.  If position or size is None, the data are averaged over all pixels.  If the Measurements are single values, these arguments are ignored.

        :param position: The position of the cutout array's center with respect to the data array. The position can be specified either as a `(x, y)` tuple of pixel coordinates or a :class:`~astropy.coordinates.SkyCoord`, which will use the :class:`~astropy.wcs.WCS` of the ::class:`~pdrtpy.measurement.Measurement`s added to this tool. See :class:`~astropy.nddata.utils.Cutout2D`.
        :type position: tuple or :class:`astropy.coordinates.SkyCoord` 
        :param size: The size of the cutout array along each axis. If size is a scalar number or a scalar :class:`~astropy.units.Quantity`, then a square cutout of size will be created. If `size` has two elements, they should be in `(ny, nx)` order. Scalar numbers in size are assumed to be in units of pixels. `size` can also be a :class:`~astropy.units.Quantity` object or contain :class:`~astropy.units.Quantity` objects. Such :class:`~astropy.units.Quantity` objects must be in pixel or angular units. For all cases, size will be converted to an integer number of pixels, rounding the the nearest integer. See the mode keyword for additional details on the final cutout size. Default value of None means use all pixels (position is ignored)
        :type size: int, array_like, or :class:`astropy.units.Quantity`
        :param fit_opr: Whether to fit the ortho-to-para ratio or not. If True, the OPR will be varied to determine the best value. If False, the OPR is fixed at the canonical LTE value of 3.
        :type fit_opr: bool
     ,dtype=object   :returns: The fit result which contains slopes, intercepts, the ortho to para ratio (OPR), and fit statistics
        :rtype:  :class:`lmfit.model.ModelResult`  
        """
        profile = kwargs.pop('profile')
        self._stats = None
        if profile:
            pr = cProfile.Profile()
            pr.enable()
        if fit_opr:
            min_points = 5
        else:
            min_points = 4
            self._opr = Measurement(data=[self._canonical_opr],
                                    uncertainty=None)

        self._params['opr'].vary = fit_opr
        energy = self.energies(line=True)
        _ee = np.array([c for c in energy.values()])
        #@ todo: allow fitting of one-temperature model
        if len(_ee) < min_points:
            raise Exception(
                f"You need at least {min_points:d} data points to determine two-temperature model"
            )
        if len(_ee) == min_points:
            warnings.warn(
                f"Number of data points is equal to number of free parameters ({min_points:d}). Fit will be over-constrained"
            )
        _energy = Measurement(_ee, unit="K")
        _ids = list(energy.keys())
        idx = self._get_ortho_indices(_ids)
        # Get Nu/gu.  Canonical opr will be used.
        if position is None or size is None:
            colden = self.column_densities(norm=True, line=True)
        else:

            colden = self.average_column_density(norm=True,
                                                 position=position,
                                                 size=size,
                                                 line=True)

        # Need to stuff the data into a single vector
        _cd = np.squeeze(np.array([c.data for c in colden.values()]))
        _er = np.squeeze(np.array([c.error for c in colden.values()]))
        _colden = Measurement(_cd,
                              uncertainty=StdDevUncertainty(_er),
                              unit="cm-2")
        fk = utils.firstkey(colden)
        x = _energy.data
        y = np.log10(_colden.data)
        #print("SHAPE Y LEN(SHAPE(Y) ",y.shape,len(y.shape))
        #kwargs_opts = {"guess": self._first_guess(x,y)}
        #kwargs_opts.update(kwargs)
        sigma = utils.LOGE * _colden.error / _colden.data
        slopecold, intcold, slopehot, inthot = self._first_guess(x, y)
        #print(slopecold,slopehot,intcold,inthot)
        tcold = (-utils.LOGE / slopecold)
        thot = (-utils.LOGE / slopehot)
        if np.shape(tcold) == ():
            tcold = np.array([tcold])
            thot = np.array([thot])
        saveshape = tcold.shape
        #print("TYPE COLD SIT",type(slopecold),type(intcold),type(tcold))
        #print("SHAPES: colden/sigma/slope/int/temp/cd: ",np.shape(_colden),np.shape(sigma),np.shape(slopecold),np.shape(intcold),np.shape(tcold),np.shape(_cd))
        #print("First guess at excitation temperatures:\n T_cold = %.1f K\n T_hot = %.1f K"%(tcold,thot))
        fmdata = np.empty(tcold.shape, dtype=object).flatten()
        #fm = FitMap(data=fmdata,wcs=colden[fk].wcs,uncertainty=None,unit=None)
        tcold = tcold.flatten()
        thot = thot.flatten()
        slopecold = slopecold.flatten()
        slopehot = slopehot.flatten()
        inthot = inthot.flatten()
        intcold = intcold.flatten()
        #sigma = sigma.flatten()
        # flatten any dimensions past 0
        shp = y.shape
        #print("NS ",shp[0],shp[1:])
        if len(shp) == 1:
            #print("adding new axis")
            y = y[:, np.newaxis]
            shp = y.shape
        yr = y.reshape((shp[0], np.prod(shp[1:])))
        sig = sigma.reshape((shp[0], np.prod(shp[1:])))
        #print("YR, SIG SHAPE",yr.shape,sig.shape)
        count = 0
        #print("LEN(TCOLD)",len(tcold))
        total = len(tcold)
        fm_mask = np.full(shape=tcold.shape, fill_value=False)
        # Suppress the incorrect warning about model parameters
        warnings.simplefilter('ignore', category=UserWarning)
        excount = 0
        badfit = 0
        # update whether opr is allowed to vary or not.
        self._model.set_param_hint('opr', vary=fit_opr)
        # use progress bar if more than one pixel
        if total > 1:
            progress = kwargs.pop("progress", True)
        else:
            progress = False
        with get_progress_bar(progress, total, leave=True, position=0) as pbar:
            for i in range(total):
                if np.isfinite(yr[:, i]).all() and np.isfinite(sig[:,
                                                                   i]).all():
                    # update Parameter hints based on first guess.
                    self._model.set_param_hint('m1',
                                               value=slopecold[i],
                                               vary=True)
                    self._model.set_param_hint('n1',
                                               value=intcold[i],
                                               vary=True)
                    self._model.set_param_hint('m2',
                                               value=slopehot[i],
                                               vary=True)
                    self._model.set_param_hint('n2',
                                               value=inthot[i],
                                               vary=True)
                    p = self._model.make_params()
                    wts = 1.0 / (sig[:, i] * sig[:, i])
                    try:
                        fmdata[i] = self._model.fit(
                            data=yr[:, i],
                            weights=wts,
                            x=x,
                            idx=idx,
                            fit_opr=fit_opr,
                            method=kwargs['method'],
                            nan_policy=kwargs['nan_policy'])
                        if fmdata[i].success and fmdata[i].errorbars:
                            count = count + 1
                        else:
                            fmdata[i] = None
                            fm_mask[i] = True
                            badfit = badfit + 1
                    except ValueError:
                        fmdata[i] = None
                        fm_mask[i] = True
                        excount = excount + 1
                else:
                    fmdata[i] = None
                    fm_mask[i] = True
                pbar.update(1)
        warnings.resetwarnings()
        fmdata = fmdata.reshape(saveshape)
        fm_mask = fm_mask.reshape(saveshape)
        self._fitresult = FitMap(fmdata,
                                 wcs=colden[fk].wcs,
                                 mask=fm_mask,
                                 name="result")
        # this will raise an exception if the fit was bad (fit errors == None)
        self._compute_quantities(self._fitresult)
        print(f"fitted {count} of {slopecold.size} pixels")
        print(f'got {excount} exceptions and {badfit} bad fits')
        #if successful, set the used position and size
        self._position = position
        self._size = size
        if profile:
            pr.disable()
            s = io.StringIO()
            sortby = SortKey.CUMULATIVE
            ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
            ps.print_stats()
            self._stats = s

    def _two_lines(self, x, m1, n1, m2, n2):
        '''This function is used to partition a fit to data using two lines and 
           an inflection point.  Second slope is steeper because slopes are 
           negative in excitation diagram.

           :param x: array of x values
           :type x: :class:`numpy.ndarray` 
           :param m1: slope of first line
           :type m1: float
           :param n1: intercept of first line
           :type n1: float
           :param m2: slope of second line
           :type m2: float
           :param n2: intercept of second line
           :type n2: float

            See https://stackoverflow.com/questions/48674558/how-to-implement-automatic-model-determination-and-two-state-model-fitting-in-py
        '''
        return np.max([m1 * x + n1, m2 * x + n2], axis=0)

    def _one_line(self, x, m1, n1):
        '''Return a line.

           :param x: array of x values
           :type x: :class:`numpy.ndarray` 
           :param m1: slope of first line
           :type m1: float
           :param n1: intercept of first line
           :type n1: float
        '''
        return m1 * x + n1

    def _partition_function(self, tex):
        '''Calculate the H2 partition function given an excitation temperature
        
        :param tex: the excitation temperature
        :type tex: :class:`~pdrtpy.measurement.Measurement` or :class:`astropy.units.quantity.Quantity`
        :returns: the partition function value
        :rtype: numpy.ndarray
        '''
        # See Herbst et al 1996
        # http://articles.adsabs.harvard.edu/pdf/1996AJ....111.2403H
        # Z(T) =  = 0.0247T * [1—exp(—6000/T)]^-1

        # This is just being defensive.  I know the temperatures used internally are in K.
        t = np.ma.masked_invalid((tex.value * u.Unit(tex.unit)).to(
            "K", equivalencies=u.temperature()).value)
        t.mask = np.logical_or(t.mask, np.logical_not(np.isfinite(t)))
        z = 0.0247 * t / (1.0 - np.exp(-6000.0 / t))
        return z
Beispiel #14
0
params.add('decay', value=0.01, vary=True)
params.add('phase_shift', value=0.2)
params.add('omega', value=4.0)

# params['amp'].value=10
# params['decay'].value=0.007
# params['phase_shift'].value=0.2
# params['omega'].value=3.0

result = minimize(residual, params, args=(x, data))

# print result.chisqr
print
print
print 'True Values:'
for name, par in true_params.items():
    print '  %s = %.4f' % (name, par.value)

print
print 'Best-Fit Values:'
for name, par in params.items():
    print '  %s = %.4f +/- %.4f ' % (name, par.value, par.stderr)

xx = np.arange(0.0, 10.0, 0.05)
fitted_model = mymodel(xx, params)
true_model = mymodel(xx, true_params)


# interactive quick plot
plt.figure()
plt.ion()
Beispiel #15
0
fit_params = Parameters()
fit_params.add('amp', value=11.0, min=5, max=20)
fit_params.add('period', value=5., min=1., max=7)
fit_params.add('shift', value=.10, min=0.0, max=0.2)
fit_params.add('decay', value=6.e-3, min=0, max=0.1)

init = residual(fit_params, x)

out = minimize(residual,
               fit_params,
               method='lbfgsb',
               args=(x, ),
               kws={'data': data})

fit = residual(fit_params, x)

for name, par in fit_params.items():
    nout = "%s:%s" % (name, ' ' * (20 - len(name)))
    print "%s: %s (%s) " % (nout, par.value, p_true[name].value)

#print out.chisqr, out.redchi, out.nfree
#
#report_fit(fit_params)

if HASPYLAB:
    pylab.plot(x, data, 'r--')
    pylab.plot(x, init, 'k')
    pylab.plot(x, fit, 'b')
    pylab.show()
Beispiel #16
0
        pi) + 0.1371 - (0.5514 / (0.5028 * sqrt(pi / 2))) * exp(
            (-2 * ((1 / pi * ((alpha * (arctan(x / sigma1) +
                                        (ceta1 - sigma1) * x) /
                               (x**2 + sigma1**2)) + (1 - alpha) *
                              (arctan(x / sigma2) + (ceta2 - sigma2) * x /
                               (x**2 + sigma2**2)))) / 0.5028)**2))


params = Parameters()
params.add('alpha', value=0.98984467)
params.add('ceta1', value=76.79684648)
params.add('ceta2', value=30.77261877)
params.add('sigma1', value=77.1949924)
params.add('sigma2', value=70.06017928)
params.add('Kb', value=24.74401521, vary=False)
for name, par in params.items():
    print ' %s =%.4f +/- ' % (name, par.value)

x = linspace(-10, 10, 101)
data = linspace(0, 0, 101)
out = minimize(objfun, params, args=(x, data), engine='leastsq')
print out.redchi
print "Best-Fit Values:"
for name, par in params.items():
    print ' %s =%.4f +/- %.4f' % (name, par.value, par.stderr)
print params

#plt.plot(x,gau(x),'-',label='dgoriginal data',markersize=1)
#plt.plot(x,fb2(params,x),'o-',label='EAM',markersize=5)
plt.plot(x, rho(params, x), '-', label='EAM', markersize=5)
#plt.plot(t,y,'.',label='original data',markersize=5)
Beispiel #17
0
data  = residual(p_true, x) + noise

fit_params = Parameters()
fit_params.add('amp', value=13.0, min=-5, max=40)
fit_params.add('period', value=2, min=0, max=7)
fit_params.add('shift', value=0.0, min=-1.5, max=1.5)
fit_params.add('decay', value=0.02, min=0, max=1.0)
#p_true.add('amp', value=14.0)
#p_true.add('period', value=5.33)
#p_true.add('shift', value=0.123)
#p_true.add('decay', value=0.010)

out = minimize(residual, fit_params, engine='anneal',
               Tf= 1000,
               args=(x,), kws={'data':data})

print out.sa_out
for key, par in fit_params.items():
    print key, par, p_true[key].value


if HASPYLAB:
    pylab.plot(x, data, 'ro')
    pylab.plot(x, fit, 'b')
    pylab.show()





Beispiel #18
0
class UDFParametersModel(QtCore.QAbstractTableModel):

    def __init__(self, params, parent=None):
        super(UDFParametersModel, self).__init__(parent)
        if params is not None:
            self.params = params
        else:
            self.params = Parameters()

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.params) + 1

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 5

    def flags(self, index):
        row = index.row()
        col = index.column()

        if row == 0 and col == 0:
            retval = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
        
        if row == 0 and col > 0:
            retval = False

        #parameter name
        if col == 0 and row > 0:
            retval = (QtCore.Qt.ItemIsEditable |
                      QtCore.Qt.ItemIsEnabled |
                      QtCore.Qt.ItemIsSelectable)

        #parameter value
        if col == 1 and row > 0:
            retval = (QtCore.Qt.ItemIsEditable |
                      QtCore.Qt.ItemIsUserCheckable |
                      QtCore.Qt.ItemIsEnabled |
                      QtCore.Qt.ItemIsSelectable)

        #min/max values
        if (col == 2 or col == 3) and row > 0:
            retval = (QtCore.Qt.ItemIsEditable |
                      QtCore.Qt.ItemIsEnabled |
                      QtCore.Qt.ItemIsSelectable)

        #expr
        if col == 4 and row > 0:
            retval = (QtCore.Qt.ItemIsEditable |
                      QtCore.Qt.ItemIsEnabled |
                      QtCore.Qt.ItemIsSelectable)

        return retval

 #    def layersAboutToBeInserted(self, start, end):
#         self.beginInsertRows(QtCore.QModelIndex(), start, end)
#
#     def layersFinishedBeingInserted(self):
#         self.endInsertRows()
#
#     def layersAboutToBeRemoved(self, start, end):
#         self.beginRemoveRows(QtCore.QModelIndex(), start, end)
#
#     def layersFinishedBeingRemoved(self):
#         self.endRemoveRows()

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        row = index.row()
        col = index.column()
        names = curvefitter.names(self.params)

        if row:
            name = names[row - 1]

        if role == QtCore.Qt.CheckStateRole:
            if row > 0 and col == 1:
                if value == QtCore.Qt.Checked:
                    self.params[name].vary = False
                else:
                    self.params[name].vary = True

        if role == QtCore.Qt.EditRole:
            if row == 0 and col == 0:
                currentparams = self.rowCount() - 1

                validator = QtGui.QIntValidator()
                voutput = validator.validate(value, 1)
                if voutput[0] is QtGui.QValidator.State.Acceptable and int(voutput[1]) >= 0:
                    newparams = int(voutput[1])

                    if newparams == currentparams:
                        return True

                    if newparams > currentparams:
                        self.beginInsertRows(
                            QtCore.QModelIndex(),
                            currentparams + 1,
                            newparams)
                    if newparams < currentparams:
                        self.beginRemoveRows(
                            QtCore.QModelIndex(),
                            newparams + 1,
                            currentparams)

                    if newparams > currentparams:
                        for i in range(currentparams, newparams):
                            self.params.add('p%d'%i, 0, True, -np.inf, np.inf, None)
                        self.endInsertRows()

                    if newparams < currentparams:
                        remove_names = names[newparams:]
                        map(self.params.pop, remove_names)
                        self.endRemoveRows()

                    self.modelReset.emit()
            if row > 0 and col in [1, 2, 3]:
                validator = QtGui.QDoubleValidator()
                voutput = validator.validate(value, 1)
                if voutput[0] == QtGui.QValidator.State.Acceptable:
                    number = float(voutput[1])
                else:
                    return False

                if col == 1:
                    self.params[name].value = number
                if col == 2:
                    self.params[name].min = number
                if col == 3:
                    self.params[name].max = number
            if row > 0 and col == 0:
                #change a parameter name requires making a new dictionary
                if not valid_symbol_name(value):
                    return False

                p = Parameters()
                param = self.params[name]
                newparam = Parameter(value, param.value, param.vary,
                                     param.min, param.max, param.expr)

                for k, v in self.params.items():
                    if k == name:
                        p[value] = newparam
                    else:
                        p[k] = v

                self.params = p

            if row > 0 and col == 4:
                #set an expression
                param = self.params[name]
                param.expr = value

        self.dataChanged.emit(index, index)
        return True

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return False

        row = index.row()
        col = index.column()
        names = curvefitter.names(self.params)

        if row:
            name = names[row - 1]

        if role == QtCore.Qt.DisplayRole:
            if col == 0:
                if row == 0:
                    return str(len(self.params))
                else:
                    return name
            elif col == 1 and row > 0:
                    return str(self.params[name].value)
            elif col == 2 and row > 0:
                return str(self.params[name].min)
            elif col == 3 and row > 0:
                return str(self.params[name].max)
            elif col == 4 and row > 0:
                return str(self.params[name].expr)

        if role == QtCore.Qt.CheckStateRole:
            if row > 0 and col == 1:
                if self.params[name].vary:
                    return QtCore.Qt.Unchecked
                else:
                    return QtCore.Qt.Checked

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        """ Set the headers to be displayed. """
        if role != QtCore.Qt.DisplayRole:
            return None

        if orientation == QtCore.Qt.Vertical:
            if section == 0:
                return 'number of parameters'
            else:
                names = self.params.keys()
                return names[section - 1]

        if orientation == QtCore.Qt.Horizontal:
            if section == 0:
                return 'name'
            if section == 1:
                return 'value'
            if section == 2:
                return 'lower limit'
            if section == 3:
                return 'upper limit'
            if section == 4:
                return 'expr'
        return None
Beispiel #19
0
xmax = 250.0
noise = random.normal(scale=0.7215, size=n)
x = linspace(xmin, xmax, n)
data = residual(p_true, x) + noise

fit_params = Parameters()
fit_params.add('amp', value=13.0, min=-5, max=40)
fit_params.add('period', value=2, min=0, max=7)
fit_params.add('shift', value=0.0, min=-1.5, max=1.5)
fit_params.add('decay', value=0.02, min=0, max=1.0)
#p_true.add('amp', value=14.0)
#p_true.add('period', value=5.33)
#p_true.add('shift', value=0.123)
#p_true.add('decay', value=0.010)

out = minimize(residual,
               fit_params,
               method='anneal',
               Tf=1000,
               args=(x, ),
               kws={'data': data})

print out.sa_out
for key, par in fit_params.items():
    print key, par, p_true[key].value

if HASPYLAB:
    pylab.plot(x, data, 'ro')
    pylab.plot(x, fit, 'b')
    pylab.show()
Beispiel #20
0
   # model = Kb/2/pi*sin((1/pi*((alpha*(arctan(x/sigma1) + (ceta1-sigma1)*x)/(x**2+sigma1**2)) + (1-alpha)*(arctan(x/sigma2) + (ceta2-sigma2)*x/(x**2+sigma2**2))) + 1/2)*4*pi)
  #  return (model-gaussian(params,x))

    return  Kb/2/pi*sin((1/pi*((alpha*(arctan(x/sigma1) + (ceta1-sigma1)*x)/(x**2+sigma1**2)) + (1-alpha)*(arctan(x/sigma2) + (ceta2-sigma2)*x/(x**2+sigma2**2))) + 1/2)*4*pi)+0.1371 -(0.5514/(0.5028*sqrt(pi/2)))*exp((-2*((1/pi*((alpha*(arctan(x/sigma1) + (ceta1-sigma1)*x)/(x**2+sigma1**2)) + (1-alpha)*(arctan(x/sigma2) + (ceta2-sigma2)*x/(x**2+sigma2**2))))/0.5028)**2))
    


params = Parameters()
params.add('alpha',value=0.98984467)
params.add('ceta1',value=76.79684648)
params.add('ceta2',value=30.77261877)
params.add('sigma1',value=77.1949924)
params.add('sigma2',value=70.06017928)
params.add('Kb',value=24.74401521,vary=False)
for name,par in params.items():
    print ' %s =%.4f +/- ' %(name, par.value)

x =linspace(-10,10,101)
data =linspace(0,0,101)
out = minimize(objfun,params,args=(x,data),engine='leastsq')
print out.redchi
print "Best-Fit Values:"
for name,par in params.items():
    print ' %s =%.4f +/- %.4f' %(name, par.value,par.stderr)
print params

#plt.plot(x,gau(x),'-',label='dgoriginal data',markersize=1)
#plt.plot(x,fb2(params,x),'o-',label='EAM',markersize=5)
plt.plot(x,rho(params,x),'-',label='EAM',markersize=5)
#plt.plot(t,y,'.',label='original data',markersize=5)
Beispiel #21
0
        def jacobian(params: Parameters):
            ### if result exist in database, ignore calculation
            result = self.db.session.query(Result).filter(Result.task == task) \
                .filter(Result.parameter == str(params)).first()
            if result is not None:
                J = result.jacobian
                if J is not None:
                    return json.loads(J)
            ###

            paras = OrderedDict()
            for k, v in params.items():
                paras[restore_para_name(k)] = v.value

            J_dens = []
            J_hvap = []
            targets = task.targets.all()
            for target in targets:
                if target.wDens > 1E-4:
                    dDdp_list = target.get_dDens_list_from_paras(paras)
                    J_dens.append([
                        i / target.density * 100 * target.wDens
                        for i in dDdp_list
                    ])  # deviation  percent
                if target.wHvap > 1E-4:
                    dHdp_list = target.get_dHvap_list_from_paras(paras)
                    J_hvap.append([
                        i / target.hvap * 100 * target.wHvap for i in dHdp_list
                    ])  # deviation  percent
            J = J_dens + J_hvap
            os.chdir(self.CWD)

            ### expansivity
            if weight_expansivity != 0:
                J_expa = []
                for i_mol in range(len(targets) // 2):
                    target_T1 = targets[2 * i_mol]
                    target_T2 = targets[2 * i_mol + 1]
                    dExpa = (target_T1.dDdp_array - target_T2.dDdp_array) / (target_T1.density - target_T2.density) \
                            * 100 * weight_expansivity
                    J_expa.append(list(dExpa))

                J += J_expa

            ### parameter penalty
            J_pena = []
            for k, v in params.items():
                if k.endswith('r0') or k.endswith('e0'):
                    d = 1 / adj_nb_paras[restore_para_name(k)]
                else:
                    d = 1
                penalty = get_penalty_for_para(k)
                J_pena.append(d * penalty * np.sqrt(len(J_dens)))
            J_pena = [list(a) for a in np.diag(J_pena)
                      ]  # convert list to diagonal matrix
            J += J_pena

            ### save result to database
            result = self.db.session.query(Result).filter(Result.task == task) \
                .filter(Result.iteration == task.iteration).first()

            result.jacobian = json.dumps(J)
            self.db.session.commit()
            ###

            ### write Jacobian to log
            txt = '\nJACOBIAN MATRIX:\n'
            for k in params.keys():
                txt += '%10s' % restore_para_name(k)
            txt += '\n'

            targets_dens = task.targets.filter(Target.wDens > 1E-4).all()
            for i, row in enumerate(J_dens):
                name = targets_dens[i].name
                prop = 'density'
                for item in row:
                    txt += '%10.2f' % item
                txt += ' %8s %s\n' % (prop, name)

            targets_hvap = task.targets.filter(Target.wHvap > 1E-4).all()
            for i, row in enumerate(J_hvap):
                name = targets_hvap[i].name
                prop = 'hvap'
                for item in row:
                    txt += '%10.2f' % item
                txt += ' %8s %s\n' % (prop, name)

            if weight_expansivity != 0:
                for i, row in enumerate(J_expa):
                    name = targets[2 * i].name
                    prop = 'expan'
                    for item in row:
                        txt += '%10.2f' % item
                    txt += ' %8s %s\n' % (prop, name)

            for i, row in enumerate(J_pena):
                name = restore_para_name(list(params.keys())[i])
                prop = 'penalty'
                for item in row:
                    txt += '%10.2f' % item
                txt += ' %8s %s\n' % (prop, name)

            print(txt)
            with open(LOG, 'a') as log:
                log.write(txt)
            ###

            return J
Beispiel #22
0
# plot acceptance
plt.figure(2)
plt.plot(result.acceptance_fraction)
plt.xlabel('walker')
plt.ylabel('acceptance fraction')
plt.savefig('acceptance.pdf')

# create corner plot of parameter distributions
emcee_plot = corner.corner(result.flatchain, labels=result.var_names,
                           truths=list(result.params.valuesdict().values()))
plt.savefig('corner.pdf')

highest_prob = np.argmax(result.lnprob)
hp_loc = np.unravel_index(highest_prob, result.lnprob.shape)
mle_soln = result.chain[hp_loc]
for i, par in enumerate(params):
    params[par].value = mle_soln[i]


print('\nMaximum Likelihood Estimation from emcee       ')
print('-------------------------------------------------')
print('Parameter  MLE Value   Median Value   Uncertainty')
fmt = '  {:5s}  {:11.5f} {:11.5f}   {:11.5f}'.format
for name, param in params.items():
    print(fmt(name, param.value, result.params[name].value,
              result.params[name].stderr))


#save model result
#save_modelresult(result, '2_state_result_8_12_20.sav')
Beispiel #23
0
x     = linspace(xmin, xmax, n)
data  = residual(p_true, x) + noise

fit_params = Parameters()
fit_params.add('amp', value=11.0, min=5, max=20)
fit_params.add('period', value=5., min=1., max=7)
fit_params.add('shift', value=.10,  min=0.0, max=0.2)
fit_params.add('decay', value=6.e-3, min=0, max=0.1)

init = residual(fit_params, x)

out = minimize(residual, fit_params, method='lbfgsb', args=(x,), kws={'data':data})

fit = residual(fit_params, x)

for name, par in fit_params.items():
    nout = "%s:%s" % (name, ' '*(20-len(name)))
    print "%s: %s (%s) " % (nout, par.value, p_true[name].value)

#print out.chisqr, out.redchi, out.nfree
#
#report_fit(fit_params)

if HASPYLAB:
    pylab.plot(x, data, 'r--')
    pylab.plot(x, init, 'k')
    pylab.plot(x, fit, 'b')
    pylab.show()


P = dict()
pylab.close('all')


for (k,column_data) in columns.items():
    print "Now estimating the dataset %s" % k
    column_data = np.array(column_data ,dtype=np.float64)
    
    # Use nonlinear least squares regression to estimate the parameters
    result = minimize(phase1_residual, params, args=(x1, column_data))
    
    report_fit(params,show_correl=False)
    
    param_string = ""
    # store the estimated parameters in dictioary P for later use
    for (param_name,param_val) in params.items():
        param_string = param_string + "| %s: %0.4f| " % (param_name,param_val.value)
        if param_name in P.keys():
            P[param_name] = np.append(P[param_name],param_val.value)
        else:
            P[param_name]=np.array([param_val.value])

    
    final = column_data + result.residual
    # output the fit results in a figure
    try:       
        pylab.plot(x1, column_data, colors[i]+"o",label='Concentration (uM): '+k)
        x1_plot = np.linspace(0,max(x1),100)
        pylab.plot(x1_plot, phase1_model(params,x1_plot), colors[i],label='Fit:'+ param_string)
    except:
        pass