# Fitting ACE

## Top-level overview

The most minimal way of generating an ACE potential is

```
using ACE1pack
params = ... # see below
fit_ace()
```

In the first instance, the parameters' dictionary (`params`

) is generated by the `*params()`

functions described in the rest of these pages. It can also be read from a `.json`

or `.yaml`

file.

`fit_ace()`

takes the dictionary generated by `fit_params()`

`ACE1pack.fit_ace`

— Function`fit_ace(params::Dict) -> IP, lsqinfo`

Function to set up and fit the least-squares problem of "atoms' positions" -> "energy, forces, virials". Takes in a dictionary with all the parameters. See `?fit_params`

for details.

`ACE1pack.fit_params`

— Function`fit_params(; kwargs...)`

Returns a dictionary containing all of the parameters needed to make an ACE potential. All parameters are passed as keyword argumts.

**Parameters**

`data`

: data parameters, see`?data_params`

for details (mandatory)`basis`

: dictionary containing dictionaries that specify the basis used in fitting. For example

```
basis = Dict(
"pair_short" => Dict( "type" => "pair", ...),
"pair_long" => Dict("type" => "pair", ...),
"manybody" => Dict("type" => "ace", ...),
"nospecies" => Dict("type" => "ace", species = ["X",], ...)
```

keys of `basis`

are ignored, so that multiple basis with different specifications (e.g. smaller and larger cutoffs) can be combined. See `?basis_params`

for more detail.

`solver`

: dictionary containing parameters that specify the solver for least squares problem (mandatory). See`?solver_params`

.`e0`

:`Dict{String, Float}`

containing reference values for isolated atoms' energies (mandatory).`weights`

: dictionary of`Dict("config_type" => Dict("E" => Float, "F => Float))`

`entries specifying fitting weights. "default" is set to`

1.0` for all of "E", "F", and "V" weights.`P`

: regularizer parameters (optional), see`?regularizer_params`

.`ACE_fname = "ACE_fit.json"`

: filename to save ACE to. Potential & info do not get saved if`ACE_fname`

isnothing() or is set to`""`

. Files already*parse*entry are renamed and not overwritten.`LSQ_DB_fname_stem = ""`

: stem to save LsqDB to. Doesn't get saved if set to an empty string (""). If the file is already present, but`fit_from_LSQ_DB`

is set to false, the old database is renamed, a new one constructed and saved under the given name.`fit_from_LSQ_DB = false`

: whether to fit from a least squares database specified with`LSQ_DB_fname_stem`

. If`LSQ_DB_fname_stem * "_kron.h5"`

file is not present, LsqDB is constructed from scratch and saved.

Parameters `LSQ_DB_fname_stem`

and `fit_from_LSQ_DB`

determine if the least-squares gets saved and/or where/how it gets fit from.

`LSQ_DB_fname_stem`

is set to`""`

: LsqDB is neither saved to nor read from file;`LSQ_DB_fname_stem`

is given:- file not present: LsqDB saved to disk and fit to;
- file present:
`fit_from_LSQ_DB`

is true: fit to the LsqDB on disk;`fit_from_LSQ_DB`

is false: rename old LsqDB; make, save and fit to a new LsqDB.

## Create LsqDB & fit it separately

It is also possible to decouple making the least-squares database from fitting to it.

`ACE1pack.make_ace_db`

— Function`make_ace_db(params::Dict)`

-> LsqDB

Makes a LsqDB from given parameters' dictionary. For `params`

see `?db_params`

; parameters from `fit_params`

also work, except unnecessary entries will be ignored. Returns `IPFitting.LsqDB`

Missing docstring for `fit_ace_db`

. Check Documenter's build log for details.

In fact, `fit_ace()`

just calls both of these in one go.

## Reading parameters in from file

Dictionaries may be read in from file with

```
params = load_dict("params.json")
# or
params = load_dict("params.yaml")
```

There is a convenience function `fill_defaults()`

that recursively fills in any missing values with defaults, so that only mandatory or non-default values have to be saved in the files.

`ACE1pack.fill_defaults`

— Function`fill_defaults(params::Dict; param_key = "fit_params") -> params`

Recursively updates any missing entries with default parameters. Accepted `param_key`

values and corresponding functions:

```
"fit_params" => ACE1pack.fit_params,
"data" => ACE1pack.data_params,
"solver" => ACE1pack.solver_params,
"basis" => ACE1pack.basis_params,
"ace" => ACE1pack.ace_basis_params,
"pair" => ACE1pack.pair_basis_params,
"radial" => ACE1pack.radial_basis_params,
"transform" => ACE1pack.transform_params,
"degree" => ACE1pack.degree_params,
"P" => ACE1pack.regularizer_params
```

## Examples of minimal set of parameters

### Minimal set

To give an overview of the structure of the parameters' dictionary below is an example with only the mandatory values filled in.

```
mandatory_params = Dict(
"data" => Dict(
"fname" => "training_set.xyz"),
"basis" => Dict(
"main_ace" => Dict(
"species" => ["Ti", "Al"],
"N" => 2, # correlation order
"maxdeg" => 10, # polynomial degree
"type" => "ace"), # specifies many-body/ace basis functions
"main_pair" => Dict(
"species" => ["Ti", "Al"],
"maxdeg" => 4, # polynomial degree for the 2-body functions
"type" => "pair" # specify 2-body basis
),),
"solver" => Dict(
"type" => "rrqr"), # use rank-revealing QR factorisation
"e0" => Dict( # isolated atom energies, eV
"Ti" => -1586.0195,
"Al" => -105.5954))
```

### With all default values

The following is the full set of parameters, including the default values.

```
params_with_defaults = Dict(
"data" => Dict(
"fname" => "training_set.xyz", # mandatory
"energy_key" => "dft_energy",
"force_key" => "dft_force",
"virial_key" => "dft_virial"),
"basis" => Dict(
"main_ace" => Dict(
"species" => ["Ti", "Al"], # mandatory
"N" => 2, # mandatory
"maxdeg" => 10, # mandatory
"type" => "ace", # mandatory/defines this as the many-body basis & params
"r0" => 2.5, # poly transform parameter, Å
"radial" => Dict( # parameters for radial basis of ACE
"r0" => 2.5, # from "main_ace" dictionary
"rcut" => 5.0, # outter cutoff, Å
"rin" => 0.5 * r0, # inner cutoff, Å
"pcut" => 2, # outter cutoff parameter
"pin" => 2, # inner cutoff parameter
"type" => "radial" ), # mandatory/defines this as radial basis
"transform" => Dict( # radial transform to use
"type" => "polynomial", # of 1/(1+r/r0)^2 - type
"p" => 2,
"r0" => 2.5),
"degree" => ...), # way to specify the total polynomial degree
"main_pair" => Dict(
"species" => ["Ti", "Al"], # mandatory
"maxdeg" => 4, # mandatory
"type" => "pair", # mandatory/defines this as pair basis
"r0" => 2.5, # poly transform parameter, Å
"rcut" => 5.0, # outer cutoff, Å
"rin" => 0.0, # inner cutoff, Å
"pcut" => 2, # outter cutoff parameter
"pin" => 0, # inner cutoff parameter
"transform" => Dict( # radial transform to use
"type" => "polynomial", # of 1/(1+r/r0)^2 - type
"p" => 2,
"r0" => 2.5),),),
"solver" => Dict(
"type" => "rrqr", # mandatory/defines this as rrqr solver
"rrqr_tol" => 1e-5), # convergence tolerance parameter
"e0" => Dict( # mandatory
"Ti" => -1586.0195,
"Al" => -105.5954),
"weights" => Dict( # defines relative importance of energy vs weight vs virial observations
"default" => Dict(
"E" => 1.0,
"F" => 1.0,
"V" => 1.0)),
"P" => nothing, # additional regularizer/prior
"ACE_fname" => "ACE_fit.json", # filename to save ACE model to
"LSQ_DB_fname_stem" => "", # do not save ACE LsqDB
"fit_from_LSQ_DB" => false # do not refit to a present database
)
```