Skip to content

Commit

Permalink
JSON output (#290)
Browse files Browse the repository at this point in the history
* Add Eigen -> std::vector conversion

* Allow negative print_level for complete silence

* Add json property output

* Add OrbitalEnergies property

* Revert mrchem_parsed.json intermediate

Now mrchem.json contains both the parsed input and the json output.
It's still possible to give user input as JSON, but file name should
still be mrchem.inp for this.

* Add missing license headers

* Add json SCF output

* Compute dipole moment by default

* Add json RSP output

* Remove GTO rsp initial guess

* Add schema for program input

* Make Response.localize default to SCF.localize

* Only MPI grand master dumps JSON

* Improve JSON output

* Add schema for program output

* Occupancy -> occupation

* Polarizability and NMR json list -> dict

* Flush very small property values to zero

* Tweak NMR output

* Update to nlohmann::json-3.6.1

* Make use of json.contains()

* Cleanup JSON output

* Fix schemas

* Remove orbital_energies input keyword

* Update test refs

* Require full basis name (legendre/interpolating)

* Make default constructors for properties

* Cleanup Molecule's handling of properties

* Geometry derivative -> geometric derivative

* Move eigen_to_vector to print_utils

* Define PropertyMap

* Move calculation of dia properties to RSP part

* Fix printing
  • Loading branch information
stigrj committed Jun 18, 2020
1 parent 7f18f27 commit 18bc949
Show file tree
Hide file tree
Showing 46 changed files with 1,547 additions and 533 deletions.
261 changes: 261 additions & 0 deletions doc/schema_input.json

Large diffs are not rendered by default.

144 changes: 144 additions & 0 deletions doc/schema_output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"output": {
"properties": { # Collection of final properties
"charge": int, # Total molecular charge
"multiplicity": int, # Total spin multiplicity
"center_of_mass": array[float], # Center of mass coordinate
"geometry": array[ # Array of atoms
{ # (one entry per atom)
"symbol": string, # Atomic symbol
"xyz": array[float] # Cartesian coordinate
}
],
"orbital_energies": { # Collection of orbital energies
"spin": array[string], # Array of spins ('p', 'a' or 'b')
"energy": array[float], # Array of energies
"occupation": array[int], # Array of orbital occupations
"sum_occupied": float # \sum_i occupation[i]*energy[i]
},
"scf_energy": { # Collection of energy contributions
"E_kin": float, # Kinetic energy
"E_nn": float, # Classical nuclear-nuclear interaction
"E_en": float, # Classical electron-nuclear interaction
"E_ee": float, # Classical electron-electron interaction
"E_next": float, # Classical nuclear-external field interaction
"E_eext": float, # Classical electron-external field interaction
"E_x": float, # Hartree-Fock exact exchange energy
"E_xc": float, # DFT exchange-correlation energy
"E_el": float, # Sum of electronic contributions
"E_nuc": float, # Sum of nuclear contributions
"E_tot": float # Sum of all contributions
},
"dipole_moment": { # Collection of electric dipole moments
id (string): { # Unique identifier, default 'dip-1'
"r_O": array[float], # Gauge origin vector
"vector": array[float], # Total dipole vector
"vector_el": array[float], # Electronic dipole vector
"vector_nuc": array[float], # Nuclear dipole vector
"magnitude": float # Magnitude of total vector
}
},
"quadrupole_moment": { # Collection of electric quadrupole moments
id (string): { # Unique identifier, default 'quad-1'
"r_O": array[float], # Gauge origin vector
"tensor": array[float], # Total quadrupole tensor
"tensor_el": array[float], # Electronic quadrupole tensor
"tensor_nuc": array[float] # Nuclear quadrupole tensor
}
},
"polarizability": { # Collection of polarizabilities
id (string): { # Unique identifier, default 'pol-${frequency}'
"frequency": float, # Perturbation frequency
"r_O": array[float], # Gauge origin vector
"tensor": array[float], # Full polarizability tensor
"isotropic_average": float # Diagonal average
}
},
"magnetizability": { # Collection of magnetizability
id (string): { # Unique identifier, default 'mag-${frequency}'
"frequency": float, # Perturbation frequency
"r_O": array[float], # Gauge origin vector
"tensor": array[float], # Full magnetizability tensor
"tensor_dia": array[float], # Diamagnetic tensor
"tensor_para": array[float], # Paramagnetic tensor
"isotropic_average": float # Diagonal average
}
},
"nmr_shielding": { # Collection of NMR shielding tensors
id (string): { # Unique identifier, default 'nmr-${index}+${symbol}'
"r_O": array[float], # Gauge origin vector
"r_K": array[float], # Nuclear coordinate vector
"tensor": array[float], # Full NMR shielding tensor
"tensor_dia": array[float], # Diamagnetic tensor
"tensor_para": array[float], # Paramagnetic tensor
"diagonalized_tensor": array[float], # Diagonalized tensor used for (an)isotropy
"isotropic_average": float, # Diagonal average
"anisotropy": float # Anisotropy of tensor
}
}
},
"scf_calculation": { # Ground state SCF calculation
"success": bool, # SCF finished successfully
"initial_energy": { # Energy computed from initial orbitals
"E_kin": float, # Kinetic energy
"E_nn": float, # Classical nuclear-nuclear interaction
"E_en": float, # Classical electron-nuclear interaction
"E_ee": float, # Classical electron-electron interaction
"E_next": float, # Classical nuclear-external field interaction
"E_eext": float, # Classical electron-external field interaction
"E_x": float, # Hartree-Fock exact exchange energy
"E_xc": float, # DFT exchange-correlation energy
"E_el": float, # Sum of electronic contributions
"E_nuc": float, # Sum of nuclear contributions
"E_tot": float # Sum of all contributions
},
"scf_solver": { # Details from SCF optimization
"converged": bool, # Optimization converged
"wall_time": float, # Wall time (sec) for SCF optimization
"cycles": array[ # Array of SCF cycles
{ # (one entry per cycle)
"energy_total": float, # Current total energy
"energy_update": float, # Current energy update
"mo_residual": float, # Current orbital residual
"wall_time": float, # Wall time (sec) for SCF cycle
"energy_terms": { # Energy contributions
"E_kin": float, # Kinetic energy
"E_nn": float, # Classical nuclear-nuclear interaction
"E_en": float, # Classical electron-nuclear interaction
"E_ee": float, # Classical electron-electron interaction
"E_next": float, # Classical nuclear-external field interaction
"E_eext": float, # Classical electron-external field interaction
"E_x": float, # Hartree-Fock exact exchange energy
"E_xc": float, # DFT exchange-correlation energy
"E_el": float, # Sum of electronic contributions
"E_nuc": float, # Sum of nuclear contributions
"E_tot": float # Sum of all contributions
}
}
]
}
},
"rsp_calculations": { # Collection of response calculations
id (string): { # Response identifier, e.g. 'ext_el-${frequency}'
"success": bool, # Response finished successfully
"frequency": float, # Frequency of perturbation
"perturbation": string, # Name of perturbation operator
"components": array[ # Array of operator components
{ # (one entry per Cartesian direction)
"rsp_solver": { # Details from response optimization
"wall_time": float, # Wall time (sec) for response calculation
"converged": bool, # Optimization converged
"cycles": array[ # Array of response cycles
{ # (one entry per cycle)
"symmetric_property": float, # Property computed from perturbation operator
"property_update": float, # Current symmetric property update
"mo_residual": float, # Current orbital residual
"wall_time": float # Wall time (sec) for response cycle
}
]
}
}
]
}
}
}

16 changes: 16 additions & 0 deletions doc/users/program_json.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----------------------------
The program input/output file
-----------------------------


Input schema
------------

.. literalinclude:: schema_input.json
:language: JSON

Output schema
-------------

.. literalinclude:: schema_output.json
:language: JSON
4 changes: 4 additions & 0 deletions doc/users/user_out.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
----------------
User output file
----------------

4 changes: 2 additions & 2 deletions external/upstream/fetch_nlohmann_json.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
find_package(nlohmann_json 3.5 CONFIG QUIET)
find_package(nlohmann_json 3.6 CONFIG QUIET)

if(TARGET nlohmann_json::nlohmann_json)
get_target_property(
Expand All @@ -13,7 +13,7 @@ else()
FetchContent_Declare(nlohmann_json_sources
QUIET
URL
https://github.com/nlohmann/json/archive/v3.5.0.tar.gz
https://github.com/nlohmann/json/archive/v3.6.1.tar.gz
)

FetchContent_GetProperties(nlohmann_json_sources)
Expand Down
2 changes: 1 addition & 1 deletion pilot/mrchem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ using namespace mrchem;

int main(int argc, char **argv) {
mpi::initialize();
const auto json_input = mrenv::fetch_input(argc, argv);
const auto json_input = mrenv::fetch_json(argc, argv);
mrenv::initialize(json_input);

Timer timer;
Expand Down
5 changes: 3 additions & 2 deletions pilot/mrchem.in
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def main():

# now that all keywords have sensible values,
# we can translate user input into program input
program_dict = translate_input(user_dict)
program_dict = {}
program_dict["input"] = translate_input(user_dict)

inp_name, ext_ext = os.path.splitext(inp_file_cmd)
xfile = inp_name + '.json'
Expand All @@ -70,7 +71,7 @@ def main():

if not dryrun:
cmd = executable + ' ' + xfile
if program_dict["printer"]["print_input"]:
if program_dict["input"]["printer"]["print_input"]:
subprocess.call('cat ' + inp_file_cmd, shell=True)
subprocess.call(cmd, shell=True)

Expand Down
105 changes: 41 additions & 64 deletions src/chemistry/Molecule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,6 @@ Molecule::Molecule(const std::vector<std::string> &coord_str, int c, int m)
readCoordinateString(coord_str);
}

void Molecule::initNuclearProperties(int nNucs) {
for (auto k = 0; k < nNucs; k++) nmr.push_back(nullptr);
}

void Molecule::initPerturbedOrbitals(bool dynamic) {
if (dynamic) {
this->orbitals_x = std::make_shared<OrbitalVector>();
Expand All @@ -86,54 +82,6 @@ void Molecule::initPerturbedOrbitals(bool dynamic) {
}
}

/** @brief Return property SCFEnergy */
SCFEnergy &Molecule::getSCFEnergy() {
if (energy == nullptr) energy = std::make_unique<SCFEnergy>();
return *energy;
}

/** @brief Return property DipoleMoment */
DipoleMoment &Molecule::getDipoleMoment() {
if (dipole == nullptr) dipole = std::make_unique<DipoleMoment>();
return *dipole;
}

/** @brief Return property QuadrupoleMoment */
QuadrupoleMoment &Molecule::getQuadrupoleMoment() {
if (quadrupole == nullptr) quadrupole = std::make_unique<QuadrupoleMoment>();
return *quadrupole;
}

/** @brief Return property Magnetizability */
Magnetizability &Molecule::getMagnetizability() {
if (magnetizability == nullptr) magnetizability = std::make_unique<Magnetizability>();
return *magnetizability;
}

/** @brief Return property NMRShielding */
NMRShielding &Molecule::getNMRShielding(int k) {
if (nmr.size() == 0) initNuclearProperties(getNNuclei());
if (nmr[k] == nullptr) nmr[k] = std::make_unique<NMRShielding>(k, nuclei[k]);
return *nmr[k];
}

/** @brief Return property Polarizability */
Polarizability &Molecule::getPolarizability(double omega) {
auto idx = -1;
for (auto i = 0; i < polarizability.size(); i++) {
auto omega_i = polarizability[i]->getFrequency();
if (std::abs(omega_i - omega) < mrcpp::MachineZero) {
idx = i;
break;
}
}
if (idx < 0) {
polarizability.push_back(std::make_unique<Polarizability>(omega));
idx = polarizability.size() - 1;
}
return *polarizability[idx];
}

/** @brief Return number of electrons */
int Molecule::getNElectrons() const {
auto totZ = 0;
Expand Down Expand Up @@ -253,31 +201,60 @@ void Molecule::printGeometry() const {
o_sym << std::setw(w2) << nuc.getElement().getSymbol();
print_utils::coord(0, o_sym.str(), nuc.getCoord());
}

mrcpp::print::separator(0, '-');
print_utils::coord(0, "Center of mass", calcCenterOfMass());
print_utils::coord(0, "Gauge origin", getGaugeOrigin());
mrcpp::print::separator(0, '=', 2);
}

void Molecule::printEnergies(const std::string &txt) const {
energy.print(txt);
epsilon.print(txt);
}

/** @brief Pretty output of molecular properties
*
* Only properties that have been initialized will be printed.
*/
void Molecule::printProperties() const {
const auto &Phi = getOrbitals();
const auto &F_mat = getFockMatrix();
orbital::print_eigenvalues(Phi, F_mat);
// For each std::map entry (first = id string, second = property)
// this will print the property with its id appearing in the header
for (const auto &dip : dipole) dip.second.print(dip.first);
for (const auto &qua : quadrupole) qua.second.print(qua.first);
for (const auto &pol : polarizability) pol.second.print(pol.first);
for (const auto &mag : magnetizability) mag.second.print(mag.first);
for (const auto &nmr : nmr_shielding) nmr.second.print(nmr.first);
}

if (this->energy != nullptr) this->energy->print();
if (this->dipole != nullptr) this->dipole->print();
if (this->quadrupole != nullptr) this->quadrupole->print();
for (auto &pol : this->polarizability) {
if (pol != nullptr) pol->print();
}
if (this->magnetizability != nullptr) this->magnetizability->print();
for (auto &nmr_k : this->nmr) {
if (nmr_k != nullptr) nmr_k->print();
nlohmann::json Molecule::json() const {
nlohmann::json json_out;
json_out["geometry"] = {};
for (auto i = 0; i < getNNuclei(); i++) {
const auto &nuc = getNuclei()[i];
nlohmann::json json_atom;
json_atom["symbol"] = nuc.getElement().getSymbol();
json_atom["xyz"] = nuc.getCoord();
json_out["geometry"].push_back(json_atom);
}
json_out["charge"] = getCharge();
json_out["multiplicity"] = getMultiplicity();
json_out["center_of_mass"] = calcCenterOfMass();

json_out["scf_energy"] = energy.json();
json_out["orbital_energies"] = epsilon.json();
if (not dipole.empty()) json_out["dipole_moment"] = {};
if (not quadrupole.empty()) json_out["quadrupole_moment"] = {};
if (not polarizability.empty()) json_out["polarizability"] = {};
if (not magnetizability.empty()) json_out["magnetizability"] = {};
if (not nmr_shielding.empty()) json_out["nmr_shielding"] = {};
for (const auto &dip : dipole) json_out["dipole_moment"][dip.first] = dip.second.json();
for (const auto &qua : quadrupole) json_out["quadrupole_moment"][qua.first] = qua.second.json();
for (const auto &pol : polarizability) json_out["polarizability"][pol.first] = pol.second.json();
for (const auto &mag : magnetizability) json_out["magnetizability"][mag.first] = mag.second.json();
for (const auto &nmr : nmr_shielding) json_out["nmr_shielding"][nmr.first] = nmr.second.json();

return json_out;
}

} // namespace mrchem
Loading

0 comments on commit 18bc949

Please sign in to comment.