// Warning! This file is autogenerated from input_schema.json using gen_input_struct.py script

namespace sirius {

std::string const locked_msg("parameters are locked");

class config_t
{
  public:
    nlohmann::json const& dict() const
    {
        return dict_;
    }
    /// Parameters of the mixer
    /**
        Control type and parameters of the mixer
    */
    class mixer_t
    {
      public:
        mixer_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Type of the mixer.
        inline auto type() const
        {
            return dict_.at("/mixer/type"_json_pointer).get<std::string>();
        }
        inline void type(std::string type__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/type"_json_pointer] = type__;
        }
        /// Mixing parameter
        inline auto beta() const
        {
            return dict_.at("/mixer/beta"_json_pointer).get<double>();
        }
        inline void beta(double beta__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/beta"_json_pointer] = beta__;
        }
        /// For Anderson mixer only: minimum allowed value of mixing parameter beta
        inline auto beta0() const
        {
            return dict_.at("/mixer/beta0"_json_pointer).get<double>();
        }
        inline void beta0(double beta0__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/beta0"_json_pointer] = beta0__;
        }
        /// Number of history steps for Broyden-type mixers
        inline auto max_history() const
        {
            return dict_.at("/mixer/max_history"_json_pointer).get<int>();
        }
        inline void max_history(int max_history__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/max_history"_json_pointer] = max_history__;
        }
        /// For Anderson mixer only: scaling factor for mixing parameter beta
        inline auto beta_scaling_factor() const
        {
            return dict_.at("/mixer/beta_scaling_factor"_json_pointer).get<double>();
        }
        inline void beta_scaling_factor(double beta_scaling_factor__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/beta_scaling_factor"_json_pointer] = beta_scaling_factor__;
        }
        /// Use Hartree potential in the inner() product for residuals
        inline auto use_hartree() const
        {
            return dict_.at("/mixer/use_hartree"_json_pointer).get<bool>();
        }
        inline void use_hartree(bool use_hartree__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/use_hartree"_json_pointer] = use_hartree__;
        }
        /// Minimum value of allowed RMS for the mixer.
        /**
            Mixer will not mix functions if the RMS between previous and current functions is below this tolerance.
        */
        inline auto rms_min() const
        {
            return dict_.at("/mixer/rms_min"_json_pointer).get<double>();
        }
        inline void rms_min(double rms_min__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/mixer/rms_min"_json_pointer] = rms_min__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& mixer() const {return mixer_;}
    inline auto& mixer() {return mixer_;}
    /// Parameters of the 'settings' section influence numerical implementation.
    /**
        Changing of setting parameters will have a small impact on the final result.
    */
    class settings_t
    {
      public:
        settings_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Point density (in a.u.^-1) for interpolating radial integrals of the local part of pseudopotential
        inline auto nprii_vloc() const
        {
            return dict_.at("/settings/nprii_vloc"_json_pointer).get<int>();
        }
        inline void nprii_vloc(int nprii_vloc__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/nprii_vloc"_json_pointer] = nprii_vloc__;
        }
        /// Point density (in a.u.^-1) for interpolating radial integrals of the beta projectors
        inline auto nprii_beta() const
        {
            return dict_.at("/settings/nprii_beta"_json_pointer).get<int>();
        }
        inline void nprii_beta(int nprii_beta__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/nprii_beta"_json_pointer] = nprii_beta__;
        }
        /// Point density (in a.u.^-1) for interpolating radial integrals of the augmentation operator
        inline auto nprii_aug() const
        {
            return dict_.at("/settings/nprii_aug"_json_pointer).get<int>();
        }
        inline void nprii_aug(int nprii_aug__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/nprii_aug"_json_pointer] = nprii_aug__;
        }
        /// Point density (in a.u.^-1) for interpolating radial integrals of the core charge density
        inline auto nprii_rho_core() const
        {
            return dict_.at("/settings/nprii_rho_core"_json_pointer).get<int>();
        }
        inline void nprii_rho_core(int nprii_rho_core__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/nprii_rho_core"_json_pointer] = nprii_rho_core__;
        }
        /// Tolerance to recompute the LAPW linearisation energies.
        inline auto auto_enu_tol() const
        {
            return dict_.at("/settings/auto_enu_tol"_json_pointer).get<double>();
        }
        inline void auto_enu_tol(double auto_enu_tol__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/auto_enu_tol"_json_pointer] = auto_enu_tol__;
        }
        /// Initial dimenstions for the fine-grain FFT grid
        inline auto fft_grid_size() const
        {
            return dict_.at("/settings/fft_grid_size"_json_pointer).get<std::array<int, 3>>();
        }
        inline void fft_grid_size(std::array<int, 3> fft_grid_size__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/fft_grid_size"_json_pointer] = fft_grid_size__;
        }
        /// Hard cutoff of the pseudopotential radial grids (a.u.)
        /**
            Integration grid for the local part of the pseudopotential is truncated at this value. This is because many pseudopotentials do not have properly decaying tails. The default is 10 a.u., and it comes from QE.
        */
        inline auto pseudo_grid_cutoff() const
        {
            return dict_.at("/settings/pseudo_grid_cutoff"_json_pointer).get<double>();
        }
        inline void pseudo_grid_cutoff(double pseudo_grid_cutoff__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/pseudo_grid_cutoff"_json_pointer] = pseudo_grid_cutoff__;
        }
        /// If true, coarse FFT grid is used to apply Hamiltonian and compute charge density from wave-functions.
        inline auto use_coarse_fft_grid() const
        {
            return dict_.at("/settings/use_coarse_fft_grid"_json_pointer).get<bool>();
        }
        inline void use_coarse_fft_grid(bool use_coarse_fft_grid__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/use_coarse_fft_grid"_json_pointer] = use_coarse_fft_grid__;
        }
        /// Simplified calculation of LAPW radial integrals.
        inline auto simple_lapw_ri() const
        {
            return dict_.at("/settings/simple_lapw_ri"_json_pointer).get<bool>();
        }
        inline void simple_lapw_ri(bool simple_lapw_ri__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/simple_lapw_ri"_json_pointer] = simple_lapw_ri__;
        }
        /// Default radial grid for LAPW species.
        inline auto radial_grid() const
        {
            return dict_.at("/settings/radial_grid"_json_pointer).get<std::string>();
        }
        inline void radial_grid(std::string radial_grid__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/radial_grid"_json_pointer] = radial_grid__;
        }
        /// Coverage of sphere in case of spherical harmonics transformation
        /**
            0 is Lebedev-Laikov coverage, 1 is unifrom coverage
        */
        inline auto sht_coverage() const
        {
            return dict_.at("/settings/sht_coverage"_json_pointer).get<int>();
        }
        inline void sht_coverage(int sht_coverage__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/sht_coverage"_json_pointer] = sht_coverage__;
        }
        /// Maximum orbital quantum number for which spherical coverage need to be generated.
        /**
            This option can be used to increase spherical coverage in muffin-tins. Impacts generation of XC potential.
        */
        inline auto sht_lmax() const
        {
            return dict_.at("/settings/sht_lmax"_json_pointer).get<int>();
        }
        inline void sht_lmax(int sht_lmax__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/sht_lmax"_json_pointer] = sht_lmax__;
        }
        /// Density RMS tolerance to switch to FP64 implementation. If zero, estimation of iterative solver tolerance is used.
        inline auto fp32_to_fp64_rms() const
        {
            return dict_.at("/settings/fp32_to_fp64_rms"_json_pointer).get<double>();
        }
        inline void fp32_to_fp64_rms(double fp32_to_fp64_rms__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/fp32_to_fp64_rms"_json_pointer] = fp32_to_fp64_rms__;
        }
        /// When true, use Laplacian in the expression for GGA; otherwise use divergence of gradient.
        inline auto xc_use_lapl() const
        {
            return dict_.at("/settings/xc_use_lapl"_json_pointer).get<bool>();
        }
        inline void xc_use_lapl(bool xc_use_lapl__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/xc_use_lapl"_json_pointer] = xc_use_lapl__;
        }
        /// Use more expensive but more accurate way to compute initial magnetisation in PP-PW case.
        inline auto smooth_initial_mag() const
        {
            return dict_.at("/settings/smooth_initial_mag"_json_pointer).get<bool>();
        }
        inline void smooth_initial_mag(bool smooth_initial_mag__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/smooth_initial_mag"_json_pointer] = smooth_initial_mag__;
        }
        /// Force occupation matrix of DFT+U+V method to be strictly real.
        inline auto real_occupation_matrix() const
        {
            return dict_.at("/settings/real_occupation_matrix"_json_pointer).get<bool>();
        }
        inline void real_occupation_matrix(bool real_occupation_matrix__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/settings/real_occupation_matrix"_json_pointer] = real_occupation_matrix__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& settings() const {return settings_;}
    inline auto& settings() {return settings_;}
    /// Unit cell representation
    class unit_cell_t
    {
      public:
        unit_cell_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Three non-collinear vectors of the primitive unit cell.
        inline auto lattice_vectors() const
        {
            return dict_.at("/unit_cell/lattice_vectors"_json_pointer).get<std::array<std::array<double, 3>, 3>>();
        }
        inline void lattice_vectors(std::array<std::array<double, 3>, 3> lattice_vectors__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/unit_cell/lattice_vectors"_json_pointer] = lattice_vectors__;
        }
        /// Scaling factor for the lattice vectors
        /**
            Lattice vectors are multiplied by this constant.
        */
        inline auto lattice_vectors_scale() const
        {
            return dict_.at("/unit_cell/lattice_vectors_scale"_json_pointer).get<double>();
        }
        inline void lattice_vectors_scale(double lattice_vectors_scale__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/unit_cell/lattice_vectors_scale"_json_pointer] = lattice_vectors_scale__;
        }
        /// Type of atomic coordinates: lattice, atomic units or Angstroms
        inline auto atom_coordinate_units() const
        {
            return dict_.at("/unit_cell/atom_coordinate_units"_json_pointer).get<std::string>();
        }
        inline void atom_coordinate_units(std::string atom_coordinate_units__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/unit_cell/atom_coordinate_units"_json_pointer] = atom_coordinate_units__;
        }
        /// List of atom type labels
        inline auto atom_types() const
        {
            return dict_.at("/unit_cell/atom_types"_json_pointer).get<std::vector<std::string>>();
        }
        inline void atom_types(std::vector<std::string> atom_types__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/unit_cell/atom_types"_json_pointer] = atom_types__;
        }
        /// Mapping between atom type labels and atomic files
        inline auto atom_files(std::string label__) const
        {
            nlohmann::json::json_pointer p("/unit_cell/atom_files");
            return dict_.at(p / label__).get<std::string>();
        }
        /// Mapping between atom type labels and muffin-tin radii (LAPW only)
        inline auto atom_type_rmt(std::string label__) const
        {
            nlohmann::json::json_pointer p("/unit_cell/atom_type_rmt");
            return dict_.at(p / label__).get<double>();
        }
        /// Atomic coordinates
        inline auto atoms(std::string label__) const
        {
            nlohmann::json::json_pointer p("/unit_cell/atoms");
            return dict_.at(p / label__).get<std::vector<std::vector<double>>>();
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& unit_cell() const {return unit_cell_;}
    inline auto& unit_cell() {return unit_cell_;}
    /// Parameters of the iterative solver.
    class iterative_solver_t
    {
      public:
        iterative_solver_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Type of the iterative solver.
        inline auto type() const
        {
            return dict_.at("/iterative_solver/type"_json_pointer).get<std::string>();
        }
        inline void type(std::string type__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/type"_json_pointer] = type__;
        }
        /// Number of steps (iterations) of the solver.
        inline auto num_steps() const
        {
            return dict_.at("/iterative_solver/num_steps"_json_pointer).get<int>();
        }
        inline void num_steps(int num_steps__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/num_steps"_json_pointer] = num_steps__;
        }
        /// Size of the variational subspace is this number times the number of bands.
        inline auto subspace_size() const
        {
            return dict_.at("/iterative_solver/subspace_size"_json_pointer).get<int>();
        }
        inline void subspace_size(int subspace_size__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/subspace_size"_json_pointer] = subspace_size__;
        }
        /// Lock eigenvectors of the smallest eigenvalues when they have converged at restart.
        inline auto locking() const
        {
            return dict_.at("/iterative_solver/locking"_json_pointer).get<bool>();
        }
        inline void locking(bool locking__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/locking"_json_pointer] = locking__;
        }
        /// Restart early when the ratio unconverged vs lockable vectors drops below this threshold.
        /**
            When there's just a few vectors left unconverged, it can be more efficient to lock the converged ones,
            such that the dense eigenproblem solved in each Davidson iteration has lower dimension.
            Restarting has some overhead in that it requires updating wave functions.
        */
        inline auto early_restart() const
        {
            return dict_.at("/iterative_solver/early_restart"_json_pointer).get<double>();
        }
        inline void early_restart(double early_restart__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/early_restart"_json_pointer] = early_restart__;
        }
        /// Tolerance for the eigen-energy difference \f$ |\epsilon_i^{old} - \epsilon_i^{new} | \f$
        /**
            This parameter is reduced during the SCF cycle to reach the high accuracy of the wave-functions.
        */
        inline auto energy_tolerance() const
        {
            return dict_.at("/iterative_solver/energy_tolerance"_json_pointer).get<double>();
        }
        inline void energy_tolerance(double energy_tolerance__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/energy_tolerance"_json_pointer] = energy_tolerance__;
        }
        /// Tolerance for the residual L2 norm.
        inline auto residual_tolerance() const
        {
            return dict_.at("/iterative_solver/residual_tolerance"_json_pointer).get<double>();
        }
        inline void residual_tolerance(double residual_tolerance__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/residual_tolerance"_json_pointer] = residual_tolerance__;
        }
        /// Relative tolerance for the residual L2 norm. (0 means this criterion is effectively not used.
        inline auto relative_tolerance() const
        {
            return dict_.at("/iterative_solver/relative_tolerance"_json_pointer).get<double>();
        }
        inline void relative_tolerance(double relative_tolerance__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/relative_tolerance"_json_pointer] = relative_tolerance__;
        }
        /// Additional tolerance for empty states.
        /**
            Setting this variable to 0 will treat empty states with the same tolerance as occupied states.
        */
        inline auto empty_states_tolerance() const
        {
            return dict_.at("/iterative_solver/empty_states_tolerance"_json_pointer).get<double>();
        }
        inline void empty_states_tolerance(double empty_states_tolerance__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/empty_states_tolerance"_json_pointer] = empty_states_tolerance__;
        }
        /// Minimum tolerance of the iterative solver.
        inline auto min_tolerance() const
        {
            return dict_.at("/iterative_solver/min_tolerance"_json_pointer).get<double>();
        }
        inline void min_tolerance(double min_tolerance__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/min_tolerance"_json_pointer] = min_tolerance__;
        }
        /// Defines the flavour of the iterative solver.
        /**
            If converge_by_energy is set to 0, then the residuals are estimated by their norm. If converge_by_energy
            is set to 1 then the residuals are estimated by the eigen-energy difference. This allows to estimate the
            unconverged residuals and then compute only the unconverged ones.
        */
        inline auto converge_by_energy() const
        {
            return dict_.at("/iterative_solver/converge_by_energy"_json_pointer).get<int>();
        }
        inline void converge_by_energy(int converge_by_energy__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/converge_by_energy"_json_pointer] = converge_by_energy__;
        }
        /// Minimum number of residuals to continue iterative diagonalization process.
        inline auto min_num_res() const
        {
            return dict_.at("/iterative_solver/min_num_res"_json_pointer).get<int>();
        }
        inline void min_num_res(int min_num_res__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/min_num_res"_json_pointer] = min_num_res__;
        }
        /// Number of singular components for the LAPW Davidson solver.
        /**
            Singular components are the eigen-vectors of the APW-APW block of overlap matrix
        */
        inline auto num_singular() const
        {
            return dict_.at("/iterative_solver/num_singular"_json_pointer).get<int>();
        }
        inline void num_singular(int num_singular__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/num_singular"_json_pointer] = num_singular__;
        }
        /// Initialize eigen-values with previous (old) values.
        inline auto init_eval_old() const
        {
            return dict_.at("/iterative_solver/init_eval_old"_json_pointer).get<bool>();
        }
        inline void init_eval_old(bool init_eval_old__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/init_eval_old"_json_pointer] = init_eval_old__;
        }
        /// Tell how to initialize the subspace.
        /**
            It can be either 'lcao', i.e. start from the linear combination of atomic orbitals or
            'random'- start from the randomized wave functions.
        */
        inline auto init_subspace() const
        {
            return dict_.at("/iterative_solver/init_subspace"_json_pointer).get<std::string>();
        }
        inline void init_subspace(std::string init_subspace__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/init_subspace"_json_pointer] = init_subspace__;
        }
        /// Orthogonalize the new subspace basis functions one more time in order to improve the numerical stability.
        inline auto extra_ortho() const
        {
            return dict_.at("/iterative_solver/extra_ortho"_json_pointer).get<bool>();
        }
        inline void extra_ortho(bool extra_ortho__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/extra_ortho"_json_pointer] = extra_ortho__;
        }
        /// Minimum occupancy below which the band is treated as being 'empty'
        inline auto min_occupancy() const
        {
            return dict_.at("/iterative_solver/min_occupancy"_json_pointer).get<double>();
        }
        inline void min_occupancy(double min_occupancy__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/min_occupancy"_json_pointer] = min_occupancy__;
        }
        /// Fine control of the empty states tolerance.
        /**
            This is the ratio between the tolerance of empty and occupied states. Used in the code like this:
            \code{.cpp}
            // tolerance of occupied bands
            double tol = ctx_.iterative_solver().energy_tolerance();
            // final tolerance of empty bands
            double empy_tol = std::max(tol * ctx_.settings().itsol_tol_ratio_, itso.empty_states_tolerance_);
            \endcode
        */
        inline auto tolerance_ratio() const
        {
            return dict_.at("/iterative_solver/tolerance_ratio"_json_pointer).get<double>();
        }
        inline void tolerance_ratio(double tolerance_ratio__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/tolerance_ratio"_json_pointer] = tolerance_ratio__;
        }
        /// Scaling parameters of the iterative  solver tolerance.
        /**
            First number is the scaling of density RMS, that gives the estimate of the new 
            tolerance. Second number is the scaling of the old tolerance. New tolerance is then the minimum 
            between the two. This is how it is done in the code: 
            \code{.cpp}
            double old_tol = ctx_.iterative_solver_tolerance();
            // estimate new tolerance of iterative solver
            double tol = std::min(ctx_.settings().itsol_tol_scale_[0] * rms, ctx_.settings().itsol_tol_scale_[1] * old_tol);
            tol = std::max(ctx_.settings().itsol_tol_min_, tol);
            // set new tolerance of iterative solver
            ctx_.iterative_solver().energy_tolerance(tol);\endcode
        */
        inline auto tolerance_scale() const
        {
            return dict_.at("/iterative_solver/tolerance_scale"_json_pointer).get<std::array<double, 2>>();
        }
        inline void tolerance_scale(std::array<double, 2> tolerance_scale__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/iterative_solver/tolerance_scale"_json_pointer] = tolerance_scale__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& iterative_solver() const {return iterative_solver_;}
    inline auto& iterative_solver() {return iterative_solver_;}
    /// Control parameters
    /**
        Parameters of the 'control' input sections do not in general change the numerics,
        but instead control how the results are obtained. Changing parameters in control section should
        not change the significant digits in final results.
    */
    class control_t
    {
      public:
        control_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// MPI grid is setting the parameters for blacs grid / band parallelisation, the rest is going to k-point parallelization.
        inline auto mpi_grid_dims() const
        {
            return dict_.at("/control/mpi_grid_dims"_json_pointer).get<std::vector<int>>();
        }
        inline void mpi_grid_dims(std::vector<int> mpi_grid_dims__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/mpi_grid_dims"_json_pointer] = mpi_grid_dims__;
        }
        /// Block size for ScaLAPACK, ELPA, and DLA-Future.
        inline auto cyclic_block_size() const
        {
            return dict_.at("/control/cyclic_block_size"_json_pointer).get<int>();
        }
        inline void cyclic_block_size(int cyclic_block_size__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/cyclic_block_size"_json_pointer] = cyclic_block_size__;
        }
        /// Reduce G-vectors by inversion symmetry.
        /**
            For real-valued functions like density and potential it is sufficient to store only
            half of the G-vectors and use the relation f(G) = f^{*}(-G) to recover second half
            of the plane-wave expansion coefficients.
        */
        inline auto reduce_gvec() const
        {
            return dict_.at("/control/reduce_gvec"_json_pointer).get<bool>();
        }
        inline void reduce_gvec(bool reduce_gvec__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/reduce_gvec"_json_pointer] = reduce_gvec__;
        }
        /// Standard eigen-value solver to use.
        inline auto std_evp_solver_name() const
        {
            return dict_.at("/control/std_evp_solver_name"_json_pointer).get<std::string>();
        }
        inline void std_evp_solver_name(std::string std_evp_solver_name__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/std_evp_solver_name"_json_pointer] = std_evp_solver_name__;
        }
        /// Generalized eigen-value solver to use.
        inline auto gen_evp_solver_name() const
        {
            return dict_.at("/control/gen_evp_solver_name"_json_pointer).get<std::string>();
        }
        inline void gen_evp_solver_name(std::string gen_evp_solver_name__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/gen_evp_solver_name"_json_pointer] = gen_evp_solver_name__;
        }
        /// Coarse grid FFT mode (`serial` or `parallel`).
        /**
            Coarse FFT grid is used in application of the Hamiltonian to the wave-functions
            and also in the construction of the charge density from the wave-functions squared.
        */
        inline auto fft_mode() const
        {
            return dict_.at("/control/fft_mode"_json_pointer).get<std::string>();
        }
        inline void fft_mode(std::string fft_mode__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/fft_mode"_json_pointer] = fft_mode__;
        }
        /// Main processing unit to use during the execution.
        inline auto processing_unit() const
        {
            return dict_.at("/control/processing_unit"_json_pointer).get<std::string>();
        }
        inline void processing_unit(std::string processing_unit__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/processing_unit"_json_pointer] = processing_unit__;
        }
        /// Maximum allowed muffin-tin radius in case of LAPW.
        inline auto rmt_max() const
        {
            return dict_.at("/control/rmt_max"_json_pointer).get<double>();
        }
        inline void rmt_max(double rmt_max__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/rmt_max"_json_pointer] = rmt_max__;
        }
        /// Tolerance of the spglib in finding crystal symmetries
        inline auto spglib_tolerance() const
        {
            return dict_.at("/control/spglib_tolerance"_json_pointer).get<double>();
        }
        inline void spglib_tolerance(double spglib_tolerance__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/spglib_tolerance"_json_pointer] = spglib_tolerance__;
        }
        /// Level of verbosity.
        /**
            The following convention in proposed:
              - 0: silent mode (no output is printed)
              - 1: basic output (low level of output)
              - 2: extended output (medium level of output)
              - 3: extensive output (high level of output)
        */
        inline auto verbosity() const
        {
            return dict_.at("/control/verbosity"_json_pointer).get<int>();
        }
        inline void verbosity(int verbosity__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/verbosity"_json_pointer] = verbosity__;
        }
        /// Level of internal verification.
        /**
            Depending on the level, more expensive self-checks will be performed.
        */
        inline auto verification() const
        {
            return dict_.at("/control/verification"_json_pointer).get<int>();
        }
        inline void verification(int verification__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/verification"_json_pointer] = verification__;
        }
        /// Number of eigen-values that are printed to the standard output.
        inline auto num_bands_to_print() const
        {
            return dict_.at("/control/num_bands_to_print"_json_pointer).get<int>();
        }
        inline void num_bands_to_print(int num_bands_to_print__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/num_bands_to_print"_json_pointer] = num_bands_to_print__;
        }
        /// If true then the stress tensor components are printed at the end of SCF run.
        inline auto print_stress() const
        {
            return dict_.at("/control/print_stress"_json_pointer).get<bool>();
        }
        inline void print_stress(bool print_stress__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/print_stress"_json_pointer] = print_stress__;
        }
        /// If true then the atomic forces are printed at the end of SCF run.
        inline auto print_forces() const
        {
            return dict_.at("/control/print_forces"_json_pointer).get<bool>();
        }
        inline void print_forces(bool print_forces__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/print_forces"_json_pointer] = print_forces__;
        }
        /// If true then the list of nearest neighbours for each atom is printed to the standard output.
        inline auto print_neighbors() const
        {
            return dict_.at("/control/print_neighbors"_json_pointer).get<bool>();
        }
        inline void print_neighbors(bool print_neighbors__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/print_neighbors"_json_pointer] = print_neighbors__;
        }
        /// True if second-variational diagonalization is used in LAPW method.
        inline auto use_second_variation() const
        {
            return dict_.at("/control/use_second_variation"_json_pointer).get<bool>();
        }
        inline void use_second_variation(bool use_second_variation__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/use_second_variation"_json_pointer] = use_second_variation__;
        }
        /// Number of atoms in a chunk of beta-projectors.
        inline auto beta_chunk_size() const
        {
            return dict_.at("/control/beta_chunk_size"_json_pointer).get<int>();
        }
        inline void beta_chunk_size(int beta_chunk_size__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/beta_chunk_size"_json_pointer] = beta_chunk_size__;
        }
        /// True if whole set of beta-projectors can be allocated on device for the entire run.
        inline auto beta_on_device() const
        {
            return dict_.at("/control/beta_on_device"_json_pointer).get<bool>();
        }
        inline void beta_on_device(bool beta_on_device__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/beta_on_device"_json_pointer] = beta_on_device__;
        }
        /// Orthogonalize LAPW radial functions.
        inline auto ortho_rf() const
        {
            return dict_.at("/control/ortho_rf"_json_pointer).get<bool>();
        }
        inline void ortho_rf(bool ortho_rf__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/ortho_rf"_json_pointer] = ortho_rf__;
        }
        /// Save LAPW radial functions in text file for inspection.
        inline auto save_rf() const
        {
            return dict_.at("/control/save_rf"_json_pointer).get<bool>();
        }
        inline void save_rf(bool save_rf__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/save_rf"_json_pointer] = save_rf__;
        }
        /// Type of the output stream (stdout:, file:name)
        inline auto output() const
        {
            return dict_.at("/control/output"_json_pointer).get<std::string>();
        }
        inline void output(std::string output__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/output"_json_pointer] = output__;
        }
        /// Split local G-vectors in chunks to reduce the GPU memory consumption of augmentation operator.
        inline auto gvec_chunk_size() const
        {
            return dict_.at("/control/gvec_chunk_size"_json_pointer).get<int>();
        }
        inline void gvec_chunk_size(int gvec_chunk_size__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/control/gvec_chunk_size"_json_pointer] = gvec_chunk_size__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& control() const {return control_;}
    inline auto& control() {return control_;}
    /// Parameters of the simulation.
    /**
        Most of this parameters control the behavior of high-level classes
        like sirius::DFT_ground_state.
    */
    class parameters_t
    {
      public:
        parameters_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Type of electronic structure method.
        inline auto electronic_structure_method() const
        {
            return dict_.at("/parameters/electronic_structure_method"_json_pointer).get<std::string>();
        }
        inline void electronic_structure_method(std::string electronic_structure_method__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/electronic_structure_method"_json_pointer] = electronic_structure_method__;
        }
        /// List of XC functionals (typically contains exchange term and correlation term).
        /**
            Naming convention of LibXC is used, names should be provided in capital letters.
        */
        inline auto xc_functionals() const
        {
            return dict_.at("/parameters/xc_functionals"_json_pointer).get<std::vector<std::string>>();
        }
        inline void xc_functionals(std::vector<std::string> xc_functionals__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/xc_functionals"_json_pointer] = xc_functionals__;
        }
        /// Type of core-states relativity in full-potential LAPW case.
        inline auto core_relativity() const
        {
            return dict_.at("/parameters/core_relativity"_json_pointer).get<std::string>();
        }
        inline void core_relativity(std::string core_relativity__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/core_relativity"_json_pointer] = core_relativity__;
        }
        /// Type of valence states relativity in full-potential LAPW case.
        inline auto valence_relativity() const
        {
            return dict_.at("/parameters/valence_relativity"_json_pointer).get<std::string>();
        }
        inline void valence_relativity(std::string valence_relativity__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/valence_relativity"_json_pointer] = valence_relativity__;
        }
        /// Number of bands.
        /**
            In spin-collinear case this is the number of bands for each spin channel.
        */
        inline auto num_bands() const
        {
            return dict_.at("/parameters/num_bands"_json_pointer).get<int>();
        }
        inline void num_bands(int num_bands__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/num_bands"_json_pointer] = num_bands__;
        }
        /// Number of first-variational states.
        inline auto num_fv_states() const
        {
            return dict_.at("/parameters/num_fv_states"_json_pointer).get<int>();
        }
        inline void num_fv_states(int num_fv_states__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/num_fv_states"_json_pointer] = num_fv_states__;
        }
        /// Width of the smearing delta-function in the units of [Ha].
        inline auto smearing_width() const
        {
            return dict_.at("/parameters/smearing_width"_json_pointer).get<double>();
        }
        inline void smearing_width(double smearing_width__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/smearing_width"_json_pointer] = smearing_width__;
        }
        /// Type of occupancy smearing.
        inline auto smearing() const
        {
            return dict_.at("/parameters/smearing"_json_pointer).get<std::string>();
        }
        inline void smearing(std::string smearing__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/smearing"_json_pointer] = smearing__;
        }
        /// Cutoff for plane-waves (for density and potential expansion) in the units of [a.u.^-1]
        inline auto pw_cutoff() const
        {
            return dict_.at("/parameters/pw_cutoff"_json_pointer).get<double>();
        }
        inline void pw_cutoff(double pw_cutoff__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/pw_cutoff"_json_pointer] = pw_cutoff__;
        }
        /// Second cutoff for effective potential expansion in the units of [a.u.^-1]
        inline auto veff_pw_cutoff() const
        {
            return dict_.at("/parameters/veff_pw_cutoff"_json_pointer).get<double>();
        }
        inline void veff_pw_cutoff(double veff_pw_cutoff__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/veff_pw_cutoff"_json_pointer] = veff_pw_cutoff__;
        }
        /// Cutoff for augmented-wave functions.
        /**
            This value is equal to R_{MT} * |G+k|_{max}
        */
        inline auto aw_cutoff() const
        {
            return dict_.at("/parameters/aw_cutoff"_json_pointer).get<double>();
        }
        inline void aw_cutoff(double aw_cutoff__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/aw_cutoff"_json_pointer] = aw_cutoff__;
        }
        /// Cutoff for |G+k| plane-waves in the units of [a.u.^-1].
        inline auto gk_cutoff() const
        {
            return dict_.at("/parameters/gk_cutoff"_json_pointer).get<double>();
        }
        inline void gk_cutoff(double gk_cutoff__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/gk_cutoff"_json_pointer] = gk_cutoff__;
        }
        /// Maximum l for APW functions.
        inline auto lmax_apw() const
        {
            return dict_.at("/parameters/lmax_apw"_json_pointer).get<int>();
        }
        inline void lmax_apw(int lmax_apw__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/lmax_apw"_json_pointer] = lmax_apw__;
        }
        /// Maximum l for density expansion in real spherical harmonics.
        inline auto lmax_rho() const
        {
            return dict_.at("/parameters/lmax_rho"_json_pointer).get<int>();
        }
        inline void lmax_rho(int lmax_rho__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/lmax_rho"_json_pointer] = lmax_rho__;
        }
        /// Maximum l for potential expansion in real spherical harmonics.
        inline auto lmax_pot() const
        {
            return dict_.at("/parameters/lmax_pot"_json_pointer).get<int>();
        }
        inline void lmax_pot(int lmax_pot__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/lmax_pot"_json_pointer] = lmax_pot__;
        }
        /// Number of dimensions of the magnetization and effective magnetic field vector.
        inline auto num_mag_dims() const
        {
            return dict_.at("/parameters/num_mag_dims"_json_pointer).get<int>();
        }
        inline void num_mag_dims(int num_mag_dims__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/num_mag_dims"_json_pointer] = num_mag_dims__;
        }
        /// A choice of scaleing muffin-tin radii automatically.
        inline auto auto_rmt() const
        {
            return dict_.at("/parameters/auto_rmt"_json_pointer).get<int>();
        }
        inline void auto_rmt(int auto_rmt__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/auto_rmt"_json_pointer] = auto_rmt__;
        }
        /// Regular k-point grid for the SCF ground state.
        inline auto ngridk() const
        {
            return dict_.at("/parameters/ngridk"_json_pointer).get<std::array<int, 3>>();
        }
        inline void ngridk(std::array<int, 3> ngridk__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/ngridk"_json_pointer] = ngridk__;
        }
        /// Shift in the k-point grid.
        inline auto shiftk() const
        {
            return dict_.at("/parameters/shiftk"_json_pointer).get<std::array<int, 3>>();
        }
        inline void shiftk(std::array<int, 3> shiftk__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/shiftk"_json_pointer] = shiftk__;
        }
        /// Specific list of k-point coordinates.
        inline auto vk() const
        {
            return dict_.at("/parameters/vk"_json_pointer).get<std::vector<std::array<double, 3>>>();
        }
        inline void vk(std::vector<std::array<double, 3>> vk__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/vk"_json_pointer] = vk__;
        }
        /// Number of SCF iterations.
        inline auto num_dft_iter() const
        {
            return dict_.at("/parameters/num_dft_iter"_json_pointer).get<int>();
        }
        inline void num_dft_iter(int num_dft_iter__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/num_dft_iter"_json_pointer] = num_dft_iter__;
        }
        /// Tolerance in total energy change (in units of [Ha]).
        inline auto energy_tol() const
        {
            return dict_.at("/parameters/energy_tol"_json_pointer).get<double>();
        }
        inline void energy_tol(double energy_tol__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/energy_tol"_json_pointer] = energy_tol__;
        }
        /// Tolerance for the density mixing.
        inline auto density_tol() const
        {
            return dict_.at("/parameters/density_tol"_json_pointer).get<double>();
        }
        inline void density_tol(double density_tol__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/density_tol"_json_pointer] = density_tol__;
        }
        ///  True if this is a molecule calculation.
        inline auto molecule() const
        {
            return dict_.at("/parameters/molecule"_json_pointer).get<bool>();
        }
        inline void molecule(bool molecule__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/molecule"_json_pointer] = molecule__;
        }
        /// True if gamma-point (real) version of the PW code is used.
        inline auto gamma_point() const
        {
            return dict_.at("/parameters/gamma_point"_json_pointer).get<bool>();
        }
        inline void gamma_point(bool gamma_point__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/gamma_point"_json_pointer] = gamma_point__;
        }
        /// True if spin-orbit correction is applied.
        inline auto so_correction() const
        {
            return dict_.at("/parameters/so_correction"_json_pointer).get<bool>();
        }
        inline void so_correction(bool so_correction__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/so_correction"_json_pointer] = so_correction__;
        }
        /// True if Hubbard U correction is applied.
        inline auto hubbard_correction() const
        {
            return dict_.at("/parameters/hubbard_correction"_json_pointer).get<bool>();
        }
        inline void hubbard_correction(bool hubbard_correction__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/hubbard_correction"_json_pointer] = hubbard_correction__;
        }
        /// True if symmetry is used.
        inline auto use_symmetry() const
        {
            return dict_.at("/parameters/use_symmetry"_json_pointer).get<bool>();
        }
        inline void use_symmetry(bool use_symmetry__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/use_symmetry"_json_pointer] = use_symmetry__;
        }
        /// Use irreducible Brillouin zone.
        inline auto use_ibz() const
        {
            return dict_.at("/parameters/use_ibz"_json_pointer).get<bool>();
        }
        inline void use_ibz(bool use_ibz__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/use_ibz"_json_pointer] = use_ibz__;
        }
        /// Radius of atom nearest-neighbour cluster.
        inline auto nn_radius() const
        {
            return dict_.at("/parameters/nn_radius"_json_pointer).get<double>();
        }
        inline void nn_radius(double nn_radius__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/nn_radius"_json_pointer] = nn_radius__;
        }
        /// Reduction of the auxiliary magnetic field at each SCF step.
        inline auto reduce_aux_bf() const
        {
            return dict_.at("/parameters/reduce_aux_bf"_json_pointer).get<double>();
        }
        inline void reduce_aux_bf(double reduce_aux_bf__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/reduce_aux_bf"_json_pointer] = reduce_aux_bf__;
        }
        /// Introduce extra charge to the system. Positive charge means extra holes, negative charge - extra electrons.
        inline auto extra_charge() const
        {
            return dict_.at("/parameters/extra_charge"_json_pointer).get<double>();
        }
        inline void extra_charge(double extra_charge__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/extra_charge"_json_pointer] = extra_charge__;
        }
        /// Fixed magnetic moment
        inline auto fixed_mag() const
        {
            return dict_.at("/parameters/fixed_mag"_json_pointer).get<double>();
        }
        inline void fixed_mag(double fixed_mag__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/fixed_mag"_json_pointer] = fixed_mag__;
        }
        /// XC density threshold (debug purposes).
        inline auto xc_dens_tre() const
        {
            return dict_.at("/parameters/xc_dens_tre"_json_pointer).get<double>();
        }
        inline void xc_dens_tre(double xc_dens_tre__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/xc_dens_tre"_json_pointer] = xc_dens_tre__;
        }
        /// True if SCF correction to total energy should be computed.
        inline auto use_scf_correction() const
        {
            return dict_.at("/parameters/use_scf_correction"_json_pointer).get<bool>();
        }
        inline void use_scf_correction(bool use_scf_correction__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/use_scf_correction"_json_pointer] = use_scf_correction__;
        }
        /// The floating point precision of the Kohn-Sham wave-functions.
        inline auto precision_wf() const
        {
            return dict_.at("/parameters/precision_wf"_json_pointer).get<std::string>();
        }
        inline void precision_wf(std::string precision_wf__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/precision_wf"_json_pointer] = precision_wf__;
        }
        /// The floating point precision of the Hamiltonian subspace matrices.
        inline auto precision_hs() const
        {
            return dict_.at("/parameters/precision_hs"_json_pointer).get<std::string>();
        }
        inline void precision_hs(std::string precision_hs__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/precision_hs"_json_pointer] = precision_hs__;
        }
        /// The final floating point precision of the ground state DFT calculation (dev options).
        inline auto precision_gs() const
        {
            return dict_.at("/parameters/precision_gs"_json_pointer).get<std::string>();
        }
        inline void precision_gs(std::string precision_gs__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/parameters/precision_gs"_json_pointer] = precision_gs__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& parameters() const {return parameters_;}
    inline auto& parameters() {return parameters_;}
    /// Non-linear conjugate gradient minimisation
    class nlcg_t
    {
      public:
        nlcg_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Maximum number of CG iterations
        inline auto maxiter() const
        {
            return dict_.at("/nlcg/maxiter"_json_pointer).get<int>();
        }
        inline void maxiter(int maxiter__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/maxiter"_json_pointer] = maxiter__;
        }
        /// CG restart
        inline auto restart() const
        {
            return dict_.at("/nlcg/restart"_json_pointer).get<int>();
        }
        inline void restart(int restart__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/restart"_json_pointer] = restart__;
        }
        /// Backtracking search, step parameter
        inline auto tau() const
        {
            return dict_.at("/nlcg/tau"_json_pointer).get<double>();
        }
        inline void tau(double tau__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/tau"_json_pointer] = tau__;
        }
        /// Temperature in Kelvin
        inline auto T() const
        {
            return dict_.at("/nlcg/T"_json_pointer).get<double>();
        }
        inline void T(double T__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/T"_json_pointer] = T__;
        }
        /// Scalar preconditioning of pseudo Hamiltonian
        inline auto kappa() const
        {
            return dict_.at("/nlcg/kappa"_json_pointer).get<double>();
        }
        inline void kappa(double kappa__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/kappa"_json_pointer] = kappa__;
        }
        /// CG tolerance
        inline auto tol() const
        {
            return dict_.at("/nlcg/tol"_json_pointer).get<double>();
        }
        inline void tol(double tol__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/tol"_json_pointer] = tol__;
        }
        /// NLCG processing unit
        inline auto processing_unit() const
        {
            return dict_.at("/nlcg/processing_unit"_json_pointer).get<std::string>();
        }
        inline void processing_unit(std::string processing_unit__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/nlcg/processing_unit"_json_pointer] = processing_unit__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& nlcg() const {return nlcg_;}
    inline auto& nlcg() {return nlcg_;}
    /// Variable cell shape stabilized quasi Newton method (VC-SQNM)
    class vcsqnm_t
    {
      public:
        vcsqnm_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Initial step size
        inline auto initial_step_size() const
        {
            return dict_.at("/vcsqnm/initial_step_size"_json_pointer).get<double>();
        }
        inline void initial_step_size(double initial_step_size__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/initial_step_size"_json_pointer] = initial_step_size__;
        }
        /// Maximal number of steps that will be stored in the history list
        inline auto nhist_max() const
        {
            return dict_.at("/vcsqnm/nhist_max"_json_pointer).get<int>();
        }
        inline void nhist_max(int nhist_max__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/nhist_max"_json_pointer] = nhist_max__;
        }
        /// Weight / size of the supercell that is used to transform lattice derivatives. Use a value between 1 and 2.
        inline auto lattice_weight() const
        {
            return dict_.at("/vcsqnm/lattice_weight"_json_pointer).get<double>();
        }
        inline void lattice_weight(double lattice_weight__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/lattice_weight"_json_pointer] = lattice_weight__;
        }
        /// Lower limit on the step size
        inline auto alpha0() const
        {
            return dict_.at("/vcsqnm/alpha0"_json_pointer).get<double>();
        }
        inline void alpha0(double alpha0__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/alpha0"_json_pointer] = alpha0__;
        }
        /// Lower limit on linear dependencies of basis vectors in history list
        inline auto eps_subsp() const
        {
            return dict_.at("/vcsqnm/eps_subsp"_json_pointer).get<double>();
        }
        inline void eps_subsp(double eps_subsp__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/eps_subsp"_json_pointer] = eps_subsp__;
        }
        /// Total error tolerance on the atomic forces
        inline auto forces_tol() const
        {
            return dict_.at("/vcsqnm/forces_tol"_json_pointer).get<double>();
        }
        inline void forces_tol(double forces_tol__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/forces_tol"_json_pointer] = forces_tol__;
        }
        /// Total error tolerance on the lattice stress
        inline auto stress_tol() const
        {
            return dict_.at("/vcsqnm/stress_tol"_json_pointer).get<double>();
        }
        inline void stress_tol(double stress_tol__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/stress_tol"_json_pointer] = stress_tol__;
        }
        /// Number of lattice relaxation steps
        inline auto num_steps() const
        {
            return dict_.at("/vcsqnm/num_steps"_json_pointer).get<int>();
        }
        inline void num_steps(int num_steps__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/vcsqnm/num_steps"_json_pointer] = num_steps__;
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& vcsqnm() const {return vcsqnm_;}
    inline auto& vcsqnm() {return vcsqnm_;}
    /// Hubbard U correction
    class hubbard_t
    {
      public:
        hubbard_t(nlohmann::json& dict__)
            : dict_(dict__)
        {
        }
        /// Method to use for generating the hubbard subspace. [none] bare hubbard orbitals,  [full_orthogonaliation] use all atomic wave functions to generate the hubbard subspace, [normalize] normalize the original hubbard wave functions, [orthogonalize] orthogonalize the hubbard wave functions
        inline auto hubbard_subspace_method() const
        {
            return dict_.at("/hubbard/hubbard_subspace_method"_json_pointer).get<std::string>();
        }
        inline void hubbard_subspace_method(std::string hubbard_subspace_method__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/hubbard_subspace_method"_json_pointer] = hubbard_subspace_method__;
        }
        /// If true, simplified version of Hubbard correction is used.
        inline auto simplified() const
        {
            return dict_.at("/hubbard/simplified"_json_pointer).get<bool>();
        }
        inline void simplified(bool simplified__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/simplified"_json_pointer] = simplified__;
        }
        /// Use constrained hubbard occupations number. Occupation matrices should be given
        inline auto constrained_calculation() const
        {
            return dict_.at("/hubbard/constrained_calculation"_json_pointer).get<bool>();
        }
        inline void constrained_calculation(bool constrained_calculation__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/constrained_calculation"_json_pointer] = constrained_calculation__;
        }
        /// Error between the actual and requested occupation numbers
        inline auto constraint_error() const
        {
            return dict_.at("/hubbard/constraint_error"_json_pointer).get<double>();
        }
        inline void constraint_error(double constraint_error__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/constraint_error"_json_pointer] = constraint_error__;
        }
        /// Maximum number of constrained iterations before returning to the normal Hubbard scheme
        inline auto constraint_max_iteration() const
        {
            return dict_.at("/hubbard/constraint_max_iteration"_json_pointer).get<int>();
        }
        inline void constraint_max_iteration(int constraint_max_iteration__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/constraint_max_iteration"_json_pointer] = constraint_max_iteration__;
        }
        /// Mixing parameters for the constrained hubbard
        inline auto constraint_beta_mixing() const
        {
            return dict_.at("/hubbard/constraint_beta_mixing"_json_pointer).get<double>();
        }
        inline void constraint_beta_mixing(double constraint_beta_mixing__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/constraint_beta_mixing"_json_pointer] = constraint_beta_mixing__;
        }
        /// Amplitude of the constrained potential
        inline auto constraint_strength() const
        {
            return dict_.at("/hubbard/constraint_strength"_json_pointer).get<double>();
        }
        inline void constraint_strength(double constraint_strength__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/constraint_strength"_json_pointer] = constraint_strength__;
        }
        /// criteria used during the constraining process
        inline auto constraint_method() const
        {
            return dict_.at("/hubbard/constraint_method"_json_pointer).get<std::string>();
        }
        inline void constraint_method(std::string constraint_method__)
        {
            if (dict_.contains("locked")) {
                throw std::runtime_error(locked_msg);
            }
            dict_["/hubbard/constraint_method"_json_pointer] = constraint_method__;
        }
        /// set of matrices containing the occupation numbers a given atomic level
        class local_constraint_t
        {
          private:
            nlohmann::json& dict_;
          public:
            local_constraint_t(nlohmann::json& dict__)
                : dict_(dict__)
            {
            }
            auto atom_index() const
            {
                return dict_.at("atom_index").get<int>();
            }
            auto n() const
            {
                return dict_.at("n").get<int>();
            }
            auto l() const
            {
                return dict_.at("l").get<int>();
            }
            auto lm_order() const
            {
                return dict_.at("lm_order").get<std::vector<int>>();
            }
            auto occupancy() const
            {
                return dict_.at("occupancy").get<std::vector<std::vector<std::vector<double>>>>();
            }
            bool contains(std::string key__) const
            {
                return dict_.contains(key__);
            }
        };
        class local_constraint_list_t
        {
          private:
            nlohmann::json& dict_;
          public:
            local_constraint_list_t(nlohmann::json& dict__)
                : dict_(dict__)
            {
            }
            int size() const
            {
                return dict_.size();
            }
            void append(nlohmann::json& node__)
            {
                dict_.push_back(node__);
            }
        };
        local_constraint_t local_constraint(int idx__)
        {
            nlohmann::json::json_pointer ptr("/hubbard/local_constraint");
            return local_constraint_t(dict_.at(ptr / idx__));
        }
        local_constraint_t local_constraint(int idx__) const
        {
            nlohmann::json::json_pointer ptr("/hubbard/local_constraint");
            return local_constraint_t(dict_.at(ptr / idx__));
        }
        local_constraint_list_t local_constraint()
        {
            nlohmann::json::json_pointer ptr("/hubbard/local_constraint");
            return local_constraint_list_t(dict_.at(ptr));
        }
        local_constraint_list_t local_constraint() const
        {
            nlohmann::json::json_pointer ptr("/hubbard/local_constraint");
            return local_constraint_list_t(dict_.at(ptr));
        }
        /// Description of the on-site (local) Hubbard interaction
        class local_t
        {
          private:
            nlohmann::json& dict_;
          public:
            local_t(nlohmann::json& dict__)
                : dict_(dict__)
            {
            }
            auto atom_type() const
            {
                return dict_.at("atom_type").get<std::string>();
            }
            auto n() const
            {
                return dict_.at("n").get<int>();
            }
            auto l() const
            {
                return dict_.at("l").get<int>();
            }
            auto U() const
            {
                return dict_.at("U").get<double>();
            }
            auto J() const
            {
                return dict_.at("J").get<double>();
            }
            auto B() const
            {
                return dict_.at("B").get<double>();
            }
            auto E2() const
            {
                return dict_.at("E2").get<double>();
            }
            auto E3() const
            {
                return dict_.at("E3").get<double>();
            }
            auto alpha() const
            {
                return dict_.at("alpha").get<double>();
            }
            auto beta() const
            {
                return dict_.at("beta").get<double>();
            }
            auto initial_occupancy() const
            {
                return dict_.at("initial_occupancy").get<std::vector<double>>();
            }
            auto total_initial_occupancy() const
            {
                return dict_.at("total_initial_occupancy").get<double>();
            }
            bool contains(std::string key__) const
            {
                return dict_.contains(key__);
            }
        };
        class local_list_t
        {
          private:
            nlohmann::json& dict_;
          public:
            local_list_t(nlohmann::json& dict__)
                : dict_(dict__)
            {
            }
            int size() const
            {
                return dict_.size();
            }
            void append(nlohmann::json& node__)
            {
                dict_.push_back(node__);
            }
        };
        local_t local(int idx__)
        {
            nlohmann::json::json_pointer ptr("/hubbard/local");
            return local_t(dict_.at(ptr / idx__));
        }
        local_t local(int idx__) const
        {
            nlohmann::json::json_pointer ptr("/hubbard/local");
            return local_t(dict_.at(ptr / idx__));
        }
        local_list_t local()
        {
            nlohmann::json::json_pointer ptr("/hubbard/local");
            return local_list_t(dict_.at(ptr));
        }
        local_list_t local() const
        {
            nlohmann::json::json_pointer ptr("/hubbard/local");
            return local_list_t(dict_.at(ptr));
        }
        /// Description of the off-site (nonlocal) Hubbard interaction
        class nonlocal_t
        {
          private:
            nlohmann::json& dict_;
          public:
            nonlocal_t(nlohmann::json& dict__)
                : dict_(dict__)
            {
            }
            auto atom_pair() const
            {
                return dict_.at("atom_pair").get<std::array<int, 2>>();
            }
            auto T() const
            {
                return dict_.at("T").get<std::array<int, 3>>();
            }
            auto n() const
            {
                return dict_.at("n").get<std::array<int, 2>>();
            }
            auto l() const
            {
                return dict_.at("l").get<std::array<int, 2>>();
            }
            auto V() const
            {
                return dict_.at("V").get<double>();
            }
            bool contains(std::string key__) const
            {
                return dict_.contains(key__);
            }
        };
        class nonlocal_list_t
        {
          private:
            nlohmann::json& dict_;
          public:
            nonlocal_list_t(nlohmann::json& dict__)
                : dict_(dict__)
            {
            }
            int size() const
            {
                return dict_.size();
            }
            void append(nlohmann::json& node__)
            {
                dict_.push_back(node__);
            }
        };
        nonlocal_t nonlocal(int idx__)
        {
            nlohmann::json::json_pointer ptr("/hubbard/nonlocal");
            return nonlocal_t(dict_.at(ptr / idx__));
        }
        nonlocal_t nonlocal(int idx__) const
        {
            nlohmann::json::json_pointer ptr("/hubbard/nonlocal");
            return nonlocal_t(dict_.at(ptr / idx__));
        }
        nonlocal_list_t nonlocal()
        {
            nlohmann::json::json_pointer ptr("/hubbard/nonlocal");
            return nonlocal_list_t(dict_.at(ptr));
        }
        nonlocal_list_t nonlocal() const
        {
            nlohmann::json::json_pointer ptr("/hubbard/nonlocal");
            return nonlocal_list_t(dict_.at(ptr));
        }
      private:
        nlohmann::json& dict_;
    };
    inline auto const& hubbard() const {return hubbard_;}
    inline auto& hubbard() {return hubbard_;}
  private:
    mixer_t mixer_{dict_};
    settings_t settings_{dict_};
    unit_cell_t unit_cell_{dict_};
    iterative_solver_t iterative_solver_{dict_};
    control_t control_{dict_};
    parameters_t parameters_{dict_};
    nlcg_t nlcg_{dict_};
    vcsqnm_t vcsqnm_{dict_};
    hubbard_t hubbard_{dict_};
  protected:
    nlohmann::json dict_;
};

}
