[6328] | 1 | pyOASIS Documentation |
---|
| 2 | ===================== |
---|
| 3 | |
---|
| 4 | .. toctree:: |
---|
| 5 | :maxdepth: 2 |
---|
| 6 | :caption: Contents: |
---|
| 7 | |
---|
| 8 | |
---|
| 9 | Introduction |
---|
| 10 | ------------ |
---|
| 11 | |
---|
| 12 | pyOASIS is a Python wrapper for OASIS written using ctypes |
---|
| 13 | and ISO C bindings to Fortran. It provides an object-oriented |
---|
| 14 | interface to OASIS3-MCT. This allows users to write and couple models |
---|
| 15 | written in Python |
---|
| 16 | or to couple models written in Python with models written |
---|
| 17 | in Fortran. |
---|
| 18 | |
---|
| 19 | It is part of the distribution of OASIS3-MCT. See https://portal.enes.org/oasis/download |
---|
| 20 | for more information about obtaining it. |
---|
| 21 | |
---|
| 22 | pyOASIS and OASIS3-MCT are distributed under the GNU Lesser General Public |
---|
| 23 | License. For more details, see the file lgpl-3.0.txt or |
---|
| 24 | https://www.gnu.org/licenses/lgpl-3.0.en.html. |
---|
| 25 | |
---|
| 26 | |
---|
| 27 | Using pyOASIS |
---|
| 28 | ------------- |
---|
| 29 | |
---|
| 30 | Code structure |
---|
| 31 | ++++++++++++++ |
---|
| 32 | |
---|
| 33 | The source code of pyOASIS is in the directory ``pyoasis/src``. |
---|
| 34 | First, the OASIS3-MCT Fortran code is wrapped in Fortran using ISO-C |
---|
| 35 | bindings. The corresponding source files are in the subdirectory |
---|
| 36 | ``lib/cbindings/fortran_isoc``. The file names are the same as the |
---|
| 37 | corresponding ones in the original source code but ending in ``_iso.F90``. |
---|
| 38 | Subsequently, the Fortran with ISO-C bindings is wrapped in C, see |
---|
| 39 | the source code is in ``lib/cbindings/c_src``. The names of the files are |
---|
| 40 | the same as the corresponding Fortran ones, but ending in ``_c.c``. Finally, |
---|
| 41 | the C is wrapped in Python in the directory ``pyoasis/src``. A low-level |
---|
| 42 | wrapper is made using the same filenames as the Fortran ones but ending in |
---|
| 43 | ``.py``. To access the python interface, one must write a ``import |
---|
| 44 | pyoasis`` in his/her python code. |
---|
| 45 | |
---|
| 46 | Users can find examples on how to use the interface in python, C and Fortran in ``pyoasis/examples``; |
---|
| 47 | to run all these examples at once, use ``make test`` in ``oasis3-mct/pyoasis``. |
---|
| 48 | More advanced testing of the python wrapping functions can be done |
---|
| 49 | with ``make wrappertest``; this will call the script ``tests/run_pytest.sh`` that invokes the standard ``pytest`` testing framework (that has to be installed). |
---|
| 50 | |
---|
| 51 | Creating a component using MPI |
---|
| 52 | ++++++++++++++++++++ |
---|
| 53 | |
---|
| 54 | In pyOASIS, components are instances of the **Component** class. To |
---|
| 55 | initialise a component, its name has to be supplied :: |
---|
| 56 | |
---|
| 57 | import pyoasis |
---|
| 58 | component_name = "component" |
---|
| 59 | comp = pyoasis.Component(component_name) |
---|
| 60 | |
---|
| 61 | It is also possible to provide an optional ``coupling_flag`` argument which |
---|
| 62 | defaults to "Trueâ, which means the component is coupled through OASIS3-MCT:: |
---|
| 63 | |
---|
| 64 | import pyoasis |
---|
| 65 | component_name = "component" |
---|
| 66 | coupling_flag = True |
---|
| 67 | comp = pyoasis.Component(component_name, coupling_flag) |
---|
| 68 | |
---|
| 69 | OASIS3-MCT couples models which communicate using MPI. By default, the |
---|
| 70 | **Component** class will set up MPI internally and provides methods |
---|
| 71 | to get access to information such as rank and number of processes in |
---|
| 72 | the local communicator gathering only the component processes :: |
---|
| 73 | |
---|
| 74 | import pyoasis |
---|
| 75 | |
---|
| 76 | comp = pyoasis.Component("component") |
---|
| 77 | |
---|
| 78 | print("Hello world from process " + str(comp.localcomm.rank) |
---|
| 79 | + " of " + str(comp.localcomm.size)) |
---|
| 80 | |
---|
| 81 | |
---|
| 82 | If the user needs to specify that the global communicator gathering at start all components of the coupled model is different from the default MPI COMM WORLD, this can be passed to the **Component** class through the communicator |
---|
| 83 | optional argument. This should be created with ``mpi4py``. :: |
---|
| 84 | |
---|
| 85 | import pyoasis |
---|
| 86 | from mpi4py import MPI |
---|
| 87 | [...] |
---|
| 88 | comm = my_global_comm |
---|
| 89 | |
---|
| 90 | component_name = "component" |
---|
| 91 | coupling_flag = True |
---|
| 92 | comp = pyoasis.Component(component_name, coupling_flag, comm) |
---|
| 93 | |
---|
| 94 | To create a coupling communicator for a subset of processes, one can |
---|
| 95 | use the method ``create_couplcomm``, with a flag being ``True`` for all these |
---|
| 96 | processes: :: |
---|
| 97 | coupled = True |
---|
| 98 | if local_comm_size > 3: |
---|
| 99 | if local_comm_rank >= local_comm_size - 2: |
---|
| 100 | coupled = False |
---|
| 101 | comp.create_couplcomm(coupled) |
---|
| 102 | |
---|
| 103 | If such a communicator already exists in the code, it should simply be |
---|
| 104 | provided to OASIS3-MCT with the method ``set_couplcomm``. Notice that the processes not involved in the coupling should still invoke this method providing the ``MPI.COMM_NULL`` communicator (predefined in ``mpi4py``): :: |
---|
| 105 | couplcomm = comp.localcomm.Split(icpl, local_comm_rank) |
---|
| 106 | if icpl == 0: |
---|
| 107 | couplcomm = MPI.COMM_NULL |
---|
| 108 | comp.set_couplcomm(couplcomm) |
---|
| 109 | |
---|
| 110 | To set up an MPI intra-communicator or inter-communicator between the local component and |
---|
| 111 | another component, e.g. the component ``receiver`` in the example |
---|
| 112 | below, one can use ``get_intracomm`` or ``get_intercomm``: :: |
---|
| 113 | intracomm = comp.get_intracomm("receiver") |
---|
| 114 | intercomm = comp.get_intercomm("receiver") |
---|
| 115 | |
---|
| 116 | To set up an MPI intra-communicator among some of the coupled components, listed in the ``comp_list`` list, |
---|
| 117 | one can use:: |
---|
| 118 | intracomm, root_ranks = comp.get_multi_intracomm(comp_list) |
---|
| 119 | |
---|
| 120 | where ``root_ranks`` is a dictionary (the keys are the elements of ``comp_list``) providing the ranks of the component roots in the intra-communicator. |
---|
| 121 | |
---|
| 122 | The current OASIS3-MCT internal debug level (``$NLOGPRT`` |
---|
| 123 | value in the ``namcouple``), can be retrieved as a property of a component, |
---|
| 124 | namely ``comp.debug_level``, as in: :: |
---|
| 125 | print("PyOasis debug level set to {}".format(comp.debug_level) |
---|
| 126 | |
---|
| 127 | and can be changed by directly modifying, the ``debug_level`` property |
---|
| 128 | of the component:: |
---|
| 129 | comp.debug_level = 2 |
---|
| 130 | |
---|
| 131 | Creating a partition |
---|
| 132 | ++++++++++++++++++++ |
---|
| 133 | |
---|
| 134 | The data can be partitioned in various ways. |
---|
| 135 | These correspond to the **SerialPartition**, **ApplePartition**, |
---|
| 136 | **BoxPartition**, **OrangePartition** and **PointsPartition** |
---|
| 137 | classes which are inherited from the **Partition** abstract class. |
---|
| 138 | For details on the different ways to describe the partitions, |
---|
| 139 | see OASIS3-MCT User Guide, section 2.2.3 and examples ``1-serial``, |
---|
| 140 | ``2-apple``, ``3-box``, ``4-orange``, ``5-points`` in ``pyoasis/examples``. |
---|
| 141 | |
---|
| 142 | The simplest situation is the serial partitioning where all the data is |
---|
| 143 | held by a single process and only the number of points has to be |
---|
| 144 | specified (see example ``1-serial``) :: |
---|
| 145 | |
---|
| 146 | n_points = 1600 |
---|
| 147 | serial_partition = pyoasis.SerialPartition(n_points) |
---|
| 148 | |
---|
| 149 | In the case of the apple partitioning, each process contains a segment |
---|
| 150 | of a linear domain. To initialise such a partitioning, an offset has to |
---|
| 151 | be supplied for each rank as well as the number of data points that |
---|
| 152 | will be stored locally. The following example, close to example ``2-apple``, if run with 4 processes, |
---|
| 153 | will produce 4 consecutive local segments containing 400 data points :: |
---|
| 154 | |
---|
| 155 | component_name = "component" |
---|
| 156 | comp = pyoasis.Component(component_name) |
---|
| 157 | rank = comp.localcomm.rank |
---|
| 158 | size = comp.localcomm.size |
---|
| 159 | n_points = 1600 |
---|
| 160 | local_size = int(n_points/size) |
---|
| 161 | offset = rank * local_size |
---|
| 162 | partition = pyoasis.ApplePartition(offset, local_size) |
---|
| 163 | |
---|
| 164 | When we use the box partitioning, a 2-dimensional domain is split |
---|
| 165 | into several rectangles. The global offset, local extents in the x and |
---|
| 166 | y directions and the global extent in the x direction have to be supplied |
---|
| 167 | to the constructor. The global offset is the index of the corner of the local |
---|
| 168 | rectangle. For example, we can split a 4x4 square domain into 4 2x2 parts with |
---|
| 169 | the following code that will have to be executed using 4 processes. The |
---|
| 170 | offset is computed from the global and local domain sizes as well as |
---|
| 171 | the rank. Another example of a box partition is available in example ``3-box`` :: |
---|
| 172 | |
---|
| 173 | rank = comp.localcomm.rank |
---|
| 174 | n_global_points_per_side = 4 |
---|
| 175 | n_partitions_per_side = 2 |
---|
| 176 | local_extent = n_global_points_per_side / n_partitions_per_side |
---|
| 177 | i_partition_x = rank / n_partitions_per_side |
---|
| 178 | i_partition_y = rank % n_partitions_per_side |
---|
| 179 | global_offset = i_partition_x * n_global_points_per_side * local_extent |
---|
| 180 | + i_partition_y * local_extent |
---|
| 181 | global_extent_x = n_global_points_per_side |
---|
| 182 | partition = pyoasis.BoxPartition(global_offset, local_extent, local_extent, |
---|
| 183 | global_extent_x) |
---|
| 184 | |
---|
| 185 | The orange partitioning consists of several segments of a linear domain. |
---|
| 186 | As a consequence, a list of offsets and local sizes have to be provided. |
---|
| 187 | In this example, each process contains 2 consecutive segments of 2 |
---|
| 188 | points. (Another example with only one segment per process is available in example ``4-orange``.) :: |
---|
| 189 | |
---|
| 190 | rank = comp.localcomm.rank |
---|
| 191 | n_segments_per_rank = 2 |
---|
| 192 | n_points_per_segment = 2 |
---|
| 193 | offset_beginning = rank * n_segments_per_rank * n_points_per_segment |
---|
| 194 | offset = [offset_beginning, offset_beginning + n_points_per_segment] |
---|
| 195 | extents = [n_points_per_segment, n_points_per_segment] |
---|
| 196 | partition = pyoasis.OrangePartition(offsets, extents) |
---|
| 197 | |
---|
| 198 | |
---|
| 199 | The last type of partitioning is points, where we have to |
---|
| 200 | specify, in a list, the global indices of the points stored by the |
---|
| 201 | process. (Another example with only one segment per process is |
---|
| 202 | available in example ``5-points``.) :: |
---|
| 203 | |
---|
| 204 | global_indices=[0, 1, 2, 3] |
---|
| 205 | partition = pyoasis.PointsPartition(global_indices) |
---|
| 206 | |
---|
| 207 | Defining the coupling grids |
---|
| 208 | +++++++++++++++++++++++++++ |
---|
| 209 | |
---|
| 210 | The grid data files, containing the definition of the grids onto which |
---|
| 211 | the coupling data is defined, can be created by the user before the |
---|
| 212 | run or can be written directly at run time by the components, either |
---|
| 213 | by one component process to write the whole grid or by each process |
---|
| 214 | holding a part of a grid. Details |
---|
| 215 | about the grid definition can be found in section 2.2.4 of OASIS3-MCT |
---|
| 216 | User Guide. A full example of writing a grid in sequential and |
---|
| 217 | parallel models can be found in ``examples/10-grid`` . |
---|
| 218 | |
---|
| 219 | To initialise a grid and write the grid longitudes and latitudes, one |
---|
| 220 | has to create an instance of the **Grid** class: :: |
---|
| 221 | [...] |
---|
| 222 | lon = np.array([-180. + comm_rank*nx_loc*dx + float(i)*dx + |
---|
| 223 | dx/2.0 for i in range(nx_loc)], dtype=np.float64) |
---|
| 224 | lon = np.tile(lon, (ny_loc, 1)).T |
---|
| 225 | |
---|
| 226 | lat = np.array([-90.0 + float(j)*dy + dy/2.0 for j in range(ny_loc)], |
---|
| 227 | dtype=np.float64) |
---|
| 228 | lat = np.tile(lat, (nx_loc, 1)) |
---|
| 229 | grid = pyoasis.Grid('pyoa', nx_global, ny_global, lon, lat, partition) |
---|
| 230 | |
---|
| 231 | To write the grid cell corner longitudes and latitudes, the |
---|
| 232 | ``set_corners`` method can be used :: |
---|
| 233 | [...] |
---|
| 234 | ncrn = 4 |
---|
| 235 | clo = pyoasis.asarray(np.zeros((nx_loc, ny_loc, ncrn), dtype=np.float64)) |
---|
| 236 | clo[:, :, 0] = lon[:, :] - dx/2.0 |
---|
| 237 | clo[:, :, 1] = lon[:, :] + dx/2.0 |
---|
| 238 | clo[:, :, 2] = clo[:, :, 1] |
---|
| 239 | clo[:, :, 3] = clo[:, :, 0] |
---|
| 240 | cla = pyoasis.asarray(np.zeros((nx_loc, ny_loc, ncrn), dtype=np.float64)) |
---|
| 241 | cla[:, :, 0] = lat[:, :] - dy/2.0 |
---|
| 242 | cla[:, :, 1] = cla[:, :, 0] |
---|
| 243 | cla[:, :, 2] = lat[:, :] + dy/2.0 |
---|
| 244 | cla[:, :, 3] = cla[:, :, 2] |
---|
| 245 | |
---|
| 246 | grid.set_corners(clo, cla) |
---|
| 247 | |
---|
| 248 | To write the grid cell areas, the |
---|
| 249 | ``set_area`` method can be used : :: |
---|
| 250 | [...] |
---|
| 251 | area = np.zeros((nx_loc, ny_loc), dtype=np.float64) |
---|
| 252 | area[:, :] = dp_conv * \ |
---|
| 253 | np.abs(np.sin(cla[:, :, 2] * dp_conv) - |
---|
| 254 | np.sin(cla[:, :, 0] * dp_conv)) * \ |
---|
| 255 | np.abs(clo[:, :, 1] - clo[:, :, 0]) |
---|
| 256 | |
---|
| 257 | grid.set_area(area) |
---|
| 258 | |
---|
| 259 | To define the mask of the grid, the ``set_mask`` method can be used (here |
---|
| 260 | a mask where all points have zero value i.e. are valid). Notice the optional |
---|
| 261 | argument ``companion`` providing the name of the corresponding ocean |
---|
| 262 | grid from which the masks and fractions are obtained: :: |
---|
| 263 | msk = np.zeros((nx_loc, ny_loc), dtype=np.int32) |
---|
| 264 | grid.set_mask(msk, companion=None) |
---|
| 265 | |
---|
| 266 | To define the grid cell water fraction, |
---|
| 267 | the ``set_frac`` method can be used: :: |
---|
| 268 | frc = np.ones((nx_loc, ny_loc), dtype=np.float64) |
---|
| 269 | frc = np.where(msk == 1, 0.0, 1.0) |
---|
| 270 | grid.set_frac(frc, companion=None) |
---|
| 271 | |
---|
| 272 | To define the grid cell angles, |
---|
| 273 | the ``set_angle`` method can be used: :: |
---|
| 274 | angle = np.zeros((nx_loc, ny_loc), dtype=np.float64) |
---|
| 275 | grid.set_angle(angle) |
---|
| 276 | |
---|
| 277 | |
---|
| 278 | Declaring the coupling data |
---|
| 279 | +++++++++++++++++++++ |
---|
| 280 | |
---|
| 281 | The coupling data is handled by the class **Var**. Its constructor requires |
---|
| 282 | its symbolic name, as it appears in the ``namcouple`` file, the partition and a flag indicating whether the |
---|
| 283 | data is incoming or outgoing. The latter is an enumerated type and can |
---|
| 284 | have the values ``pyoasis.OasisParameters.OASIS_OUT`` or |
---|
| 285 | ``pyoasis.OasisParameters.OASIS_IN``. In the following example, we wish |
---|
| 286 | to send data to a process having the rank 1 and we use a partition that was |
---|
| 287 | previously created.:: |
---|
| 288 | data_name = "name" |
---|
| 289 | variable = pyoasis.Var(data_name, partition, |
---|
| 290 | pyoasis.OasisParameters.OASIS_OUT) |
---|
| 291 | |
---|
| 292 | In the case of the receiving model, the code is: :: |
---|
| 293 | |
---|
| 294 | data_name = "name" |
---|
| 295 | variable = pyoasis.Var(data_name, partition, |
---|
| 296 | pyoasis.OasisParameters.OASIS_IN) |
---|
| 297 | |
---|
| 298 | The property ``is_active`` can be tested to check if the variable is |
---|
| 299 | activated in the ``namcouple`` configuring file: :: |
---|
| 300 | variable2 = pyoasis.Var("NOTANAME", partition, OASIS.OUT) |
---|
| 301 | if variable2.is_active: |
---|
| 302 | print("{} is active".format(variable2)) |
---|
| 303 | else: |
---|
| 304 | print("{} is not active".format(variable2)) |
---|
| 305 | |
---|
| 306 | The coupling period(s) of the data, as defined in the ``namcouple``, can be |
---|
| 307 | accessed with the property ``cpl_freqs`` and the number of coupling exchanges in |
---|
| 308 | which the data is involved by ``len(cpl_freqs)``:: |
---|
| 309 | var_1 = pyoasis.Var("FRECVATM_1", partition, OASIS.IN) |
---|
| 310 | print("Recv_one: coupling frequencies for {} = ".format(var_1.name), |
---|
| 311 | var_1.cpl_freqs) |
---|
| 312 | |
---|
| 313 | The method ``put_inquire`` of the variable tells what would happen |
---|
| 314 | to the corresponding data at that date |
---|
| 315 | below the corresponding send action. This maybe useful if, for |
---|
| 316 | example, the calculation of a coupling field is costly and if one |
---|
| 317 | wants to compute it only when it is really sent out. The |
---|
| 318 | different possible return codes are listed in section 2.2.9 of |
---|
| 319 | OASIS3-MCT User Guide. :: |
---|
| 320 | |
---|
| 321 | for date in range(43200): |
---|
| 322 | if var_1.put_inquire(date) == OASIS.SENT: |
---|
| 323 | var_1.put(date, pyoasis.asarray([date], dtype=np.float64)) |
---|
| 324 | [...] |
---|
| 325 | |
---|
| 326 | |
---|
| 327 | Ending the definition phase |
---|
| 328 | +++++++++++++++++++++ |
---|
| 329 | |
---|
| 330 | We must end the definition of the component by calling the ``enddef()`` |
---|
| 331 | method. :: |
---|
| 332 | |
---|
| 333 | comp.enddef() |
---|
| 334 | |
---|
| 335 | However this must be done only once the partitioning and the variable data have been initalised. |
---|
| 336 | |
---|
| 337 | |
---|
| 338 | Sending and receiving data |
---|
| 339 | ++++++++++++++++++++++++++ |
---|
| 340 | |
---|
| 341 | pyOASIS expects data to be provided as a **pyoasis.asarray** object: |
---|
| 342 | :: |
---|
| 343 | field = pyoasis.asarray(range(n_points)) |
---|
| 344 | |
---|
| 345 | This is a Numpy array but ordered in the Fortran way. |
---|
| 346 | In C, multidimensional arrays store data in row-major order where |
---|
| 347 | contiguous elements are accessed by incrementing the rightmost index |
---|
| 348 | while varying the other indices will correspond to increasing strides in |
---|
| 349 | memory as we use indices further towards the left. By default, Numpy arrays |
---|
| 350 | use that ordering as well. Fortran, on the other hand, uses column-major |
---|
| 351 | order. In that case, contiguous elements are accessed by incrementing |
---|
| 352 | the leftmost index. **pyoasis.asarray** objects use the same ordering as |
---|
| 353 | Fortran. As a consequence, it is not necessary to transform data in order to |
---|
| 354 | use it in the OASIS3-MCT Fortran library. :: |
---|
| 355 | |
---|
| 356 | The sending and receiving actions may be called by the component at |
---|
| 357 | each timestep. The date argument is automatically analysed and actions |
---|
| 358 | are actually performed only if date corresponds to a time for which it |
---|
| 359 | should be activated, given the period indicated by the user in the |
---|
| 360 | namcouple. See OASIS3-MCT User Guide section 2.2.7 for details. :: |
---|
| 361 | |
---|
| 362 | The data is sent with the following function. :: |
---|
| 363 | |
---|
| 364 | date = int(0) |
---|
| 365 | variable.put(date, field) |
---|
| 366 | |
---|
| 367 | Conversely, it is received with the function :: |
---|
| 368 | |
---|
| 369 | variable.get(date, field) |
---|
| 370 | |
---|
| 371 | This will fill the |
---|
| 372 | **pyoasis.asarray** object. |
---|
| 373 | |
---|
| 374 | Termination |
---|
| 375 | ++++++++++ |
---|
| 376 | |
---|
| 377 | Finally, the coupling is terminated in the destructor of |
---|
| 378 | the component: :: |
---|
| 379 | del comp |
---|
| 380 | |
---|
| 381 | |
---|
| 382 | Exceptions and aborting |
---|
| 383 | ++++++++++ |
---|
| 384 | |
---|
| 385 | When an error occurs in OASIS3-MCT, the code coupler returns an error |
---|
| 386 | code and an **OasisException** is raised. In practice, OASIS3-MCT will |
---|
| 387 | internally handle the error, write an error message in its |
---|
| 388 | debug log files and to the screen, and abort before the exception is raised. It |
---|
| 389 | may also happen that the code aborts before the error message appears |
---|
| 390 | on the screen. |
---|
| 391 | |
---|
| 392 | When an error is caught by the pyOASIS wrapper, such as an incorrect parameter |
---|
| 393 | or a wrong argument type, a **PyOasisException** is raised. |
---|
| 394 | |
---|
| 395 | In the following example, where we attempt to initialise a component, |
---|
| 396 | a **PyOasisException** will be raised as the user supplies an empty |
---|
| 397 | name : :: |
---|
| 398 | |
---|
| 399 | try: |
---|
| 400 | comp = pyoasis.Component("") |
---|
| 401 | except (pyoasis.PyOasisException) as exception: |
---|
| 402 | pyoasis.pyoasis_abort(exception) |
---|
| 403 | |
---|
| 404 | The function ``pyoasis.pyoasis_abort`` takes an exception as argument. |
---|
| 405 | It stops the execution of all the processes after having displayed an error message and |
---|
| 406 | written information in the log files about the error and the context in |
---|
| 407 | which it took place. |
---|
| 408 | |
---|
| 409 | Another function is available, ``pyoasis.oasis_abort``, |
---|
| 410 | for the cases where a voluntary abort is needed in the code where or |
---|
| 411 | not an exception has been raised. Its interface mimics the |
---|
| 412 | corresponding OASIS3-MCT functio ``oasis_abort``. |
---|
| 413 | |
---|
| 414 | |
---|
| 415 | Examples |
---|
| 416 | -------- |
---|
| 417 | |
---|
| 418 | Serial partitions |
---|
| 419 | +++++++++++++++++ |
---|
| 420 | |
---|
| 421 | The example in examples/1-serial/python, containing the full source code as well as the namcouple file and a script |
---|
| 422 | to run this example, consists in two models, one sending data to |
---|
| 423 | another. |
---|
| 424 | The sender and receiver start in the same way. |
---|
| 425 | |
---|
| 426 | -Import pyOASIS and initialise the MPI communicator :: |
---|
| 427 | |
---|
| 428 | import pyoasis |
---|
| 429 | from mpi4py import MPI |
---|
| 430 | comm = MPI.COMM_WORLD |
---|
| 431 | |
---|
| 432 | -Initialisation of the component :: |
---|
| 433 | |
---|
| 434 | comp = pyoasis.Component(component_name) |
---|
| 435 | |
---|
| 436 | -Initialisation of the serial partition :: |
---|
| 437 | |
---|
| 438 | partition = pyoasis.SerialPartition(n_points) |
---|
| 439 | |
---|
| 440 | -From this point, the sender and the receiver start to |
---|
| 441 | differ. In the sender, the variable data is initialised |
---|
| 442 | by :: |
---|
| 443 | |
---|
| 444 | variable = pyoasis.Var("FSENDOCN", partition, |
---|
| 445 | pyoasis.OasisParameters.OASIS_OUT) |
---|
| 446 | |
---|
| 447 | whereas, in the receiver, we have :: |
---|
| 448 | |
---|
| 449 | variable = pyoasis.Var("FRECVATM", partition, |
---|
| 450 | pyoasis.OasisParameters.OASIS_IN) |
---|
| 451 | |
---|
| 452 | where the last flag is instead ``pyoasis.OasisParameters.OASIS_IN`` |
---|
| 453 | to indicate that, in this case, the data will be incoming. |
---|
| 454 | |
---|
| 455 | In both scripts, the initialisation of the component ends by :: |
---|
| 456 | |
---|
| 457 | comp.enddef() |
---|
| 458 | |
---|
| 459 | In the sender, the data is subsequently transmitted by :: |
---|
| 460 | |
---|
| 461 | time_in_the_model = int(0) |
---|
| 462 | field = pyoasis.asarray(range(n_points)) |
---|
| 463 | variable.put(date, field) |
---|
| 464 | |
---|
| 465 | while, in the receiver, it is recovered by :: |
---|
| 466 | |
---|
| 467 | time_in_the_model = int(0) |
---|
| 468 | field = pyoasis.asarray(numpy.zeros(n_points)) |
---|
| 469 | variable.get(date, field) |
---|
| 470 | |
---|
| 471 | |
---|
| 472 | Apple and orange partitions |
---|
| 473 | +++++++++++++++++++++++++++ |
---|
| 474 | |
---|
| 475 | In the example in ``examples/6-apple_and_orange/python``, both models run as several processes. The beginning |
---|
| 476 | of the code is identical to the previous example. We will highlight |
---|
| 477 | only the differences. In the sender, the data is split according to the apple partitioning :: |
---|
| 478 | |
---|
| 479 | partition = pyoasis.ApplePartition(offset, local_size) |
---|
| 480 | |
---|
| 481 | whereas the receiver uses the orange partitioning. :: |
---|
| 482 | |
---|
| 483 | partition = pyoasis.OrangePartition(offsets, extents) |
---|
| 484 | |
---|
| 485 | In both cases, the offsets and sizes of the local part of the |
---|
| 486 | data have to be specified. Each process subsequently transmits and |
---|
| 487 | receives its share of the data as previously. In the sender, we have :: |
---|
| 488 | |
---|
| 489 | date = int(0) |
---|
| 490 | field = pyoasis.asarray(numpy.zeros(local_size)) |
---|
| 491 | for i in range(local_size): |
---|
| 492 | field[i] = offset + i |
---|
| 493 | variable.put(date, field) |
---|
| 494 | |
---|
| 495 | |
---|
| 496 | while, in the receiver, :: |
---|
| 497 | |
---|
| 498 | date = int(0) |
---|
| 499 | field = pyoasis.asarray(numpy.zeros(extent)) |
---|
| 500 | variable.get(date, field) |
---|
| 501 | |
---|
| 502 | |
---|
| 503 | Fortran and Python interoperability |
---|
| 504 | +++++++++++++++++++++++++++++++++++ |
---|
| 505 | |
---|
| 506 | In order to illustrate the possibility to couple models written in |
---|
| 507 | Python and in Fortran, the previous example is repeated in pyoasis/examples/8-interoperability/fortran_and_python but this time, the sender |
---|
| 508 | has been written in Fortran. The receiver is the same as above. |
---|
| 509 | |
---|
| 510 | The sender consists in an analogous sequence using the OASIS3-MCT |
---|
| 511 | Fortran API |
---|
| 512 | |
---|
| 513 | -Initialisation of the component :: |
---|
| 514 | |
---|
| 515 | call oasis_init_comp(comp_id, comp_name, kinfo) |
---|
| 516 | |
---|
| 517 | -Initialisation of the apple partition with the relevant offset and |
---|
| 518 | local size :: |
---|
| 519 | |
---|
| 520 | part_params=[1, offset, local_size] |
---|
| 521 | call oasis_def_partition(part_id, part_params, kinfo) |
---|
| 522 | |
---|
| 523 | -Creation of the variable data, a bundle with two fields :: |
---|
| 524 | |
---|
| 525 | var_nodims=[1, 2] |
---|
| 526 | call oasis_def_var(var_id, var_name, part_id, var_nodims, OASIS_OUT, |
---|
| 527 | [1], OASIS_REAL, kinfo) |
---|
| 528 | |
---|
| 529 | -End of the definition of the component :: |
---|
| 530 | |
---|
| 531 | call oasis_enddef(kinfo) |
---|
| 532 | |
---|
| 533 | -Transmission of the local part of the data to the other component :: |
---|
| 534 | |
---|
| 535 | call oasis_put(var_id, date, bundle, kinfo) |
---|
| 536 | |
---|
| 537 | -End of the coupling :: |
---|
| 538 | |
---|
| 539 | call oasis_terminate(kinfo) |
---|
| 540 | |
---|
| 541 | |
---|
| 542 | API reference |
---|
| 543 | ------------- |
---|
| 544 | |
---|
| 545 | .. autoclass:: pyoasis.Component |
---|
| 546 | :members: |
---|
| 547 | |
---|
| 548 | .. autoclass:: pyoasis.SerialPartition |
---|
| 549 | |
---|
| 550 | .. autoclass:: pyoasis.ApplePartition |
---|
| 551 | |
---|
| 552 | .. autoclass:: pyoasis.BoxPartition |
---|
| 553 | |
---|
| 554 | .. autoclass:: pyoasis.OrangePartition |
---|
| 555 | |
---|
| 556 | .. autoclass:: pyoasis.PointsPartition |
---|
| 557 | |
---|
| 558 | .. autofunction:: pyoasis.OasisParameters() |
---|
| 559 | |
---|
| 560 | .. autofunction:: pyoasis.asarray |
---|
| 561 | |
---|
| 562 | .. autoclass:: pyoasis.Var |
---|
| 563 | :members: |
---|
| 564 | |
---|
| 565 | .. autoclass:: pyoasis.Grid |
---|
| 566 | :members: |
---|
| 567 | |
---|
| 568 | .. autoclass:: pyoasis.OasisException |
---|
| 569 | |
---|
| 570 | .. autoclass:: pyoasis.PyOasisException |
---|
| 571 | |
---|
| 572 | .. autofunction:: pyoasis.pyoasis_abort |
---|
| 573 | |
---|
| 574 | .. autofunction:: pyoasis.oasis_abort |
---|
| 575 | |
---|
| 576 | |
---|
| 577 | Correspondence with the OASIS3-MCT Fortran API |
---|
| 578 | --------------------------------------- |
---|
| 579 | |
---|
| 580 | These tables show the correspondence between the functions |
---|
| 581 | described in the OASIS3-MCT user guide and their analogue in pyoasis. |
---|
| 582 | All the functions have been implemented with their full interface |
---|
| 583 | except ``put`` which, in the case of pyoasis, accepts only |
---|
| 584 | a single field. |
---|
| 585 | |
---|
| 586 | |
---|
| 587 | Component |
---|
| 588 | +++++++++ |
---|
| 589 | |
---|
| 590 | +------------------------------------+-----------------------------------+ |
---|
| 591 | | OASIS3-MCT | pyoasis.Component. | |
---|
| 592 | +====================================+===================================+ |
---|
| 593 | | oasis_init_comp(comp_id, comp_name,| __init__(name, coupled=True, | |
---|
| 594 | | ierror, coupled, comm_world) | communicator=None) | |
---|
| 595 | +------------------------------------+-----------------------------------+ |
---|
| 596 | | oasis_terminate(ierror) | __del__() | |
---|
| 597 | +------------------------------------+-----------------------------------+ |
---|
| 598 | | oasis_create_couplcomm(icpl, | create_couplcomm(coupled) | |
---|
| 599 | | localcomm, couplcomm, kinfo) | | |
---|
| 600 | +------------------------------------+-----------------------------------+ |
---|
| 601 | | oasis_set_couplcomm(couplcomm, | set_couplcomm(couplcomm) | |
---|
| 602 | | kinfo) | | |
---|
| 603 | +------------------------------------+-----------------------------------+ |
---|
| 604 | | oasis_get_multi_intracomm(newcomm, | get_multi_intracomm(complist) | |
---|
| 605 | | cdnam, root_ranks, kinfo) | | |
---|
| 606 | +------------------------------------+-----------------------------------+ |
---|
| 607 | | oasis_get_intracomm(newcomm, cdnam,| get_intracomm(compname) | |
---|
| 608 | | kinfo) | | |
---|
| 609 | +------------------------------------+-----------------------------------+ |
---|
| 610 | | oasis_get_intercomm(newcomm, | get_intercomm(compname) | |
---|
| 611 | | cdnam, kinfo) | | |
---|
| 612 | +------------------------------------+-----------------------------------+ |
---|
| 613 | | oasis_enddef(ierror) | enddef() | |
---|
| 614 | +------------------------------------+-----------------------------------+ |
---|
| 615 | | oasis_get_local_comm(local_comm, | localcomm | |
---|
| 616 | | ierror) | | |
---|
| 617 | +------------------------------------+-----------------------------------+ |
---|
| 618 | | oasis_get_debug(debugvalue) | debug_level | |
---|
| 619 | +------------------------------------+-----------------------------------+ |
---|
| 620 | | oasis_set_debug(debugvalue) | debug_level | |
---|
| 621 | +------------------------------------+-----------------------------------+ |
---|
| 622 | |
---|
| 623 | Partition |
---|
| 624 | +++++++++ |
---|
| 625 | |
---|
| 626 | +------------------------------------+-----------------------------------+ |
---|
| 627 | | OASIS3-MCT | pyoasis. | |
---|
| 628 | +====================================+===================================+ |
---|
| 629 | | oasis_def_partition(ilpart_id, | SerialPartition(size, | |
---|
| 630 | | igparal, ierror, isize, name) | global_size=-1, name="") | |
---|
| 631 | | | ApplePartition(offset, size, | |
---|
| 632 | | | global_size=-1, name="") | |
---|
| 633 | | | BoxPartition(global_offset, | |
---|
| 634 | | | local_extent_x, local_extent_y, | |
---|
| 635 | | | global_extent_x, global_size=-1,| |
---|
| 636 | | | name="") | |
---|
| 637 | | | OrangePartition(offsets, extents, | |
---|
| 638 | | | global_size=-1, name="") | |
---|
| 639 | | | PointsPartition(global_indices, | |
---|
| 640 | | | global_size=-1, name="") | |
---|
| 641 | +------------------------------------+-----------------------------------+ |
---|
| 642 | |
---|
| 643 | Var |
---|
| 644 | +++ |
---|
| 645 | |
---|
| 646 | +------------------------------------+-----------------------------------+ |
---|
| 647 | | OASIS3-MCT | pyoasis.var. | |
---|
| 648 | +====================================+===================================+ |
---|
| 649 | | oasis_def_var(var_id, name, | __init__(name, partition, inout, | |
---|
| 650 | | il_part_id, var_nodims, kinout, | bundle_size=1) | |
---|
| 651 | | var_actual_shape, vartype, | | |
---|
| 652 | | ierror) | | |
---|
| 653 | +------------------------------------+-----------------------------------+ |
---|
| 654 | | oasis_put(varid, date, fld1, info, | put(date, field, | |
---|
| 655 | | fld2, fld3, fld4, fld5, | write_restart=False) | |
---|
| 656 | | write_restart) | | |
---|
| 657 | +------------------------------------+-----------------------------------+ |
---|
| 658 | | oasis_get(varid, date, fld, info) | get(date, field) | |
---|
| 659 | +------------------------------------+-----------------------------------+ |
---|
| 660 | | oasis_put_inquire(varid, date, | put_inquire(date) | |
---|
| 661 | | kinfo) | | |
---|
| 662 | +------------------------------------+-----------------------------------+ |
---|
| 663 | | oasis_get_ncpl(varid, ncpl, kinfo) | len(cpl_freqs) | |
---|
| 664 | +------------------------------------+-----------------------------------+ |
---|
| 665 | | oasis_get_freqs(varid, mop, ncpl, | cpl_freqs | |
---|
| 666 | | cplfreqs, kinfo) | | |
---|
| 667 | +------------------------------------+-----------------------------------+ |
---|
| 668 | |
---|
| 669 | Grid |
---|
| 670 | ++++ |
---|
| 671 | |
---|
| 672 | +---------------------------------+------------------------------+ |
---|
| 673 | | OASIS3-MCT | pyoasis.grid. | |
---|
| 674 | +=================================+==============================+ |
---|
| 675 | | oasis_start_grids_writing(flag) | __init__(cgrid, nx_global, | |
---|
| 676 | | oasis_write_grid(cgrid, | ny_global, lon, lat, | |
---|
| 677 | | nx_global, ny_global, lon, | partition=None) | |
---|
| 678 | | lat, il_partid) | | |
---|
| 679 | +---------------------------------+------------------------------+ |
---|
| 680 | | oasis_write_corner(cgrid, | set_corners(clo, cla) | |
---|
| 681 | | nx_global, ny_global, nc, | | |
---|
| 682 | | clon, clat, il_partid) | | |
---|
| 683 | +---------------------------------+------------------------------+ |
---|
| 684 | | oasis_write_area(cgrid, | set_area(area) | |
---|
| 685 | | nx_global, ny_global, | | |
---|
| 686 | | area, il_partid) | | |
---|
| 687 | +---------------------------------+------------------------------+ |
---|
| 688 | | oasis_write_mask(cgrid, | set_mask(mask, | |
---|
| 689 | | nx_glo, ny_glo, mask, | companion=None) | |
---|
| 690 | | part_id, companion) | | |
---|
| 691 | +---------------------------------+------------------------------+ |
---|
| 692 | | oasis_write_frac(cgrid, | set_frac(frac, | |
---|
| 693 | | nx_glo, ny_glo, frac, | companion=None) | |
---|
| 694 | | part_id, companion) | | |
---|
| 695 | +---------------------------------+------------------------------+ |
---|
| 696 | | oasis_write_angle(cgrid, | set_angle(angle) | |
---|
| 697 | | nx_global, ny_global, | | |
---|
| 698 | | angle, il_partid) | | |
---|
| 699 | +---------------------------------+------------------------------+ |
---|
| 700 | | oasis_terminate_grids_writing() | write() | |
---|
| 701 | +---------------------------------+------------------------------+ |
---|
| 702 | |
---|
| 703 | Utilities |
---|
| 704 | +++++++++ |
---|
| 705 | |
---|
| 706 | +---------------------------------+-------------------------------+ |
---|
| 707 | | OASIS3-MCT | pyoasis. | |
---|
| 708 | +=================================+===============================+ |
---|
| 709 | |oasis_abort(compid, routinename, | oasis_abort(component_id, | |
---|
| 710 | | abortmessage, rcode) | routine, message, filename, | |
---|
| 711 | | | line, error) | |
---|
| 712 | | | pyoasis_abort(exception) | |
---|
| 713 | +---------------------------------+-------------------------------+ |
---|
| 714 | |
---|
| 715 | |
---|
| 716 | Installation |
---|
| 717 | ------------ |
---|
| 718 | |
---|
| 719 | |
---|
| 720 | Under GNU/Linux |
---|
| 721 | +++++++++++++++ |
---|
| 722 | |
---|
| 723 | Prerequisites |
---|
| 724 | ............. |
---|
| 725 | |
---|
| 726 | - A Fortran and C compiler suite |
---|
| 727 | - An MPI library |
---|
| 728 | - NetCDF 4 |
---|
| 729 | - Python 3 with mpi4py, numpy and netCDF4 |
---|
| 730 | - Extra optional packages for plotting (as in examples ``11-test-interpolation`` and ``12-grid-functions``) |
---|
| 731 | - matplotlib |
---|
| 732 | - GEOS (Geometry Engine, Open Source): |
---|
| 733 | package libgeos-dev under Debian or Ubuntu, geos-devel under Fedora |
---|
| 734 | - proj: |
---|
| 735 | package libproj-dev under Debian or Ubuntu, proj-devel under Fedora |
---|
| 736 | - Optional packages for generating the documentation: |
---|
| 737 | - sphinx |
---|
| 738 | - Tex Live |
---|
| 739 | - Optional package for automated testing: |
---|
| 740 | - pytest |
---|
| 741 | |
---|
| 742 | OASIS3-MCT Installation |
---|
| 743 | ....................... |
---|
| 744 | |
---|
| 745 | - Obtain OASIS3-MCT (refer to OASIS3-MCT User Guide for details). |
---|
| 746 | - Change directory to ${OASIS_ROOT}/util/make_dir. |
---|
| 747 | - Create your own make.inc file based on make.intel_davinci, make.gfortran_openmpi_linux or make.bindings. |
---|
| 748 | - Build and install OASIS3-MCT and pyOASIS:: |
---|
| 749 | |
---|
| 750 | make -f TopMakefile realclean |
---|
| 751 | make -f TopMakefile pyoasis |
---|
| 752 | |
---|
| 753 | - Append the lines displayed at the end of the compilation to your .bashrc file or, alternatively, before using pyOASIS, source the script mentioned there. |
---|
| 754 | |
---|
| 755 | Virtual Python environment |
---|
| 756 | ................ |
---|
| 757 | |
---|
| 758 | - Create a virtual environment (set ``VENVDIR`` as a directory of your choice containing the virtual environment): :: |
---|
| 759 | |
---|
| 760 | export VENVDIR=~/INSTALL/PY_ENV/PyO |
---|
| 761 | python3 -m venv ${VENVDIR} |
---|
| 762 | source ${VENVDIR}/bin/activate |
---|
| 763 | |
---|
| 764 | - Install packages: :: |
---|
| 765 | |
---|
| 766 | pip install --upgrade pip |
---|
| 767 | pip install mpi4py |
---|
| 768 | pip install numpy |
---|
| 769 | pip install netcdf4 |
---|
| 770 | |
---|
| 771 | Extra software |
---|
| 772 | .............. |
---|
| 773 | |
---|
| 774 | - For applications using Cartopy plots, as in the examples ``11-test-interpolation`` and ``12-grid-functions`` : :: |
---|
| 775 | |
---|
| 776 | pip install scipy |
---|
| 777 | pip install matplotlib |
---|
| 778 | pip uninstall shapely |
---|
| 779 | pip install shapely --no-binary shapely |
---|
| 780 | pip install cartopy |
---|
| 781 | |
---|
| 782 | - Optional package for performances optimisation: :: |
---|
| 783 | |
---|
| 784 | pip install pykdtree |
---|
| 785 | |
---|
| 786 | |
---|
| 787 | Under macOS |
---|
| 788 | +++++++++++ |
---|
| 789 | |
---|
| 790 | Prerequisites |
---|
| 791 | ............. |
---|
| 792 | |
---|
| 793 | Refer to the GNU/Linux section. |
---|
| 794 | |
---|
| 795 | If using Brew (tested with gnu up to version 10.2.0): :: |
---|
| 796 | |
---|
| 797 | brew install gcc |
---|
| 798 | brew install openblas |
---|
| 799 | brew install openmpi |
---|
| 800 | |
---|
| 801 | For the optional packages: :: |
---|
| 802 | |
---|
| 803 | brew install geos |
---|
| 804 | brew install proj |
---|
| 805 | |
---|
| 806 | |
---|
| 807 | OASIS3-MCT Installation |
---|
| 808 | ....................... |
---|
| 809 | |
---|
| 810 | Same as under GNU/Linux. See previous section. |
---|
| 811 | |
---|
| 812 | |
---|
| 813 | Virtual Python environment |
---|
| 814 | .......................... |
---|
| 815 | |
---|
| 816 | - Create a virtual environment: |
---|
| 817 | |
---|
| 818 | Same as under GNU/Linux, see previous section. |
---|
| 819 | |
---|
| 820 | - Install packages: :: |
---|
| 821 | |
---|
| 822 | pip install mpi4py |
---|
| 823 | pip uninstall numpy |
---|
| 824 | pip cache remove numpy |
---|
| 825 | OPENBLAS="$(brew --prefix openblas)" pip install --global-option=build-ext numpy |
---|
| 826 | pip install netcdf4 |
---|
| 827 | |
---|
| 828 | Extra software |
---|
| 829 | .............. |
---|
| 830 | |
---|
| 831 | - For applications using Cartopy plots, as in the examples ``11-test-interpolation`` and ``12-grid-functions``: :: |
---|
| 832 | |
---|
| 833 | pip uninstall scipy |
---|
| 834 | pip cache remove scipy |
---|
| 835 | pip install --global-option=build-ext scipy |
---|
| 836 | pip uninstall shapely |
---|
| 837 | pip install shapely --no-binary shapely |
---|
| 838 | pip install matplotlib |
---|
| 839 | pip install cartopy |
---|
| 840 | |
---|
| 841 | - Optional package for performances optimisation: :: |
---|
| 842 | |
---|
| 843 | pip install pykdtree |
---|
| 844 | |
---|
| 845 | Set up the environment |
---|
| 846 | ...................... |
---|
| 847 | At the end of your .bashrc, :: |
---|
| 848 | |
---|
| 849 | export TMPDIR=/tmp |
---|
| 850 | export OMPI_MCA_mca_base_env_list=LD_LIBRARY_PATH=${PYOASIS_ROOT}/lib |
---|
| 851 | |
---|
| 852 | |
---|
| 853 | Documentation |
---|
| 854 | +++++++++++++ |
---|
| 855 | |
---|
| 856 | If pyOASIS is modified, this document can be regenerated, using Sphinx, |
---|
| 857 | by typing the following command in the directory ${OASIS_ROOT}/pyoasis:: |
---|
| 858 | |
---|
| 859 | make doc |
---|
| 860 | |
---|
| 861 | |
---|
| 862 | Acknowledgments |
---|
| 863 | --------------- |
---|
| 864 | |
---|
| 865 | This work has been financed by the IS-ENES3 project which has received funding from the European Unionâs Horizon 2020 research and innovation programme under grant agreement No 824084. |
---|
| 866 | |
---|
| 867 | .. image:: euflag.png |
---|
| 868 | |
---|
| 869 | |
---|
| 870 | Index and search |
---|
| 871 | ---------------- |
---|
| 872 | |
---|
| 873 | * :ref:`genindex` |
---|
| 874 | * :ref:`search` |
---|