Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Introducing the grcli development tool #231

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ endif()

option(GRACKLE_USE_DOUBLE "compile the code with double precision" ON)
option(GRACKLE_EXAMPLES "compile grackle examples" ${GRACKLE_IS_TOP_LEVEL})
option(GRACKLE_BUILD_GRCLI "compile grcli" ${GRACKLE_IS_TOP_LEVEL})
option(GRACKLE_DEFINE_INSTALL_RULES
"enable Grackle's installation rules" ${GRACKLE_IS_TOP_LEVEL})

Expand Down Expand Up @@ -175,6 +176,11 @@ endif()
# ------------------
add_subdirectory(src/clib)

# declare build-recipe for grcli
if (GRACKLE_BUILD_GRCLI)
add_subdirectory(src/grcli)
endif()

# declare build-recipies for examples
if (GRACKLE_EXAMPLES)
add_subdirectory(src/example)
Expand Down
5 changes: 5 additions & 0 deletions doc/source/Installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@ This first table describes the Grackle-specific options to configure the build.
* - ``GRACKLE_USE_OPENMP``
- Turn on to build Grackle with OpenMP
- ``"OFF"``
* - ``GRACKLE_BUILD_GRCLI``
- enables/disables building of the ``grcli`` development tool
- ``"ON"``\*

In the above table, any entry where the default value is marked with an asterisk, indicates that the default value is ``OFF`` in the special case when a Grackle build is embedded within the build of another project (don't worry about this if you don't know what this means).

This second table highlights a subset of standardized CMake options that may also be useful.

Expand Down
4 changes: 2 additions & 2 deletions src/clib/initialize_chemistry_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "grackle_types.h"
#include "grackle_chemistry_data.h"
#include "phys_constants.h"
#include "show.h"
#ifdef _OPENMP
#include <omp.h>
#endif
Expand All @@ -37,7 +38,6 @@ extern chemistry_data_storage grackle_rates;
void auto_show_config(FILE *fp);
void auto_show_flags(FILE *fp);
grackle_version get_grackle_version();
void show_parameters(FILE *fp, chemistry_data *my_chemistry);

int _free_cloudy_data(cloudy_data *my_cloudy, chemistry_data *my_chemistry, int primordial);
int initialize_cloudy_data(chemistry_data *my_chemistry,
Expand All @@ -56,7 +56,7 @@ int initialize_rates(chemistry_data *my_chemistry, chemistry_data_storage *my_ra

void initialize_empty_UVBtable_struct(UVBtable *table);

static void show_version(FILE *fp)
void show_version(FILE *fp)
{
grackle_version gversion = get_grackle_version();
fprintf (fp, "\n");
Expand Down
40 changes: 40 additions & 0 deletions src/clib/show.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/***********************************************************************
/
/ Declare functions used internally for formatting data (across routines)
/
/
/ Copyright (c) 2013, Enzo/Grackle Development Team.
/
/ Distributed under the terms of the Enzo Public Licence.
/
/ The full license is in the file LICENSE, distributed with this
/ software.
************************************************************************/

#ifndef GRACKLE_SHOW_H
#define GRACKLE_SHOW_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include "grackle.h"

void show_parameters(FILE *fp, chemistry_data *my_chemistry);
void show_version(FILE *fp);

static inline void show_units(FILE *fp, code_units* my_units) {
fprintf(fp, "comoving_coordinates = %d\n", my_units->comoving_coordinates);
fprintf(fp, "density_units = %g\n", my_units->density_units);
fprintf(fp, "length_units = %g\n", my_units->length_units);
fprintf(fp, "time_units = %g\n", my_units->time_units);
fprintf(fp, "a_units = %g\n", my_units->a_units);
fprintf(fp, "a_value = %g\n", my_units->a_value);
}


#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */

#endif /* GRACKLE_SHOW_H */
42 changes: 42 additions & 0 deletions src/grcli/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

include(FetchContent)
FetchContent_Declare(
benchmark
# the following url contains the hash for version 1.8.5
URL https://github.com/google/benchmark/archive/a6ad7fbbdc2e14fab82bb8a6d27760d700198cbf.zip
)

set (BENCHMARK_ENABLE_GTEST_TESTS OFF)
FetchContent_MakeAvailable(benchmark)


add_executable(Grackle_Grcli
ChemistryData.h
args/CliParser.h
args/EscapedArgsConfig.h
FieldData.h FieldData.C
cmd_bench.h cmd_bench.C
cmd_show.h cmd_show.C
handle_gr_parameters.h handle_gr_parameters.C
grid_problem.h grid_problem.C
main.C
operation.h operation.C
utils.h utils.C
view.h
)

set_target_properties(Grackle_Grcli
PROPERTIES CXX_STANDARD 17
OUTPUT_NAME grcli # controls what the file is named
#EXPORT_NAME Grcli # <- if we want to export, uncomment this
)

target_include_directories(Grackle_Grcli
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(Grackle_Grcli
PRIVATE Grackle::Grackle benchmark::benchmark)

target_compile_definitions(Grackle_Grcli PUBLIC USE_BENCHMARK=1)

add_executable(Grackle::Grcli ALIAS Grackle_Grcli)
172 changes: 172 additions & 0 deletions src/grcli/ChemistryData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#ifndef CHEMISTRY_DATA_H
#define CHEMISTRY_DATA_H

#include <cstring> // std::memcpy
#include <memory> // std::unique_ptr
#include <string>
#include <string_view>
#include <type_traits> // std::is_same
#include <unordered_set>
#include <utility> // std::pair
#include <vector>

#include "grackle.h"

#include "utils.h"

/// Wrapper around the chemistry_data struct
///
/// @note
/// This is a stripped-down (and slightly updated) version of Enzo-E's
/// `GrackleChemistryData` class
///
/// @note
/// Instances of this class satisfy 2 invariants:
/// 1. the stored pointer to the ``chemistry_data`` struct is always valid;
/// it's not allowed to be a ``nullptr``. While lazily initializing the
/// pointer would make the move constructor cheaper, it's probably not a
/// worthwhile tradeoff (it would be REALLY easy to forget to lazily
/// initialize the pointer and then cause a bug)
/// 2. the str_allocs_ attribute manages the lifetime of all strings
/// stored in fields of the ``chemistry_data`` struct. The only
/// exception is the default value (which may be a string literal or a
/// ``nullptr``)
class ChemistryData {

/// pointer to the chemistry_data struct
std::unique_ptr<chemistry_data> ptr_;

/// manages the lifetime of string fields used in chemistry_data
///
/// We explicitly avoid using std::string because SSO (small string
/// optimization) could cause all sorts of bugs in the future that would be
/// VERY hard to debug
///
/// The number of entries in this parameter is currently small. In the
/// future, it may be sensible to replace vector with unordered_set if the
/// number grows
std::vector<std::unique_ptr<char[]>> str_allocs_;

bool try_set_int_(const std::string& field, int value) {
int* field_ptr = local_chemistry_data_access_int(ptr_.get(), field.c_str());
if (!field_ptr) { return false; }
*field_ptr = value;
return true;
}


bool try_set_dbl_(const std::string& field, double value) {
double* field_ptr = local_chemistry_data_access_double(ptr_.get(),
field.c_str());
if (!field_ptr) { return false; }
*field_ptr = value;
return true;
}

inline bool try_set_str_(const std::string& field, std::string_view value);

public:
/// construct a new ChemistryData
ChemistryData()
: ptr_{new chemistry_data}, str_allocs_()
{ local_initialize_chemistry_parameters(ptr_.get()); }

/// destroy a ChemistryData instance
~ChemistryData() = default;

// eliminate the ability to perform deepcopies (for now)
ChemistryData(const ChemistryData& other) = delete;
ChemistryData& operator= (const ChemistryData&) = delete;

/// move constructor
ChemistryData(ChemistryData&& other)
: ChemistryData()
{ this->swap(other); }

/// move-assignment operation
ChemistryData& operator= (ChemistryData&& other) noexcept
{ this->swap(other); return *this; }

/// exchange the contents of this with other
void swap(ChemistryData& other) noexcept
{ ptr_.swap(other.ptr_); str_allocs_.swap(other.str_allocs_); }

/// returns a pointer to the managed chemistry_data struct
///
/// This is primarily intended to be used when calling a grackle function.
/// The pointer is only valid during the ChemistryData's lifetime
inline chemistry_data* get_ptr() { return ptr_.get(); }
inline const chemistry_data* get_ptr() const { return ptr_.get(); }

/// try to update a parameter value stored in the chemistry_data struct.
///
/// @retval true the stored value was succesfully updated
/// @retval false there is no know parameter of the specified type
///
/// @note
/// specializations of this method follow the class declaration
template<class T>
bool try_set(const std::string& field, T value) {
using cleanT = std::remove_cv_t<std::remove_reference_t<T>>;

if constexpr (std::is_same_v<cleanT,int>) {
return try_set_int_(field, value);
} else if constexpr (std::is_same_v<cleanT,double>) {
return try_set_dbl_(field, value);
} else if constexpr (std::is_same_v<cleanT,std::string>) {
return try_set_str_(field, value);
} else if constexpr (std::is_same_v<cleanT,std::string_view>) {
return try_set_str_(field, value);
} else if constexpr (std::is_same_v<cleanT,char*>) {
return try_set_str_(field, value);
} else {
GRCLI_ERROR("the value must be an int, a double, or a string");
}
}

};


inline bool ChemistryData::try_set_str_(const std::string& field,
std::string_view value)
{

// NOTE: we should NOT directly modify characters held by field_ptr
char ** field_ptr = local_chemistry_data_access_string(ptr_.get(),
field.c_str());

if (!field_ptr) { return false; }

// deallocate the existing value (if applicable)
if ((*field_ptr) != nullptr){

// check whether *field_ptr matches the address of any pointers held within
// str_allocs_, if so delete that pointer from str_allocs_
for (std::size_t i = 0; i < str_allocs_.size(); i++){

if (str_allocs_[i].get() == *field_ptr){
// we will only enter this part of the loop if *field_ptr doesn't refer
// to a string literal (that could have been set as a default value)
str_allocs_.erase(str_allocs_.begin() + i);
break;
}
}

}

// allocate a new c-string and copy data from value into it
const std::size_t length_with_nul = value.size() + 1;
std::unique_ptr<char[]> new_alloc(new char[length_with_nul]);
std::memcpy(new_alloc.get(), value.data(), length_with_nul-1);
new_alloc.get()[length_with_nul-1] = '\0';

// update the field of the chemistry_data struct
(*field_ptr) = new_alloc.get();

// finally, move the newly allocated c-string into str_allocs_
str_allocs_.push_back(std::move(new_alloc));
return true;
}


#endif /* CHEMISTRY_DATA_H */
Loading