MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
ncmodelexportmodule Module Reference

This module contains the NCModelExportModule. More...

Data Types

type  exportpackagetype
 
type  ncexportannotation
 netcdf export attribute annotations More...
 
type  ncmodelexporttype
 base class for an export model More...
 
type  ncbasemodelexporttype
 abstract type for model netcdf export type More...
 
interface  model_define
 abstract interfaces for model netcdf export type More...
 
interface  model_step
 
interface  package_export
 
interface  package_export_ilayer
 

Enumerations

enum  
 netcdf export types enumerator More...
 

Functions/Subroutines

subroutine epkg_init (this, mf6_input, mshape, naux, param_names, nparam)
 initialize dynamic package export object More...
 
subroutine epkg_destroy (this)
 destroy dynamic package export object More...
 
subroutine set (this, modelname, modeltype, modelfname, nctype, disenum)
 set netcdf file scoped attributes More...
 
subroutine export_init (this, modelname, modeltype, modelfname, nc_fname, disenum, nctype, iout)
 initialization of model netcdf export More...
 
class(exportpackagetype) function, pointer export_get (this, idx)
 retrieve dynamic export object from package list More...
 
character(len=linelength) function input_attribute (this, pkgname, idt)
 build modflow_input attribute string More...
 
integer(i4b) function istp (this)
 step index for timeseries data More...
 
character(len=linelength) function, public export_varname (pkgname, tagname, mempath, layer, iaux)
 build netcdf variable name More...
 
character(len=linelength) function, public export_longname (longname, pkgname, tagname, mempath, layer, iaux, component_type, subcomponent_type)
 build netcdf variable longname More...
 
subroutine export_input (this)
 netcdf dynamic package period export More...
 
character(len=linelength) function, public wkt_to_cf_gridmapping (wkt)
 map a WKT string to a CF-1.13 grid_mapping_name More...
 
real(dp) function wkt_find_param (wkt, name, found)
 find the first number following a quoted parameter name More...
 
subroutine wkt_find_ellipsoid (wkt, semi_major_axis, inverse_flattening, found)
 find semi-major axis and inverse flattening from a WKT string More...
 
subroutine, public wkt_transverse_mercator_params (wkt, longitude_of_central_meridian, latitude_of_projection_origin, scale_factor_at_central_meridian, false_easting, false_northing, semi_major_axis, inverse_flattening, found)
 extract CF transverse_mercator grid_mapping parameters More...
 
subroutine, public wkt_lambert_conformal_conic_params (wkt, standard_parallel_1, standard_parallel_2, longitude_of_central_meridian, latitude_of_projection_origin, false_easting, false_northing, semi_major_axis, inverse_flattening, is_2sp, found)
 extract CF lambert_conformal_conic grid_mapping parameters More...
 
subroutine, public wkt_albers_conical_equal_area_params (wkt, standard_parallel_1, standard_parallel_2, longitude_of_central_meridian, latitude_of_projection_origin, false_easting, false_northing, semi_major_axis, inverse_flattening, found)
 extract CF albers_conical_equal_area grid_mapping parameters More...
 
character(len=lenbigline) function, public wrap_rotated_crs (wkt, xorigin, yorigin, angrot)
 wrap a WKT2 PROJCRS in a derived CRS encoding grid rotation More...
 
subroutine export_destroy (this)
 destroy model netcdf export object More...
 

Variables

@, public netcdf_undef = 0
 undefined netcdf export type More...
 
@, public netcdf_structured = 1
 netcdf structrured export More...
 
@, public netcdf_mesh2d = 2
 netcdf ugrid layered mesh export More...
 

Detailed Description

This module defines a model export and base type for supported netcdf files and is not dependent on netcdf libraries.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
private

Definition at line 35 of file NCModel.f90.

Function/Subroutine Documentation

◆ epkg_destroy()

subroutine ncmodelexportmodule::epkg_destroy ( class(exportpackagetype), intent(inout)  this)

Definition at line 198 of file NCModel.f90.

200  class(ExportPackageType), intent(inout) :: this
201  if (allocated(this%param_names)) deallocate (this%param_names)
Input definition module.
Input parameter definition. Describes an input parameter.

◆ epkg_init()

subroutine ncmodelexportmodule::epkg_init ( class(exportpackagetype), intent(inout)  this,
type(modflowinputtype), intent(in)  mf6_input,
integer(i4b), dimension(:), intent(in), pointer, contiguous  mshape,
integer(i4b), intent(in)  naux,
character(len=linelength), dimension(:), intent(in), allocatable  param_names,
integer(i4b), intent(in)  nparam 
)
private
Parameters
[in]mshapemodel shape

Definition at line 150 of file NCModel.f90.

156  class(ExportPackageType), intent(inout) :: this
157  type(ModflowInputType), intent(in) :: mf6_input
158  integer(I4B), dimension(:), pointer, contiguous, intent(in) :: mshape !< model shape
159  integer(I4B), intent(in) :: naux
160  character(len=LINELENGTH), dimension(:), allocatable, &
161  intent(in) :: param_names
162  integer(I4B), intent(in) :: nparam
163  integer(I4B) :: n
164  character(len=LENVARNAME) :: rs_varname
165  character(len=LENMEMPATH) :: input_mempath
166  integer(I4B), pointer :: rsvar
167 
168  this%mf6_input = mf6_input
169  this%mshape => mshape
170  this%nparam = nparam
171  this%naux = naux
172  this%eper = 0
173 
174  input_mempath = create_mem_path(component=mf6_input%component_name, &
175  subcomponent=mf6_input%subcomponent_name, &
176  context=idm_context)
177 
178  ! allocate param arrays
179  allocate (this%param_names(nparam))
180  allocate (this%param_reads(nparam))
181  allocate (this%varids_param(nparam, mshape(1)))
182  allocate (this%varids_aux(naux, mshape(1)))
183 
184  ! set param arrays
185  do n = 1, nparam
186  this%param_names(n) = param_names(n)
187  rs_varname = rsv_name(param_names(n))
188  call mem_setptr(rsvar, rs_varname, mf6_input%mempath)
189  this%param_reads(n)%invar => rsvar
190  end do
191 
192  ! set pointer to loaded input period
193  call mem_setptr(this%iper, 'IPER', mf6_input%mempath)
character(len=lenmempath) function create_mem_path(component, subcomponent, context)
returns the path to the memory object
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=linelength) idm_context
Here is the call graph for this function:

◆ export_destroy()

subroutine ncmodelexportmodule::export_destroy ( class(ncmodelexporttype), intent(inout)  this)

Definition at line 1012 of file NCModel.f90.

1014  use simvariablesmodule, only: idm_context
1015  class(NCModelExportType), intent(inout) :: this
1016  ! override in derived class
1017  deallocate (this%deflate)
1018  deallocate (this%shuffle)
1019  deallocate (this%input_attr)
1020  deallocate (this%chunk_time)
1021  ! Deallocate idm memory
1022  if (this%ncf_mempath /= '') then
1023  call memorystore_remove(this%modelname, 'NCF', idm_context)
1024  end if
subroutine, public memorystore_remove(component, subcomponent, context)
Here is the call graph for this function:

◆ export_get()

class(exportpackagetype) function, pointer ncmodelexportmodule::export_get ( class(ncmodelexporttype), intent(inout)  this,
integer(i4b), intent(in)  idx 
)

Definition at line 403 of file NCModel.f90.

404  use listmodule, only: listtype
405  class(NCModelExportType), intent(inout) :: this
406  integer(I4B), intent(in) :: idx
407  class(ExportPackageType), pointer :: res
408  class(*), pointer :: obj
409  nullify (res)
410  obj => this%pkglist%GetItem(idx)
411  if (associated(obj)) then
412  select type (obj)
413  class is (exportpackagetype)
414  res => obj
415  end select
416  end if
A generic heterogeneous doubly-linked list.
Definition: List.f90:14

◆ export_init()

subroutine ncmodelexportmodule::export_init ( class(ncmodelexporttype), intent(inout)  this,
character(len=*), intent(in)  modelname,
character(len=*), intent(in)  modeltype,
character(len=*), intent(in)  modelfname,
character(len=*), intent(in)  nc_fname,
integer(i4b), intent(in)  disenum,
integer(i4b), intent(in)  nctype,
integer(i4b), intent(in)  iout 
)

Definition at line 281 of file NCModel.f90.

283  use tdismodule, only: datetime0, nper, nstp
287  use inputoutputmodule, only: lowcase
290  class(NCModelExportType), intent(inout) :: this
291  character(len=*), intent(in) :: modelname
292  character(len=*), intent(in) :: modeltype
293  character(len=*), intent(in) :: modelfname
294  character(len=*), intent(in) :: nc_fname
295  integer(I4B), intent(in) :: disenum
296  integer(I4B), intent(in) :: nctype
297  integer(I4B), intent(in) :: iout
298  character(len=LENMEMPATH) :: model_mempath
299  type(UtlNcfParamFoundType) :: ncf_found
300 
301  ! allocate
302  allocate (this%deflate)
303  allocate (this%shuffle)
304  allocate (this%input_attr)
305  allocate (this%chunk_time)
306 
307  ! initialize
308  this%modelname = modelname
309  this%modeltype = modeltype
310  this%modelfname = modelfname
311  this%nc_fname = nc_fname
312  this%gridmap_name = ''
313  this%ncf_mempath = ''
314  this%wkt = ''
315  this%crs_wkt = ''
316  this%datetime = ''
317  this%xname = ''
318  this%lenunits = ''
319  this%disenum = disenum
320  this%ncid = 0
321  this%totnstp = 0
322  this%deflate = -1
323  this%shuffle = 0
324  this%input_attr = 1
325  this%chunk_time = -1
326  this%iout = iout
327  this%chunking_active = .false.
328 
329  ! set file scoped attributes
330  call this%annotation%set(modelname, modeltype, modelfname, nctype, disenum)
331 
332  ! set dependent variable basename
333  select case (modeltype)
334  case ('GWF')
335  this%xname = 'head'
336  case ('GWT')
337  this%xname = 'concentration'
338  case ('GWE')
339  this%xname = 'temperature'
340  case default
341  errmsg = trim(modeltype)//' models not supported for NetCDF export.'
342  call store_error(errmsg)
343  call store_error_filename(modelfname)
344  end select
345 
346  ! set discretization input mempath
347  if (disenum == dis) then
348  this%dis_mempath = create_mem_path(modelname, 'DIS', idm_context)
349  else if (disenum == disu) then
350  this%dis_mempath = create_mem_path(modelname, 'DISU', idm_context)
351  else if (disenum == disv) then
352  this%dis_mempath = create_mem_path(modelname, 'DISV', idm_context)
353  end if
354 
355  ! set dependent variable pointer
356  model_mempath = create_mem_path(component=modelname)
357  call mem_setptr(this%x, 'X', model_mempath)
358 
359  ! set ncf_mempath if provided
360  if (filein_fname(this%ncf_mempath, 'NCF6_MEMPATH', this%dis_mempath, &
361  modelfname)) then
362  call mem_set_value(this%wkt, 'WKT', this%ncf_mempath, &
363  ncf_found%wkt)
364  call mem_set_value(this%crs_wkt, 'CRS_WKT', this%ncf_mempath, &
365  ncf_found%crs_wkt)
366  call mem_set_value(this%deflate, 'DEFLATE', this%ncf_mempath, &
367  ncf_found%deflate)
368  call mem_set_value(this%shuffle, 'SHUFFLE', this%ncf_mempath, &
369  ncf_found%shuffle)
370  call mem_set_value(this%input_attr, 'ATTR_OFF', this%ncf_mempath, &
371  ncf_found%attr_off)
372  call mem_set_value(this%chunk_time, 'CHUNK_TIME', this%ncf_mempath, &
373  ncf_found%chunk_time)
374  end if
375 
376  if (ncf_found%wkt .or. ncf_found%crs_wkt) then
377  this%gridmap_name = 'projection'
378  end if
379 
380  ! ATTR_OFF turns off modflow 6 input attributes
381  if (ncf_found%attr_off) then
382  this%input_attr = 0
383  end if
384 
385  ! set datetime string
386  if (datetime0 /= '') then
387  this%datetime = 'days since '//trim(datetime0)
388  else
389  ! January 1, 1970 at 00:00:00 UTC
390  this%datetime = 'days since 1970-01-01T00:00:00'
391  end if
392 
393  ! set total nstp
394  if (isim_mode == mvalidate) then
395  this%totnstp = nper
396  else
397  this%totnstp = sum(nstp)
398  end if
subroutine, public lowcase(word)
Convert to lower case.
This module contains the SourceCommonModule.
Definition: SourceCommon.f90:7
logical(lgp) function, public filein_fname(filename, tagname, input_mempath, input_fname)
enforce and set a single input filename provided via FILEIN keyword
integer(i4b), dimension(:), pointer, public, contiguous nstp
number of time steps in each stress period
Definition: tdis.f90:42
character(len=lendatetime), pointer, public datetime0
starting date and time for the simulation
Definition: tdis.f90:44
integer(i4b), pointer, public nper
number of stress period
Definition: tdis.f90:24
Here is the call graph for this function:

◆ export_input()

subroutine ncmodelexportmodule::export_input ( class(ncbasemodelexporttype), intent(inout)  this)

Definition at line 548 of file NCModel.f90.

549  use tdismodule, only: kper
550  class(NCBaseModelExportType), intent(inout) :: this
551  integer(I4B) :: idx
552  class(ExportPackageType), pointer :: export_pkg
553  do idx = 1, this%pkglist%Count()
554  export_pkg => this%get(idx)
555  ! period input already exported
556  if (export_pkg%eper >= kper) cycle
557  ! update export package
558  call this%package_step(export_pkg)
559  ! update exported iper
560  export_pkg%eper = kper
561  end do
integer(i4b), pointer, public kper
current stress period number
Definition: tdis.f90:26

◆ export_longname()

character(len=linelength) function, public ncmodelexportmodule::export_longname ( character(len=*), intent(in)  longname,
character(len=*), intent(in)  pkgname,
character(len=*), intent(in)  tagname,
character(len=*), intent(in)  mempath,
integer(i4b), intent(in), optional  layer,
integer(i4b), intent(in), optional  iaux,
character(len=*), intent(in), optional  component_type,
character(len=*), intent(in), optional  subcomponent_type 
)

Definition at line 494 of file NCModel.f90.

498  use inputoutputmodule, only: lowcase
500  character(len=*), intent(in) :: longname
501  character(len=*), intent(in) :: pkgname
502  character(len=*), intent(in) :: tagname
503  character(len=*), intent(in) :: mempath
504  integer(I4B), optional, intent(in) :: layer
505  integer(I4B), optional, intent(in) :: iaux
506  character(len=*), optional, intent(in) :: component_type
507  character(len=*), optional, intent(in) :: subcomponent_type
508  character(len=LINELENGTH) :: lname
509  type(CharacterStringType), dimension(:), pointer, &
510  contiguous :: auxnames
511  character(len=LINELENGTH) :: pname, vname, auxname
512  pname = pkgname
513  vname = tagname
514  call lowcase(pname)
515  call lowcase(vname)
516  if (longname == '') then
517  lname = trim(pname)//' '//trim(vname)
518  else
519  lname = longname
520  if (present(component_type) .and. present(subcomponent_type)) then
521  if (idm_multi_package(component_type, subcomponent_type)) then
522  lname = trim(pname)//' '//trim(lname)
523  end if
524  end if
525  end if
526 
527  if (present(iaux)) then
528  if (iaux > 0) then
529  if (tagname == 'AUX') then
530  ! reset vname to auxiliary variable name
531  call mem_setptr(auxnames, 'AUXILIARY', mempath)
532  auxname = auxnames(iaux)
533  call lowcase(auxname)
534  lname = trim(lname)//' '//trim(auxname)
535  end if
536  end if
537  end if
538 
539  if (present(layer)) then
540  if (layer > 0) then
541  write (lname, '(a,i0)') trim(lname)//' layer ', layer
542  end if
543  end if
logical function, public idm_multi_package(component, subcomponent)
This class is used to store a single deferred-length character string. It was designed to work in an ...
Definition: CharString.f90:23
Here is the call graph for this function:
Here is the caller graph for this function:

◆ export_varname()

character(len=linelength) function, public ncmodelexportmodule::export_varname ( character(len=*), intent(in)  pkgname,
character(len=*), intent(in)  tagname,
character(len=*), intent(in)  mempath,
integer(i4b), intent(in), optional  layer,
integer(i4b), intent(in), optional  iaux 
)

Definition at line 453 of file NCModel.f90.

457  use inputoutputmodule, only: lowcase
458  character(len=*), intent(in) :: pkgname
459  character(len=*), intent(in) :: tagname
460  character(len=*), intent(in) :: mempath
461  integer(I4B), optional, intent(in) :: layer
462  integer(I4B), optional, intent(in) :: iaux
463  character(len=LINELENGTH) :: varname
464  type(CharacterStringType), dimension(:), pointer, &
465  contiguous :: auxnames
466  character(len=LINELENGTH) :: pname, vname
467  vname = tagname
468  pname = pkgname
469 
470  if (present(iaux)) then
471  if (iaux > 0) then
472  if (tagname == 'AUX') then
473  ! reset vname to auxiliary variable name
474  call mem_setptr(auxnames, 'AUXILIARY', mempath)
475  vname = auxnames(iaux)
476  end if
477  end if
478  end if
479 
480  call lowcase(vname)
481  call lowcase(pname)
482  varname = trim(pname)//'_'//trim(vname)
483 
484  if (present(layer)) then
485  if (layer > 0) then
486  !write (varname, '(a,i0)') trim(varname)//'_L', layer
487  write (varname, '(a,i0)') trim(varname)//'_l', layer
488  end if
489  end if
Here is the call graph for this function:
Here is the caller graph for this function:

◆ input_attribute()

character(len=linelength) function ncmodelexportmodule::input_attribute ( class(ncmodelexporttype), intent(inout)  this,
character(len=*), intent(in)  pkgname,
type(inputparamdefinitiontype), intent(in), pointer  idt 
)

Definition at line 421 of file NCModel.f90.

422  use inputoutputmodule, only: lowcase
425  class(NCModelExportType), intent(inout) :: this
426  character(len=*), intent(in) :: pkgname
427  type(InputParamDefinitionType), pointer, intent(in) :: idt
428  character(len=LINELENGTH) :: attr
429  attr = ''
430  if (this%input_attr > 0) then
431  attr = trim(this%modelname)//mempathseparator//trim(pkgname)// &
432  mempathseparator//trim(idt%tagname)
433  call lowcase(attr)
434  end if
character(len=lenmemseparator), parameter mempathseparator
used to build up the memory address for the stored variables
Here is the call graph for this function:

◆ istp()

integer(i4b) function ncmodelexportmodule::istp ( class(ncmodelexporttype), intent(inout)  this)

Definition at line 439 of file NCModel.f90.

440  use tdismodule, only: kstp, kper, nstp
441  class(NCModelExportType), intent(inout) :: this
442  integer(I4B) :: n, istp
443  istp = kstp
444  if (kper > 1) then
445  do n = 1, kper - 1
446  istp = istp + nstp(n)
447  end do
448  end if
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:27

◆ set()

subroutine ncmodelexportmodule::set ( class(ncexportannotation), intent(inout)  this,
character(len=*), intent(in)  modelname,
character(len=*), intent(in)  modeltype,
character(len=*), intent(in)  modelfname,
integer(i4b), intent(in)  nctype,
integer(i4b), intent(in)  disenum 
)

Definition at line 206 of file NCModel.f90.

207  use versionmodule, only: fullversion
208  use inputoutputmodule, only: lowcase
209  class(NCExportAnnotation), intent(inout) :: this
210  character(len=*), intent(in) :: modelname
211  character(len=*), intent(in) :: modeltype
212  character(len=*), intent(in) :: modelfname
213  integer(I4B), intent(in) :: nctype
214  integer(I4B), intent(in) :: disenum
215  integer :: values(8)
216 
217  this%title = ''
218  this%model = ''
219  this%mesh = ''
220  this%grid = ''
221  this%history = ''
222  this%source = ''
223  this%conventions = ''
224  this%stdname = ''
225  this%longname = ''
226 
227  ! set file conventions
228  this%conventions = 'CF-1.13'
229  if (nctype == netcdf_mesh2d) this%conventions = &
230  trim(this%conventions)//' UGRID-1.0'
231 
232  ! set model specific attributes
233  select case (modeltype)
234  case ('GWF')
235  this%title = trim(modelname)//' hydraulic head'
236  this%longname = 'head'
237  case ('GWT')
238  this%title = trim(modelname)//' concentration'
239  this%longname = 'concentration'
240  case ('GWE')
241  this%title = trim(modelname)//' temperature'
242  this%longname = 'temperature'
243  case default
244  errmsg = trim(modeltype)//' models not supported for NetCDF export.'
245  call store_error(errmsg)
246  call store_error_filename(modelfname)
247  end select
248 
249  if (isim_mode == mvalidate) then
250  this%title = trim(this%title)//' array input'
251  end if
252 
253  ! set mesh type
254  if (nctype == netcdf_mesh2d) then
255  this%mesh = 'layered'
256  end if
257 
258  ! set grid type
259  if (disenum == dis) then
260  this%grid = 'structured'
261  else if (disenum == disv) then
262  this%grid = 'vertex'
263  end if
264 
265  ! model description string (lowercase, no version suffix, matching CF convention)
266  this%model = trim(modeltype)//': '//trim(modelname)
267  call lowcase(this%model)
268 
269  ! modflow6 version string
270  this%source = 'MODFLOW 6 '//trim(adjustl(fullversion))
271 
272  ! create timestamp
273  call date_and_time(values=values)
274  write (this%history, '(a,i0,a,i0,a,i0,a,i0,a,i0,a,i0,a,i0)') &
275  'first created ', values(1), '/', values(2), '/', values(3), ' ', &
276  values(5), ':', values(6), ':', values(7), '.', values(8)
This module contains version information.
Definition: version.f90:7
character(len= *), parameter fullversion
Definition: version.f90:24
Here is the call graph for this function:

◆ wkt_albers_conical_equal_area_params()

subroutine, public ncmodelexportmodule::wkt_albers_conical_equal_area_params ( character(len=*), intent(in)  wkt,
real(dp), intent(out)  standard_parallel_1,
real(dp), intent(out)  standard_parallel_2,
real(dp), intent(out)  longitude_of_central_meridian,
real(dp), intent(out)  latitude_of_projection_origin,
real(dp), intent(out)  false_easting,
real(dp), intent(out)  false_northing,
real(dp), intent(out)  semi_major_axis,
real(dp), intent(out)  inverse_flattening,
logical(lgp), intent(out)  found 
)

Best-effort extraction from a WKT1 or WKT2 string – MF6 has no CRS parsing library. found=.false. if any parameter cannot be located.

Definition at line 867 of file NCModel.f90.

875  character(len=*), intent(in) :: wkt
876  real(DP), intent(out) :: standard_parallel_1
877  real(DP), intent(out) :: standard_parallel_2
878  real(DP), intent(out) :: longitude_of_central_meridian
879  real(DP), intent(out) :: latitude_of_projection_origin
880  real(DP), intent(out) :: false_easting
881  real(DP), intent(out) :: false_northing
882  real(DP), intent(out) :: semi_major_axis
883  real(DP), intent(out) :: inverse_flattening
884  logical(LGP), intent(out) :: found
885  logical(LGP) :: f1, f2, f3, f4, f5, f6, f7
886 
887  standard_parallel_1 = wkt_find_param(wkt, 'standard_parallel_1', f1)
888  if (.not. f1) standard_parallel_1 = &
889  wkt_find_param(wkt, 'Latitude of 1st standard parallel', f1)
890 
891  standard_parallel_2 = wkt_find_param(wkt, 'standard_parallel_2', f2)
892  if (.not. f2) standard_parallel_2 = &
893  wkt_find_param(wkt, 'Latitude of 2nd standard parallel', f2)
894 
895  longitude_of_central_meridian = wkt_find_param(wkt, 'longitude_of_center', f3)
896  if (.not. f3) longitude_of_central_meridian = &
897  wkt_find_param(wkt, 'Longitude of false origin', f3)
898 
899  latitude_of_projection_origin = wkt_find_param(wkt, 'latitude_of_center', f4)
900  if (.not. f4) latitude_of_projection_origin = &
901  wkt_find_param(wkt, 'Latitude of false origin', f4)
902 
903  false_easting = wkt_find_param(wkt, 'false_easting', f5)
904  if (.not. f5) false_easting = &
905  wkt_find_param(wkt, 'Easting at false origin', f5)
906 
907  false_northing = wkt_find_param(wkt, 'false_northing', f6)
908  if (.not. f6) false_northing = &
909  wkt_find_param(wkt, 'Northing at false origin', f6)
910 
911  call wkt_find_ellipsoid(wkt, semi_major_axis, inverse_flattening, f7)
912 
913  found = f1 .and. f2 .and. f3 .and. f4 .and. f5 .and. f6 .and. f7
Here is the call graph for this function:
Here is the caller graph for this function:

◆ wkt_find_ellipsoid()

subroutine ncmodelexportmodule::wkt_find_ellipsoid ( character(len=*), intent(in)  wkt,
real(dp), intent(out)  semi_major_axis,
real(dp), intent(out)  inverse_flattening,
logical(lgp), intent(out)  found 
)

Matches SPHEROID["name",A,B,...] (WKT1) or ELLIPSOID["name",A,B,...] (WKT2). Best-effort only. found=.false. if neither keyword is present or the two numbers cannot be read.

Definition at line 698 of file NCModel.f90.

699  use inputoutputmodule, only: upcase
700  character(len=*), intent(in) :: wkt
701  real(DP), intent(out) :: semi_major_axis
702  real(DP), intent(out) :: inverse_flattening
703  logical(LGP), intent(out) :: found
704  character(len=LENBIGLINE) :: wkt_upper
705  integer :: ipos, i, iend, ios, wkt_len, nquote
706 
707  found = .false.
708  semi_major_axis = dzero
709  inverse_flattening = dzero
710  wkt_len = len_trim(wkt)
711  wkt_upper = wkt
712  call upcase(wkt_upper)
713 
714  ipos = index(wkt_upper(1:wkt_len), 'SPHEROID[')
715  if (ipos == 0) ipos = index(wkt_upper(1:wkt_len), 'ELLIPSOID[')
716  if (ipos == 0) return
717 
718  ! skip past the quoted ellipsoid name (its closing quote)
719  i = ipos
720  nquote = 0
721  do while (i <= wkt_len)
722  if (wkt(i:i) == '"') nquote = nquote + 1
723  if (nquote == 2) exit
724  i = i + 1
725  end do
726  if (nquote < 2) return
727  i = i + 1
728  if (wkt(i:i) == ',') i = i + 1
729 
730  iend = i
731  do while (iend <= wkt_len)
732  if (wkt(iend:iend) == ',') exit
733  iend = iend + 1
734  end do
735  if (iend <= i) return
736  read (wkt(i:iend - 1), *, iostat=ios) semi_major_axis
737  if (ios /= 0) return
738 
739  i = iend + 1
740  iend = i
741  do while (iend <= wkt_len)
742  if (wkt(iend:iend) == ',' .or. wkt(iend:iend) == ']') exit
743  iend = iend + 1
744  end do
745  if (iend <= i) return
746  read (wkt(i:iend - 1), *, iostat=ios) inverse_flattening
747  if (ios == 0) found = .true.
subroutine, public upcase(word)
Convert to upper case.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ wkt_find_param()

real(dp) function ncmodelexportmodule::wkt_find_param ( character(len=*), intent(in)  wkt,
character(len=*), intent(in)  name,
logical(lgp), intent(out)  found 
)

Matches e.g. PARAMETER["central_meridian",-117] (WKT1) or PARAMETER["Longitude of natural origin",-117,...] (WKT2). Best-effort only – MF6 has no CRS parsing library. found=.false. if name is not present or no number follows it.

Definition at line 656 of file NCModel.f90.

657  use inputoutputmodule, only: upcase
658  character(len=*), intent(in) :: wkt
659  character(len=*), intent(in) :: name
660  logical(LGP), intent(out) :: found
661  real(DP) :: value
662  character(len=LENBIGLINE) :: wkt_upper
663  character(len=LINELENGTH) :: target_upper
664  integer :: ipos, istart, iend, ios, wkt_len
665 
666  found = .false.
667  value = dzero
668  wkt_len = len_trim(wkt)
669  target_upper = '"'//trim(name)//'"'
670  wkt_upper = wkt
671  call upcase(wkt_upper)
672  call upcase(target_upper)
673  ipos = index(wkt_upper(1:wkt_len), trim(target_upper))
674  if (ipos == 0) return
675 
676  istart = ipos + len_trim(target_upper)
677  do while (istart <= wkt_len)
678  if (wkt(istart:istart) == ',') exit
679  istart = istart + 1
680  end do
681  istart = istart + 1
682  iend = istart
683  do while (iend <= wkt_len)
684  if (wkt(iend:iend) == ',' .or. wkt(iend:iend) == ']') exit
685  iend = iend + 1
686  end do
687  if (iend <= istart) return
688  read (wkt(istart:iend - 1), *, iostat=ios) value
689  if (ios == 0) found = .true.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ wkt_lambert_conformal_conic_params()

subroutine, public ncmodelexportmodule::wkt_lambert_conformal_conic_params ( character(len=*), intent(in)  wkt,
real(dp), intent(out)  standard_parallel_1,
real(dp), intent(out)  standard_parallel_2,
real(dp), intent(out)  longitude_of_central_meridian,
real(dp), intent(out)  latitude_of_projection_origin,
real(dp), intent(out)  false_easting,
real(dp), intent(out)  false_northing,
real(dp), intent(out)  semi_major_axis,
real(dp), intent(out)  inverse_flattening,
logical(lgp), intent(out)  is_2sp,
logical(lgp), intent(out)  found 
)

Best-effort extraction from a WKT1 or WKT2 string – MF6 has no CRS parsing library. 2SP (two standard parallels) only: CF's lambert_conformal_conic has no scale_factor attribute, so a 1SP (scale-factor-based) WKT cannot be exactly represented without trigonometric conversion MF6 has no library for. is_2sp=.false. if a second standard parallel is not present (i.e. likely 1SP); found=.false. if is_2sp but any other parameter cannot be located.

Definition at line 807 of file NCModel.f90.

814  character(len=*), intent(in) :: wkt
815  real(DP), intent(out) :: standard_parallel_1
816  real(DP), intent(out) :: standard_parallel_2
817  real(DP), intent(out) :: longitude_of_central_meridian
818  real(DP), intent(out) :: latitude_of_projection_origin
819  real(DP), intent(out) :: false_easting
820  real(DP), intent(out) :: false_northing
821  real(DP), intent(out) :: semi_major_axis
822  real(DP), intent(out) :: inverse_flattening
823  logical(LGP), intent(out) :: is_2sp
824  logical(LGP), intent(out) :: found
825  logical(LGP) :: f1, f2, f3, f4, f5, f6, f7
826 
827  standard_parallel_2 = wkt_find_param(wkt, 'standard_parallel_2', f2)
828  if (.not. f2) standard_parallel_2 = &
829  wkt_find_param(wkt, 'Latitude of 2nd standard parallel', f2)
830  is_2sp = f2
831  if (.not. is_2sp) then
832  found = .false.
833  return
834  end if
835 
836  standard_parallel_1 = wkt_find_param(wkt, 'standard_parallel_1', f1)
837  if (.not. f1) standard_parallel_1 = &
838  wkt_find_param(wkt, 'Latitude of 1st standard parallel', f1)
839 
840  longitude_of_central_meridian = wkt_find_param(wkt, 'central_meridian', f3)
841  if (.not. f3) longitude_of_central_meridian = &
842  wkt_find_param(wkt, 'Longitude of false origin', f3)
843 
844  latitude_of_projection_origin = wkt_find_param(wkt, 'latitude_of_origin', f4)
845  if (.not. f4) latitude_of_projection_origin = &
846  wkt_find_param(wkt, 'Latitude of false origin', f4)
847 
848  false_easting = wkt_find_param(wkt, 'false_easting', f5)
849  if (.not. f5) false_easting = &
850  wkt_find_param(wkt, 'Easting at false origin', f5)
851 
852  false_northing = wkt_find_param(wkt, 'false_northing', f6)
853  if (.not. f6) false_northing = &
854  wkt_find_param(wkt, 'Northing at false origin', f6)
855 
856  call wkt_find_ellipsoid(wkt, semi_major_axis, inverse_flattening, f7)
857 
858  found = f1 .and. f3 .and. f4 .and. f5 .and. f6 .and. f7
Here is the call graph for this function:
Here is the caller graph for this function:

◆ wkt_to_cf_gridmapping()

character(len=linelength) function, public ncmodelexportmodule::wkt_to_cf_gridmapping ( character(len=*), intent(in)  wkt)

Accepts either WKT1 (OGC 01-009, PROJECTION["name"]) or WKT2 (ISO 19162:2019, METHOD["name"]) format. WKT1 is tried first; if PROJECTION[ is absent the WKT2 METHOD[ path is attempted. Returns '' for geographic CRS or unrecognised projection names. Only common groundwater projections included. Matching is case-insensitive.

Definition at line 573 of file NCModel.f90.

574  use inputoutputmodule, only: upcase
575  character(len=*), intent(in) :: wkt
576  character(len=LINELENGTH) :: gmname
577  character(len=LINELENGTH) :: proj_name
578  character(len=LENBIGLINE) :: wkt_upper
579  integer :: istart, iend
580 
581  gmname = ''
582  proj_name = ''
583 
584  ! uppercase copy for case-insensitive keyword search
585  wkt_upper = wkt
586  call upcase(wkt_upper)
587 
588  ! --- WKT1 path: PROJECTION["name"] ---
589  istart = index(wkt_upper, 'PROJECTION[')
590  if (istart /= 0) then
591  istart = istart + len('PROJECTION[')
592  do while (istart <= len(wkt))
593  if (wkt(istart:istart) == '"') exit
594  istart = istart + 1
595  end do
596  if (istart > len(wkt)) return
597  istart = istart + 1
598  iend = index(wkt(istart:), '"')
599  if (iend == 0) return
600  iend = istart + iend - 2
601  if (iend < istart) return
602  proj_name = wkt(istart:iend)
603  call upcase(proj_name)
604  select case (trim(proj_name))
605  case ('TRANSVERSE_MERCATOR')
606  gmname = 'transverse_mercator'
607  case ('LAMBERT_CONFORMAL_CONIC_2SP', 'LAMBERT_CONFORMAL_CONIC_1SP')
608  gmname = 'lambert_conformal_conic'
609  case ('ALBERS_CONIC_EQUAL_AREA')
610  gmname = 'albers_conical_equal_area'
611  case ('MERCATOR_1SP', 'MERCATOR_2SP')
612  gmname = 'mercator'
613  case ('POLAR_STEREOGRAPHIC')
614  gmname = 'polar_stereographic'
615  end select
616  return
617  end if
618 
619  ! --- WKT2 path: METHOD["name"] ---
620  istart = index(wkt_upper, 'METHOD[')
621  if (istart == 0) return
622  istart = istart + len('METHOD[')
623  do while (istart <= len(wkt))
624  if (wkt(istart:istart) == '"') exit
625  istart = istart + 1
626  end do
627  if (istart > len(wkt)) return
628  istart = istart + 1
629  iend = index(wkt(istart:), '"')
630  if (iend == 0) return
631  iend = istart + iend - 2
632  if (iend < istart) return
633  proj_name = wkt(istart:iend)
634  call upcase(proj_name)
635  select case (trim(proj_name))
636  case ('TRANSVERSE MERCATOR')
637  gmname = 'transverse_mercator'
638  case ('LAMBERT CONIC CONFORMAL (2SP)', 'LAMBERT CONIC CONFORMAL (1SP)')
639  gmname = 'lambert_conformal_conic'
640  case ('ALBERS EQUAL AREA')
641  gmname = 'albers_conical_equal_area'
642  case ('MERCATOR (VARIANT A)', 'MERCATOR (VARIANT B)')
643  gmname = 'mercator'
644  case ('POLAR STEREOGRAPHIC (VARIANT A)', 'POLAR STEREOGRAPHIC (VARIANT B)')
645  gmname = 'polar_stereographic'
646  end select
Here is the call graph for this function:
Here is the caller graph for this function:

◆ wkt_transverse_mercator_params()

subroutine, public ncmodelexportmodule::wkt_transverse_mercator_params ( character(len=*), intent(in)  wkt,
real(dp), intent(out)  longitude_of_central_meridian,
real(dp), intent(out)  latitude_of_projection_origin,
real(dp), intent(out)  scale_factor_at_central_meridian,
real(dp), intent(out)  false_easting,
real(dp), intent(out)  false_northing,
real(dp), intent(out)  semi_major_axis,
real(dp), intent(out)  inverse_flattening,
logical(lgp), intent(out)  found 
)

Best-effort extraction from a WKT1 or WKT2 string – MF6 has no CRS parsing library. Tries WKT1 parameter names first, falling back to WKT2 names. found=.false. if any parameter cannot be located.

Definition at line 757 of file NCModel.f90.

763  character(len=*), intent(in) :: wkt
764  real(DP), intent(out) :: longitude_of_central_meridian
765  real(DP), intent(out) :: latitude_of_projection_origin
766  real(DP), intent(out) :: scale_factor_at_central_meridian
767  real(DP), intent(out) :: false_easting
768  real(DP), intent(out) :: false_northing
769  real(DP), intent(out) :: semi_major_axis
770  real(DP), intent(out) :: inverse_flattening
771  logical(LGP), intent(out) :: found
772  logical(LGP) :: f1, f2, f3, f4, f5, f6
773 
774  longitude_of_central_meridian = wkt_find_param(wkt, 'central_meridian', f1)
775  if (.not. f1) longitude_of_central_meridian = &
776  wkt_find_param(wkt, 'Longitude of natural origin', f1)
777 
778  latitude_of_projection_origin = wkt_find_param(wkt, 'latitude_of_origin', f2)
779  if (.not. f2) latitude_of_projection_origin = &
780  wkt_find_param(wkt, 'Latitude of natural origin', f2)
781 
782  scale_factor_at_central_meridian = wkt_find_param(wkt, 'scale_factor', f3)
783  if (.not. f3) scale_factor_at_central_meridian = &
784  wkt_find_param(wkt, 'Scale factor at natural origin', f3)
785 
786  false_easting = wkt_find_param(wkt, 'false_easting', f4)
787  if (.not. f4) false_easting = wkt_find_param(wkt, 'False easting', f4)
788 
789  false_northing = wkt_find_param(wkt, 'false_northing', f5)
790  if (.not. f5) false_northing = wkt_find_param(wkt, 'False northing', f5)
791 
792  call wkt_find_ellipsoid(wkt, semi_major_axis, inverse_flattening, f6)
793 
794  found = f1 .and. f2 .and. f3 .and. f4 .and. f5 .and. f6
Here is the call graph for this function:
Here is the caller graph for this function:

◆ wrap_rotated_crs()

character(len=lenbigline) function, public ncmodelexportmodule::wrap_rotated_crs ( character(len=*), intent(in)  wkt,
real(dp), intent(in)  xorigin,
real(dp), intent(in)  yorigin,
real(dp), intent(in)  angrot 
)

Builds a DerivedProjectedCRS wrapping the given WKT2 PROJCRS in an EPSG:9624 (affine parametric transformation) deriving conversion parameterized from xorigin/yorigin/angrot (degrees). Per ISO 19111, a deriving conversion is directed base->derived, so the parameters encode the world->local (inverse) rotation; a CRS-aware consumer applies the inverse to resolve true position from local coordinates. Returns '' if wkt is not recognized as a WKT2 PROJCRS – a best-effort structural check only, as MF6 has no CRS parsing library and does not validate the WKT beyond this.

Definition at line 928 of file NCModel.f90.

929  use inputoutputmodule, only: upcase
930  character(len=*), intent(in) :: wkt
931  real(DP), intent(in) :: xorigin, yorigin, angrot
932  character(len=LENBIGLINE) :: derived_wkt
933  character(len=LENBIGLINE) :: wkt_trim, wkt_upper, base_body, conversion
934  character(len=30) :: a0s, a1s, a2s, b0s, b1s, b2s
935  integer :: nstart, i, depth, closures, iend, wkt_len
936  real(DP) :: ang, a0, a1, a2, b0, b1, b2
937 
938  derived_wkt = ''
939 
940  ! best-effort check that wkt is a WKT2 PROJCRS
941  wkt_trim = trim(adjustl(wkt))
942  wkt_len = len_trim(wkt_trim)
943  wkt_upper = wkt_trim
944  call upcase(wkt_upper)
945  if (wkt_len < 9 .or. wkt_upper(1:8) /= 'PROJCRS[') return
946 
947  ! locate the end of PROJCRS's 2nd top-level child (the mandatory
948  ! BASEGEOGCRS/BASEGEODCRS and CONVERSION elements, in that order),
949  ! by tracking bracket depth from just inside the opening '['
950  nstart = 9
951  depth = 0
952  closures = 0
953  iend = 0
954  do i = nstart, wkt_len
955  if (wkt_trim(i:i) == '[') then
956  depth = depth + 1
957  else if (wkt_trim(i:i) == ']') then
958  depth = depth - 1
959  if (depth == 0) then
960  closures = closures + 1
961  if (closures == 2) then
962  iend = i
963  exit
964  end if
965  end if
966  end if
967  end do
968  if (iend == 0) return
969  base_body = wkt_trim(nstart:iend)
970 
971  ! EPSG:9624 affine parameters, base(world) -> derived(local grid)
972  ang = angrot * dpio180
973  a0 = -(xorigin * cos(ang) + yorigin * sin(ang))
974  a1 = cos(ang)
975  a2 = sin(ang)
976  b0 = xorigin * sin(ang) - yorigin * cos(ang)
977  b1 = -sin(ang)
978  b2 = cos(ang)
979  write (a0s, '(es22.15)') a0
980  write (a1s, '(es22.15)') a1
981  write (a2s, '(es22.15)') a2
982  write (b0s, '(es22.15)') b0
983  write (b1s, '(es22.15)') b1
984  write (b2s, '(es22.15)') b2
985 
986  conversion = 'DERIVINGCONVERSION["MODFLOW 6 grid rotation",'// &
987  'METHOD["Affine parametric transformation",'// &
988  'ID["EPSG",9624]],'// &
989  'PARAMETER["A0",'//trim(adjustl(a0s))// &
990  ',LENGTHUNIT["metre",1]],'// &
991  'PARAMETER["A1",'//trim(adjustl(a1s))// &
992  ',SCALEUNIT["unity",1]],'// &
993  'PARAMETER["A2",'//trim(adjustl(a2s))// &
994  ',SCALEUNIT["unity",1]],'// &
995  'PARAMETER["B0",'//trim(adjustl(b0s))// &
996  ',LENGTHUNIT["metre",1]],'// &
997  'PARAMETER["B1",'//trim(adjustl(b1s))// &
998  ',SCALEUNIT["unity",1]],'// &
999  'PARAMETER["B2",'//trim(adjustl(b2s))// &
1000  ',SCALEUNIT["unity",1]]]'
1001 
1002  derived_wkt = 'DERIVEDPROJCRS["MODFLOW 6 rotated grid CRS",'// &
1003  'BASEPROJCRS['//trim(base_body)//'],'// &
1004  trim(conversion)//','// &
1005  'CS[Cartesian,2],'// &
1006  'AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],'// &
1007  'AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]]]'
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ netcdf_mesh2d

@, public ncmodelexportmodule::netcdf_mesh2d = 2

Definition at line 38 of file NCModel.f90.

38  ENUMERATOR :: NETCDF_MESH2D = 2 !< netcdf ugrid layered mesh export

◆ netcdf_structured

@, public ncmodelexportmodule::netcdf_structured = 1

Definition at line 37 of file NCModel.f90.

37  ENUMERATOR :: NETCDF_STRUCTURED = 1 !< netcdf structrured export

◆ netcdf_undef

@, public ncmodelexportmodule::netcdf_undef = 0

Definition at line 36 of file NCModel.f90.

36  ENUMERATOR :: NETCDF_UNDEF = 0 !< undefined netcdf export type