Loading...
Searching...
No Matches
record_and_export.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * export_and_record_mean_field() — the single call both flavors make.
5 *
6 * Does two things for a finished run:
7 *
8 * 1. runs the requested exporter ("none" resolves to the Null object, so no
9 * call site branches on the format name); and
10 * 2. when an export was actually requested, also drops the format-agnostic
11 * MeanFieldRecord into `<prefix>.save/mean-field.q3mf`, so the same run
12 * can later be converted to a *different* format with `q3_utility convert`
13 * instead of being recomputed.
14 *
15 * The record is deliberately NOT written when `mean_field_export` is "none".
16 * It duplicates the wavefunctions (which `wfc*.dat` already stores, in a
17 * write-only QE-compatible format with no reader), so writing it
18 * unconditionally would roughly double every run's output for users who never
19 * convert anything. Tying it to "the user asked for a mean-field export" costs
20 * nothing to everyone else, needs no new control key, and still covers the case
21 * the converter exists for -- exporting to a second format later. Teaching the
22 * converter to read `wfc*.dat` and `charge-density.dat` directly would retire
23 * the duplication entirely; that is the natural follow-up, and needs a reader
24 * for those two formats first.
25 *
26 * A failure to write the record must not lose the export the user asked for, so
27 * it is logged rather than thrown: WFN/RHO/vxc.dat are already on disk by then.
28 */
29
30#include <string>
31#include <utility>
32
33#include "io/input/data.hpp"
34#include "io/logger.hpp"
39
40namespace q3 {
41
43 const InputData& input,
44 const std::string& directory) {
45 create_mean_field_exporter(input.mean_field_export)->export_to_directory(record, directory);
46
47 if (input.mean_field_export.empty() || input.mean_field_export == "none")
48 return;
49
50 const std::string record_path = directory + "/" + input.prefix + ".save/mean-field.q3mf";
51 try {
52 write_mean_field_record(record, record_path);
53 log::info(" Wrote mean-field record: {}", record_path);
54 } catch (const std::exception& error) {
55 log::warn(" mean-field record not written ({}); the exported files above are "
56 "unaffected, but `q3_utility convert` will not be able to re-convert this run.",
57 error.what());
58 }
59}
60
61/**
62 * Topology-aware entry point -- the one both flavors call.
63 *
64 * The exporter serializes the state it can see; it has no gather step. Under
65 * `q3_mpi`, EVERY multi-rank topology distributes that state, so no single
66 * rank holds a complete mean field. Both halves were established empirically
67 * by running a 2-rank silicon export (2x2x2 shifted grid, 2 IBZ k-points) and
68 * reading the files back:
69 *
70 * - K-point pools (number_of_pools() > 1) distribute k-points: each pool
71 * holds only its own. Measured: a 2-pool run wrote a WELL-FORMED WFN with
72 * 1 of 2 k-points and its weight renormalized to 1.0 -- exactly the shape
73 * a BerkeleyGW consumer cannot detect.
74 *
75 * - Band groups (group_size() > 1) distribute the plane-wave coefficients
76 * themselves: each rank stores a G-slab of every band, not the full
77 * sphere. Measured: rank 0 held 809 of silicon's 1546 plane waves at
78 * k-point 1, and the pre-guard export wrote those 809-coefficient slabs
79 * into full-length records with both ranks interleaving onto the same
80 * file -- corrupted Fortran framing, with the run still exiting 0.
81 *
82 * So the export happens only when one rank holds everything: exactly one pool
83 * AND exactly one rank in it. Serial policies report precisely that, so the
84 * serial binaries take the writing path unconditionally and there is exactly
85 * one code path to reason about. Everything else refuses loudly. The checkpoint
86 * is the way around it: with one rank per pool every rank holds its k-points
87 * whole, so a pooled SCF writes the same `<prefix>.save/` a serial one does
88 * (see write_checkpoints()), and a serial non-self-consistent run restores it
89 * and exports. The export itself still has no gather, which is why it does not
90 * follow the checkpoint onto pools.
91 *
92 * The refusal is decided from topology data that is IDENTICAL on every rank,
93 * before any collective operation. That is load-bearing: `build_record` runs
94 * a rho_r_to_g FFT that is a pool-collective under MPI, so any structure in
95 * which ranks disagree about reaching it deadlocks (measured, the first
96 * version of this guard did).
97 *
98 * `build_record` is a callable so refusing topologies skip the record
99 * assembly entirely rather than building a record nobody may write.
100 */
101template <typename RecordBuilder>
103 const execution::PlanewaveExecutionPolicy& execution_policy,
104 RecordBuilder&& build_record,
105 const InputData& input,
106 const std::string& directory) {
107 const bool one_rank_holds_the_complete_mean_field =
108 execution_policy.one_process_owns_complete_state();
109
110 if (!one_rank_holds_the_complete_mean_field) {
111 const bool export_was_requested =
112 !input.mean_field_export.empty() && input.mean_field_export != "none";
113 if (execution_policy.is_root() && export_was_requested) {
114 log::error("mean_field_export is not supported under MPI: k-point pools hold only "
115 "their own k-points and band groups hold only a G-vector slab of each "
116 "band, so no rank sees the complete mean field. Nothing was exported. "
117 "Request it from a serial non_self_consistent_field run that restores "
118 "the checkpoint (written when every rank has its own k-point pool), or "
119 "run the calculation with a serial binary.");
120 }
121 return;
122 }
123
124 export_and_record_mean_field(std::forward<RecordBuilder>(build_record)(), input, directory);
125}
126
127} // namespace q3
virtual bool is_root() const =0
virtual bool one_process_owns_complete_state() const =0
void warn(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:187
void error(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:192
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
void export_and_record_mean_field(const MeanFieldRecord &record, const InputData &input, const std::string &directory)
void write_mean_field_record(const MeanFieldRecord &record, const std::string &file_path)
Serializes record to file_path. Throws on any write failure.
void export_and_record_mean_field_under_topology(const execution::PlanewaveExecutionPolicy &execution_policy, RecordBuilder &&build_record, const InputData &input, const std::string &directory)
std::unique_ptr< MeanFieldExporter > create_mean_field_exporter(const std::string &mean_field_export)
Definition factory.hpp:27
Parsed input data — holds raw values read from input files.
Definition data.hpp:27
std::string mean_field_export
Definition data.hpp:219
std::string prefix
Definition data.hpp:178