60 std::cout <<
colorizer.heading(
"Compile-time features:") << std::endl;
62 std::istringstream stream(FEATURES_STRING);
64 while (std::getline(stream, line)) {
65 if (line.find(
": enabled") != std::string::npos) {
67 size_t colonPos = line.find(
": ");
68 if (colonPos != std::string::npos) {
69 std::string featureName = line.substr(0, colonPos + 2);
70 std::string status = line.substr(colonPos + 2);
71 std::cout << featureName <<
colorizer.warning(status) << std::endl;
73 std::cout << line << std::endl;
75 }
else if (line.find(
": disabled") != std::string::npos) {
77 size_t colonPos = line.find(
": ");
78 if (colonPos != std::string::npos) {
79 std::string featureName = line.substr(0, colonPos + 2);
80 std::string status = line.substr(colonPos + 2);
81 std::cout << featureName <<
colorizer.error(status) << std::endl;
83 std::cout << line << std::endl;
86 std::cout << line << std::endl;
94 bool sflag =
false, mflag =
false, pflag =
false, cflag =
false;
95 double optConvergedForce = 0.001;
96 std::string potential;
98 std::string confileout;
99 std::string optimizer(
"cg");
100 std::optional<std::string> config_path;
102#ifdef WITH_SERVE_MODE
103 std::optional<std::string> serve_spec;
104 std::optional<std::string> serve_host(
"localhost");
105 std::optional<uint16_t> serve_port(12345);
106 std::optional<size_t> replicas(1);
107 std::optional<bool> gateway(
false);
112 const char *progname = (argc ? argv[0] :
"eonclient");
116 parser.add(Option(
"--help",
"-h")
117 .help(
"show this help message and exit")
120 auto helpText = parser.formatHelp(progname);
121 std::cout <<
colorizer.heading(
"eOn Client - Help")
123 std::cout << helpText;
124 std::exit(EXIT_SUCCESS);
127 parser.add(Option(
"--version",
"-v")
128 .help(
"Print version information")
130 std::cout << VERSION_STRING << std::endl;
131 std::exit(EXIT_SUCCESS);
135 Option(
"--features").help(
"Print compile-time features").handler([&]() {
137 std::exit(EXIT_SUCCESS);
140 parser.add(Option(
"--minimize",
"-m")
141 .help(
"Minimization of inputConfile saves to outputConfile")
142 .handler([&]() { mflag =
true; }));
144 parser.add(Option(
"--single",
"-s")
145 .help(
"Single point energy of inputConfile")
146 .handler([&]() { sflag =
true; }));
148 parser.add(Option(
"--compare",
"-c")
149 .help(
"Compare structures of inputConfile to outputConfile")
150 .handler([&]() { cflag =
true; }));
153 Option(
"--optimizer",
"-o")
155 .help(
"Optimization method")
156 .handler([&](
const std::string_view &value) { optimizer = value; }));
158 parser.add(Option(
"--force",
"-f")
160 .help(
"Convergence force")
161 .handler([&](
const std::string_view &value) {
162 optConvergedForce = parseFloatingPoint<double>(value);
165 parser.add(Option(
"--tolerance",
"-t")
167 .help(
"Distance tolerance")
168 .handler([&](
const std::string_view &value) {
169 params.structure_comparison_options.distance_difference =
170 parseFloatingPoint<double>(value);
173 parser.add(Option(
"--potential",
"-p")
174 .argName(
"POTENTIAL")
175 .help(
"The potential (e.g. qsc, lj, eam_al)")
176 .handler([&](
const std::string_view &value) {
181#ifdef WITH_SERVE_MODE
185 .help(
"Serve potential(s) over rgpot Cap'n Proto RPC. "
186 "Spec: 'potential:port' or 'pot1:port1,pot2:port2'")
187 .handler([&](
const std::string_view &value) { serve_spec = value; }));
190 Option(
"--serve-host")
192 .help(
"Host to bind RPC server(s) to")
193 .handler([&](
const std::string_view &value) { serve_host = value; }));
195 parser.add(Option(
"--serve-port")
197 .help(
"Port for single-potential serve mode (used with -p)")
198 .handler([&](
const std::string_view &value) {
199 serve_port = parseIntegral<uint16_t>(value);
202 parser.add(Option(
"--replicas")
204 .help(
"Number of replicated server instances (used with -p)")
205 .handler([&](
const std::string_view &value) {
206 replicas = parseIntegral<size_t>(value);
209 parser.add(Option(
"--gateway")
210 .help(
"Run a single gateway port backed by N pool instances "
211 "(use with -p and --replicas)")
212 .handler([&]() { gateway =
true; }));
214 parser.add(Option(
"--config")
216 .help(
"Config file for potential parameters (INI format, "
217 "e.g. [Metatomic] model_path=model.pt)")
218 .handler([&](
const std::string_view &value) {
227 Positional(
"confile")
228 .help(
"Input structure file")
229 .occurs(zeroOrOneTime)
230 .handler([&](
const std::string_view &value) { confile = value; }));
233 Positional(
"confileout")
234 .help(
"Output structure file (optional)")
235 .occurs(zeroOrOneTime)
236 .handler([&](
const std::string_view &value) { confileout = value; }));
239 parser.parse(argc, argv);
240 }
catch (
const ParsingException &ex) {
241 std::cerr <<
colorizer.error(ex.message()) <<
'\n';
242 std::cerr <<
colorizer.warning(parser.formatUsage(progname)) <<
'\n';
243 std::exit(EXIT_FAILURE);
246 if (sflag && mflag) {
248 "Cannot specify both minimization and single point\n");
249 std::exit(EXIT_FAILURE);
252 if (!pflag && (sflag || mflag)) {
253 std::cerr <<
colorizer.error(
"Must specify a potential\n");
254 std::exit(EXIT_FAILURE);
257 if (cflag && confileout.empty()) {
259 "Comparison needs two structure files: the input con file and the "
260 "one to compare it against\n");
261 std::cerr <<
colorizer.warning(parser.formatUsage(progname)) <<
'\n';
262 std::exit(EXIT_FAILURE);
265#ifdef WITH_SERVE_MODE
268 if (config_path.has_value()) {
269 std::ifstream config_file(config_path.value());
270 if (!config_file.is_open()) {
271 std::cerr <<
colorizer.error(
"Cannot open config file: ")
272 << config_path.value() <<
'\n';
273 std::exit(EXIT_FAILURE);
275 params.load(config_path.value());
279 if (serve_spec.has_value()) {
281 if (endpoints.empty()) {
282 std::cerr <<
colorizer.error(
"No valid serve endpoints in spec: ")
283 << serve_spec.value() <<
'\n';
284 std::exit(EXIT_FAILURE);
287 std::exit(EXIT_SUCCESS);
291 if (pflag && !sflag && !mflag && !cflag &&
292 (serve_port.has_value() || replicas.has_value() || gateway.has_value())) {
293 for (
auto &ch : potential) {
294 ch = std::tolower(
static_cast<unsigned char>(ch));
296 params.potential_options.potential =
297 magic_enum::enum_cast<PotType>(potential, magic_enum::case_insensitive)
299 auto host = serve_host.value_or(
"localhost");
300 auto port = serve_port.value_or(12345);
301 auto reps = replicas.value_or(1);
302 bool gw = gateway.value_or(
false);
306 }
else if (reps > 1) {
311 std::exit(EXIT_SUCCESS);
315 if (!pflag && !sflag && !mflag && !cflag && config_path.has_value() &&
316 !serve_spec.has_value() &&
317 (!params.serve_options.endpoints.empty() ||
318 params.serve_options.gateway_port > 0 ||
319 params.serve_options.replicas > 1)) {
321 std::exit(EXIT_SUCCESS);
326 if (confile.empty()) {
328 "At least one non-option argument is required: the con file\n");
329 std::cerr <<
colorizer.warning(parser.formatUsage(progname)) <<
'\n';
330 std::exit(EXIT_FAILURE);
334 for (
auto &ch : potential) {
335 ch = std::tolower(
static_cast<unsigned char>(ch));
340 params.potential_options.potential =
341 magic_enum::enum_cast<PotType>(potential, magic_enum::case_insensitive)
346 params.optimizer_options.method =
347 magic_enum::enum_cast<OptType>(optimizer, magic_enum::case_insensitive)
349 params.optimizer_options.converged_force = optConvergedForce;
355 params.structure_comparison_options.check_rotation =
true;
359 auto matter = std::make_unique<Matter>(
pot, params);
360 auto matter2 = std::make_unique<Matter>(
pot, params);
362 std::cerr <<
"Failed to load " << confile << std::endl;
363 std::exit(EXIT_FAILURE);
369 minimize(std::move(matter), confileout);
372 std::cerr <<
"Failed to load " << confileout << std::endl;
373 std::exit(EXIT_FAILURE);
375 if (matter->compare(*matter2,
true)) {
376 std::cout <<
"Structures match\n";
378 std::cout <<
colorizer.error(
"Structures do not match\n");