C ++ Best practice per gestire molte costanti, variabili nei codici scientifici


17

Sto sviluppando un codice per simulare il flusso di fluidi con sostanze biologiche presenti nel flusso. Ciò comporta le equazioni standard di Navier-Stokes accoppiate ad alcuni modelli biologici aggiuntivi. Ci sono molti parametri / costanti.

Ho scritto funzioni per gestire i calcoli principali, ma un problema che sto riscontrando è il gran numero di costanti / parametri da cui dipendono questi calcoli. Sembra ingombrante passare 10-20 argomenti a una funzione.

Un'alternativa è rendere globali tutte le costanti variabili, ma so che questo è disapprovato in C ++.

Qual è il modo standard di gestire molti input per una funzione? Dovrei fare una strutt e passarla invece?

Grazie


7
Se è possibile, prova a far valutare le costanti in fase di compilazione usando constexpr. Provo a includere la maggior parte di questi in un file di intestazione separato. Per le variabili, ho scoperto che una classe separata ha dei vantaggi, ma a costo di potenzialmente più bug perché devi inizializzare la classe prima di passare alla funzione.
Biswajit Banerjee,

3
È difficile rispondere correttamente senza una sorta di esempio di codice. Dovrei fare una struttura e passarla invece? In generale, sì, questa è assolutamente la solita strada da percorrere. Raggruppa i parametri / le costanti in base al loro significato.
Kirill

1
"Un'alternativa è rendere tutte le costanti variabili globali, ma so che questo è disapprovato in C ++" È vero?
Lightness Races con Monica il

1
Sono davvero, davvero costanti? Cosa succede se si desidera applicare il modello in un dominio diverso? Consiglierei di metterli in una piccola classe. Questo almeno ti darà un po 'di flessibilità in futuro
André,

@ André La maggior parte di essi è controllata dall'utente tramite un file di parametri, motivo per cui concordo sul fatto che la soluzione di classe sia la migliore.
EternusVia,

Risposte:


13

Se hai delle costanti che non cambieranno prima dell'esecuzione, dichiarale in un file di intestazione:

//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

Il motivo per cui vorresti farlo è che consente al compilatore di calcolare i valori costanti in anticipo prima del runtime, il che è positivo se ne hai molti.

Puoi anche usare una semplice classe per passare valori in giro:

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);
}

Tutte ottime risposte, ma la soluzione di classe funziona meglio per la mia situazione.
EternusVia,

8
Se rendi le variabili globali constexpr, includile almeno in un namespacemodo che non calpestino altri simboli globali. L'uso di una variabile globale chiamata Grichiede solo problemi.
Wolfgang Bangerth,

1
Perché conduci includi guardie con _? Non dovresti mai scrivere nulla che inizi con _, rischi la collisione con il compilatore. Dovresti fare qualcosa del genere ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Inoltre, non sai perché hai utilizzato le costanti in maiuscolo, ma non le macro di protezione include. In genere vuoi capitalizzare tutte le macro, soprattutto perché non sono sanitarie. Per le costanti la capitalizzazione non ha senso in generale . Gva bene perché è SI, ma mass_earth è più appropriato e dovrebbe essere qualificato con uno spazio dei nomi per indicare globale cioè constants::mass_earth.
quando il

12

Un'altra alternativa che può essere in linea con il tuo treno di pensieri è quella di utilizzare uno spazio dei nomi (o spazi dei nomi nidificati) per raggruppare correttamente le costanti. Un esempio potrebbe essere:

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

Usando la tecnica sopra, è possibile localizzare le costanti di riferimento in alcuni file e spazi dei nomi desiderati, rendendoli più controllati delle variabili globali e ottenendo alcuni dei vantaggi simili. Quando usi le costanti, è semplice come fare:

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

Se non ti piacciono le catene lunghe di spazi dei nomi nidificati, puoi sempre abbreviare le cose quando necessario utilizzando un alias dello spazio dei nomi:

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

2
Questo è un approccio, seguito da OpenFOAM , vedi un esempio casuale del codice sorgente di OpenFOAM . OpenFOAM è una libreria di codici C ++ che implementa il metodo del volume finito, ampiamente utilizzato nella fluidodinamica.
Dohn Joe,

1

Un modo che faccio è usare singleton.

Quando avvii il tuo programma, avvia il tuo singleton e lo riempi con i dati costanti (probabilmente da un file delle proprietà che hai per l'esecuzione). Ottieni questo in ogni classe in cui hai bisogno dei valori e semplicemente lo usi.


Avvertenza: a volte ho avuto singleton serializzare gli accessi in codice multi-thread. Quindi potresti voler controllare questo come parte della tua fase di profilazione.
Richard,

Non li metterei certamente in un singleton ... In pratica quelle costanti cambieranno in futuro quando (non se) applicherai il tuo modello in un dominio diverso. Avere un singleton rende molto difficile quindi testare con parametri diversi.
André,

Sono tutte costanti. Non è necessario qui per un singleton. Una classe di accesso statica è un uso migliore qui. Ancora meglio sarebbe una classe statica in cui i valori vengono estratti da un file di configurazione (quindi se l'utente finale vede un errore o desidera maggiore precisione, può regolare il file di configurazione senza ottenere una nuova build).
Scuba Steve,

I single sono raramente, se mai, una buona idea. L'iniezione di dipendenza è una soluzione molto più pulita e flessibile. Tuttavia, con solo costanti, direi che tieni le costanti in un header da qualche parte.
Mascoj,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.