1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2 ! MODULE GRIDINFO_MODULE
4 ! This module handles (i.e., acquires, stores, and makes available) all data
5 ! describing the model domains to be processed.
6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10 use misc_definitions_module
14 integer, parameter :: MAX_DOMAINS = 21
17 integer :: iproj_type, n_domains, io_form_output, dyn_opt
18 integer, dimension(MAX_DOMAINS) :: parent_grid_ratio, parent_id, ixdim, jydim
19 integer, dimension(MAX_DOMAINS) :: subgrid_ratio_x, subgrid_ratio_y
20 real :: known_lat, known_lon, pole_lat, pole_lon, stand_lon, truelat1, truelat2, &
21 known_x, known_y, dxkm, dykm, phi, lambda, ref_lat, ref_lon, ref_x, ref_y, &
23 real, dimension(MAX_DOMAINS) :: parent_ll_x, parent_ll_y, parent_ur_x, parent_ur_y
24 character (len=MAX_FILENAME_LEN) :: geog_data_path, opt_output_from_geogrid_path, opt_geogrid_tbl_path
26 character (len=128), dimension(MAX_DOMAINS) :: geog_data_res
27 character (len=1) :: gridtype
28 logical :: do_tiled_output
29 logical, dimension(MAX_DOMAINS) :: grid_is_active
30 integer :: debug_level
34 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
35 ! Name: get_grid_params
37 ! Purpose: This subroutine retrieves all parameters regarding the model domains
38 ! to be processed by geogrid.exe. This includes map parameters, domain
39 ! size and location, and nest information.
40 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
41 subroutine get_grid_params()
46 integer :: i, j, max_dom, funit, io_form_geogrid, interval_seconds
47 real :: dx, dy, rparent_gridpts
48 integer, dimension(MAX_DOMAINS) :: i_parent_start, j_parent_start, &
49 s_we, e_we, s_sn, e_sn, &
50 start_year, start_month, start_day, start_hour, start_minute, start_second, &
51 end_year, end_month, end_day, end_hour, end_minute, end_second
52 character (len=128) :: map_proj
53 character (len=128), dimension(MAX_DOMAINS) :: start_date, end_date
54 character (len=3) :: wrf_core
55 logical :: is_used, nest_outside
56 logical, dimension(MAX_DOMAINS) :: active_grid
59 namelist /share/ wrf_core, max_dom, start_date, end_date, &
60 start_year, end_year, start_month, end_month, &
61 start_day, end_day, start_hour, end_hour, &
62 start_minute, end_minute, start_second, end_second, &
64 io_form_geogrid, opt_output_from_geogrid_path, &
65 debug_level, active_grid, &
66 subgrid_ratio_x, subgrid_ratio_y, &
68 namelist /geogrid/ parent_id, parent_grid_ratio, &
69 i_parent_start, j_parent_start, s_we, e_we, s_sn, e_sn, &
70 map_proj, ref_x, ref_y, ref_lat, ref_lon, &
71 pole_lat, pole_lon, truelat1, truelat2, stand_lon, &
72 dx, dy, geog_data_res, geog_data_path, opt_geogrid_tbl_path
74 ! Set defaults for namelist variables
79 geog_data_path = 'NOT_SPECIFIED'
93 geog_data_res(i) = 'default'
95 parent_grid_ratio(i) = INVALID
112 start_date(i) = '0000-00-00_00:00:00'
113 end_date(i) = '0000-00-00_00:00:00'
114 active_grid(i) = .true.
115 subgrid_ratio_x(i) = 1
116 subgrid_ratio_y(i) = 1
118 opt_output_from_geogrid_path = './'
119 opt_geogrid_tbl_path = 'geogrid/'
120 interval_seconds = INVALID
123 ! Read parameters from Fortran namelist
125 inquire(unit=funit, opened=is_used)
126 if (.not. is_used) exit
128 open(funit,file='namelist.wps',status='old',form='formatted',err=1000)
133 ! BUG: More properly handle debug_level in module_debug
134 if (debug_level.gt.100) then
135 call set_debug_level(DEBUG)
137 call set_debug_level(WARN)
140 call mprintf(.true.,LOGFILE,'Using the following namelist variables:')
141 call mprintf(.true.,LOGFILE,'&SHARE')
142 call mprintf(.true.,LOGFILE,' WRF_CORE = %s',s1=wrf_core)
143 call mprintf(.true.,LOGFILE,' MAX_DOM = %i',i1=max_dom)
144 call mprintf(.true.,LOGFILE,' START_YEAR = %i',i1=start_year(1))
146 call mprintf(.true.,LOGFILE,' = %i',i1=start_year(i))
148 call mprintf(.true.,LOGFILE,' START_MONTH = %i',i1=start_month(1))
150 call mprintf(.true.,LOGFILE,' = %i',i1=start_month(i))
152 call mprintf(.true.,LOGFILE,' START_DAY = %i',i1=start_day(1))
154 call mprintf(.true.,LOGFILE,' = %i',i1=start_day(i))
156 call mprintf(.true.,LOGFILE,' START_HOUR = %i',i1=start_hour(1))
158 call mprintf(.true.,LOGFILE,' = %i',i1=start_hour(i))
160 call mprintf(.true.,LOGFILE,' START_MINUTE = %i',i1=start_minute(1))
162 call mprintf(.true.,LOGFILE,' = %i',i1=start_minute(i))
164 call mprintf(.true.,LOGFILE,' START_SECOND = %i',i1=start_second(1))
166 call mprintf(.true.,LOGFILE,' = %i',i1=start_second(i))
168 call mprintf(.true.,LOGFILE,' END_YEAR = %i',i1=end_year(1))
170 call mprintf(.true.,LOGFILE,' = %i',i1=end_year(i))
172 call mprintf(.true.,LOGFILE,' END_MONTH = %i',i1=end_month(1))
174 call mprintf(.true.,LOGFILE,' = %i',i1=end_month(i))
176 call mprintf(.true.,LOGFILE,' END_DAY = %i',i1=end_day(1))
178 call mprintf(.true.,LOGFILE,' = %i',i1=end_day(i))
180 call mprintf(.true.,LOGFILE,' END_HOUR = %i',i1=end_hour(1))
182 call mprintf(.true.,LOGFILE,' = %i',i1=end_hour(i))
184 call mprintf(.true.,LOGFILE,' END_MINUTE = %i',i1=end_minute(1))
186 call mprintf(.true.,LOGFILE,' = %i',i1=end_minute(i))
188 call mprintf(.true.,LOGFILE,' END_SECOND = %i',i1=end_second(1))
190 call mprintf(.true.,LOGFILE,' = %i',i1=end_second(i))
192 call mprintf(.true.,LOGFILE,' START_DATE = %s',s1=start_date(1))
194 call mprintf(.true.,LOGFILE,' = %s',s1=start_date(i))
196 call mprintf(.true.,LOGFILE,' END_DATE = %s',s1=end_date(1))
198 call mprintf(.true.,LOGFILE,' = %s',s1=end_date(i))
200 call mprintf(.true.,LOGFILE,' INTERVAL_SECONDS = %i',i1=interval_seconds)
201 call mprintf(.true.,LOGFILE,' IO_FORM_GEOGRID = %i',i1=io_form_geogrid)
202 call mprintf(.true.,LOGFILE,' OPT_OUTPUT_FROM_GEOGRID_PATH = %s',s1=opt_output_from_geogrid_path)
203 call mprintf(.true.,LOGFILE,' SUBGRID_RATIO_X = %i',i1=subgrid_ratio_x(1))
205 call mprintf(.true.,LOGFILE,' = %i',i1=subgrid_ratio_x(i))
207 call mprintf(.true.,LOGFILE,' SUBGRID_RATIO_Y = %i',i1=subgrid_ratio_y(1))
209 call mprintf(.true.,LOGFILE,' = %i',i1=subgrid_ratio_y(i))
212 call mprintf(.true.,LOGFILE,' DEBUG_LEVEL = %i',i1=debug_level)
213 call mprintf(.true.,LOGFILE,' ACTIVE_GRID = %l',l1=active_grid(1))
215 call mprintf(.true.,LOGFILE,' = %l',l1=active_grid(i))
217 call mprintf(.true.,LOGFILE,' NOCOLONS = %l',l1=nocolons)
218 call mprintf(.true.,LOGFILE,'/')
220 call mprintf(.true.,LOGFILE,'&GEOGRID')
221 call mprintf(.true.,LOGFILE,' PARENT_ID = %i',i1=parent_id(1))
223 call mprintf(.true.,LOGFILE,' = %i',i1=parent_id(i))
225 call mprintf(.true.,LOGFILE,' PARENT_GRID_RATIO = %i',i1=parent_grid_ratio(1))
227 call mprintf(.true.,LOGFILE,' = %i',i1=parent_grid_ratio(i))
229 call mprintf(.true.,LOGFILE,' I_PARENT_START = %i',i1=i_parent_start(1))
231 call mprintf(.true.,LOGFILE,' = %i',i1=i_parent_start(i))
233 call mprintf(.true.,LOGFILE,' J_PARENT_START = %i',i1=j_parent_start(1))
235 call mprintf(.true.,LOGFILE,' = %i',i1=j_parent_start(i))
237 call mprintf(.true.,LOGFILE,' S_WE = %i',i1=s_we(1))
239 call mprintf(.true.,LOGFILE,' = %i',i1=s_we(i))
241 call mprintf(.true.,LOGFILE,' E_WE = %i',i1=e_we(1))
243 call mprintf(.true.,LOGFILE,' = %i',i1=e_we(i))
245 call mprintf(.true.,LOGFILE,' S_SN = %i',i1=s_sn(1))
247 call mprintf(.true.,LOGFILE,' = %i',i1=s_sn(i))
249 call mprintf(.true.,LOGFILE,' E_SN = %i',i1=e_sn(1))
251 call mprintf(.true.,LOGFILE,' = %i',i1=e_sn(i))
253 call mprintf(.true.,LOGFILE,' GEOG_DATA_RES = %s',s1=geog_data_res(1))
255 call mprintf(.true.,LOGFILE,' = %s',s1=geog_data_res(i))
257 call mprintf(.true.,LOGFILE,' DX = %f',f1=dx)
258 call mprintf(.true.,LOGFILE,' DY = %f',f1=dy)
259 call mprintf(.true.,LOGFILE,' MAP_PROJ = %s',s1=map_proj)
260 call mprintf(.true.,LOGFILE,' POLE_LAT = %f',f1=pole_lat)
261 call mprintf(.true.,LOGFILE,' POLE_LON = %f',f1=pole_lon)
262 call mprintf(.true.,LOGFILE,' REF_LAT = %f',f1=ref_lat)
263 call mprintf(.true.,LOGFILE,' REF_LON = %f',f1=ref_lon)
264 call mprintf(.true.,LOGFILE,' REF_X = %f',f1=ref_x)
265 call mprintf(.true.,LOGFILE,' REF_Y = %f',f1=ref_y)
266 call mprintf(.true.,LOGFILE,' TRUELAT1 = %f',f1=truelat1)
267 call mprintf(.true.,LOGFILE,' TRUELAT2 = %f',f1=truelat2)
268 call mprintf(.true.,LOGFILE,' STAND_LON = %f',f1=stand_lon)
269 call mprintf(.true.,LOGFILE,' GEOG_DATA_PATH = %s',s1=geog_data_path)
270 call mprintf(.true.,LOGFILE,' OPT_GEOGRID_TBL_PATH = %s',s1=opt_geogrid_tbl_path)
271 call mprintf(.true.,LOGFILE,'/')
281 ! Convert wrf_core to uppercase letters
283 if (ichar(wrf_core(i:i)) >= 97) wrf_core(i:i) = char(ichar(wrf_core(i:i))-32)
286 ! Before doing anything else, we must have a valid grid type
288 if (wrf_core == 'ARW') then
291 else if (wrf_core == 'NMM') then
296 ! Next, if this is NMM, we need to subtract 1 from the specified E_WE and E_SN;
297 ! for some reason, these two variables need to be set to 1 larger than they
298 ! really ought to be in the WRF namelist, so, to be consistent, we will do
299 ! the same in the WPS namelist
300 if (gridtype == 'E') then
302 e_we(i) = e_we(i) - 1
303 e_sn(i) = e_sn(i) - 1
307 call mprintf(gridtype /= 'C' .and. gridtype /= 'E', ERROR, &
308 'A valid wrf_core must be specified in the namelist. '// &
309 'Currently, only "ARW" and "NMM" are supported.')
311 call mprintf(max_dom > MAX_DOMAINS, ERROR, &
312 'In namelist, max_dom must be <= %i. To run with more'// &
313 ' than %i domains, increase the MAX_DOMAINS parameter.', &
314 i1=MAX_DOMAINS, i2=MAX_DOMAINS)
316 ! Every domain must have a valid parent id
318 call mprintf(parent_id(i) <= 0 .or. parent_id(i) >= i, ERROR, &
319 'In namelist, the parent_id of domain %i must be in '// &
320 'the range 1 to %i.', i1=i, i2=i-1)
323 ! Check for valid geog_data_path
325 do i=1,len(geog_data_path)
326 geog_data_path(j:j) = geog_data_path(i:i)
327 if (geog_data_path(i:i) /= ' ') j = j + 1
329 if (geog_data_path(1:1) == ' ') then
330 call mprintf(.true.,ERROR,'In namelist, geog_data_path must be specified.')
332 j = len_trim(geog_data_path)
333 if (j >= MAX_FILENAME_LEN) then
334 call mprintf(.true.,ERROR, &
335 'In namelist, geog_data_path must be strictly less '// &
336 'than 128 characters in length.')
338 if (geog_data_path(j:j) /= '/') then
339 geog_data_path(j+1:j+1) = '/'
343 ! Paths need to end with a /
344 j = len_trim(opt_geogrid_tbl_path)
345 if (opt_geogrid_tbl_path(j:j) /= '/') then
346 opt_geogrid_tbl_path(j+1:j+1) = '/'
349 j = len_trim(opt_output_from_geogrid_path)
350 if (opt_output_from_geogrid_path(j:j) /= '/') then
351 opt_output_from_geogrid_path(j+1:j+1) = '/'
354 ! Handle IOFORM+100 to do tiled IO
355 if (io_form_geogrid > 100) then
356 do_tiled_output = .true.
357 io_form_geogrid = io_form_geogrid - 100
359 do_tiled_output = .false.
362 ! Check for valid io_form_geogrid
365 io_form_geogrid /= BINARY .and. &
368 io_form_geogrid /= NETCDF .and. &
371 io_form_geogrid /= GRIB1 .and. &
374 call mprintf(.true.,WARN,'Valid io_form_geogrid values are:')
376 call mprintf(.true.,WARN,' %i (=BINARY)',i1=BINARY)
379 call mprintf(.true.,WARN,' %i (=NETCDF)',i1=NETCDF)
382 call mprintf(.true.,WARN,' %i (=GRIB1)',i1=GRIB1)
384 call mprintf(.true.,ERROR,'No valid value for io_form_geogrid was specified in the namelist.')
386 io_form_output = io_form_geogrid
388 ! Convert map_proj to uppercase letters
390 if (ichar(map_proj(i:i)) >= 97) map_proj(i:i) = char(ichar(map_proj(i:i))-32)
393 ! Assign parameters to module variables
394 if ((index(map_proj, 'LAMBERT') /= 0) .and. &
395 (len_trim(map_proj) == len('LAMBERT'))) then
398 else if ((index(map_proj, 'MERCATOR') /= 0) .and. &
399 (len_trim(map_proj) == len('MERCATOR'))) then
400 iproj_type = PROJ_MERC
402 else if ((index(map_proj, 'POLAR') /= 0) .and. &
403 (len_trim(map_proj) == len('POLAR'))) then
406 else if ((index(map_proj, 'ROTATED_LL') /= 0) .and. &
407 (len_trim(map_proj) == len('ROTATED_LL'))) then
408 iproj_type = PROJ_ROTLL
410 else if ((index(map_proj, 'LAT-LON') /= 0) .and. &
411 (len_trim(map_proj) == len('LAT-LON'))) then
412 iproj_type = PROJ_CASSINI
415 call mprintf(.true.,ERROR,&
416 'In namelist, invalid map_proj specified. Valid '// &
417 'projections are "lambert", "mercator", "polar", '// &
418 '"lat-lon", and "rotated_ll".')
421 ! For Cassini / lat-lon projections
422 if (iproj_type == PROJ_CASSINI) then
424 ! If no dx,dy specified, assume global grid
425 if (dx == NAN .and. dy == NAN) then
426 dlondeg = 360. / (e_we(1)-s_we(1)) ! Here, we really do not want e_we-s_we+1
427 dlatdeg = 180. / (e_sn(1)-s_sn(1)) ! Here, we really do not want e_we-s_we+1
430 known_lon = stand_lon + dlondeg/2.
431 known_lat = -90. + dlatdeg/2.
432 dxkm = EARTH_RADIUS_M * PI * 2.0 / (e_we(1)-s_we(1))
433 dykm = EARTH_RADIUS_M * PI / (e_sn(1)-s_sn(1))
435 ! If dx,dy specified, however, assume regional grid
439 dxkm = dlondeg * EARTH_RADIUS_M * PI * 2.0 / 360.0
440 dykm = dlatdeg * EARTH_RADIUS_M * PI * 2.0 / 360.0
441 if (known_lat == NAN .or. known_lon == NAN) then
442 call mprintf(.true.,ERROR,'For lat-lon projection, if dx/dy are specified, '// &
443 'a regional domain is assumed, and a ref_lat,ref_lon must also be specified')
448 ! Manually set truelat2 = truelat1 if truelat2 not specified for Lambert
449 if (iproj_type == PROJ_LC .and. truelat2 == NAN) then
450 call mprintf ((truelat1 == NAN), ERROR, "No TRUELAT1 specified for Lambert conformal projection.")
457 ! For C grid, let ixdim and jydim be the number of velocity points in
458 ! each direction; for E grid, we will put the row and column back
459 ! later; maybe this should be changed to be more clear, though.
461 ixdim(i) = e_we(i) - s_we(i) + 1
462 jydim(i) = e_sn(i) - s_sn(i) + 1
465 if (gridtype == 'E') then
466 phi = dykm*real(jydim(1)-1)/2.
467 lambda = dxkm*real(ixdim(1)-1)
470 ! If the user hasn't supplied a known_x and known_y, assume the center of domain 1
471 if (gridtype == 'E' .and. (known_x /= NAN .or. known_y /= NAN)) then
472 call mprintf(.true.,WARN, &
473 'Namelist variables ref_x and ref_y cannot be used for NMM grids.'// &
474 ' (ref_lat, ref_lon) will refer to the center of the coarse grid.')
475 else if (gridtype == 'C') then
476 if (known_x == NAN .and. known_y == NAN) then
477 known_x = ixdim(1) / 2.
478 known_y = jydim(1) / 2.
479 else if (known_x == NAN .or. known_y == NAN) then
480 call mprintf(.true.,ERROR, &
481 'In namelist.wps, neither or both of ref_x, ref_y must be specified.')
485 ! Checks specific to E grid
486 if (gridtype == 'E') then
488 ! E grid supports only the rotated lat/lon projection
489 if (iproj_type /= PROJ_ROTLL) then
490 call mprintf(.true., WARN, &
491 'For the NMM core, projection type must be rotated '// &
492 'lat/lon (map_proj=rotated_ll)')
493 call mprintf(.true.,WARN,'Projection will be set to rotated_ll')
494 iproj_type = PROJ_ROTLL
497 ! In the following check, add back the 1 that we had to subtract above
498 ! for the sake of being consistent with WRF namelist
499 call mprintf(mod(e_sn(1)+1,2) /= 0, ERROR, &
500 'For the NMM core, E_SN must be an even number for grid %i.', i1=1)
503 call mprintf((parent_grid_ratio(i) /= 3), WARN, &
504 'For the NMM core, the parent_grid_ratio must be 3 for '// &
505 'domain %i. A ratio of 3 will be assumed.', i1=i)
506 parent_grid_ratio(i) = 3
509 ! Check that nests have an acceptable number of grid points in each dimension
511 ! rparent_gridpts = real(ixdim(i)+2)/real(parent_grid_ratio(i))
512 ! if (floor(rparent_gridpts) /= ceiling(rparent_gridpts)) then
513 ! call mprintf(.true.,ERROR,'For nest %i, e_we must be 3n-2 '// &
514 ! 'for some integer n > 1.', &
517 ! rparent_gridpts = real(jydim(i)+2)/real(parent_grid_ratio(i))
518 ! if (floor(rparent_gridpts) /= ceiling(rparent_gridpts)) then
519 ! call mprintf(.true.,ERROR,'For nest %i, e_sn must be 3n-2 '// &
520 ! 'for some odd integer n > 1.', &
530 ! Checks specific to C grid
531 else if (gridtype == 'C') then
533 ! C grid does not support the rotated lat/lon projection
534 call mprintf((iproj_type == PROJ_ROTLL), ERROR, &
535 'Rotated lat/lon projection is not supported for the ARW core. '// &
536 'Valid projecitons are "lambert", "mercator", "polar", and "lat-lon".')
538 ! Check that nests have an acceptable number of grid points in each dimension
540 rparent_gridpts = real(ixdim(i)-1)/real(parent_grid_ratio(i))
541 if (floor(rparent_gridpts) /= ceiling(rparent_gridpts)) then
542 call mprintf(.true.,ERROR,'For nest %i, (e_we-s_we+1) must be one greater '// &
543 'than an integer multiple of the parent_grid_ratio of %i.', &
544 i1=i, i2=parent_grid_ratio(i))
546 rparent_gridpts = real(jydim(i)-1)/real(parent_grid_ratio(i))
547 if (floor(rparent_gridpts) /= ceiling(rparent_gridpts)) then
548 call mprintf(.true.,ERROR,'For nest %i, (e_sn-s_sn+1) must be one greater '// &
549 'than an integer multiple of the parent_grid_ratio of %i.', &
550 i1=i, i2=parent_grid_ratio(i))
554 ! Check that a nest does not extend outside of its parent grid
555 nest_outside = .false.
557 if (i_parent_start(i) >= ixdim(parent_id(i))) then
558 call mprintf(.true.,WARN,'Nest %i cannot have i_parent_start outside of parent domain.',i1=i)
559 nest_outside = .true.
560 else if (i_parent_start(i) + (ixdim(i) - 1)/parent_grid_ratio(i) > ixdim(parent_id(i))) then
561 call mprintf(.true.,WARN,'Nest %i extends beyond its parent grid in the west-east direction.',i1=i)
562 call mprintf(.true.,WARN,' Maximum allowable e_we for current i/j_parent_start is %i.', &
563 i1=(ixdim(parent_id(i))-i_parent_start(i))*parent_grid_ratio(i)+1 )
564 nest_outside = .true.
566 if (j_parent_start(i) >= jydim(parent_id(i))) then
567 call mprintf(.true.,WARN,'Nest %i cannot have j_parent_start outside of parent domain.',i1=i)
568 nest_outside = .true.
569 else if (j_parent_start(i) + (jydim(i) - 1)/parent_grid_ratio(i) > jydim(parent_id(i))) then
570 call mprintf(.true.,WARN,'Nest %i extends beyond its parent grid in the south-north direction.',i1=i)
571 call mprintf(.true.,WARN,' Maximum allowable e_sn for current i/j_parent_start is %i.', &
572 i1=(jydim(parent_id(i))-j_parent_start(i))*parent_grid_ratio(i)+1 )
573 nest_outside = .true.
576 if (nest_outside) then
577 call mprintf(.true.,ERROR,'One or more nested domains extend beyond their parent domains.')
581 parent_ll_x(i) = real(i_parent_start(i))
582 parent_ll_y(i) = real(j_parent_start(i))
583 parent_ur_x(i) = real(i_parent_start(i))+real(ixdim(i))/real(parent_grid_ratio(i))-1.
584 parent_ur_y(i) = real(j_parent_start(i))+real(jydim(i))/real(parent_grid_ratio(i))-1.
585 grid_is_active(i) = active_grid(i)
592 1000 call mprintf(.true.,ERROR,'Error opening file namelist.wps')
594 end subroutine get_grid_params
596 end module gridinfo_module