Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear response with solvent #471

Merged
merged 20 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,4 @@ Pipfile.lock
.vscode

cube_vectors/
tests/**/*.out
robertodr marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion cmake/compiler_flags/CXXFlags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

option(ENABLE_ARCH_FLAGS "Enable architecture-specific compiler flags" ON)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS FALSE)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
Expand Down
30 changes: 23 additions & 7 deletions doc/users/user_ref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -905,13 +905,7 @@ User input reference

**Default** ``1.0``

:epsilon_out: Permittivity outside the cavity. This is characteristic of the solvent used.

**Type** ``float``

**Default** ``1.0``

:formulation: Formulation of the Permittivity function. Currently only the exponential is used.
:formulation: Formulation of the Permittivity function. Currently only the exponential is available.

**Type** ``str``

Expand All @@ -920,6 +914,28 @@ User input reference
**Predicates**
- ``value.lower() in ['exponential']``

:red:`Sections`
:epsilon_out: Parameters for the continuum solvent outside the cavity.

:red:`Keywords`
:nonequilibrium: Whether to use the nonequilibrium formulation of response, *i.e.* use the dynamic permittivity for the calculation of the response reaction field. Defaults to false.

**Type** ``bool``

**Default** ``False``

:static: Static permittivity outside the cavity. This is characteristic of the solvent used.

**Type** ``float``

**Default** ``1.0``

:dynamic: Dynamic permittivity outside the cavity. This is characteristic of the solvent used and relevant only in response calculations. Defaults to the same value as `epsilon_static`.

**Type** ``float``

**Default** ``user['PCM']['Permittivity']['epsilon_out']['static']``

:Constants: Physical and mathematical constants used by MRChem

:red:`Keywords`
Expand Down
42 changes: 31 additions & 11 deletions python/mrchem/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,7 @@ def write_scf_fock(user_dict, wf_dict, origin):

# Reaction
if user_dict["WaveFunction"]["environment"].lower() == "pcm":
fock_dict["reaction_operator"] = {
"poisson_prec": user_dict["world_prec"],
"kain": user_dict["PCM"]["SCRF"]["kain"],
"max_iter": user_dict["PCM"]["SCRF"]["max_iter"],
"dynamic_thrs": user_dict["PCM"]["SCRF"]["dynamic_thrs"],
"density_type": user_dict["PCM"]["SCRF"]["density_type"],
"epsilon_in": user_dict["PCM"]["Permittivity"]["epsilon_in"],
"epsilon_out": user_dict["PCM"]["Permittivity"]["epsilon_out"],
"formulation": user_dict["PCM"]["Permittivity"]["formulation"],
}
fock_dict["reaction_operator"] = _reaction_operator_handler(user_dict)

# Coulomb
if wf_dict["method_type"] in ["hartree", "hf", "dft"]:
Expand Down Expand Up @@ -128,6 +119,32 @@ def write_scf_fock(user_dict, wf_dict, origin):
return fock_dict


def _reaction_operator_handler(user_dict, rsp=False):
# convert density_type from string to integer
if user_dict["PCM"]["SCRF"]["density_type"] == "total":
density_type = 0
elif user_dict["PCM"]["SCRF"]["density_type"] == "electronic":
density_type = 1
else:
density_type = 2

return {
"poisson_prec": user_dict["world_prec"],
"kain": user_dict["PCM"]["SCRF"]["kain"],
"max_iter": user_dict["PCM"]["SCRF"]["max_iter"],
"dynamic_thrs": user_dict["PCM"]["SCRF"]["dynamic_thrs"],
# if doing a response calculation, then density_type is set to 1 (electronic only)
"density_type": 1 if rsp else density_type,
"epsilon_in": user_dict["PCM"]["Permittivity"]["epsilon_in"],
"epsilon_static": user_dict["PCM"]["Permittivity"]["epsilon_out"]["static"],
"epsilon_dynamic": user_dict["PCM"]["Permittivity"]["epsilon_out"]["dynamic"],
"nonequilibrium": user_dict["PCM"]["Permittivity"]["epsilon_out"][
"nonequilibrium"
],
"formulation": user_dict["PCM"]["Permittivity"]["formulation"],
}


def write_scf_guess(user_dict, wf_dict):
guess_str = user_dict["SCF"]["guess_type"].lower()
guess_type = guess_str.split("_")[0]
Expand Down Expand Up @@ -305,7 +322,6 @@ def write_rsp_calc(omega, user_dict, origin):

rsp_calc["components"] = []
for dir in [0, 1, 2]:

rsp_comp = {}

program_guess_type = user_guess_type
Expand Down Expand Up @@ -405,6 +421,10 @@ def write_rsp_fock(user_dict, wf_dict):
},
}

# Reaction
if user_dict["WaveFunction"]["environment"].lower() == "pcm":
fock_dict["reaction_operator"] = _reaction_operator_handler(user_dict, rsp=True)

return fock_dict


Expand Down
2 changes: 1 addition & 1 deletion python/mrchem/input_parser/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
This file was automatically generated by parselglossy on 2023-11-15
This file was automatically generated by parselglossy on 2023-11-22
Editing is *STRONGLY DISCOURAGED*
2 changes: 1 addition & 1 deletion python/mrchem/input_parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

# This file was automatically generated by parselglossy on 2023-11-15
# This file was automatically generated by parselglossy on 2023-11-22
# Editing is *STRONGLY DISCOURAGED*
17 changes: 12 additions & 5 deletions python/mrchem/input_parser/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

# This file was automatically generated by parselglossy on 2023-11-15
# This file was automatically generated by parselglossy on 2023-11-22
# Editing is *STRONGLY DISCOURAGED*

from copy import deepcopy
Expand Down Expand Up @@ -607,16 +607,23 @@ def stencil() -> JSONDict:
{ 'keywords': [ { 'default': 1.0,
'name': 'epsilon_in',
'type': 'float'},
{ 'default': 1.0,
'name': 'epsilon_out',
'type': 'float'},
{ 'default': 'exponential',
'name': 'formulation',
'predicates': [ 'value.lower() '
'in '
"['exponential']"],
'type': 'str'}],
'name': 'Permittivity'}]},
'name': 'Permittivity',
'sections': [ { 'keywords': [ { 'default': False,
'name': 'nonequilibrium',
'type': 'bool'},
{ 'default': 1.0,
'name': 'static',
'type': 'float'},
{ 'default': "user['PCM']['Permittivity']['epsilon_out']['static']",
'name': 'dynamic',
'type': 'float'}],
'name': 'epsilon_out'}]}]},
{ 'keywords': [ { 'default': 78.9451185,
'name': 'hartree2simagnetizability',
'type': 'float'},
Expand Down
2 changes: 1 addition & 1 deletion python/mrchem/input_parser/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

# This file was automatically generated by parselglossy on 2023-11-15
# This file was automatically generated by parselglossy on 2023-11-22
# Editing is *STRONGLY DISCOURAGED*

import argparse
Expand Down
30 changes: 23 additions & 7 deletions python/mrchem/input_parser/docs/user_ref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -905,13 +905,7 @@ User input reference

**Default** ``1.0``

:epsilon_out: Permittivity outside the cavity. This is characteristic of the solvent used.

**Type** ``float``

**Default** ``1.0``

:formulation: Formulation of the Permittivity function. Currently only the exponential is used.
:formulation: Formulation of the Permittivity function. Currently only the exponential is available.

**Type** ``str``

Expand All @@ -920,6 +914,28 @@ User input reference
**Predicates**
- ``value.lower() in ['exponential']``

:red:`Sections`
:epsilon_out: Parameters for the continuum solvent outside the cavity.

:red:`Keywords`
:nonequilibrium: Whether to use the nonequilibrium formulation of response, *i.e.* use the dynamic permittivity for the calculation of the response reaction field. Defaults to false.

**Type** ``bool``

**Default** ``False``

:static: Static permittivity outside the cavity. This is characteristic of the solvent used.

**Type** ``float``

**Default** ``1.0``

:dynamic: Dynamic permittivity outside the cavity. This is characteristic of the solvent used and relevant only in response calculations. Defaults to the same value as `epsilon_static`.

**Type** ``float``

**Default** ``user['PCM']['Permittivity']['epsilon_out']['static']``

:Constants: Physical and mathematical constants used by MRChem

:red:`Keywords`
Expand Down
2 changes: 1 addition & 1 deletion python/mrchem/input_parser/plumbing/lexer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

# This file was automatically generated by parselglossy on 2023-11-15
# This file was automatically generated by parselglossy on 2023-11-22
# Editing is *STRONGLY DISCOURAGED*

import json
Expand Down
34 changes: 27 additions & 7 deletions python/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -936,20 +936,40 @@ sections:
docstring: |
Permittivity inside the cavity. 1.0 is the permittivity of free
space, anything other than this is undefined behaviour.
- name: epsilon_out
type: float
default: 1.0
docstring: |
Permittivity outside the cavity. This is characteristic of the
solvent used.
- name: formulation
type: str
default: exponential
predicates:
- value.lower() in ['exponential']
docstring: |
Formulation of the Permittivity function. Currently only the
exponential is used.
exponential is available.
sections:
- name: epsilon_out
docstring: |
Parameters for the continuum solvent outside the cavity.
keywords:
- name: static
type: float
default: 1.0
docstring: |
Static permittivity outside the cavity. This is characteristic of the
solvent used.
- name: dynamic
type: float
default: user['PCM']['Permittivity']['epsilon_out']['static']
docstring: |
Dynamic permittivity outside the cavity. This is
characteristic of the solvent used and relevant only in
response calculations. Defaults to the same value as
`epsilon_static`.
- name: nonequilibrium
type: bool
default: false
docstring: |
Whether to use the nonequilibrium formulation of response,
*i.e.* use the dynamic permittivity for the calculation of the
response reaction field. Defaults to false.
- name: Constants
docstring: Physical and mathematical constants used by MRChem
keywords:
Expand Down
54 changes: 47 additions & 7 deletions src/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ namespace driver {
DerivativeOperator_p get_derivative(const std::string &name);
template <int I> RankOneOperator<I> get_operator(const std::string &name, const json &json_oper);
template <int I, int J> RankTwoOperator<I, J> get_operator(const std::string &name, const json &json_oper);
void build_fock_operator(const json &input, Molecule &mol, FockBuilder &F, int order);
void build_fock_operator(const json &input, Molecule &mol, FockBuilder &F, int order, bool is_dynamic = false);
void init_properties(const json &json_prop, Molecule &mol);

namespace scf {
Expand Down Expand Up @@ -746,7 +746,7 @@ json driver::rsp::run(const json &json_rsp, Molecule &mol) {

FockBuilder F_1;
const auto &json_fock_1 = json_rsp["fock_operator"];
driver::build_fock_operator(json_fock_1, mol, F_1, 1);
driver::build_fock_operator(json_fock_1, mol, F_1, 1, dynamic);

const auto &json_pert = json_rsp["perturbation"];
auto h_1 = driver::get_operator<3>(json_pert["operator"], json_pert);
Expand Down Expand Up @@ -983,7 +983,7 @@ void driver::rsp::calc_properties(const json &json_prop, Molecule &mol, int dir,
* construct all operator which are present in this input. Option to set
* perturbation order of the operators.
*/
void driver::build_fock_operator(const json &json_fock, Molecule &mol, FockBuilder &F, int order) {
void driver::build_fock_operator(const json &json_fock, Molecule &mol, FockBuilder &F, int order, bool is_dynamic) {
auto &nuclei = mol.getNuclei();
auto Phi_p = mol.getOrbitals_p();
auto X_p = mol.getOrbitalsX_p();
Expand Down Expand Up @@ -1043,26 +1043,66 @@ void driver::build_fock_operator(const json &json_fock, Molecule &mol, FockBuild
///////////////////////////////////////////////////////////
if (json_fock.contains("reaction_operator")) {

// only perturbaton orders 0 and 1 are implemented
if (order > 1) MSG_ABORT("Invalid perturbation order");

// preparing Reaction Operator
auto poisson_prec = json_fock["reaction_operator"]["poisson_prec"];
auto P_p = std::make_shared<PoissonOperator>(*MRA, poisson_prec);
auto D_p = std::make_shared<mrcpp::ABGVOperator<3>>(*MRA, 0.0, 0.0);
auto cavity_p = mol.getCavity_p();
cavity_p->printParameters();

auto kain = json_fock["reaction_operator"]["kain"];
auto max_iter = json_fock["reaction_operator"]["max_iter"];
auto dynamic_thrs = json_fock["reaction_operator"]["dynamic_thrs"];
// the input parser helpers have converted this parameter from string
// to integer, compatibly with the DensityType enum
auto density_type = json_fock["reaction_operator"]["density_type"];
// permittivity inside the cavity
auto eps_i = json_fock["reaction_operator"]["epsilon_in"];
auto eps_o = json_fock["reaction_operator"]["epsilon_out"];
// static permittivity outside the cavity
auto eps_s = json_fock["reaction_operator"]["epsilon_static"];
// dynamic permittivity outside the cavity
auto eps_d = json_fock["reaction_operator"]["epsilon_dynamic"];
// whether nonequilibrium response was requested
auto noneq = json_fock["reaction_operator"]["nonequilibrium"];
auto formulation = json_fock["reaction_operator"]["formulation"];

Permittivity dielectric_func(mol.getCavity_p(), eps_i, eps_o, formulation);
dielectric_func.printParameters();
// compute nuclear charge density
Density rho_nuc(false);
rho_nuc = chemistry::compute_nuclear_density(poisson_prec, nuclei, 100);

// decide which permittivity to use outside of the cavity
auto eps_o = [order, is_dynamic, noneq, eps_s, eps_d] {
if (order >= 1 && noneq && is_dynamic) {
// in response (order >= 1), use dynamic permittivity if:
// a. nonequilibrium was requested (noneq is true), and
// b. the frequency is nonzero (is_dynamic is true)
return eps_d;
} else {
// for the ground state, always use the static permittivity
return eps_s;
}
}();

// initialize Permittivity function with static or dynamic epsilon, based on perturbation order
Permittivity dielectric_func(cavity_p, eps_i, eps_o, formulation);
dielectric_func.printParameters();

// initialize SCRF object
auto scrf_p = std::make_unique<SCRF>(dielectric_func, rho_nuc, P_p, D_p, kain, max_iter, dynamic_thrs, density_type);
auto V_R = std::make_shared<ReactionOperator>(std::move(scrf_p), Phi_p);

// initialize reaction potential object
auto V_R = [&] {
if (order == 0) {
return std::make_shared<ReactionOperator>(std::move(scrf_p), Phi_p);
} else {
return std::make_shared<ReactionOperator>(std::move(scrf_p), Phi_p, X_p, Y_p);
}
}();

// set reaction potential in FockBuilder object
F.getReactionOperator() = V_R;
}
///////////////////////////////////////////////////////////
Expand Down
Loading