diff --git a/CHANGELOG.md b/CHANGELOG.md index 338ced5..b2cec34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,24 @@ All notable changes to `libcasm-monte` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [v2.0a1] - 2024-03-15 + +## [2.0a2] - 2024-07-16 + +### Added + +- Added to_json for CompletionCheckParams, SamplingFixtureParams, SamplingParams, jsonResultsIO +- Added "json_quantities" option to SamplingParams +- Added Conversions::species_list() + +### Changed + +- Use shared_ptr to hold sampling fixtures in RunManager +- Output scalar quantities under "value" key in JSON results output +- Allow MethodLog to output to stdout +- Allow constructing libcasm.monte.ValueMap from dict + + +## [2.0a1] - 2024-03-15 The libcasm-monte package provides useful building blocks for Monte Carlo simulations. This includes: diff --git a/include/casm/monte/Conversions.hh b/include/casm/monte/Conversions.hh index 71429a1..89467a4 100644 --- a/include/casm/monte/Conversions.hh +++ b/include/casm/monte/Conversions.hh @@ -100,6 +100,7 @@ class Conversions { Index species_size() const; Index species_index(std::string species_name) const; + std::vector const &species_list() const; xtal::Molecule const &species_to_mol(Index species_index) const; std::string const &species_name(Index species_index) const; Index components_size(Index species_index) const; @@ -122,14 +123,14 @@ class Conversions { Index m_Nasym; std::vector m_unitl_to_asym; - std::vector > m_asym_to_unitl; - std::vector > m_asym_to_b; + std::vector> m_asym_to_unitl; + std::vector> m_asym_to_b; /// m_occ_to_species[asym][occ_index] -> species_index - std::vector > m_occ_to_species; + std::vector> m_occ_to_species; /// m_species_to_occ[asym][species_index] -> occ_index - std::vector > m_species_to_occ; + std::vector> m_species_to_occ; }; } // namespace monte diff --git a/include/casm/monte/MethodLog.hh b/include/casm/monte/MethodLog.hh index c52cf61..106a163 100644 --- a/include/casm/monte/MethodLog.hh +++ b/include/casm/monte/MethodLog.hh @@ -30,6 +30,11 @@ struct MethodLog { log.reset(*fout); } } + + void reset_to_stdout() { + fout.reset(); + log.reset(); + } }; } // namespace monte diff --git a/include/casm/monte/checks/io/json/CompletionCheck_json_io.hh b/include/casm/monte/checks/io/json/CompletionCheck_json_io.hh index ba2761f..edcb4ce 100644 --- a/include/casm/monte/checks/io/json/CompletionCheck_json_io.hh +++ b/include/casm/monte/checks/io/json/CompletionCheck_json_io.hh @@ -18,8 +18,13 @@ template struct CompletionCheckParams; /// \brief Construct CompletionCheckParams from JSON -inline void parse(InputParser> &parser, - StateSamplingFunctionMap const &sampling_functions); +void parse(InputParser> &parser, + StateSamplingFunctionMap const &sampling_functions); + +/// \brief Convert CompletionCheckParams to JSON +jsonParser &to_json( + CompletionCheckParams const &completion_check_params, + jsonParser &json); /// \brief CompletionCheckResults to JSON template @@ -116,17 +121,15 @@ void _parse_components( (parser.self.find_at(option / "component_index") != parser.self.end()); bool has_name = (parser.self.find_at(option / "component_name") != parser.self.end()); - if (has_index && has_name) { - parser.insert_error(option, - "Error: cannot specify both \"component_index\" and " - "\"component_name\""); - } else if (has_index) { + if (has_index) { _parse_component_index(parser, option, function, precision, requested_precision); - } else if (has_name) { + } + if (has_name) { _parse_component_name(parser, option, function, precision, requested_precision); - } else { + } + if (!has_index && !has_name) { // else, converge all components for (Index index = 0; index < function.component_names.size(); ++index) { requested_precision.emplace( @@ -365,6 +368,54 @@ inline void parse(InputParser> &parser, } } +/// \brief Convert CompletionCheckParams to JSON +inline jsonParser &to_json( + CompletionCheckParams const &completion_check_params, + jsonParser &json) { + // TODO: write out calc_statistics_f parameters + // to_json["calc_statistics_f_confidence"] = 0.95; + // to_json["calc_statistics_f_weighted_observations_method"] = 1; + // to_json["calc_statistics_f_n_resamples"] = 10000; + + json["cutoff"] = completion_check_params.cutoff_params; + + json["convergence"] = jsonParser::array(); + for (auto const &pair : completion_check_params.requested_precision) { + auto const &key = pair.first; + auto const &req_prec = pair.second; + + jsonParser tmp; + tmp["quantity"] = key.sampler_name; + + std::vector component_index; + component_index.push_back(key.component_index); + tmp["component_index"] = component_index; + + std::vector component_name; + component_name.push_back(key.component_name); + tmp["component_name"] = component_name; + + to_json(req_prec, tmp); + + json["convergence"].push_back(tmp); + } + + // "spacing" + if (completion_check_params.log_spacing == false) { + json["spacing"] = "linear"; + json["begin"] = completion_check_params.check_begin; + json["period"] = completion_check_params.check_period; + } else { + json["spacing"] = "log"; + json["begin"] = completion_check_params.check_begin; + json["base"] = completion_check_params.check_base; + json["shift"] = completion_check_params.check_shift; + json["period_max"] = completion_check_params.check_period_max; + } + + return json; +} + /// \brief CompletionCheckResults to JSON template jsonParser &to_json(CompletionCheckResults const &value, diff --git a/include/casm/monte/methods/kinetic_monte_carlo.hh b/include/casm/monte/methods/kinetic_monte_carlo.hh index 7e22d9b..8670484 100644 --- a/include/casm/monte/methods/kinetic_monte_carlo.hh +++ b/include/casm/monte/methods/kinetic_monte_carlo.hh @@ -137,9 +137,9 @@ void kinetic_monte_carlo( kmc_data.time = 0.0; kmc_data.atom_positions_cart = occ_location.atom_positions_cart(); kmc_data.prev_atom_positions_cart.clear(); - for (auto &fixture : run_manager.sampling_fixtures) { - kmc_data.prev_time.emplace(fixture.label(), kmc_data.time); - kmc_data.prev_atom_positions_cart.emplace(fixture.label(), + for (auto &fixture_ptr : run_manager.sampling_fixtures) { + kmc_data.prev_time.emplace(fixture_ptr->label(), kmc_data.time); + kmc_data.prev_atom_positions_cart.emplace(fixture_ptr->label(), kmc_data.atom_positions_cart); } diff --git a/include/casm/monte/run_management/ResultsAnalysisFunction.hh b/include/casm/monte/run_management/ResultsAnalysisFunction.hh index e0aa077..431abff 100644 --- a/include/casm/monte/run_management/ResultsAnalysisFunction.hh +++ b/include/casm/monte/run_management/ResultsAnalysisFunction.hh @@ -69,7 +69,8 @@ template std::map make_analysis( Results const &results, ResultsAnalysisFunctionMap const - &analysis_functions); + &analysis_functions, + std::vector analysis_names); // --- Implementation --- @@ -110,9 +111,15 @@ template std::map make_analysis( Results const &results, ResultsAnalysisFunctionMap const - &analysis_functions) { + &analysis_functions, + std::vector analysis_names) { std::map analysis; - for (auto const &pair : analysis_functions) { + for (std::string name : analysis_names) { + auto it = analysis_functions.find(name); + if (it == analysis_functions.end()) { + continue; + } + auto const &pair = *it; auto const &f = pair.second; try { analysis.emplace(f.name, f(results)); diff --git a/include/casm/monte/run_management/RunManager.hh b/include/casm/monte/run_management/RunManager.hh index 8580335..b8cc92d 100644 --- a/include/casm/monte/run_management/RunManager.hh +++ b/include/casm/monte/run_management/RunManager.hh @@ -35,7 +35,7 @@ struct RunManager { std::shared_ptr engine; /// Sampling fixtures - std::vector sampling_fixtures; + std::vector> sampling_fixtures; /// \brief If true, the run is complete if any sampling fixture /// is complete. Otherwise, all sampling fixtures must be @@ -83,13 +83,14 @@ struct RunManager { next_sample_time(0.0), break_point_set(false) { for (auto const ¶ms : _sampling_fixture_params) { - sampling_fixtures.emplace_back(params, engine); + sampling_fixtures.emplace_back( + std::make_shared(params, engine)); } } void initialize(Index steps_per_pass) { - for (auto &fixture : sampling_fixtures) { - fixture.initialize(steps_per_pass); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->initialize(steps_per_pass); } break_point_set = false; } @@ -102,8 +103,8 @@ struct RunManager { // check results bool all_complete = true; bool any_complete = false; - for (auto &fixture : sampling_fixtures) { - if (fixture.is_complete()) { + for (auto &fixture_ptr : sampling_fixtures) { + if (fixture_ptr->is_complete()) { any_complete = true; } else { all_complete = false; @@ -116,32 +117,32 @@ struct RunManager { } void write_status_if_due() { - for (auto &fixture : sampling_fixtures) { - fixture.write_status_if_due(run_index); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->write_status_if_due(run_index); } } void increment_n_accept() { - for (auto &fixture : sampling_fixtures) { - fixture.increment_n_accept(); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->increment_n_accept(); } } void increment_n_reject() { - for (auto &fixture : sampling_fixtures) { - fixture.increment_n_reject(); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->increment_n_reject(); } } void increment_step() { - for (auto &fixture : sampling_fixtures) { - fixture.increment_step(); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->increment_step(); } } void set_time(double event_time) { - for (auto &fixture : sampling_fixtures) { - fixture.set_time(event_time); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->set_time(event_time); } } @@ -151,7 +152,8 @@ struct RunManager { state_type const &state, PreSampleActionType pre_sample_f = PreSampleActionType(), PostSampleActionType post_sample_f = PostSampleActionType()) { - for (auto &fixture : sampling_fixtures) { + for (auto &fixture_ptr : sampling_fixtures) { + auto &fixture = *fixture_ptr; if (fixture.params().sampling_params.sample_mode != SAMPLE_MODE::BY_TIME) { if (fixture.counter().count == fixture.next_sample_count()) { @@ -193,7 +195,8 @@ struct RunManager { void update_next_sampling_fixture() { // update next_sample_time and next_sampling_fixture next_sampling_fixture = nullptr; - for (auto &fixture : sampling_fixtures) { + for (auto &fixture_ptr : sampling_fixtures) { + auto &fixture = *fixture_ptr; if (fixture.params().sampling_params.sample_mode == SAMPLE_MODE::BY_TIME) { if (next_sampling_fixture == nullptr || @@ -210,8 +213,8 @@ struct RunManager { /// Notes: /// - Calls `finalize` for all sampling fixtures void finalize(state_type const &final_state) { - for (auto &fixture : sampling_fixtures) { - fixture.finalize(final_state, run_index); + for (auto &fixture_ptr : sampling_fixtures) { + fixture_ptr->finalize(final_state, run_index); } } }; diff --git a/include/casm/monte/run_management/SamplingFixture.hh b/include/casm/monte/run_management/SamplingFixture.hh index 2f8dc3e..f3e0903 100644 --- a/include/casm/monte/run_management/SamplingFixture.hh +++ b/include/casm/monte/run_management/SamplingFixture.hh @@ -36,6 +36,7 @@ struct SamplingFixtureParams { _analysis_functions, monte::SamplingParams _sampling_params, monte::CompletionCheckParams _completion_check_params, + std::vector _analysis_names, std::unique_ptr _results_io = nullptr, monte::MethodLog _method_log = monte::MethodLog()) : label(_label), @@ -44,6 +45,7 @@ struct SamplingFixtureParams { analysis_functions(_analysis_functions), sampling_params(_sampling_params), completion_check_params(_completion_check_params), + analysis_names(_analysis_names), results_io(std::move(_results_io)), method_log(_method_log) { for (auto const &name : sampling_params.sampler_names) { @@ -84,6 +86,9 @@ struct SamplingFixtureParams { /// Completion check params monte::CompletionCheckParams completion_check_params; + /// Analysis functions to evaluate + std::vector analysis_names; + /// Results I/O implementation -- May be empty notstd::cloneable_ptr results_io; @@ -408,7 +413,8 @@ class SamplingFixture { Log &log = m_params.method_log.log; m_results.elapsed_clocktime = log.time_s(); m_results.completion_check_results = m_completion_check.results(); - m_results.analysis = make_analysis(m_results, m_params.analysis_functions); + m_results.analysis = make_analysis(m_results, m_params.analysis_functions, + m_params.analysis_names); m_results.n_accept = m_counter.n_accept; m_results.n_reject = m_counter.n_reject; diff --git a/include/casm/monte/run_management/io/ResultsIO.hh b/include/casm/monte/run_management/io/ResultsIO.hh index 7289d66..3d4c740 100644 --- a/include/casm/monte/run_management/io/ResultsIO.hh +++ b/include/casm/monte/run_management/io/ResultsIO.hh @@ -3,6 +3,7 @@ #include +#include "casm/casm_io/json/jsonParser.hh" #include "casm/global/definitions.hh" #include "casm/misc/cloneable_ptr.hh" @@ -19,6 +20,8 @@ class ResultsIO : public notstd::Cloneable { virtual void write(results_type const &results, ValueMap const &conditions, Index run_index) = 0; + + virtual jsonParser to_json() = 0; }; } // namespace monte diff --git a/include/casm/monte/run_management/io/json/SamplingFixtureParams_json_io.hh b/include/casm/monte/run_management/io/json/SamplingFixtureParams_json_io.hh index 53acd90..8a63bf2 100644 --- a/include/casm/monte/run_management/io/json/SamplingFixtureParams_json_io.hh +++ b/include/casm/monte/run_management/io/json/SamplingFixtureParams_json_io.hh @@ -28,6 +28,10 @@ void parse( &results_io_methods, bool time_sampling_allowed); +template +jsonParser &to_json(SamplingFixtureParams params, + jsonParser &json); + // ~~~ Definition ~~~ /// \brief Construct sampling_fixture_params_type from JSON @@ -81,23 +85,19 @@ void parse( for (auto const &element : sampling_functions) { sampling_function_names.insert(element.first); } + std::set json_sampling_function_names; + for (auto const &element : json_sampling_functions) { + json_sampling_function_names.insert(element.first); + } auto sampling_params_subparser = parser.template subparse( - "sampling", sampling_function_names, time_sampling_allowed); + "sampling", sampling_function_names, json_sampling_function_names, + time_sampling_allowed); if (!parser.valid()) { return; } monte::SamplingParams const &sampling_params = *sampling_params_subparser->value; - StateSamplingFunctionMap selected_sampling_functions; - for (auto const &name : sampling_params.sampler_names) { - selected_sampling_functions.emplace(name, sampling_functions.at(name)); - } - jsonStateSamplingFunctionMap selected_json_sampling_functions; - for (auto const &name : sampling_params.json_sampler_names) { - selected_json_sampling_functions.emplace(name, - json_sampling_functions.at(name)); - } // Read completion check params auto completion_check_params_subparser = @@ -105,17 +105,13 @@ void parse( "completion_check", sampling_functions); // Read analysis functions - std::vector function_names; + std::vector analysis_names; fs::path functions_path = fs::path("analysis") / "functions"; - parser.optional(function_names, functions_path); + parser.optional(analysis_names, functions_path); - ResultsAnalysisFunctionMap - selected_analysis_functions; - for (auto const &name : function_names) { + for (auto const &name : analysis_names) { auto it = analysis_functions.find(name); - if (it != analysis_functions.end()) { - selected_analysis_functions.insert(*it); - } else { + if (it == analysis_functions.end()) { std::stringstream msg; msg << "Error: function '" << name << "' not recognized"; parser.insert_error(functions_path, msg.str()); @@ -144,13 +140,43 @@ void parse( if (parser.valid()) { parser.value = std::make_unique>( - label, selected_sampling_functions, - selected_json_sampling_functions, selected_analysis_functions, - sampling_params, *completion_check_params_subparser->value, + label, sampling_functions, json_sampling_functions, + analysis_functions, sampling_params, + *completion_check_params_subparser->value, analysis_names, std::move(results_io_subparser->value), method_log); } } +template +jsonParser &to_json(SamplingFixtureParams params, + jsonParser &json) { + json.put_obj(); + + to_json(params.sampling_params, json["sampling"]); + to_json(params.completion_check_params, json["completion_check"]); + + json["analysis"] = jsonParser::object(); + json["analysis"]["functions"] = jsonParser::array(); + for (std::string name : params.analysis_names) { + json["analysis"]["functions"].push_back(name); + } + + if (params.results_io) { + json["results_io"] = params.results_io->to_json(); + } else { + json["results_io"].put_null(); + } + + if (!params.method_log.logfile_path.empty()) { + json["log"] = jsonParser::object(); + json["log"]["file"] = params.method_log.logfile_path.string(); + if (params.method_log.log_frequency.has_value()) { + json["log"]["frequency_in_s"] = params.method_log.log_frequency.value(); + } + } + return json; +} + } // namespace monte } // namespace CASM diff --git a/include/casm/monte/run_management/io/json/jsonResultsIO.hh b/include/casm/monte/run_management/io/json/jsonResultsIO.hh index 494028a..44eedbd 100644 --- a/include/casm/monte/run_management/io/json/jsonResultsIO.hh +++ b/include/casm/monte/run_management/io/json/jsonResultsIO.hh @@ -1,6 +1,7 @@ #ifndef CASM_monte_results_io_jsonResultsIO #define CASM_monte_results_io_jsonResultsIO +#include "casm/casm_io/json/jsonParser.hh" #include "casm/misc/cloneable_ptr.hh" #include "casm/monte/run_management/ResultsAnalysisFunction.hh" #include "casm/monte/run_management/State.hh" @@ -27,6 +28,9 @@ class jsonResultsIO : public ResultsIO<_ResultsType> { void write(results_type const &results, ValueMap const &conditions, Index run_index) override; + /// \brief Write input parameters to JSON + jsonParser to_json() override; + protected: /// \brief Write summary.json with results from each individual run void write_summary(results_type const &results, ValueMap const &conditions); diff --git a/include/casm/monte/run_management/io/json/jsonResultsIO_impl.hh b/include/casm/monte/run_management/io/json/jsonResultsIO_impl.hh index 9fb22b1..bb6151f 100644 --- a/include/casm/monte/run_management/io/json/jsonResultsIO_impl.hh +++ b/include/casm/monte/run_management/io/json/jsonResultsIO_impl.hh @@ -75,17 +75,26 @@ inline jsonParser &append_condition_to_json( // write shape j["shape"] = shape; - // write component names - j["component_names"] = component_names; - - // write value for each component separately - Index i = 0; - for (auto const &component_name : component_names) { - if (!j.contains(component_name)) { - j[component_name].put_array(); + if (shape.size() == 0) { + // scalar + if (!j.contains("value")) { + j["value"].put_array(); + } + j["value"].push_back(value(0)); + + } else { + // write component names + j["component_names"] = component_names; + + // write value for each component separately + Index i = 0; + for (auto const &component_name : component_names) { + if (!j.contains(component_name)) { + j[component_name].put_array(); + } + j[component_name].push_back(value(i)); + ++i; } - j[component_name].push_back(value(i)); - ++i; } return json; } @@ -124,6 +133,7 @@ inline jsonParser &append_matrix_condition_to_json( /// \brief Append sampled data quantity to summary JSON /// +/// For non-scalar values: /// \code /// : { /// "shape": [...], // Scalar: [], Vector: [rows], Matrix: [rows, cols] @@ -135,6 +145,18 @@ inline jsonParser &append_matrix_condition_to_json( /// } /// } /// \endcode +/// +/// For scalar values: +/// \code +/// : { +/// "shape": [...], // Scalar: [], Vector: [rows], Matrix: [rows, cols] +/// "value": { +/// "mean": [...], <-- appends to +/// "calculated_precision": [...] <-- appends to +/// "is_converged": [...] <-- appends to, only if requested to converge +/// } +/// } +/// \endcode template jsonParser &append_statistics_to_json( std::pair> quantity, jsonParser &json, @@ -158,7 +180,8 @@ jsonParser &append_statistics_to_json( }; if (qstats.is_scalar) { - append(quantity_json, 0); + ensure_initialized_objects(quantity_json, {"value"}); + append(quantity_json["value"], 0); } else { // write component names - if not scalar quantity_json["component_names"] = qstats.component_names; @@ -325,6 +348,18 @@ void jsonResultsIO<_ResultsType>::write(results_type const &results, } } +/// \brief Write input parameters to JSON +template +jsonParser jsonResultsIO<_ResultsType>::to_json() { + jsonParser json; + json["method"] = "json"; + json["kwargs"] = jsonParser::object(); + json["kwargs"]["output_dir"] = m_output_dir.string(); + json["kwargs"]["write_trajectory"] = m_write_trajectory; + json["kwargs"]["write_observations"] = m_write_observations; + return json; +} + /// \brief Write summary.json with results from each individual run /// /// The summary format appends each new run result to form arrays of values for @@ -353,6 +388,8 @@ void jsonResultsIO<_ResultsType>::write(results_type const &results, /// }, /// }, /// "analyzed_data": { +/// "shape": [...], +/// "component_names": [...], /// : [...], /// }, /// "completion_check_results": { @@ -467,8 +504,8 @@ void jsonResultsIO<_ResultsType>::write_observations( json[pair.first]["shape"] = pair.second->shape(); bool is_scalar = (pair.second->shape().size() == 0); if (is_scalar) { - to_json(pair.second->values().col(0), json[pair.first]["value"], - jsonParser::as_array()); + CASM::to_json(pair.second->values().col(0), json[pair.first]["value"], + jsonParser::as_array()); } else { json[pair.first]["component_names"] = pair.second->component_names(); json[pair.first]["value"] = pair.second->values(); diff --git a/include/casm/monte/sampling/io/json/SamplingParams_json_io.hh b/include/casm/monte/sampling/io/json/SamplingParams_json_io.hh index 11ad786..ad9e43f 100644 --- a/include/casm/monte/sampling/io/json/SamplingParams_json_io.hh +++ b/include/casm/monte/sampling/io/json/SamplingParams_json_io.hh @@ -8,6 +8,7 @@ namespace CASM { template class InputParser; +class jsonParser; namespace monte { struct SamplingParams; @@ -15,8 +16,12 @@ struct SamplingParams; /// \brief Construct SamplingParams from JSON void parse(InputParser &parser, std::set const &sampling_function_names, + std::set const &json_sampling_function_names, bool time_sampling_allowed); +/// \brief Convert SamplingParams to JSON +jsonParser &to_json(SamplingParams const &sampling_params, jsonParser &json); + } // namespace monte } // namespace CASM diff --git a/python/libcasm/monte/events/__init__.py b/python/libcasm/monte/events/__init__.py index c4e7a62..1c6db4c 100644 --- a/python/libcasm/monte/events/__init__.py +++ b/python/libcasm/monte/events/__init__.py @@ -20,17 +20,17 @@ OccTransform, OccTransformVector, choose_canonical_swap, - choose_semigrand_canonical_swap, choose_semigrand_canonical_multiswap, + choose_semigrand_canonical_swap, is_allowed_canonical_swap, make_canonical_swaps, - make_semigrand_canonical_swaps, make_multiswaps, + make_semigrand_canonical_swaps, propose_canonical_event, propose_canonical_event_from_swap, propose_semigrand_canonical_event, - propose_semigrand_canonical_multiswap_event, - propose_semigrand_canonical_event_from_swap, propose_semigrand_canonical_event_from_multiswap, + propose_semigrand_canonical_event_from_swap, + propose_semigrand_canonical_multiswap_event, swaps_allowed_per_unitcell, ) diff --git a/python/src/monte.cpp b/python/src/monte.cpp index d3dcdab..9a87b24 100644 --- a/python/src/monte.cpp +++ b/python/src/monte.cpp @@ -35,12 +35,27 @@ using namespace CASM; typedef std::mt19937_64 engine_type; typedef monte::RandomNumberGenerator generator_type; -monte::MethodLog make_MethodLog(std::string logfile_path, +monte::ValueMap make_ValueMap(std::optional data) { + if (!data.has_value()) { + data = nlohmann::json{}; + } + jsonParser json{static_cast(*data)}; + monte::ValueMap values; + from_json(values, json); + return values; +} + +monte::MethodLog make_MethodLog(std::optional logfile_path, std::optional log_frequency) { monte::MethodLog method_log; - method_log.logfile_path = logfile_path; - method_log.log_frequency = log_frequency; - method_log.reset(); + if (logfile_path.has_value()) { + method_log.logfile_path = logfile_path.value(); + method_log.log_frequency = log_frequency; + method_log.reset(); + } else { + method_log.log_frequency = log_frequency; + method_log.reset_to_stdout(); + } return method_log; } @@ -103,12 +118,13 @@ PYBIND11_MODULE(_monte, m) { Parameters ---------- - logfile_path : str - File location for log output + logfile_path : Optional[str] + File location for log output. If None, log to stdout. log_frequency : Optional[float] How often to log method status, in seconds )pbdoc", - py::arg("logfile_path"), py::arg("log_frequency") = std::nullopt) + py::arg("logfile_path") = std::nullopt, + py::arg("log_frequency") = std::nullopt) .def( "logfile_path", [](monte::MethodLog const &x) { return x.logfile_path.string(); }, @@ -125,6 +141,10 @@ PYBIND11_MODULE(_monte, m) { R"pbdoc( Reset log file, creating parent directories as necessary )pbdoc") + .def("reset_to_stdout", &monte::MethodLog::reset_to_stdout, + R"pbdoc( + Reset to print to stdout. + )pbdoc") // .def( "section", @@ -371,12 +391,23 @@ PYBIND11_MODULE(_monte, m) { different type. Conversions for input/output are made to/from a single combined dict. )pbdoc") - .def(py::init<>(), + .def(py::init<>(&make_ValueMap), R"pbdoc( .. rubric:: Constructor - Default constructor only. - )pbdoc") + Notes + ----- + + - The constructor is equivalent to :func:`ValueMap.from_dict`, except + that it also accepts ``None``. + + Parameters + ---------- + data: Optional[dict] = None + A dict with keys of type `str` and boolean, scalar, vector, or + matrix values. + )pbdoc", + py::arg("data") = std::nullopt) .def_readwrite("boolean_values", &monte::ValueMap::boolean_values, R"pbdoc( :class:`~libcasm.monte.BooleanValueMap`: A Dict[str, bool]-like object. diff --git a/src/casm/monte/Conversions.cc b/src/casm/monte/Conversions.cc index c5c76f0..5b0024f 100644 --- a/src/casm/monte/Conversions.cc +++ b/src/casm/monte/Conversions.cc @@ -260,6 +260,10 @@ Index Conversions::species_index(Index asym, Index occ_index) const { return m_occ_to_species[asym][occ_index]; } +std::vector const &Conversions::species_list() const { + return m_struc_mol; +} + Index Conversions::occ_index(Index asym, Index species_index) const { // returns occ_size(asym) if species not allowed return m_species_to_occ[asym][species_index]; diff --git a/src/casm/monte/sampling/io/json/SamplingParams_json_io.cc b/src/casm/monte/sampling/io/json/SamplingParams_json_io.cc index 7309f61..c03670a 100644 --- a/src/casm/monte/sampling/io/json/SamplingParams_json_io.cc +++ b/src/casm/monte/sampling/io/json/SamplingParams_json_io.cc @@ -54,11 +54,10 @@ namespace monte { /// type of Monte Carlo calculation and should be keys in the /// sampling_functions map. /// -/// save_initial_state: bool (optional, default=false) -/// If true, request that the initial configuration is saved. -/// -/// save_final_state: bool (optional, default=false) -/// If true, request that the final configuration is saved. +/// json_quantities: array of string (optional) +/// Specifies which JSON quantities will be sampled. Options depend on the +/// type of Monte Carlo calculation and should be keys in the +/// json_sampling_functions map. /// /// sample_trajectory: bool (optional, default=false) /// If true, request that the entire configuration is saved each time @@ -66,6 +65,7 @@ namespace monte { /// void parse(InputParser &parser, std::set const &sampling_function_names, + std::set const &json_sampling_function_names, bool time_sampling_allowed) { SamplingParams sampling_params; @@ -145,6 +145,16 @@ void parse(InputParser &parser, } } + // "json_quantities" + parser.optional(sampling_params.json_sampler_names, "json_quantities"); + for (std::string name : sampling_params.json_sampler_names) { + if (!json_sampling_function_names.count(name)) { + std::stringstream msg; + msg << "Error: \"" << name << "\" is not a JSON sampling option."; + parser.insert_error("json_quantities", msg.str()); + } + } + // "sample_trajectory" parser.optional(sampling_params.do_sample_trajectory, "sample_trajectory"); @@ -155,5 +165,61 @@ void parse(InputParser &parser, } } +/// \brief Convert SamplingParams to JSON +jsonParser &to_json(SamplingParams const &sampling_params, jsonParser &json) { + json.put_obj(); + + // "sample_by" + if (sampling_params.sample_mode == SAMPLE_MODE::BY_PASS) { + json["sample_by"] = "pass"; + } else if (sampling_params.sample_mode == SAMPLE_MODE::BY_STEP) { + json["sample_by"] = "step"; + } else if (sampling_params.sample_mode == SAMPLE_MODE::BY_TIME) { + json["sample_by"] = "time"; + } else { + throw std::runtime_error( + "Error converting SamplingParams to json: invalid sample_mode"); + } + + // "spacing" + if (sampling_params.sample_method == SAMPLE_METHOD::LINEAR) { + json["spacing"] = "linear"; + + auto check_if_integral = [&](double value, std::string key) { + if (std::abs(value - std::round(value)) < CASM::TOL) { + json[key] = static_cast(std::round(value)); + } else { + json[key] = value; + } + }; + check_if_integral(sampling_params.begin, "begin"); + check_if_integral(sampling_params.period, "period"); + } else if (sampling_params.sample_method == SAMPLE_METHOD::LOG) { + json["spacing"] = "log"; + json["begin"] = sampling_params.begin; + json["base"] = sampling_params.base; + json["shift"] = sampling_params.shift; + } else { + throw std::runtime_error( + "Error converting SamplingParams to json: invalid sample_method"); + } + + if (sampling_params.stochastic_sample_period == true) { + json["stochastic_sample_period"] = sampling_params.stochastic_sample_period; + } + if (!sampling_params.sampler_names.empty()) { + json["quantities"] = sampling_params.sampler_names; + } + if (!sampling_params.json_sampler_names.empty()) { + json["json_quantities"] = sampling_params.json_sampler_names; + } + + if (sampling_params.do_sample_trajectory == true) { + json["sample_trajectory"] = sampling_params.do_sample_trajectory; + } + + return json; +} + } // namespace monte } // namespace CASM