1
+ /*
2
+ * (C) Copyright 2025- ECMWF.
3
+ *
4
+ * This software is licensed under the terms of the Apache Licence Version 2.0
5
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6
+ *
7
+ * In applying this licence, ECMWF does not waive the privileges and immunities
8
+ * granted to it by virtue of its status as an intergovernmental organisation nor
9
+ * does it submit to any jurisdiction.
10
+ */
11
+ #include < iostream>
12
+ #include < stdlib.h>
13
+
14
+ #include " eckit/config/YAMLConfiguration.h"
15
+ #include " eckit/filesystem/PathName.h"
16
+ #include " eckit/mpi/Comm.h"
17
+
18
+ #include " atlas/library.h"
19
+ #include " atlas/option/Options.h"
20
+ #include " atlas/parallel/mpi/mpi.h"
21
+ #include " atlas/runtime/AtlasTool.h"
22
+
23
+ #include " plume/Manager.h"
24
+ #include " plume/data/ModelData.h"
25
+ #include " plume/data/ParameterCatalogue.h"
26
+
27
+ #include " nwp_data_provider.h"
28
+ #include " nwp_definitions.h"
29
+
30
+ using namespace nwp_emulator ;
31
+
32
+ /* *
33
+ * @class NWPEmulator
34
+ * @brief Emulates a model run and makes data available at each time step to facilitate Plume and plugins testing.
35
+ */
36
+ class NWPEmulator final : public atlas::AtlasTool {
37
+
38
+ int execute (const Args& args) override ;
39
+ std::string briefDescription () override { return " NWP model emulator to facilitate Plume & plugins development" ; }
40
+ std::string usage () override {
41
+ return name () +
42
+ " [--grib-src=<path> | --config-src=<path>] [--plume-cfg=<path>] [OPTION]... [--help]\n "
43
+ " --plume-cfg is optional, pass it to run Plume, else the emulator will do a dry run\n " ;
44
+ }
45
+
46
+ int numberOfPositionalArguments () override { return -1 ; }
47
+ int minimumPositionalArguments () override { return 0 ; }
48
+
49
+ public:
50
+ NWPEmulator (int argc, char ** argv) : dataSourceType_(DataSourceType::INVALID), atlas::AtlasTool(argc, argv) {
51
+ add_option (new SimpleOption<std::string>(" grib-src" , " Path to GRIB files source" ));
52
+ add_option (new SimpleOption<std::string>(" config-src" , " Path to emulator config file" ));
53
+ add_option (new SimpleOption<std::string>(" plume-cfg" , " Path to Plume configuration" ));
54
+ }
55
+
56
+ private:
57
+ std::string dataSourcePath_;
58
+ DataSourceType dataSourceType_;
59
+
60
+ // / Plume members if needed
61
+ std::string plumeConfigPath_;
62
+ plume::data::ModelData plumeData_;
63
+
64
+ /* *
65
+ * @brief Sets up Plume framework and load plugins compatible with model params.
66
+ *
67
+ * @param dataProvider The object that provides the data for the Atlas fields offered by the emulated model.
68
+ *
69
+ * @return true if Plume configuration, negotiation and feeding are successful, false otherwise.
70
+ */
71
+ bool setupPlume (NWPDataProvider& dataProvider);
72
+
73
+ /* *
74
+ * @brief Update necessary parameters offered to Plume other than Atlas fields, and run the plugins for the step.
75
+ *
76
+ * @param step The internal model step number.
77
+ */
78
+ void runPlume (int step);
79
+ };
80
+
81
+ int NWPEmulator::execute (const Args& args) {
82
+ // Emulator configuration
83
+ std::string gribSrcArg;
84
+ if (args.get (" grib-src" , gribSrcArg)) {
85
+ dataSourcePath_ = gribSrcArg;
86
+ dataSourceType_ = DataSourceType::GRIB;
87
+ }
88
+ std::string configSrcPath;
89
+ if (args.get (" config-src" , configSrcPath)) {
90
+ if (dataSourceType_ != DataSourceType::INVALID) {
91
+ eckit::Log::error () << " Usage : " << usage () << std::endl;
92
+ return 1 ;
93
+ }
94
+ dataSourcePath_ = configSrcPath;
95
+ dataSourceType_ = DataSourceType::CONFIG;
96
+ }
97
+ if (dataSourcePath_.empty ()) {
98
+ eckit::Log::error () << " Usage : " << usage () << std::endl;
99
+ return 1 ;
100
+ }
101
+ args.get (" plume-cfg" , plumeConfigPath_);
102
+
103
+ size_t root = 0 ;
104
+ size_t nprocs = eckit::mpi::comm ().size ();
105
+ size_t rank = eckit::mpi::comm ().rank ();
106
+
107
+ NWPDataProvider dataProvider (dataSourceType_, eckit::PathName{dataSourcePath_}, rank, root, nprocs);
108
+
109
+ // Plume loading if a Plume configuration file has been passed, emulator dry run otherwise
110
+ if (!plumeConfigPath_.empty ()) {
111
+ eckit::Log::info () << " The emulator will run Plume with configuration '" << plumeConfigPath_ << " '"
112
+ << std::endl;
113
+ setupPlume (dataProvider);
114
+ }
115
+
116
+ // Run the emulator
117
+ while (dataProvider.getStepData ()) {
118
+ // This is a model step
119
+ if (!plumeConfigPath_.empty ()) {
120
+ runPlume (dataProvider.getStep ());
121
+ }
122
+ eckit::mpi::comm ().barrier ();
123
+ }
124
+
125
+ // Tear down where appropriate and wait for all processes before finishing
126
+ if (!plumeConfigPath_.empty ()) {
127
+ plume::Manager::teardown ();
128
+ }
129
+
130
+ eckit::mpi::comm ().barrier ();
131
+ std::cout << " Process " << rank << " finished..." << std::endl;
132
+ eckit::Log::info () << " Emulator run completed..." << std::endl;
133
+ return 0 ;
134
+ }
135
+
136
+ bool NWPEmulator::setupPlume (NWPDataProvider& dataProvider) {
137
+ plume::Manager::configure (eckit::YAMLConfiguration (eckit::PathName (plumeConfigPath_)));
138
+
139
+ plume::Protocol offers; // / Define data offered by Plume
140
+ offers.offerInt (" NSTEP" , " always" , " Simulation Step" );
141
+ auto fields = dataProvider.getModelFieldSet ();
142
+ for (const auto & field: fields) {
143
+ offers.offerAtlasField (field.name (), " on-request" , field.name ());
144
+ }
145
+ plume::Manager::negotiate (offers);
146
+ plumeData_.createInt (" NSTEP" , 0 ); // / Initialise parameters
147
+ for (auto & field: fields) {
148
+ if (plume::Manager::isParamRequested (field.name ())) {
149
+ plumeData_.provideAtlasFieldShared (field.name (), field.get ());
150
+ }
151
+ }
152
+
153
+ plume::Manager::feedPlugins (plumeData_);
154
+ return true ;
155
+ }
156
+
157
+ void NWPEmulator::runPlume (int step) {
158
+ plumeData_.updateInt (" NSTEP" , step);
159
+ plume::Manager::run ();
160
+ }
161
+
162
+ int main (int argc, char ** argv) {
163
+ setenv (" ATLAS_LOG_FILE" , " true" , 1 );
164
+ NWPEmulator emulator (argc, argv);
165
+ return emulator.start ();
166
+ }
0 commit comments