C ++ Najlepsze praktyki postępowania z wieloma stałymi, zmiennymi w kodach naukowych

17

Opracowuję kod do symulacji przepływu płynu z obecnymi w nim substancjami biologicznymi. Dotyczy to standardowych równań Naviera-Stokesa połączonych z niektórymi dodatkowymi modelami biologicznymi. Istnieje wiele parametrów / stałych.

Napisałem funkcje do obsługi głównych obliczeń, ale mam problem z dużą liczbą stałych / parametrów, od których zależą te obliczenia. Przekazanie funkcji 10-20 argumentów wydaje się kłopotliwe.

Jedną z możliwości jest uczynienie wszystkich stałych zmiennymi globalnymi, ale wiem, że w C ++ jest to niezadowolone.

Jaki jest standardowy sposób obsługi wielu danych wejściowych do funkcji? Czy zamiast tego powinienem utworzyć strukturę i przekazać ją?

Dziękuję Ci

EternusVia
źródło
7
Jeśli to możliwe, spróbuj oszacować stałe w czasie kompilacji za pomocą constexpr. Staram się zawrzeć większość z nich w osobnym pliku nagłówkowym. W przypadku zmiennych odkryłem, że osobna klasa ma zalety, ale kosztem potencjalnie większej liczby błędów, ponieważ musisz zainicjować klasę przed przejściem do funkcji.
Biswajit Banerjee
3
Trudno jest odpowiedzieć poprawnie bez jakiejś próbki kodu. Czy zamiast tego powinienem utworzyć strukturę i przekazać ją? Ogólnie rzecz biorąc, tak, jest to absolutnie zwykła droga. Pogrupuj parametry / stałe według ich znaczenia.
Kirill
1
„Jedną z możliwości jest uczynienie wszystkich stałych zmiennymi globalnymi, ale wiem, że w C ++ jest to niezadowolone” .
Wyścigi lekkości z Moniką
1
Czy są naprawdę, naprawdę stałe? Co jeśli chcesz zastosować swój model w innej domenie? Poleciłbym umieszczenie ich w małej klasie. To przynajmniej daje ci trochę elastyczności w przyszłości
André
@ André Większość z nich jest kontrolowana przez użytkownika za pomocą pliku parametrów, dlatego zgodziłbym się, że rozwiązanie klasowe jest najlepsze.
EternusVia

Odpowiedzi:

13

Jeśli masz stałe, które nie zmienią się przed uruchomieniem, zadeklaruj je w pliku nagłówkowym:

//constants.hpp
#ifndef PROJECT_NAME_constants_hpp
#define PROJECT_NAME_constants_hpp
namespace constants {
  constexpr double G        = 6.67408e-11;
  constexpr double M_EARTH  = 5.972e24;
  constexpr double GM_EARTH = G*M_EARTH; 
}
#endif

//main.cpp
using namespace constants;
auto f_earth = GM_EARTH*m/r/r;  //Good
auto f_earth = G*M_EARTH*m/r/r; //Also good: compiler probably does math here too

Powodem, dla którego chcesz to zrobić, jest to, że pozwala on kompilatorowi na obliczenie stałych wartości z wyprzedzeniem przed czasem wykonywania, co jest dobre, jeśli masz ich dużo.

Możesz także użyć prostej klasy do przekazywania wartości:

class Params {
 public:
  double a,b,c,d;
  Params(std::string config_file_name){
    //Load configuration here
  }
};

void Foo(const Params &params) {
  ...
}

int main(int argc, char **argv){
  Params params(argv[1]);
  Foo(params);
}
Richard
źródło
Wszystkie świetne odpowiedzi, ale rozwiązanie klasy działa najlepiej w mojej sytuacji.
EternusVia
8
Jeśli zmienimy te zmienne na globalne constexpr, przynajmniej otoczmy je namespacetak, aby nie opierały się na innych symbolach globalnych. Używanie zmiennej globalnej o nazwie Gjest po prostu wzywaniem do kłopotów.
Wolfgang Bangerth
1
Dlaczego prowadzisz, włączając strażników z _? Nigdy nie powinieneś pisać niczego, co zaczyna się od _, ryzykujesz kolizję z kompilatorami. Powinieneś robić coś takiego ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Nie wiesz też, dlaczego wstawiłeś wielkie litery, ale nie włączaj makr wartowników. Zasadniczo chcesz wykorzystać wszystkie makra, zwłaszcza że nie są one sanitarne. W przypadku stałych wielkie litery nie mają w ogóle sensu . Gjest w porządku, ponieważ jego SI, ale mass_earth jest bardziej odpowiednie i powinien być zakwalifikowany z przestrzenią nazw oznaczającą globalną, tj constants::mass_earth.
kiedy
12

Inną alternatywą, która może być zgodna z twoim tokiem myślenia, jest użycie przestrzeni nazw (lub zagnieżdżonych przestrzeni nazw) do prawidłowego grupowania stałych. Przykładem może być:

namespace constants {
   namespace earth {
      constexpr double G = 6.67408e-11;
      constexpr double Mass_Earth = 5.972e24;
      constexpr double GM = G*Mass_Earth;
   }// constant properties about Earth

   namespace fluid {
      constexpr double density = 0.999; // g/cm^3
      constexpr double dyn_viscosity = 1.6735; //mPa * s
   }// constants about fluid at 2C

   // ... 

} // end namespace for constants

Korzystając z powyższej techniki, możesz zlokalizować stałe odniesienia do niektórych pożądanych plików i przestrzeni nazw, dzięki czemu są one bardziej kontrolowane niż zmienne globalne, a jednocześnie uzyskujesz podobne korzyści. Kiedy używasz stałych, jest to tak proste, jak:

constexpr double G_times_2 = 2.0*constants::earth::G;

Jeśli nie lubisz długich łańcuchów zagnieżdżonych przestrzeni nazw, zawsze możesz skrócić rzeczy, gdy to konieczne, używając aliasu przestrzeni nazw:

namespace const_earth = constants::earth;
constexpr double G_times_2 = 2.0*const_earth::G;
spektr
źródło
2
Jest to podejście, po którym następuje OpenFOAM , patrz przypadkowy przykład kodu źródłowego OpenFOAM . OpenFOAM to biblioteka kodów C ++ implementująca metodę objętości skończonej, która jest szeroko stosowana w dynamice płynów.
Dohn Joe
1

Jednym ze sposobów jest użycie singletona.

Po uruchomieniu programu inicjujesz singletona i wypełniasz go stałymi danymi (prawdopodobnie z pliku właściwości, który masz do uruchomienia). Dostajesz to w każdej klasie, której potrzebujesz wartości i po prostu z niej korzystaj.

Ashkan
źródło
Ostrzeżenie: czasami miałem singletony serializujące dostęp w kodzie wielowątkowym. Dlatego warto to sprawdzić w ramach etapu profilowania.
Richard
Na pewno nie umieściłbym ich w singletonie ... W praktyce te stałe zmienią się w przyszłości, gdy (nie jeśli) zastosujesz swój model w innej dziedzinie. Posiadanie singletonu bardzo utrudnia testowanie przy użyciu różnych parametrów.
André
Wszystkie są stałe. Nie ma tu potrzeby singletonu. Lepszym zastosowaniem jest tutaj statyczna klasa akcesorium. Jeszcze lepsza byłaby klasa statyczna, w której wartości są pobierane z pliku konfiguracyjnego (więc jeśli użytkownik końcowy zauważy błąd lub chce większej precyzji, może dostosować plik konfiguracyjny bez nowej kompilacji).
Scuba Steve
Singletony są rzadko, jeśli w ogóle, dobrym pomysłem. Wstrzykiwanie zależności jest znacznie czystszym i bardziej elastycznym rozwiązaniem. Jednak z tylko stałymi, powiedziałbym, żeby po prostu trzymać je gdzieś w nagłówku.
mascoj