Loading...
Searching...
No Matches
field.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * VolumetricField — one scalar field sampled on a run's real-space grid,
5 * together with the cell and the atoms it belongs to.
6 *
7 * The seam between "where the numbers came from" and "which file format they
8 * are written as". Every source in `io/` that can put a field on the dense FFT
9 * grid — the charge density, the effective potential, a band's probability
10 * density — fills one of these, and every writer under `io/volumetric/` reads
11 * one. A new format is then a writer and nothing else, and a new field is a
12 * filler and nothing else.
13 *
14 * **Grid ordering.** `values` is indexed `(i1 * n2 + i2) * n3 + i3`, which is
15 * how the engine stores every real-space array (both
16 * `FFTGrid::g_vector_to_fft_grid_index` and `FFTGrid::global_real_space_index`
17 * lay a grid out this way): the **third** lattice direction varies fastest.
18 * Point `(i1, i2, i3)` sits at `r = i1/n1 · a1 + i2/n2 · a2 + i3/n3 · a3`, the
19 * grid is periodic, and the boundary point is stored once — a format that
20 * wants it duplicated (XSF does) wraps the index itself.
21 *
22 * **Units.** Lengths are bohr; the field's own unit is whatever `unit` names,
23 * which is electrons/bohr^3 for a density and Rydberg for a potential. Formats
24 * wanting Ångström, or a volume-weighted density (some want both), convert
25 * inside their own writer, so exactly one place per format knows its
26 * conventions.
27 */
28
29#include <array>
30#include <cmath>
31#include <cstddef>
32#include <string>
33#include <vector>
34
37
38namespace q3 {
39
41 /// Grid point counts along a1, a2, a3.
42 std::array<int, 3> grid_dimensions = {0, 0, 0};
43 /// Rows are a1, a2, a3, Cartesian, in bohr.
45
46 /// Element symbols, one per species.
47 std::vector<std::string> species_symbols;
48 /// Valence charge of each species, parallel to `species_symbols`.
49 std::vector<Real> species_valence_charges;
50 /// Species of each atom, an index into `species_symbols`.
51 std::vector<int> atom_species_indices;
52 /// Fractional (crystal) coordinates, parallel to `atom_species_indices`.
53 std::vector<Vector3D> atom_positions_crystal;
54
55 /// The samples themselves, `number_of_grid_points()` of them.
56 std::vector<Real> values;
57 /// Free text for a format's comment line, e.g. "charge density".
58 std::string description;
59 /// The unit `values` is in, e.g. "e/bohr^3".
60 std::string unit;
61 /**
62 * Whether `integral()` is a particle count — true for the charge density
63 * and for a band's probability density, false for a potential.
64 *
65 * Formats disagree about what they hold, and the disagreement is silent:
66 * one format stores the density multiplied by the cell volume, so that
67 * summing it and dividing by the point count gives electrons, while a
68 * potential in the same file shape is stored unscaled. A writer that
69 * cannot honour the distinction refuses the field rather than applying the
70 * wrong one.
71 */
72 bool is_a_density = false;
73
74 [[nodiscard]] int number_of_grid_points() const {
76 }
77
78 [[nodiscard]] int number_of_atoms() const {
79 return static_cast<int>(atom_species_indices.size());
80 }
81
82 [[nodiscard]] Real cell_volume_bohr3() const {
83 return std::abs(lattice_vectors_bohr.determinant());
84 }
85
86 /// The sample at grid point (i1, i2, i3), third index fastest.
87 [[nodiscard]] Real value(int index_1, int index_2, int index_3) const {
88 const std::size_t flat_index =
89 (static_cast<std::size_t>(index_1) * grid_dimensions[1] + index_2) *
91 index_3;
92 return values[flat_index];
93 }
94
95 /**
96 * The field integrated over the cell by the midpoint rule the grid is a
97 * sample of: `Omega / N * sum(values)`.
98 *
99 * For a charge density this is the electron count, which is the one number
100 * that says whether a written file is right — a transposed grid or a
101 * missed unit changes it, and a viewer's isosurface does not.
102 */
103 [[nodiscard]] Real integral() const {
104 Real sum = 0.0;
105 for (Real sample : values)
106 sum += sample;
107 return sum * cell_volume_bohr3() / static_cast<Real>(number_of_grid_points());
108 }
109
110 /// Cartesian position of one atom, in bohr.
111 [[nodiscard]] Vector3D cartesian_position_bohr(int atom_index) const {
112 const Vector3D& fractional = atom_positions_crystal[atom_index];
113 Vector3D position;
114 for (int dimension = 0; dimension < 3; dimension++) {
115 position[dimension] = fractional.x * lattice_vectors_bohr[0][dimension] +
116 fractional.y * lattice_vectors_bohr[1][dimension] +
117 fractional.z * lattice_vectors_bohr[2][dimension];
118 }
119 return position;
120 }
121
122 /// Atomic number of one atom, from its element symbol.
123 [[nodiscard]] int atomic_number(int atom_index) const {
126 }
127
128 /// How many atoms each species has, in species order, for formats that group atoms so.
129 [[nodiscard]] std::vector<int> atoms_per_species() const {
130 std::vector<int> counts(species_symbols.size(), 0);
131 for (int species_index : atom_species_indices) {
132 if (species_index >= 0 && species_index < static_cast<int>(counts.size()))
133 counts[species_index]++;
134 }
135 return counts;
136 }
137};
138
139} // namespace q3
static int atomic_number_by_element_symbol(const std::string &symbol)
double Real
3×3 matrix stored row-major: row i = mat[i]
Real determinant() const
Determinant.
Vector3D cartesian_position_bohr(int atom_index) const
Cartesian position of one atom, in bohr.
Definition field.hpp:111
int number_of_atoms() const
Definition field.hpp:78
std::string unit
The unit values is in, e.g. "e/bohr^3".
Definition field.hpp:60
Matrix3x3 lattice_vectors_bohr
Rows are a1, a2, a3, Cartesian, in bohr.
Definition field.hpp:44
std::vector< int > atom_species_indices
Species of each atom, an index into species_symbols.
Definition field.hpp:51
int number_of_grid_points() const
Definition field.hpp:74
std::vector< Vector3D > atom_positions_crystal
Fractional (crystal) coordinates, parallel to atom_species_indices.
Definition field.hpp:53
std::vector< std::string > species_symbols
Element symbols, one per species.
Definition field.hpp:47
Real integral() const
Definition field.hpp:103
std::vector< Real > species_valence_charges
Valence charge of each species, parallel to species_symbols.
Definition field.hpp:49
std::string description
Free text for a format's comment line, e.g. "charge density".
Definition field.hpp:58
std::vector< int > atoms_per_species() const
How many atoms each species has, in species order, for formats that group atoms so.
Definition field.hpp:129
Real cell_volume_bohr3() const
Definition field.hpp:82
Real value(int index_1, int index_2, int index_3) const
The sample at grid point (i1, i2, i3), third index fastest.
Definition field.hpp:87
std::vector< Real > values
The samples themselves, number_of_grid_points() of them.
Definition field.hpp:56
int atomic_number(int atom_index) const
Atomic number of one atom, from its element symbol.
Definition field.hpp:123
std::array< int, 3 > grid_dimensions
Grid point counts along a1, a2, a3.
Definition field.hpp:42