114 bool include_kpoint_fields)
116 , stitle_(std::move(stitle))
117 , include_kpoint_fields_(include_kpoint_fields) {}
127 const std::array<int32_t, 3>& projector_dimensions)
129 , stitle_(std::move(stitle))
130 , include_kpoint_fields_(true)
131 , projector_dimensions_(projector_dimensions) {}
137 const std::array<int32_t, 3>&) =
delete;
163 [[nodiscard]]
static std::pair<std::array<int32_t, 3>, std::array<double, 3>>
165 const std::vector<Real>& kpoint_weights,
166 const std::vector<SymmetryOperation>& symmetry_operations) {
167 std::vector<Vector3D> star;
168 star.reserve(2 * kpoint_coordinates.size() * (symmetry_operations.size() + 1));
169 for (
const Vector3D& kpoint : kpoint_coordinates) {
170 star.push_back(kpoint);
171 star.push_back(-kpoint);
172 for (
const auto& operation : symmetry_operations) {
174 for (
int row = 0; row < 3; row++)
175 for (
int column = 0; column < 3; column++)
176 rotated[row] += operation.reciprocal_rotation[row][column] * kpoint[column];
177 star.push_back(rotated);
178 star.push_back(-rotated);
182 if (star_grid.first[0] > 0)
186 const auto& [dimensions, shifts] = listed_grid;
187 const bool equal_weights =
188 std::all_of(kpoint_weights.begin(), kpoint_weights.end(), [&](
Real weight) {
189 return weight == kpoint_weights.front();
192 static_cast<int64_t
>(kpoint_coordinates.size()) ==
193 static_cast<int64_t
>(dimensions[0]) * dimensions[1] * dimensions[2])
195 return {{0, 0, 0}, {0.0, 0.0, 0.0}};
207 [[nodiscard]]
static std::pair<std::array<int32_t, 3>, std::array<double, 3>>
209 constexpr int maximum_grid_dimension = 1000;
211 auto distance_from_integer = [](
Real value) {
return value - std::round(value); };
212 const std::pair<std::array<int32_t, 3>, std::array<double, 3>> unset = {{0, 0, 0},
215 std::array<int32_t, 3> dimensions = {0, 0, 0};
216 std::array<double, 3> shifts = {0.0, 0.0, 0.0};
217 if (kpoint_coordinates.empty())
219 for (
int axis = 0; axis < 3; axis++) {
220 for (
int grid_dimension = 1; grid_dimension <= maximum_grid_dimension;
223 distance_from_integer(grid_dimension * kpoint_coordinates.front()[axis]);
224 const bool on_grid = std::all_of(
225 kpoint_coordinates.begin(), kpoint_coordinates.end(), [&](
const Vector3D& k) {
226 return std::abs(distance_from_integer(grid_dimension * k[axis] - offset)) <=
230 dimensions[axis] = grid_dimension;
231 shifts[axis] = offset <= -0.5 + tolerance ? offset + 1.0 : offset;
235 if (dimensions[axis] == 0)
239 std::set<std::array<int64_t, 3>> occupied_points;
240 for (
const Vector3D& kpoint : kpoint_coordinates) {
241 std::array<int64_t, 3> index{};
242 for (
int axis = 0; axis < 3; axis++) {
243 const int64_t on_grid =
244 std::llround(dimensions[axis] * kpoint[axis] - shifts[axis]);
245 index[axis] = ((on_grid % dimensions[axis]) + dimensions[axis]) % dimensions[axis];
247 occupied_points.insert(index);
249 if (
static_cast<int64_t
>(occupied_points.size()) !=
250 static_cast<int64_t
>(dimensions[0]) * dimensions[1] * dimensions[2])
252 return {dimensions, shifts};
256 void write(std::ofstream& output_file)
const {
259 write_title_record(output_file);
260 write_dimension_record(output_file,
static_cast<int>(symmetry_operations.size()));
261 write_grid_record(output_file);
262 write_direct_lattice_record(output_file);
263 write_reciprocal_lattice_record(output_file);
264 write_symmetry_rotation_record(output_file, symmetry_operations);
265 write_symmetry_translation_record(output_file, symmetry_operations);
266 write_atom_record(output_file);
272 bool include_kpoint_fields_;
274 std::optional<std::array<int32_t, 3>> projector_dimensions_;
283 [[nodiscard]]
int cell_symmetry()
const {
295 [[nodiscard]]
static Matrix3x3 metric_tensor(
const Matrix3x3& vectors,
Real scale) {
297 Real scale_squared = scale * scale;
298 for (
int row = 0; row < 3; row++) {
299 for (
int column = 0; column < 3; column++) {
300 result[row][column] = vectors[row].dot(vectors[column]) * scale_squared;
310 static void write_padded_string(
char* destination,
const std::string& text,
size_t length) {
311 size_t copy_length = std::min(text.size(), length);
312 std::memcpy(destination, text.data(), copy_length);
313 std::memset(destination + copy_length,
' ', length - copy_length);
322 void write_title_record(std::ofstream& output_file)
const {
323 std::vector<char> buffer(96,
' ');
324 write_padded_string(buffer.data(), stitle_, 32);
326 std::time_t now = std::time(
nullptr);
327 std::tm local_time{};
328 localtime_r(&now, &local_time);
330 std::strftime(date_text,
sizeof(date_text),
"%d-%b-%Y", &local_time);
332 std::strftime(time_text,
sizeof(time_text),
"%H:%M:%S", &local_time);
334 write_padded_string(buffer.data() + 32, date_text, 32);
335 write_padded_string(buffer.data() + 64, time_text, 32);
338 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size()));
350 void write_dimension_record(std::ofstream& output_file,
351 int number_of_symmetry_operations)
const {
354 int32_t number_of_symmetry_operations_i32 = number_of_symmetry_operations;
355 int32_t cell_symmetry_value = cell_symmetry();
359 size_t fixed_size = 4 *
sizeof(int32_t) +
sizeof(int32_t) +
sizeof(double);
360 size_t kpoint_fields_size =
361 include_kpoint_fields_ ? (3 *
sizeof(int32_t) +
sizeof(
double)) : 0;
362 if (projector_dimensions_) {
363 kpoint_fields_size += 2 *
sizeof(int32_t);
365 std::vector<char> buffer(fixed_size + kpoint_fields_size);
368 auto append = [&](
const void* value,
size_t size) {
369 std::memcpy(buffer.data() + offset, value, size);
372 append(&number_of_spin_components,
sizeof(int32_t));
373 append(&number_of_g_vectors,
sizeof(int32_t));
374 append(&number_of_symmetry_operations_i32,
sizeof(int32_t));
375 append(&cell_symmetry_value,
sizeof(int32_t));
376 append(&number_of_atoms,
sizeof(int32_t));
377 append(&charge_density_cutoff_ry,
sizeof(
double));
379 if (include_kpoint_fields_) {
383 int32_t maximum_plane_waves = plane_waves_per_kpoint.empty()
385 : *std::max_element(plane_waves_per_kpoint.begin(),
386 plane_waves_per_kpoint.end());
389 append(&number_of_kpoints,
sizeof(int32_t));
390 if (projector_dimensions_) {
391 append(projector_dimensions_->data(), 3 *
sizeof(int32_t));
393 append(&number_of_bands,
sizeof(int32_t));
395 append(&maximum_plane_waves,
sizeof(int32_t));
396 append(&wavefunction_cutoff_ry,
sizeof(
double));
400 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size()));
419 void write_grid_record(std::ofstream& output_file)
const {
424 if (!include_kpoint_fields_) {
425 record_format_.
write_record(output_file, fft_dimensions,
sizeof(fft_dimensions));
431 int32_t kpoint_grid_dimensions[3] = {
432 grid_dimensions[0], grid_dimensions[1], grid_dimensions[2]};
433 double kpoint_shifts[3] = {
434 0.5 * grid_shifts[0], 0.5 * grid_shifts[1], 0.5 * grid_shifts[2]};
435 if (grid_dimensions[0] == 0 && grid_dimensions[1] == 0 && grid_dimensions[2] == 0) {
438 for (
int axis = 0; axis < 3; axis++) {
439 kpoint_grid_dimensions[axis] = inferred_dimensions[axis];
440 kpoint_shifts[axis] = inferred_shifts[axis];
442 if (inferred_dimensions[0] == 0)
443 log::warn(
" BerkeleyGW header: kgrid left at 0 0 0 -- the k-point list is "
444 "neither a complete grid nor the symmetry-reduced wedge of one, so "
445 "BerkeleyGW's epsilon will not accept this WFN. List every point of "
446 "the grid instead.");
449 std::vector<char> buffer(
sizeof(fft_dimensions) +
sizeof(kpoint_grid_dimensions) +
450 sizeof(kpoint_shifts));
452 std::memcpy(buffer.data() + offset, fft_dimensions,
sizeof(fft_dimensions));
453 offset +=
sizeof(fft_dimensions);
454 std::memcpy(buffer.data() + offset, kpoint_grid_dimensions,
sizeof(kpoint_grid_dimensions));
455 offset +=
sizeof(kpoint_grid_dimensions);
456 std::memcpy(buffer.data() + offset, kpoint_shifts,
sizeof(kpoint_shifts));
459 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size()));
472 void write_direct_lattice_record(std::ofstream& output_file)
const {
475 Matrix3x3 direct_metric = metric_tensor(lattice_vectors, lattice_parameter);
477 std::vector<double> buffer;
480 buffer.push_back(lattice_parameter);
481 for (
int vector_index = 0; vector_index < 3; vector_index++) {
482 buffer.push_back(lattice_vectors[vector_index].x);
483 buffer.push_back(lattice_vectors[vector_index].y);
484 buffer.push_back(lattice_vectors[vector_index].z);
486 for (
int column = 0; column < 3; column++) {
487 for (
int row = 0; row < 3; row++) {
488 buffer.push_back(direct_metric[row][column]);
493 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size() *
sizeof(
double)));
502 void write_reciprocal_lattice_record(std::ofstream& output_file)
const {
505 Matrix3x3 reciprocal_metric =
506 metric_tensor(reciprocal_lattice_vectors, two_pi_over_lattice_parameter);
508 std::vector<double> buffer;
511 buffer.push_back(two_pi_over_lattice_parameter);
512 for (
int vector_index = 0; vector_index < 3; vector_index++) {
513 buffer.push_back(reciprocal_lattice_vectors[vector_index].x);
514 buffer.push_back(reciprocal_lattice_vectors[vector_index].y);
515 buffer.push_back(reciprocal_lattice_vectors[vector_index].z);
517 for (
int column = 0; column < 3; column++) {
518 for (
int row = 0; row < 3; row++) {
519 buffer.push_back(reciprocal_metric[row][column]);
524 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size() *
sizeof(
double)));
545 void write_symmetry_rotation_record(
546 std::ofstream& output_file,
547 const std::vector<SymmetryOperation>& symmetry_operations)
const {
548 std::vector<int32_t> buffer;
549 buffer.reserve(9 * symmetry_operations.size());
550 for (
const auto& operation : symmetry_operations) {
551 for (
int row = 0; row < 3; row++) {
552 for (
int column = 0; column < 3; column++) {
553 buffer.push_back(operation.rotation[row][column]);
558 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size() *
sizeof(int32_t)));
591 void write_symmetry_translation_record(
592 std::ofstream& output_file,
593 const std::vector<SymmetryOperation>& symmetry_operations)
const {
594 constexpr Real boundary_tolerance = 1.0e-6;
595 std::vector<double> buffer;
596 buffer.reserve(3 * symmetry_operations.size());
597 for (
const auto& operation : symmetry_operations) {
598 Real rotated_translation[3];
599 for (
int component = 0; component < 3; component++) {
601 for (
int contracted = 0; contracted < 3; contracted++) {
602 sum -=
static_cast<Real>(operation.reciprocal_rotation[contracted][component]) *
603 operation.fractional_translation[contracted];
605 rotated_translation[component] = sum;
607 for (
int component = 0; component < 3; component++) {
608 Real& value = rotated_translation[component];
609 if (value >= boundary_tolerance + 0.5)
610 value -= std::trunc(value + 0.5);
611 if (value < boundary_tolerance - 0.5)
612 value -= std::trunc(value - 0.5);
614 for (
int component = 0; component < 3; component++) {
619 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size() *
sizeof(
double)));
633 void write_atom_record(std::ofstream& output_file)
const {
635 std::vector<char> buffer(number_of_atoms * (3 *
sizeof(
double) +
sizeof(int32_t)));
638 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
640 double position_components[3] = {position.x, position.y, position.z};
641 std::memcpy(buffer.data() + offset, position_components,
sizeof(position_components));
642 offset +=
sizeof(position_components);
645 std::memcpy(buffer.data() + offset, &atomic_number,
sizeof(atomic_number));
646 offset +=
sizeof(atomic_number);
650 output_file, buffer.data(),
static_cast<int32_t
>(buffer.size()));