Xplasma -- D. McCune Apr. 2000 -- PPPL -- dmccune@pppl.gov
Xplasma2 -- D. McCune Sept. 2006 -- PPPL -- dmccune@pppl.gov
This document describes xplasma -- a set of routines for numerical
representation of fusion plasma MHD equilibrium and plasma parameter
profiles.
Abstract (Sept 2006 updated version) --
The xplasma software offers a representation standard for tokamak
plasma MHD equilibria and plasma parameter profiles defined over a
variety of coordinates. Each xplasma dataset defines a "dictionary",
a single namespace, in which each name refers to a specific data item--
typically, a list, a grid discretizing a coordinate, or a profile
defined over one, two, or three gridded coordinates. Xplasma provides
efficient methods for accessing the data associated with these items,
or for adding any number of new items to the dictionary and collective
dataset. An xplasma dataset (dictionary and all item contents) can be
written to or restored from a NetCDF file by means of simple subroutine
calls.
The original implementation (xplasma 1.0) provided a single global
xplasma dataset that was accessible by a set of fortran-77 style calls.
An executable program could only contains one xplasma dataset at a time.
The full fortran-95 implementation (xplasma 2.0) allows multiple
instantiation of xplasma datasets in a single process, while preserving
the fortran-77 interface with an implied reference to a "golden" xplasma
pointer set aside for this purpose.
Layered over efficient spline interpolation software (pspline [1]),
xplasma has been used for several years to communicate data between
integrated simulation codes (e.g. TRANSP, ONETWO) and NTCC modules
(e.g. NUBEAM). There are also widely used applications for accessing
experimental data (TRANSP & EFIT MDSplus trees) that use xplasma for
internal storage of data items. It is planned to use xplasma for
implementation of a plasma state component in the Fusion Simulation
Project (SciDAC-2 SWIM project).
[1] https://w3.pppl.gov/NTCC/PSPLINE/pspline.html
The main topics are:
Introductory Information
F95 Upgrade (changes from xplasma vsn 1 to xplasma vsn 2).
F95 (vsn 2) xplasma basic concepts
Using F95 xplasma data
Here, methods for accessing existing xplasma data are described.
Initializing F95 xplasma data
Here, first steps for creating a new xplasma object from scratch.
Modifying F95 xplasma data
Here, methods for writing data into xplasma are introduced.
Error Handling
Time evolution of xplasma
Here, a discussion of what can and what cannot be easily changed.
Creation and update of named xplasma data items
More information on writing data into xplasma
Global Items
A few bits of modifiable globally accessible xplasma data
Saving to File
-- the remainder of the document: the original xplasma 1 (f77 interface)
documentation.
Xplasma is currently being used to communicate numerical representations
of axisymmetric tokamak plasma timeslices between experimental databases
and simulation codes, and between components or processes of multi-
component or multi-process (distributed) simulation codes.
Xplasma defines a sorted dictionary of named data items. It provides
efficient mechanisms for looking up and accessing data. And it allows new
data to be named and added in a straight-forward manner. The details are
given in the subtopics.
The contents of each xplasma dataset is thought of as a snapshot of the
plasma state at a particular time in an experiment or simulation.
Each xplasma data item has a dictionary entry with:
a) a unique NAME[1]
b) a unique integer ID code
c) an AUTHOR name [1]
d) a LABEL [2]
e) a UNITS label [2]
f) an integer data TYPE code
The xplasma public module defines:
integer, parameter, public :: xplasma_listType = 1 ! LIST data type code
integer, parameter, public :: xplasma_coordType= 2 ! COORDinate data type
integer, parameter, public :: xplasma_gridType = 3 ! GRID data type code
integer, parameter, public :: xplasma_profType = 4 ! PROFILE data type code
integer, parameter, public :: xplasma_blkbxType= 5 ! BLACK BOX data type code
Notes:
[1] up to 32 characters long, uppercase converted alphanumeric characters
and underscore ("_"); first character must not be a numeric digit.
[2] any ascii character string, or blank. Although xplasma does not
restrict the length, these labels may be used by graphical display
codes which may impose their own length restrictions e.g. by truncation.
Recommendations: labels <~ 50 characters, units <~ 16 characters.
COORDINATES-- an abstract coordinate, discretized by zero or more grids.
There are two types: periodic, and non-periodic.
Periodic coordinates are discretized by grids spanning [-pi:pi] or
[0:2*pi]. For purposes of interpolation, there are no bounds: shifts
of 2*pi*N are applied to bring interpolation coordinate data into bounds.
For very large values of |N|, accuracy of interpolation will be lost.
Non-periodic coordinates have fixed minimum and maximum values. Xplasma
enforces prescribed limits for some coordinates; otherwise the minimum
and maximum values are defined when the first grid discretizing the
coordinate is provided.
The xplasma public module defines (this is a partial list):
integer, parameter, public :: xplasma_rho_coord = 1 ! radial flux coord.
integer, parameter, public :: xplasma_theta_coord = 2 ! poloidal angle coord.
integer, parameter, public :: xplasma_phi_coord = 3 ! toroidal angle coord.
integer, parameter, public :: xplasma_R_coord = 4 ! R coord
integer, parameter, public :: xplasma_Z_coord = 5 ! Z coord
Xplasma methods exist for finding the periodicity attribute, min and max
value (when defined), and number of GRIDs discretizing the coordinate.
GRIDS-- specific discretization of a coordinate. A grid is a strictly
ascending finite sequence of numbers, the first of which precisely matches
the minimum value of the corresponding coordinate (or -pi or 0 in the case
of a periodic coordinate), and the last of which precisely matches the
maximum value of the corresponding coordinate (or pi or 2*pi in the case
of a periodic coordinate).
In gridded calculations, the GRID in the xplasma sense of the word is
usually the set of boundaries of the finite zones of the calculation.
The set of zone centers only constitutes a grid in the xplasma sense of
the word, when it is augmented with the inner and outer boundaries of
the gridded region.
Xplasma methods exist for finding the size of any grid and retrieving its
values.
PROFILES-- 1d, 2d, or 3d interpolating functions based on array data defined
over xplasma GRIDs. Interpolation method is defined when the profile is
defined; there are at present 4 options:
type: storage requirement:
1d 2d 3d
NC step function N-1 (N1-1)* (N1-1)*(N2-1)*(N3-1)
(N2-1)
C0 piecewise linear N N1*N2 N1*N2*N3 f continuous
C1 cubic Hermite 2*N 4*N1*N2 8*N1*N2*N3 df/dx continuous
C2 cubic Spline 2*N 4*N1*N2 8*N1*N2*N3 d2f/dx2 continuous
(N, N1, N2, N3 refer to the sizes of GRIDs discretizing known coordinates).
When profile data is accessed by interpolation, it is only necessary to
know the coordinates over which it is defined, not the specific grids. On
the other hand, if the original data is needed, it is straightforward to
acquire the specific grid information and the original data upon which the
profile interpolating function is based.
Cubic interpolation is affected by boundary conditions. For data defined
over periodic coordinates, periodic boundary conditions are employed. For
data defined over non-periodic coordinates, the boundary conditions are set
when the data is defined.
Xplasma methods exist for interpolating profiles as well as finding the
underlying coordinates and grids. For multidimensional profiles, input
coordinate data are tagged by COORDINATE or GRID ids; it is not necessary
for the caller to know the order of storage of the data.
Xplasma methods also allow straightforward means of acquiring information
on the interpolation method and underlying grids.
LISTS-- a list, identified by a single name, can contain 1 or more elements;
each element consists of:
-- a 32 character name (not part of the xplasma dictionary namespace).
-- an integer value
-- a (real*8) floating point value
-- character data, any length, trailing blanks trimmed away.
List lengths, element names, and contents are readily acquired by available
xplasma methods.
BLACK BOXES-- a named BLACK BOX consists of:
-- an integer type code. The meaning is user defined meaning, but, it
should be noted that xplasma itself uses black box type codes K,
0 <= K <= 101, to aid internal calculations and methods. User defined
type codes should be outside this range.
-- an integer 1d array of length specified by the user.
-- a floating point (real*8) 1d array of length specified by the user.
Black box datasets are used by xplasma for various purposes. Examples are:
data caches for flux surface integration, representation of quantities
defined over non-rectilinear grids.
Xplasma was developed for communication of data on axisymmetric tokamaks.
Physics units: MKS; KeV for temperatures and particle energies.
Flux coordinates:
Radial flux coordinate "rho" --
rho = sqrt([enclosed-toroidal-flux]/[toroidal-flux-enclosed-by-boundary])
Poloidal angle coordinate "theta" -- theta=0 on large major radius side
of plasma; theta increases along flux surfaces in counter-clockwise
direction when plasma cross-section is viewed to the right of the axis
of symmetry. But: data access methods provide optional controls to
enable user data to be interpreted as
theta_rev = 2*pi - theta
grad(theta_rev) = -grad(theta),
i.e. a clockwise poloidal angle coordinate.
Toroidal angle coordinate "phi" -- increasing phi draws a circle in a
counter-clockwise direction about the axis of symmetry, when the machine
is viewed from above.
-------------------
Note: adaptation of xplasma for non-tokamak applications may well be possible,
but has not been attempted as of September 2006. A major effort would be
required, e.g. to support stellarators or other non-axisymmetric plasma
configurations.
Setting the orientation of the poloidal angle coordinate "theta" (aka "chi"
in the fortran-77 interface) can now ONLY be done on a call-by-call basis.
In the xplasma (vsn 2) fortran-95 interface, use the ccwflag[n]
optional logical arguments. I.e. if the 2nd coordinate of a 2d
profile evaluation call is a poloidal angle which is to be
interpreted as having reversed orientation, set the optional
argument ccwflag2=.FALSE.; if these arguments are omitted the
normal orientation (.TRUE.) is assumed to apply.
In the fortran-77 interface, most routines affected by the poloidal
angle orientation include a vector size argument (ivec). To signal
that the poloidal angle is to be considered reversed, set ivec to
[the vector size] x (-1). I.e. a negative value of ivec signals
poloidal coordinate interpretation is to be reversed. This will
be verified in the documentation of the specific routines.
xplasma supports several interpolation methods for data. The
interpolation method is defined when an interpolating function
is set up. In the xplasma software documentation, the desired
interpolation method is indicated by an argument "iorder" in the
setup routine. (Sometimes also referred to as the "spline type").
Generally the following methods are available:
iorder name comment
-1 zonal data step function
0 piecewise linear 1st derivatives defined, not continuous
1 Akima Hermite 1st derivatives continuous
2 Cubic Spline 1st & 2nd derivatives continuous
Hermite is a piecewise cubic method that is C1 given 1st derivatives
at the grid points. Akima Hermite uses a numerical method [1] for
computing these derivatives which minimizes "ringing" in the case
of noisy data.
Cubic splines are C2 and preferable for smooth data, but can yield
"ringing" artifacts in the case of noisy data.
[1] Hiroshi Akima, Communications of the ACM, Jan 1974, Vol. 17 No. 1
The core plasma is represented on a user defined flux coordinate grid
(rho,theta), where "rho" is a flux surface label-- by convention,
rho=sqrt(normalized-toroidal-flux) in xplasma itself, although
physics applications can use any coordinate as long as the mapping
to rho is known.
After completion of the setup sequence, xplasma's core plasma model
will contain
* the flux surface geometry R(rho,theta), Z(rho,theta), in m.
* equilibrium related magnetic profiles g(rho) (Tesla*m), and the
poloidal flux profile psi(rho) (Wb/rad). Other magnetic profiles
such as q(rho) are optional but often present.
* the field components BR(rho,theta),BZ(rho,theta), and
[mod(B)](rho,theta), all in Tesla.
* any number of user defined 1d plasma parameter profiles
f(rho)
* any number of user defined 2d plasma parameter profiles
g(rho,theta).
The flux surface geometry and field components are represented
as bicubic splines. User defined plasma parameter profiles are
represented according to the user's specification, with splines,
Hermite, piecewise linear, and zonal step function representations
being available.
For each profile there is an associated id number and name
string. For reasons of efficiency, the id numbers are used
for profile identification in the interpolation routines.
Routines for fetching the id number from the name are provided
and are efficient to log(N) of the number of names, but still
should not be used in applications' innermost loops.
There are two options for the "limiter" or "vessel wall" specification:
* a closed sequence of (R,Z) points outside the core plasma, and,
* a list of bounding circles and infinite lines enclosing a space
that contains the core plasma.
The "scrape off plasma" is considered to be in the space inside
the limiters but outside the core plasma.
Generally, after the limiter is specified the user will want to
set up an (R,Z) rectilinear grid which covers a rectangular space
enclosing both the core plasma and the scrape-off-layer region
between the core plasma and the limiters or vessel wall as given.
Automatic generation of evenly spaced (R,Z) meshes is available
as an option.
Once this is done, xplasma will construct a piecewise bilinear
function d(R,Z), where d gives (in R,Z units) the distance of
(R,Z) from the nearest limiter, and
* a negative number denotes a position inside the limiter
* a zero denotes a position on the limiter
* a positive number denotes a position outside the limiter.
After limiters are defined, a rectilinear (R,Z) grid can be
constructed which covers both the core plasma and the scrape-off
plasma. An (R,Z) representation of equilibrium, fields, and plasma
profiles can then be built up as needed.
It is envisioned that xplasma might be used as follows:
(1) a transport simulation code evolves a plasma MHD equilibrium
and a set of plasma parameters.
(2) "physics modules" are programmed to acquire data from the
xplasma module and write data back into it.
(3) the transport simulation code uses routines that add data to
xplasma, to specify the plasma equilibrium and necessary plasma
parameter profiles, and then it calls the physics module.
(4) the physics modules reads its input data using the xplasma
read routines, and then does its modeling calculations,
developing a set of output profiles.
(5) the physics module writes its results back into xplasma using
more data definition routines, then exits.
(6) the transport simulation code reads back the data created by
the physics module.
This reading and writing through xplasma will handle interpolation
so that the main transport code and the physics module need not
share COMMON blocks nor use the same numerical grids.
Since the purpose of xplasma is to facilitate communication of data
between separately developed physics models, it is necessary to
recommend a convention regarding physical units. The convention
adopted is based on emerging consensus in the tokamak physics
modeling community: MKS for all quantities, except for temperatures
and energies where KeV are used.
Thus:
MKS/KeV NOT cgs/eV (sorry, TRANSP...)
"g" Tesla*m NOT gauss*cm
"psi" Webers/rad NOT gauss*cm**2/rad
"R,Z" m NOT cm
"Te,Ti" KeV NOT eV
"ne" m**-3 NOT cm**-3
[note it is customary to give the poloidal flux function normalized
per unit toroidal angle; then Bpol = (1/R)*grad(psi), note (1/R) not
(1/(2*pi*R))].
Considerable effort has gone into optimization of the performance of
xplasma subroutines. In particular, xplasma is layered over the pspline
library, which has been shown to work well on modern vector and super-
scalar computer architectures.
But, general purpose routines (e.g. for interpolation) necessarily have
a fair amount of control logic and error checking to support general purpose
use. This will inevitably result in performance somewhat less than what is
optimally achievable for a specific code.
Codes for which e.g. field or metric interpolation are critical performance
considerations likely already have optimized code for these purposes. In
many cases it will be best simply to use xplasma as a way to load the input
data structures for these optimized routines at the start of the calculation,
and use the optimized routines (not xplasma calls) in the innermost loops
of the calculation.
Although the xplasma interface is designed to allow eventual support
of non-axisymmetric field equilibria, the September 2006 version of the
software is limited to axisymmetric configurations only.
A project has been completed to upgrade xplasma for the Fusion Simulation
Project. The upgrade includes:
-- simplified fortran-95 interface; simplified underlying data structures.
-- object-oriented implementation, allowing multiple xplasma "instances"
to co-exist independently in memory. (This is fortran-95; the term
"object-oriented" is used loosely).
-- ability to update equilibrium without deleting entire object contents
and rebuilding from the ground up.
The fortran-77 style original xplasma interface is maintained for the
most part, but a few routines have been dropped-- see subtopic.
The f77 routines all refer implicitly to a reserved xplasma object
pointer set aside for this purpose. To use f77 routines to modify any
other xplasma object, it is necessary to reset the f77 object pointer:
see the subtopic
To reset the f77 xplasma pointer-- which defines the xplasma object to
which all legacy f77 xplasma calls refer-- use the following module and
its pair of contained routines
grab_f77_xplasma(swant,ierr)
release_f77_xplasma(ierr)
--------------------------------------
details:
use xplasma_obj_instance
...
! the following points to an xplasma object, to which future
! f77 xplasma calls should refer. This pointer has been initialized
! elsewhere...!
type (xplasma), pointer :: swant
integer :: ierr ! status code, 0=normal on exit
! reset the f77 xplasma pointer with this call:
call grab_f77_xplasma(swant,ierr)
if(ierr.ne.0) [handle-unlikely-error]
To later restore the f77 xplasma pointer to its prior state, use:
call release_f77_xplasma(ierr)
if(ierr.ne.0) [handle-unlikely-error]
Note that these calls should occur in pairs! If (e.g. due to error
handling logic) the pairing is broken, there are two likely serious
consequences:
(a) F77 xplasma calls interact unexpectedly with a different xplasma
object, with unpredictable results;
(b) eventual f77 xplasma pointer stack underflow or overflow error.
The "C" interface to fortran-77 xplasma will be frozen.
The "R4" interface to fortran-77 xplasma will be frozen. All the
fortran-95 interfaces are in REAL*8 precision.
The following fortran-77 style routines will be dropped in the upgraded
version:
eqm_flxbdy / eqm_rbdy -- "move limiter to plasma boundary" /
"restore original limiter".
dropped because: (a) no evidence of use; (b) error-prone; if
"eqm_flxbdy" call is not followed by "eqm_rbdy", xplasma suffers
a side effect; distance to limiter calculations, used e.g. for fast
ion orbit loss calculations, are modified.
advice: codes wishing to treat the plasma boundary as the limiter
can use routines like "eq_dfast" to compute distance-to-boundary.
Such routines return a positive number for locations beyond the
plasma boundary, and a negative number for locations within the
plasma boundary.
eqm_chi_cwdir_set, eqm_chi_cwdir_restore,
eqm_chi_cwdir, eq_get_chi_cwdir
control clockwise/counter-clockwise orientation of poloidal angle
variable. Dropped due to being error prone-- side effect of a
subroutine's reset of chi_cwdir seen by all subsequent calls to
xplasma routines.
advice: In the xplasma fortran-95 interface, optional arguments
are provided to specify angle coordinate orientations. In affected
fortran-77 interface routines, the poloidal angle coordinate
orientation can be reversed, separately on each call, by setting
the argument vector length to a negative number. Details are
provided in the updated subroutine documentation, below.
eqm_phi_cwdir_set, eqm_phi_cwdir_restore,
eqm_phi_cwdir, eq_get_phi_cwdir
dropped: no evidence of use. Optional arguments in fortran-95
interface only.
eqm_spordr_set, eqm_spordr_get
dropped: xplasma feature to control spline storage mode
(compact or non-compact) was found to be unnecessary and
has been removed.
eqm_nfast
dropped: control of resolution of a 2nd (R,Z)->(rho,theta)
inverse map, no longer necessary.
The original fortran-77 xplasma API used "chi" to designate the
poloidal angle coordinate in a 2d flux coordinate representation
of a tokamak plasma equilibrium.
The new fortran-95 interface uses "theta" to designate this coordinate.
However, since the fortran-77 and fortran-95 interfaces are both
maintained, both names persist in the xplasma API.
Hopefully this will not cause much confusion.
In order to use the Fortran-95 Xplasma Interface in code, it is necessary
to be able to refer to xplasma objects. A consequence of the design goal
of allowing multiple xplasma objects to co-exist, is that each subroutine
that accesses or manipulates xplasma data must include an argument which
specifies which instance of xplasma is to be affected or used.
This argument is a "pointer to an xplasma object". A pointer is used,
because each xplasma object can be quite large, such that it is sometimes
advantageous to reuse existing xplasma code by means of resetting a pointer,
rather than having to explicitly copy the entire contents from one xplasma
object to another.
Advanced users may want for various reasons to invent additional fortran-95
modules which declare xplasma objects and pointers. However, for starting
out it is recommended to use the public module provided with the software:
use xplasma_obj_instance
This causes the following pointers to be defined, with suggested semantics:
s -- pointer to currently active xplasma object
F77 xplasma calls implicitly refer to this one.
s1-- pointer to previous xplasma object (e.g. from a prior timestep
in a time-dependent simulation).
s2-- spare.
The module xplasma_obj_instance also imports all public xplasma modules
and definitions.
INITIALIZATION-- xplasma_obj_instance defines SAVEd global data. Because
fortran-95 does not yet allow pointers to be initialized at compile time
to point to any particular object, the pointers are initially NULL. To
be activated, the pointers in this module must be initialized with by
calling a public subroutine defined in the module:
call xoi_init(ierr)
This call simply associates the pointers with specific xplasma objects
inside the module; no data initialization is performed. It also sets
a flag inside the module indicating that initialization has been performed,
so that if the call is repeated, no action is taken.
The integer argument "ierr" returns a status code, 0=normal. Almost all
xplasma subroutine calls return such a status code.
(NOTE: spelling variant: some xplasma routines spell this "ier" in
their argument lists).
Nearly all f95 xplasma calls take an xplasma object pointer as their first
argument. In the documentation, the symbol "s" is used for this pointer.
In some applications of xplasma, the symbol can be taken as short hand for
"plasma state". Almost all xplasma calls also have a status code "ierr" as
their final required argument.
Ordering of calling arguments in f95 xplasma calls:
Example:
call xplasma_eval_prof(s,id,x,result,ierr,ideriv1=1,n_out_of_bounds=iout)
1st argument -- s -- xplasma object pointer
next set of arguments -- id,x -- required inputs
(here, the profile id and the x values at which to evaluate it)
next set of arguments -- result,ierr -- required outputs
(here, the result of the evaluation, and an error status code)
("ierr" is almost always the last required output argument).
next set of arguments -- ideriv1 -- optional inputs
(here, requesting the 1st derivative of the profile w.r.t. x).
(the documentation will indicate that the default is to return
the profile value unless otherwise directed by optional arguments).
final set of arguments -- n_out_of_bounds -- optional outputs
(here, the number of x values that were out of bounds, with
respect to the coordinate over which the profile identified
by "id" is defined).
Of course, not all xplasma subroutines contain arguments in every one of
these categories. Nevertheless, the ordering of arguments is consistent
with this ordering for all f95 xplasma public interfaces.
xplasma_obj_instance
uses "xplasma_obj_instance_mod"
(where xplasma objects, pointers and xoi_init are defined).
uses "xplasma_definitions", which...
uses "xplasma_profs" -- methods for creating certain profiles.
uses "xplasma_mcgrid"-- methods for creating and accessing a type
of fast ion distribution function "black box"
and related items.
uses "xplasma_rzgeo" -- methods for defining MHD equilibrium
geometry and related profiles.
uses "xplasma_flxint"-- methods for computing flux surface integrals
and averages.
uses "xplasma_sol" -- methods for defining limiter and scrape-off
region.
uses "xplasma_ctran" -- coordinate transformation methods and
evaluation of metric jacobian.
uses "xplasma_obj" -- the kernel -- dictionary and supported
data types; error handling support;
NetCDF i/o.
The following are public parameters that are defined in the public
xplasma modules, which may be helpful in user applications:
Data types known to the xplasma dictionary:
integer, parameter, public :: xplasma_listType = 1 ! LIST data type code
integer, parameter, public :: xplasma_coordType= 2 ! COORDinate data type
integer, parameter, public :: xplasma_gridType = 3 ! GRID data type code
integer, parameter, public :: xplasma_profType = 4 ! PROFILE data type code
integer, parameter, public :: xplasma_blkbxType= 5 ! BLACK BOX data type code
Commonly used coordinates:
integer, parameter, public :: xplasma_rho_coord = 1 ! radial flux coord.
integer, parameter, public :: xplasma_theta_coord = 2 ! poloidal angle coord.
integer, parameter, public :: xplasma_phi_coord = 3 ! toroidal angle coord.
integer, parameter, public :: xplasma_R_coord = 4 ! R coord
integer, parameter, public :: xplasma_Z_coord = 5 ! Z coord
Designation for coordinate subsets (usable as argument in a handful of
xplasma routines):
integer, parameter, public :: xplasma_flux_coords = 1 ! generic flux coord.
integer, parameter, public :: xplasma_cyl_coords = 2 ! generic cyl. coord.
Author name usable to designate quantities that should be deleted when the
plasma MHD equilibrium changes:
character*32, parameter, public :: xplasma_xmhd = '__XPLASMA_MHDEQ_DERIVED'
The following arguments define algorithm choices for the
[R,Z] -> [rho,theta] inverse map, in order from the slowest but most
accurate to the fastest but most approximate...
integer, parameter, public :: xp_imap_newton = 1
integer, parameter, public :: xp_imap_polar = 2
integer, parameter, public :: xp_imap_rzlinear = 3
For reasons of compatibility with the original xplasma implementation
and existing uses of the software, certain item names related to the
plasma MHD equilibrium are reserved. These are:
name -- definition (all are profiles unless otherwise specified)
G -- g(rho) = R*B_phi
PSI -- psi(rho); (1/R)*grad(Psi) = B_pol
P -- P(rho), plasma pressure for MHD equilibrium
Q -- q(rho), rotational transform d[toroidal_flux]/d[poloidal_flux]
R -- R(rho,theta), (with Z) position of flux surfaces
Z -- Z(rho,theta), (with R) position of flux surfaces
PSI_RZ -- Psi(R,Z)
BR -- BR(rho,theta), field component in R direction
BZ -- BZ(rho,theta), field component in Z direction
BPHI -- Bphi(rho,theta), field component in phi direction
BMOD -- mod(B)(rho,theta), total field magnitude
BR_RZ -- BR(R,Z), field component in R direction
BZ_RZ -- BZ(R,Z), field component in Z direction
BPHI_RZ -- Bphi(R,Z), field component in phi direction
BMOD_RZ -- mod(B)(R,Z), total field magnitude
MAG_AXIS -- list, [R,Z] at magnetic axis
BDY_RZ_MINMAX -- list,{Rmin,Rmax,Zmin,Zmax} of plasma boundary
(nominal last closed flux surface).
User applications should avoid using these names for storage of items in
xplasma.
Several additional named items created by XPLASMA start with "__", two
consecutive underscores. Users should avoid defining names that start
with this prefix.
Although the public module xplasma_obj_instance defines sevaral xplasma
instances and associated pointers, the user may want to define his own
xplasma instances at some point. The existing modules
old_xplasma/xplasma_obj_instance.f90
old_xplasma/xplasma_obj_instance_mod.f90
can be used as examples for how to do this.
The user's code will need to see
use xplasma_definitions
in order to make use of xplasma calls.
In this section, the new Fortran-95 Xplasma interface is described,
with emphasis on methods for using data in an existing dataset.
The xplasma pointers (s,s1) defined in xplasma_obj_instance are presumed
to be available in the coding examples.
The argument "ierr" is always a return status code, 0=normal.
For simplicity of examples, error checking / error handling code is
usually omitted -- but real codes should check the error codes!
To access any xplasma call, the calling code must declare
use xplasma_obj_instance
or else use a user-defined module that otherwise provides access to
xplasma object and interface definitions. In all f95 code examples it
is assumed that this has been done.
The xplasma module comes with a debug library, which supports SGlib/ElVis
graphical examination of xplasma datasets.
The library is called xplasma_debug.a; the main source code for this
library is xplasma_debug_module.f90-- this code has examples of pretty
much every kind of access to xplasma data.
Given an xplasma file path, or a URL pointing to a publicly accessible
xplasma file, the data can be plotted running "plot_xplasma", a command
line interactive fortran program, which uses the debug library.
The f77 interface to xplasma has been re-implemented by using the
f95 xplasma routines internally. This interface resides in the
library old_xplasma.a; the source routines for this library, e.g.
eqm_rhofun.f90, contain examples of use of xplasma f95 routines. This
may be useful especially for anyone already familiar with the f77
routines.
! copy contents of "s" to "s1". Prior contents of "s1" are removed; all
! associated memory is freed; "s" is not changed.
call xplasma_copy(s,s1,ierr)
! read in new contents of "s" from a file. Prior contents of "s", if any,
! are removed, and associated memory is freed. "s" and "s1" are not sharing
! any memory, so, the removal of the prior contents of "s" has no effect on
! "s1".
call xplasma_read(s,filename,ierr)
! "filename" is a character string variable containing a file path to
! a read accessible xplasma NetCDF file.
!--------------------------
! example:
use xplasma_obj_instance
use my_module ! defines "file_path" and "runid" character string variables.
implicit NONE
character*100 filename
filename = trim(file_path)//trim(runid)//'_my_xplasma.cdf'
call xplasma_read(s,filename,ierr)
if(ierr.ne.0) then
write(6,*) ' ? xplasma open or read failure: '//trim(filename)
call xplasma_error(s,ierr,6)
call bad_exit
endif
Note: if the filename argument has the form of a URL (i.e. starts with
" http://"), xplasma_read will attempt to spawn a /usr/bin/curl process
to download the indicated URL to a local disk file (in the process current
working directory), and will then attempt a read of the downloaded file.
Certain global information, such as a global label, the current simulation
or experiment time in seconds, sign or orientation of the toroidal field
and toroidal plasma current, are stored in each xplasma. The accuracy
of this information depends on the software that created the original
xplasma object or file.
To retrieve this information use the following routine. Other than the
status code, all arguments are optional, allowing the user to select
which piece or pieces of information are desired.
subroutine xplasma_global_info(s,ier, &
initLabel,time,axisymm,scrapeoff,bphi_ccw,jphi_ccw,nitems, &
prof_counter,eq_counter,bdytol,ajac_maxVaR,rzOrder,kmom)
! this routine returns global xplasma object information, according
! to which optional arguments are provided...
type (xplasma), pointer :: s
integer, intent(out) :: ier ! error set iff s is uninitialized.
! this is the label passed in an xplasma_(re)Init call...
character*(*), intent(out), optional :: initLabel
! can also use xplasma_time_get for this:
real*8, intent(out), optional :: time
logical, intent(out), optional :: axisymm ! axisymmetry flag
logical, intent(out), optional :: scrapeoff ! SOL flag
integer, intent(out), optional :: bphi_ccw ! Bphi counter-clockwise flag
! 0 means: never specified; 1: counter-clockwise (CCW); -1: CW
integer, intent(out), optional :: jphi_ccw ! Jphi counter-clockwise flag
! 0 means: never specified; 1: counter-clockwise (CCW); -1: CW
integer, intent(out), optional :: nitems ! number of items in xplasma
! dictionary at time of call.
integer, intent(out), optional :: prof_counter ! profile counter
! global counter incremented for each creation or update of each profile
integer, intent(out), optional :: eq_counter ! profile counter
! global counter incremented for each update of MHD equilibrium
real*8, intent(out), optional :: bdytol ! out-of-range error tolerance
! relative tolerance applied for interpolations over non-periodic
! coordinates; for periodic coordinates shifts of 2*n*pi are applied
! as necessary to bring interpolations in range
real*8, intent(out), optional :: ajac_maxVar ! det[J] variation tol.
! relative tolerance for variations of det[J] within a flux surface;
! used to check for singular or near-singular metric tensor in
! R(rho,theta),Z(rho,theta) flux surface definitions.
integer, intent(out), optional :: rzOrder ! spline fit order for MHD
! equilibrium geometry R(rho,theta) z(rho,theta) data
! 1 for C1 Hermite; 2 (the default) for C2 Spline.
integer, intent(out), optional :: kmom ! #moments for Fourier
! representation of equilibrium
Also available:
subroutine xplasma_time_get(s,ztime,ier)
! fetch the time (seconds) affiliated with xplasma object "s".
type (xplasma), pointer :: s
real*8, intent(out) :: ztime
integer, intent(out) :: ier
The plasma MHD equilibrium defines the transformation between magnetic
flux coordinates (rho,phi,theta) and cylindrical coordinates (R,phi,Z).
It also defines the magnetic field and flux surface geometry.
Xplasma provides a routine for transforming coordinate sets between
cartesian, cylindrical, and flux coordinate spaces. It also provides
for retrieval or calculation of extrema-- e.g. minimum and maximum
R, Z, and mod(B) on any flux surface, and for finding the distance
to the nearest point on any flux surface.
These are described in the subtopics...
To get the magnetic axis postion (m) and field strength(T):
real*8 :: R_axis, Z_axis, B_axis
Use this call:
call xplasma_mag_axis(s,ierr, raxis=R_axis, zaxis=Z_axis, baxis=B_axis)
Note that the last 3 arguments are keyworded and optional-- any user
selected subset of the information may also be had using the same
subroutine. The B field information is mod(B); to get the sign use
xplasma_global_info and fetch "bphi_ccw" (see preceding topic). Note
that this is the actual field strength, not the "vacuum" field-- i.e.
g(0)/R_axis, not g(1)/R_axis, where g = R*B_phi, constant on each flux
surface-- xplasma profile name "G".
To find the minimum and maximum R and/or Z and/or |B| occuring on a
plasma flux surface:
real*8 :: R_min, R_max, Z_min, Z_max ! spatial extrema, m
real*8 :: B_min, B_max ! extrema in mod(B), T
For the plasma boundary surface:
subroutine xplasma_RZminmax_plasma(s, rmin,rmax, zmin,zmax, ier, &
ccw_theta,i2pi, thRmin,thRmax, thZmin,thZmax)
! return the plasma boundary R & Z min & max
type(xplasma), pointer :: s
real*8, intent(out) :: Rmin,Rmax
real*8, intent(out) :: Zmin,Zmax
integer, intent(out) :: ier
! optionally return the poloidal angle locations of the extrema
logical, intent(in), optional :: ccw_theta ! Theta CCW in poloidal plane, default=T
integer, intent(in), optional :: i2pi ! angle coord normalization options
! i2pi=1 (default) returned value in range [0,2pi]
! i2pi=2 returned value in range [-pi,pi]
real*8, intent(out), optional :: thRmin,thRmax
real*8, intent(out), optional :: thZmin,thZmax
The optional controls ccw_theta and i2pi only have an effect if theta
location of extrema are being returned.
For any plasma surface identified by "rho":
subroutine xplasma_RZminmax(s,rho,ier, phi, &
ccw_theta,i2pi, &
rmin,rmax, zmin,zmax, &
thRmin,thRmax, thZmin,thZmax)
! very accurately determine Rmin/max, Zmin/max of a flux surface.
type (xplasma), pointer :: s
real*8, intent(in) :: rho ! required input: flux surface.
integer, intent(out) :: ier ! completion code
real*8, intent(in), optional :: phi ! toroidal angle location
! ignored for axisymmetric cases
real*8, intent(out), optional :: rmin,rmax ! Rmin & Rmax of surface
real*8, intent(out), optional :: zmin,zmax ! Zmin & Zmax of surface
! optionally return the poloidal angle locations of the extrema
logical, intent(in), optional :: ccw_theta ! Theta CCW in poloidal plane, default=T
integer, intent(in), optional :: i2pi ! angle coord normalization options
! i2pi=1 (default) returned value in range [0,2pi]
! i2pi=2 returned value in range [-pi,pi]
real*8, intent(out), optional :: thRmin,thRmax
real*8, intent(out), optional :: thZmin,thZmax
The optional controls ccw_theta and i2pi only have an effect if theta
location of extrema are being returned.
For the minimum and maximum mod(B) on any surface "rho":
subroutine xplasma_Bminmax(s,rho,ier, phi, &
ccw_theta,i2pi, bmin,bmax, thbmin,thbmax)
use eqi_rzbox_module
! very accurately determine Bmin/max of a flux surface
type (xplasma), pointer :: s
real*8, intent(in) :: rho ! required input: flux surface.
integer, intent(out) :: ier ! completion code
real*8, intent(in), optional :: phi ! toroidal angle location
! ignored for axisymmetric cases
logical, intent(in), optional :: ccw_theta ! Theta CCW in poloidal plane, default=T
integer, intent(in), optional :: i2pi ! angle coord normalization options
! i2pi=1 (default) returned value in range [0,2pi]
! i2pi=2 returned value in range [-pi,pi]
real*8, intent(out), optional :: Bmin,Bmax ! Bmin & Bmax of surface
real*8, intent(out), optional :: thBmin,thBmax
The optional controls ccw_theta and i2pi only have an effect of thBmin and/or
thBmax are being returned.
For the computational domain (this generally encompasses a rectangle in
[R,Z] space that covers and extends beyond the plasma boundary; if there
is no [R,Z] rectangle defined, the plasma boundary extrema are returned:
subroutine xplasma_RZminmax_extended(s, rmin,rmax, zmin,zmax, ier, &
sol, id_Rgrid, id_Zgrid)
! return the R & Z min & max of the extended mapped region; if
! there is no such region return the R & Z min & max of the
! plasma
type(xplasma), pointer :: s
real*8, intent(out) :: Rmin,Rmax
real*8, intent(out) :: Zmin,Zmax
integer, intent(out) :: ier
A single interface "xplasma_ctrans" enables computation of any coordinate
transformation between cartesian, cylindric, and flux coordinates.
The interface xplasma_ctrans aliases two routines xplasma_ctran1 and
xplasma_ctrann which accept scalar and vector arguments, respectively.
The transformations are defined as follows:
(R,phi,Z) --> (x,y,z)
x = R*cos(phi)
y = R*sin(phi)
z = Z
(x,y,z) --> (R,phi,Z)
R = sqrt(x**2+y**2)
if R > 0, phi = atan2(y,x), otherwise phi = 0
Z = z
(rho,theta) --> (R,Z)
bicubic splines R(rho,theta), Z(rho,theta) are evaluated.
An extrapolation of the system defines (rho,theta) --> (R,Z) for
rho > 1 -- it is continuous at the boundary rho=1 with the interior
splines, but not continuously differentiable.
(R,Z) --> (rho,theta)
various options for estimating the inverse map are provided.
The options control an accuracy/speed trade off. If many evaluations are
needed (e.g. for straight line tracking in flux coordinate space), the
fastest option is to compute a bilinear profile interpolant pair rho(R,Z)
and theta(R,Z) on some grid. The map is calculated accurately at the
grid points; the interpolation of theta(R,Z) is handled carefully in
the vicinity of the theta branch cut (2*pi discontinuity). This method
is supported by the "maptype=xp_imap_rzlinear" option and is sufficient
for many purposes.
For greater cost in cpu time, the (R,Z) splines can be inverted using
a Newton method with accuracy tolerances set down near machine precision
if necessary: "maptype=xp_imap_newton", the default, combined with
"tol=<desired relative accuracy tolerance>"; code comments:
real*8, intent(in), optional :: tol ! map tolerance
! tol applies to Newton map (maptype = xp_imap_newton = default) ONLY
! (R_in,Z_in)->(rho_out,theta_out):
! |R_in-R(rho_out,theta_out)| < tol*max(R_in,|Z_in|)
! |Z_in-Z(rho_out,theta_out)| < tol*max(R_in,|Z_in|)
! default: s%bdytol
! s%bdytol's own default value is 1.0d-10
! cf xplasma_bdytol_set; xplasma_global_info.
An option of intermediate speed that is generally accurate to ~1.0d-4
and requires no pre-tabulation of map data, can be had by setting
"maptype=xp_imap_polar".
Almost all arguments are optional-- the presence of arguments defines
what transformation is desired.
Examples:
INTEGER, PARAMETER :: R8=SELECTED_REAL_KIND(12,100)
real(R8) :: r,z,rho,theta ! real*8 also works...
real(R8) :: r2(2),z2(2),rho2(2),theta2(2)
! scalar (rho,theta) -> (R,Z)
call xplasma_ctrans(s,ierr,rho_in=1.0_R8,theta_in=0.0_R8, &
r_out=r,z_out=z)
! scalar (R,Z) -> (rho,theta)
call xplasma_ctrans(s,ierr,r_in=r,z_in=z,rho_out=rho,theta_out=theta)
r2(1)=r
r2(2)=r-0.01_R8
z2(1:2)=z
! vector (R,Z) -> (rho,theta)
call xplasma_ctrans(s,xp_use_coord_vecsize,ier, &
r_in=r2, z_in=z2, rho_out=rho2, theta_out=theta2)
"xp_use_coord_vecsize" is a logical parameter (value is .TRUE.) defined
by the xplasma public module; it means the vector size is inferred
from the arguments (all sizes must match). The alternative is
"xp_set_coord_vecsize" (.FALSE.) which allows a size less than or equal
to the smallest passed vector size to be used.
Full interface:
interface xplasma_ctrans
module procedure xplasma_ctran1 ! scalar
module procedure xplasma_ctrann ! vector
end interface
Scalar interface:
subroutine xplasma_ctran1(s,ier, &
x_in,y_in,z_in, r_in,phi_in, rho_in,theta_in, &
tol, maptype, ccw_theta,ccw_phi, i2pi, &
x_out,y_out,z_out, r_out,phi_out, rho_out,theta_out, &
nregion,cosphi,sinphi)
Vector interface:
subroutine xplasma_ctrann(s,vsize_flag,ier, &
isize, &
x_in,y_in,z_in, r_in,phi_in, rho_in,theta_in, &
tol, maptype, ccw_theta,ccw_phi, i2pi, &
x_out,y_out,z_out, r_out,phi_out, rho_out,theta_out, &
nregion,cosphi,sinphi)
! VECTOR coordinate mapping interface
type (xplasma), pointer :: s
logical, intent(in) :: vsize_flag ! vector size flag
! .TRUE. means size is to be inferred from passed vector sizes,
! all of which must match;
! .FALSE. means size is to be set by optional argument "isize";
! all vectors must size.ge.isize then.
integer, intent(out) :: ier ! completion code 0=OK
!------------------------
! optional inputs...
integer, intent(in), optional :: isize ! vector size if(vsize_flag)
real*8, intent(in), dimension(:), optional :: x_in,y_in,z_in ! Cartesian Coords in
real*8, intent(in), dimension(:), optional :: r_in,phi_in ! Cylindric Coords (z also)
real*8, intent(in), dimension(:), optional :: rho_in,theta_in ! Flux Coords (phi also)
!------------------------------------------------------------
! the following applies to (R,Z) -> (rho,theta) "inverse" map:
real*8, intent(in), optional :: tol ! map tolerance
! tol applies to Newton map (maptype = xp_imap_newton = default) ONLY
! (R_in,Z_in)->(rho_out,theta_out):
! |R_in-R(rho_out,theta_out)| < tol*max(R_in,|Z_in|)
! |Z_in-Z(rho_out,theta_out)| < tol*max(R_in,|Z_in|)
! default: s%bdytol; s%bdytol's own default value is 1.0d-10
integer, intent(in), optional :: maptype
! = 1 = xp_imap_newton = default -- iterative Newton root finder
! with initial guess generator using polar map (slowest but
! most accurate, accuracy can be controlled with tol.
! = 2 = xp_imap_polar -- polar map inversion method
! much faster, not as accurate as Newton map
! = 3 = xp_imap_rzlinear -- bilinear inverse map -- after
! initialization (using polar map), the fastest, but less
! accurate still (depends on R,Z grid resolution). This
! method requires a user defined (R,Z) rectangle grid; if
! none is available the polar map is used instead.
!------------------------------------------------------------
logical, intent(in), optional :: ccw_phi ! Phi CCW view from above, default=T
logical, intent(in), optional :: ccw_theta ! Theta CCW in poloidal plane, default=T
integer, intent(in), optional :: i2pi ! angle coord normalization options
! i2pi=1 (default) returned value in range [0,2pi]
! i2pi=2 returned value in range [-pi,pi]
!-------------------------
! optional outputs...
real*8, intent(out), dimension(:), optional :: x_out,y_out,z_out ! Cartesian Coords in
real*8, intent(out), dimension(:), optional :: r_out,phi_out ! Cyl. Coords (z also)
real*8, intent(out), dimension(:), optional :: rho_out,theta_out ! Flux Coords (phi also)
real*8, intent(out), dimension(:), optional :: cosphi,sinphi ! phi sin & cos
integer, intent(out), dimension(:), optional :: nregion ! region code
! 0=not checked; 1=inside core plasma; 2=outside plasma but inside
! mapped region; 3=beyond mapped region.
The 2x2 form of the metric jacobian [J]
[ dR/drho dR/dtheta ]
[ dZ/drho dZ/dtheta ]
This is useful for various calculations. For example,
2*pi*R*det[J]*drho*dtheta is the differential volume element;
grad(rho) and grad(theta) are readily derived from [J]; for
f=f(rho,theta), grad(f) = (df/drho)*grad(rho) + (df/dtheta)*grad(theta).
There is a generic interface xplasma_rzjac that maps to two specific
routines: xplasma_rzjac1 for all scalar arguments; xplasma_rzjacn for
all vector arguments.
The metric jacobian array elements, or R, or Z, or det[J], or components
of grad(rho) or grad(theta), or any combination thereof, can be acquired
with xplasma_rzjac. The full interface follows...
Full interface:
interface xplasma_rzjac
module procedure xplasma_rzjac1 ! scalar
module procedure xplasma_rzjacn ! vector
end interface
Scalar interface:
subroutine xplasma_rzjac1(s,rho_in,theta_in,ier, ccwflag1, &
r1,z1,drdrho1,dzdrho1,drdtheta1,dzdtheta1, &
rzdetj1,drhodr1,drhodz1,dthdr1,dthdz1)
Vector interface:
subroutine xplasma_rzjacn(s,rho_in,theta_in,ier, &
ccwflag, r,z,drdrho,dzdrho,drdtheta,dzdtheta, &
rzdetj, drhodr,drhodz, dthdr,dthdz)
! evaluate terms associated with 2d Jacobean -- vector version
! [dR/drho dR/dtheta]
! [dZ/drho dZ/dtheta]
! *** all vectors must be of the same length ***
type (xplasma), pointer :: s
real*8, dimension(:) :: rho_in ! vector of radial coordinate in
real*8, dimension(:) :: theta_in ! vector of poloidal angle coordinate in
! (see ccwflag below)
integer, intent(out) :: ier ! completion code 0=OK
! Poloidal angle orientation: TRUE (default) if dZ/dtheta is
! positive on the large major radius size and negative on the
! small major radius side of each flux surface; FALSE for the
! reverse.
logical, intent(in), optional :: ccwflag
real*8, intent(out), dimension(:), optional :: r,z
! (R,Z) values if desired
real*8, intent(out), dimension(:), optional :: drdrho,dzdrho
! rho derivatives if desired
real*8, intent(out), dimension(:), optional :: drdtheta,dzdtheta
! theta derivatives if desired
real*8, intent(out), dimension(:), optional :: rzdetj
! determinant of 2x2 jacobian matrix
real*8, intent(out), dimension(:), optional :: drhodr,drhodz
! grad(rho) components if desired
real*8, intent(out), dimension(:), optional :: dthdr,dthdz
! grad(theta) components if desired
For any point [R,Z] or a vector of points {R(1:n),Z(1:n)}, it is possible
to very accurately find the closest point of approach on a chosen plasma
flux surface.
The public interface xplasma_bdfind aliases two routines, xplasma_bdfind1
with scalar input and output arguments, and xplasma_bdfindn with vector
input and output arguments, to compute these distances.
Three methods are available, which trade accuracy for speed-- as described
in the interface comments.
Distances returned are in meters; positive numbers denote points outside
the flux surface; negative numbers denote points inside. The user can
optionally receive information on the location of the point on the flux
surface which is closest to the input point(s).
Both these routines are aliased to "xplasma_bdfind" in the module interface.
Full interface:
interface xplasma_bdfind
module procedure xplasma_bdfind1 ! scalar
module procedure xplasma_bdfindn ! vector
end interface
Scalar arguments:
subroutine xplasma_bdfind1(s,zR,zZ,ier, &
maptype, phi_in, rho_in, ccw_theta, ccw_phi, i2pi, outside_only, &
theta_out, phi_out, dist)
Vector arguments:
subroutine xplasma_bdfindn(s,isize,zR,zZ,ier, &
maptype, phi_in, rho_in, ccw_theta, ccw_phi, i2pi, outside_only, &
theta_out, phi_out, dist)
! accurately find the distance of points (R,Z) from a flux surface
! and the closest point on the flux surface
! although phi arguments are supplied to support a possible future
! upgrade to 3d, these have no effect on axisymmetric calculations.
use eqi_rzbox_module
type (xplasma), pointer :: s
integer, intent(in) :: isize ! vector size
! all array arguments' sizes must be .ge.isize
! the target points whose distances from a surface are to be
! calculated:
real*8, dimension(:), intent(in) :: zR ! R values
real*8, dimension(:), intent(in) :: zZ ! Z values
integer, intent(out) :: ier ! completion code 0=OK
!------------
! optional arguments
integer, intent(in), optional :: maptype
! = 1 = xp_imap_newton = default -- iterative Newton root finder
! with initial guess by searching method, accurate w.r.t.
! spline fit of R & Z, to machine precision, but costly...
! = 2 = xp_imap_polar -- polar "circle-fit" distance estimate:
! The boundary is treated as a piecewise linear interpolation
! between circles fit to nearby points on the boundary
! much faster, not as accurate as Newton map
! = 3 = xp_imap_rzlinear -- bilinear inverse map -- after
! initialization (using polar map), the fastest, but less
! accurate still (depends on R,Z grid resolution). This
! method requires a user defined (R,Z) rectangle grid; if
! none is available the polar map is used instead.
real*8, dimension(:), intent(in), optional :: phi_in ! toroidal angle
! (not needed for axisymmetric equilibria)
real*8, intent(in), optional :: rho_in ! **SCALAR** -- the surface from
! which to calculate distances. DEFAULT is the boundary surface rho=1.0
logical, intent(in), optional :: ccw_phi ! Phi CCW view from above, default=T
logical, intent(in), optional :: ccw_theta ! Theta CCW in poloidal plane, default=T
integer, intent(in), optional :: i2pi ! angle coord normalization options
! i2pi=1 (default) returned value in range [0,2pi]
! i2pi=2 returned value in range [-pi,pi]
logical, intent(in), optional :: outside_only ! flag that caller only
! expects to need data from beyond plasma boundary (default .FALSE.)
! (this switch only useful for optimization of maptype=3 calls).
!-------------
! results-- optional but at least one should be present
real*8, dimension(:), intent(out), optional :: theta_out ! theta of
! nearest point on surface
real*8, dimension(:), intent(out), optional :: phi_out ! phi of
! nearest point on surface
real*8, dimension(:), intent(out), optional :: dist ! distance to
! surface. If dist(j) > 0.0 then (zR(j),zZ(j)) is outside the test
! surface; if dist(j) < 0.0 then the point is inside the test surface;
! if dist(j) = 0.0 then the point is on the test flux surface.
A caveat: when the fastest (bilinear) map is used: the distance information
is good to within the resolution of the map, but... the location-on-surface
(theta_out) data can be inaccurate in the vicinity of discontinuities in the
location of the nearest point. These discontinuities occur when passing
through a plane where two widely separate nearest points are equidistant.
The bilinear interpolation of the fast map data then yields a theta value
somewhere between the two nearly equidistant points, which is not correct.
The solution, when the accuracy of the angle coordinate of the nearest
point on the surface is of concern, is to use the slower but more accurate
mapping options.
When xplasma is loaded with an equilibrium, two spline functions
Volume(rho) -- volume, m**3, enclosed by rho surface, 0 <= rho <= 1.
Area(rho) -- area, m**2, of poloidal cross section enclosed by rho
surface, 0 <= rho <= 1.
These are splines defined on the rho grid of the equilibrium. Two
convenience routines are for evaluating these functions: xplasma_volume
and xplasma_area. These are interface names that map to two actual
routines, one which just returns the total plasma enclosed volume
(or area), the other accepting vector of rho values for which either
the volume (area) or its derivative w.r.t. rho may be returned.
This may best be illustrated by example:
real*8, parameter :: rho_bdy = 1.0d0
real*8 :: plasma_volume, plasma_area
real*8 :: my_rho_grid(5) = (/ 0.0d0, 0.25d0, 0.5d0, 0.75d0, 1.0d0 /)
real*8 :: my_vols(5),my_areas(5)
call xplasma_volume(s,plasma_volume, ierr)
call xplasma_volume(s,my_rho_grid,my_vols, ierr)
call xplasma_area(s,plasma_area, ierr)
call xplasma_area(s,my_rho_grid,my_areas, ierr)
The variant with vector arguments accepts an optional argument with which
the derivative of the volume are area may be retrieved:
interface xplasma_volume
module procedure xplasma_vol1
module procedure xplasma_voln
end interface
subroutine xplasma_voln(s,xs,ans,ier, ideriv)
! ** public **
! return Volume(x) or dVol/dx -- calculate if needed.
type (xplasma), pointer :: s
real*8, dimension(:), intent(in) :: xs ! evaluation vector
real*8, dimension(:), intent(out) :: ans ! result of evaluation
integer, intent(out) :: ier ! completion code 0=OK
integer, intent(in), optional :: ideriv ! derivative option 0,1,2
(Here "x" is used for "rho" -- radial flux coordinate).
The accuracies of volumes and areas are to 64 bit machine precision for
the given equilibrium representation (see next subtopic).
There are many flux surface averaged metric and/or field quantities that
may be needed in a plasma simulation, beyond area and volume.
So, xplasma includes a highly efficient and accurate facility for computing
flux surface integrated or averaged metrics on any user specified grid.
To do this, a program using the xplasma library needs write access (see
subtopic).
The steps involved are:
a) enable write access to xplasma (xplasma_author_set).
b) define integration grid & have xplasma initialize data structures
to support integration.
c) have xplasma compute each desired integration, one at a time. If
integrand data caching is enabled, the efficiency is greatly
improved, when multiple integrals or averages need to be computed.
d) clean up temporary data structures
e) release write access (xplasma_author_clear).
The integration method used is a 10 point gauss-kronrod-patterson non-
adaptive method, segmented to evaluate a separate 10 point sum between each
pair of grid points on the equilibrium grid. This means that that the
segments, or, algebraic sums, products, and quotients of cubic spline
segments for the geometry and magnetic fields as interpolated over
the equilibrium grid. For non-singular integrands, this yields accuracy
to 64 bit machine precision, as has been verified by comparing to higher-N
gauss-kronrod-patterson summations. The code to carry this out is derived
from www.netlib.org quadpack/dqng.for.
Of the standard integrations, only the "neoclassical" flux surface
averages "<F(H)NCLASS>S" and "<F(H)NC_TSC>S" involve integrand
singularities. These are of the form sqrt(1-B/Bmax). At present, for
the sake of convenience and speed, these are evaluated using the same
non-adaptive method with a simple patch to the singularity, resulting
in accuracy of about 1 part in 10**4 compared to a slow, precise method.
This is thought to be sufficient for the purposes of these quantities.
User defined spline profiles f(rho,theta) can also be integrated or
averaged, but the machine precision accuracy will only be obtained if
the user splines are defined over the equilibrium grid-- i.e. the same
grid as used by "R", R(rho,theta), "Z", Z(rho,theta), and the magnetic
field splines BMOD, BZ, etc. The accuracies claimed are with respect to
these spline representations of the equilibrium geometry and fields.
Using the flux surface integration facility requires writing working data
to be written into xplasma-- so, it is necessary to introduce routines to
enable this.
By default, xplasma is opened "write locked"-- dictionary items may not be
added. To open xplasma to allow new items to be written, it is necessary
to call the following pair of routines:
character*32 :: author_name = <name-of-my-program-in-quotes>
call xplasma_author_set(s,author_name,ierr)
[... execute code requiring write access to xplasma ...]
call xplasma_author_clear(s,author_name,ierr)
The code actually maintains a stack of author names-- i.e. the code
executed could include subroutine calls to another library which writes
xplasma data under a different authorname, with its own pair of
xplasma_author_set and xplasma_author_clear calls.
(If a routine fails to issue the trailing xplasma_author_clear call, the
next routine that does issue such a call will see an error, because the
author_name passed will not match the author_name at the top of the
author stack).
The user's integration grid spans a subspace of rho [0,1] containing
at least one grid zone. The entire [0,1] space need not be covered.
The subdivided space is defined by passing the rho zone boundaries to
the setup routine:
real*8 :: my_rho_grid(5) = (/ 0.0d0, 0.25d0, 0.5d0, 0.75d0, 1.0d0 /)
integer :: id_integ ! integrator object ID
call xplasma_create_integ(s,'my_integrator',my_rho_grid,id_integ,ierr, &
cache_enable = .TRUE.)
Subsequent integrations using this grid will refer to the integer ID
"id_integ" to reference the integrator.
The integrator defined is now ready to compute surface averages at each
of the grid points (my_rho_grid(1:5)), or, zonal averages covering the
grid zones my_rho_grid(1:2), my_rho_grid(2:3), etc.
For many applications, the 1d integrator is all that is needed. However,
it is also possible to set up a 2d grid, with an additional subdivision
of space by the poloidal angle coordinate:
integer ::id_integ_2d ! 2d integrator object ID
real*8 :: my_theta_grida(n)
my_theta_grid(1) = 0.0d0
...
my_theta_grid(n) = 2*pi ! can also use [-pi,pi]
! or perhaps not span the entire theta coordinate range.
call xplasma_augment_integ(s,'my_2d_integrator',id_integ,my_theta_grid, &
id_integ_2d,ierr)
Note that now two integrators are defined, with IDs:
id_integ -- 1d integrator
id_integ_2d -- 2d integrator.
The typical call to evaluate a flux surface average looks like this:
real*8 :: answer(nn) ! nn the grid size specified earlier to
real*8 :: ans_zon(nn-1) ! "xplasma_creat_integ"...
real*8 :: ans_2d(nth,nn) ! nth the theta grid size specified earlier
real*8 :: ans_2dzon(nth,nn-1) ! to "xplasma_augment_integ"...
! get the differential volume averaged <1/R^2> on my flux surfaces...
call xplasma_rho_zonint(s,id_integ,'<1/R^2>S',answer,ierr)
! one can also evaluate
call xplasma_rho_zonint(s,id_integ,'<1/R^2>',ans_zon,ierr)
! BUT it is much slower and usually the differential average is enough
! for modeling applications...
! <1/R^2> = zonal integral[dVol*(1/R**2)]/integral[dVol] over entire zone
! <1/R^2>S = surface integral[dVol*(1/R**2)]/integral[dVol]
! = surface integral[dVol*(1/R**2)]/(dVol/drho at surface)
! dVol = 2*pi*R*[(dl/dtheta)/grad(rho)]*dtheta*drho
! (available from the equilibrium metric tensor)
! integrations can also be done separately for each theta zone. The
! 1st dimension of ans_2d or ans_2dzon must match the number of theta
! zones:
! get volume of each zone in a 2d partitioning of flux space:
call xplasma_2d_zonint(s,id_integ_2d,'dVOL',ans_2dzon,ierr)
Note that the 3rd argument (character string) identifies the integration
to be computed.
The following integrals and averages are available. The intregral name
passed argument must match one of these exactly, after uppercase conversion:
surface-oriented zone-oriented remark
"VOL" enclosed volume, m**3
"dVOL" volume in each zone
"AREA" enclosed cross sectional area, m**2
"dAREA" area in each zone
"DVDRHO" dVol/drho at each surface, m**3
"LPOL" poloidal path length, m
"SURF" area of flux surface, m**2
"ITOR" enclosed toroidal current, A
"<1/R^2>S" "<1/R^2>" m**-2
"<1/R>S" "<1/R>" m**-1
"<1/R^3>S" "<1/R^3>" m**-2
"<R^2>S" "<R^2>" m**2
"<R>S" "<R>" m
"<1/B^2>S" "<1/B^2>" T**-2
"<1/B>S" "<1/B>" T**-1
"<B^2>S" "<B^2>" T**2
"<B>S" "<B>" T
"<BZ^2>S" "<BZ^2>" T**2
"<grad(RHO)>S" "<grad(RHO)>" m**-1
"<grad(RHO)^2>S" "<grad(RHO)^2>" m**-2
"<grad(RHO)^2/R^2>S" "<grad(RHO)^2/R^2>" m**-4
"<grad(RHO)^2/R^3>S" "<grad(RHO)^2/R^3>" m**-5
"<R^2*grad(RHO)^2>S" "<R^2*grad(RHO)^2>" dimensionless
"<1/(R*grad(RHO))>S" "<1/(R*grad(RHO))>" dimensionless
"<grad(RHO)^2/B^2>S" "<grad(RHO)^2/B^2>" m**-2*T**-2
the following have sqrt singularities; the result is less accurate than
with continuously differentiable integrands:
"<F(H)NCLASS>S" <(1-sqrt(1-h))(1+h/2)/(h*h)>; h=B/Bmax
"<F(H)NC_TSC>S" <(1/B**2)*sqrt(1-h)*(2+h)/3>; h=B/Bmax
if "USER_FUNC" is the name of a profile vs. (rho,theta) existing in the
current xplasma:
"<USER_FUNC>S" "<USER_FUNC>" surface or zone averaged USER_FUNC
"I(USER_FUNC)S" "I(USER_FUNC)" surface or zone integral of USER_FUNC
Any profile of (rho,theta) can be integrated or averaged in this way.
subroutine xplasma_rho_zonint(s,idi,int_name,result,ier, &
dvol_out,iwarn_dvol,dvdrho_out,iwarn_dvdrho)
! integrator -- over rho zones result of form f(rho) indexed by zone
! 1 result per zone (#zones)
! or rho surfaces, also result of form f(rho)
! 1 result persurface (#zones + 1)
type (xplasma), pointer :: s
integer, intent(in) :: idi ! integrator data structure ("black box") id
character*(*),intent(in) :: int_name ! name of desired integration
real*8, dimension(:), intent(out) :: result
integer, intent(out) :: ier ! status code 0=OK
! ** optional outputs **
real*8, dimension(:), optional :: dvol_out ! zone volumes
integer, intent(out), optional :: iwarn_dvol ! =0 if zone volumes OK
! iwarn_dvol=1: zone volumes not available; =2: array dimension error.
real*8, dimension(:), optional :: dvdrho_out ! dV/drho @ surfaces
integer, intent(out), optional :: iwarn_dvdrho ! =0 if dV/drho OK
! iwarn_dvdrho=1: dV/drho not available; =2: array dimension error.
subroutine xplasma_2d_zonint(s,idi,int_name,result,ier, &
dvol_out,iwarn_dvol,dvdrho_out,iwarn_dvdrho)
! integrator -- 2d domain; return integrations or averages
! over theta^rho zones or across theta zones at rho surfaces.
type (xplasma), pointer :: s
integer, intent(in) :: idi ! integrator data structure ("black box") id
character*(*),intent(in) :: int_name ! name of desired integration
real*8, dimension(:,:), intent(out) :: result
integer, intent(out) :: ier ! status code 0=OK
! result is dimension [#theta-zones,#rho-zones] or
! [#theta-zones,#rho-surfaces] according as the integrand
! specified is zone oriented or surface oriented.
! The #zones are defined when the integrator dataset (idi)
! is defined.
! ** optional outputs **
real*8, dimension(:,:), optional :: dvol_out ! zone volumes
integer, intent(out), optional :: iwarn_dvol ! =0 if zone volumes OK
! iwarn_dvol=1: zone volumes not available; =2: array dimension error.
real*8, dimension(:,:), optional :: dvdrho_out ! dV/drho @ surfaces
integer, intent(out), optional :: iwarn_dvdrho ! =0 if dV/drho OK
! iwarn_dvdrho=1: dV/drho not available; =2: array dimension error.
! Options for integrands is the same as for xplasma_rho_zonint;
! Surface oriented results e.g. LPOL are on rho surfaces at each theta
! zone;
! Zone orented results e.g. dVol are on rho^theta zones.
After completion of use of the integrator or integrators, it is a good idea
to clean up, i.e. free the memory and temporary data associated with the
integrators.
call xplasma_remove_item(s,id_integ,ierr) ! remove integrator (rho)
...and if integrations segmented in theta space were done:
call xplasma_remove_item(s,id_integ_2d,ierr) ! remove integrator (rho,theta)
(In general, "xplasma_remove_item" will report an error in case of an
attempt to remove a non-existent item, or an item not owned by the current
author).
Finally, as previously mentioned, it is important to close the "write"
connection to xplasma:
call xplasma_author_clear(s,author_name,ierr)
(author_name the same character string as used in a prior
xplasma_author_set call).
For codes that use a grid based on poloidal flux, it can be useful to
get a mapping to rho=sqrt(toroidal_flux/toroidal_flux_at_bdy). The
following xplasma subroutine provides a precise mapping:
subroutine xplasma_rhopsi_find(s,psivals,rhovals,ierr, tol, iwarn)
! find rho values corresponding to a specified set of Psi values
! Psi -- Poloidal flux, Wb/rad
! rho -- sqrt(Tor_flux/Tor_flux_at_bdy)
! all input Psi values should be in the range [Psimin,Psimax] where
! Psimin corresponds to the magnetic axis and Psimax-Psimin
! corresponds to the (unsigned) poloidal flux, Wb/rad, enclosed
! within the core plasma. Usually Psimin=0 is set.
type (xplasma), pointer :: s
real*8, dimension(:), intent(in) :: Psivals ! Psi values in any order
real*8, dimension(:), intent(out) :: rhovals ! rho values output
! sizes of Psivals and rhovals must match
integer, intent(out) :: ierr ! status code, =0 on normal exit
! error occurs if xplasma is unitialized or contains no MHD equilibrium;
! Psi-out-of-range is handled (see iwarn, below).
real*8, intent(in), optional :: tol ! accuracy tolerance
! on output, rho values satisfy
! abs(Psi(rhovals(i))-Psivals(i)) <= tol*[Psi at bdy]
! (1:npsi) -- sqrt(toroidal_flux/toroidal_flux_at_bdy)
! 0 on axis, 1 at the edge.
integer, intent(out), optional :: iwarn ! #of Psi values out of range
! if Psi <= Psimin, rho=0 is returned; if Psi >= Psimax rho=1 is
! returned.
Caution-- note that the input Psi values are in physical units, Wb/rad,
not normalized to 1.000 at the boundary. But the following conventions
are used:
Psi=0 at the magnetic axis
Psi is "unsigned", always non-negative; rho > 0 ==> Psi'(rho) > 0.
The range of currently known Psi values within the plasma can be had
with the following call:
real*8 psimin,psimax
call xplasma_psi_range(s,psimin,psimax)
The sign of the poloidal flux can be had by fetching jphi_ccw, as in:
integer :: jphi_ccw
call xplasma_global_info(s,ierr, jphi_ccw=jphi_ccw)
The convention is: jphi_ccw=1 means that toroidal current flows counter-
clockwise in the tokamak as viewed from above.
Acquiring xplasma data generally involves two steps:
a) translate name to integer ID (look up in dictionary)
b) access the data, using the ID.
This section describes step (a).
If data of a known type is expected to exist under a known name, the
following calls are the easiest to use:
character*32 :: name
integer :: id
name = <the-name-you-want>
call xplasma_profId(s,name,id) ! look up a PROFILE
--or--
call xplasma_gridId(s,name,id) ! look up a GRID
--or--
call xplasma_coordId(s,name,id) ! look up a PROFILE
--or--
call xplasma_listId(s,name,id) ! look up a LIST
--or--
call xplasma_blkbxId(s,name,id) ! look up a BLACK BOX
Note there is no "ierr" code. If the item does not exist, or if it is
of the wrong type, or if there is some other error (e.g. "s" is a NULL
pointer), id=0 is returned.
! this returns the ID of an item, no matter what its type is.
! setting nf_noerr=.TRUE. causes non-existence of the item not to
! be considered an error; this is an optional argument.
call xplasma_find_item(s,name,id,ierr,nf_noerr=.TRUE.)
! itype is an integer...
call xplasma_get_item_info(s,id,ierr, itype=itype)
if(itype.eq.xplasma_profType) then
<it is a profile>
else if(itype.eq.xplasma_gridType) then
<it is a grid>
...etc...
The following call is used mainly within xplasma to retrieve the IDs of
commonly used items; it is publicly accessible:
subroutine xplasma_common_ids(s,ier, &
id_g,id_psi,id_P,id_R,id_Z,id_Bmod,id_BR,id_BZ, &
id_axis,id_RZminmax)
! return commonly requested profile IDs
! g(rho), psi(rho), R(rho,theta), Z(rho,theta)
type (xplasma), pointer :: s
integer, intent(out) :: ier
integer, intent(out), optional :: id_g
integer, intent(out), optional :: id_psi
integer, intent(out), optional :: id_P
integer, intent(out), optional :: id_R
integer, intent(out), optional :: id_Z
integer, intent(out), optional :: id_BR
integer, intent(out), optional :: id_BZ
integer, intent(out), optional :: id_BMOD
integer, intent(out), optional :: id_axis
integer, intent(out), optional :: id_RZminmax
Use "xplasma_get_item_info" to retrieve one or more of the following:
character*32 :: name
character*32 :: author
character*(*) :: label
character*(*) :: units
integer :: itype ! type of item (list, profile, grid, etc.)
All the return arguments are OPTIONAL and keyworded outputs. An optional
input argument nf_noerr can be set .TRUE. to ignore "invalid ID" errors,
i.e. not to create a message or set an error code in this case.
Example:
character*32 :: my_name,my_author
call xplasma_get_item_info(s,id,ierr, name=my_name, author=my_author)
Full interface:
subroutine xplasma_get_item_info(s,id,ier, &
nf_noerr, &
name,label,units,itype,author)
! return info on item-- what info determined by presence of
! optional arguments
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of item for which info is requested
integer, intent(out) :: ier ! return status code (0=OK)
! optional control:
logical, intent(in), optional :: nf_noerr ! .TRUE. to allow ivalid id
! here is what can be returned...
character*(*), intent(out), optional :: name ! name of item
character*(*), intent(out), optional :: label ! item label
character*(*), intent(out), optional :: units ! item units label
integer, intent(out), optional :: itype ! item type
character*(*), intent(out), optional :: author ! name of author
It is possible to retrieve an entire table of contents of any category
of xplasma data type. Since the interface uses optional arguments, the
user's code decides what type of table of contents list is to be fetched.
In addition, there is special treatment of lists containing elements that
identify profiles (list element name and integer value match name and id of
profile).
The following routine fetches the lengths of the ID lists for tables of
contents:
subroutine xplasma_num_items(s,ier, &
author_only, &
num_lists, num_plists, num_coords, num_grids, num_profs, num_blkbxs)
! return the total numbers of items of various types in xplasma
! container object.
type (xplasma), pointer :: s
integer, intent(out) :: ier ! error code -- only set if s not initialized
character*(*), intent(in), optional :: author_only
! set this to restrict the list to items owned by the specified author
integer, intent(out), optional :: num_lists ! no. of lists
integer, intent(out), optional :: num_plists ! no. lists refering
! to profiles
integer, intent(out), optional :: num_coords ! no. of coordinates
integer, intent(out), optional :: num_grids ! no. of grids
integer, intent(out), optional :: num_profs ! no. of profiles
integer, intent(out), optional :: num_blkbxs ! no. of black boxes
The following routine fetches the actual lists of ids of the indicated type.
Note that ierr=60 is returned if a passed integer array meant to receive
the contents list is too small...
subroutine xplasma_contents(s,ier, &
author_only, &
id_lists, id_plists, id_coords, id_grids, id_profs, id_blkbxs)
! return sorted list(s) of ids of objects of indicated type(s).
! lists of lists, lists of profiles, etc.
type (xplasma), pointer :: s
integer, intent(out) :: ier ! error code, 0=OK
character*(*), intent(in), optional :: author_only
! set this to restrict the list to items owned by the specified author
integer, dimension(:), intent(out), optional :: id_lists ! lists
integer, dimension(:), intent(out), optional :: id_plists ! lists that
! refer to profiles
integer, dimension(:), intent(out), optional :: id_coords ! coordinates
integer, dimension(:), intent(out), optional :: id_grids ! grids
integer, dimension(:), intent(out), optional :: id_profs ! profiles
integer, dimension(:), intent(out), optional :: id_blkbxs ! black boxes
The returned contents lists are sorted in alphabetic order of the names
corresponding to each item ID.
The LIST is one of the data types supported within xplasma.
Each list has a name, and contains a named set of elements. Each element
in addition to its name can have an integer, a floating point value, and
a character string associated with it. Element names must be unique within
the list but are not part of and do not conflict with the xplasma namespace--
indeed, one of the uses of lists is to gather the names and IDs of related
xplasma data items.
Some example applications of lists:
-- storage of commonly referenced data, such as the plasma magnetic axis
location;
-- storage of scalar data, such as plasma ion species lists with the
Z and A of each ion species specified.
-- storage of lists of names and IDs of useful sets of xplasma profiles.
If a list name is known, use the following call to get its integer ID:
call xplasma_listId(s,name,id) ! look up a LIST
Methods for accessing list data are described in the subtopics.
This routine optionally returns the size of a list and labeling information
for the list (the list itself, not the member elements):
subroutine xplasma_list_info(s,id,ier, &
nelems,name,label,units,author)
!
! get the size of an existing list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
integer, intent(out) :: ier ! completion code
integer, intent(out), optional :: nelems ! no. of list elements in list
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
To just get the size of a list:
subroutine xplasma_getList_size(s,id,nelems,ier)
!
! get the size of an existing list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
integer, intent(out) :: nelems ! no. of list elements in list
integer, intent(out) :: ier ! completion code
To get the names of list elements, and, optionally, element data and/or
global labels for lists, this routine can be used:
subroutine xplasma_getList_names(s,id,enames,ier, &
name,label,units,author,ivals,r8vals,chvals)
!
! get the element names from an existing list
! optional arguments allow additional data to be fetched as well.
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
character*(*), dimension(:), intent(out) :: enames ! the names...
integer, intent(out) :: ier ! completion code
character*(*), intent(out), optional :: name ! name (of whole list)
character*(*), intent(out), optional :: label ! label (for whole list)
character*(*), intent(out), optional :: units ! units label (whole list)
character*(*), intent(out), optional :: author ! author
integer, intent(out), dimension(:), optional :: ivals ! integer values
real*8, intent(out), dimension(:), optional :: r8vals ! real*8 values
character*(*), intent(out), dimension(:), optional :: chvals ! str vals
subroutine xplasma_getList_ivals(s,id,ivals,ier)
!
! get integer values in list (r4 precision)
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
integer, dimension(:), intent(out) :: ivals ! the integer values
integer, intent(out) :: ier ! completion code
subroutine xplasma_getList_r8vals(s,id,r8vals,ier)
!
! get real*8 values in list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
real*8, dimension(:), intent(out) :: r8vals ! the real*8 values
integer, intent(out) :: ier ! completion code
4 xplasma_getList_r4vals
subroutine xplasma_getList_r4vals(s,id,r4vals,ier)
!
! get floating point values in list (r4 precision)
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
real, dimension(:), intent(out) :: r4vals ! the floating point values
integer, intent(out) :: ier ! completion code
subroutine xplasma_getList_chvals(s,id,chvals,ier)
!
! get character string values in list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
character*(*), dimension(:), intent(out) :: chvals
! the character string values
integer, intent(out) :: ier ! completion code
The COORDINATE is one of the data types supported within xplasma. It
contains little actual data-- it exists mainly as a mechanism to define
the semantics of grids, with the following associations:
(a) profiles are defined over grids;
(b) grids are associated with coordinates.
Each specific grid is associated with a single coordinate. Multiple grids
can be associated with the same coordinate. Interpolation of profiles is
organized by means of coordinates.
Like all xplasma data items, coordinates have names, labels, physical units,
and authors. Additional attributes of coordinates are:
(a) whether or not a coordinate is periodic;
(b) the minimum and maximum value (range) of a coordinate.
When an xplasma object is initialized, several coordinates are predefined;
their IDs are public parameters supplied by xplasma f90 modules:
integer, parameter, public :: xplasma_rho_coord = 1 ! radial flux coord.
integer, parameter, public :: xplasma_theta_coord = 2 ! poloidal angle coord.
integer, parameter, public :: xplasma_phi_coord = 3 ! toroidal angle coord.
integer, parameter, public :: xplasma_R_coord = 4 ! R coord
integer, parameter, public :: xplasma_Z_coord = 5 ! Z coord
integer, parameter, public :: xplasma_rhox_coord =6 ! "rho" extrapolation
integer, parameter, public :: xplasma_thx_coord = 7 ! "theta" extrapolation
integer, parameter, public :: xplasma_B_coord = 8 ! mod(B) grid
integer, parameter, public :: xplasma_vpv_coord = 9 ! vpll/v coord.
These parameters are frequently used, e.g. to identify the coordinates
of input arguments in multivariate profile interpolation calls.
The above list is sufficient for some applications. However, codes may
define additional coordinates. For example, in the NUBEAM fast ion Monte
Carlo model, a separate energy coordinate is defined for each fast ion
species distribution function, because, the energy range desired for
distribution functions varies from species to species, e.g. fusion
product ion species vs. beam ion species.
Several routines exist for making inquiries about coordinates, as shown
in the subtopics.
subroutine xplasma_coord_info(s,id,ier, &
ngrids,periodic,xmin,xmax,name,label,units,author)
type (xplasma), pointer :: s
integer, intent(in) :: id ! coordinate object id
integer, intent(out) :: ier ! completion code, 0=OK
integer, intent(out), optional :: ngrids ! # of grid discretizations
logical, intent(out), optional :: periodic ! periodicity flag
real*8, intent(out), optional :: xmin ! min value
real*8, intent(out), optional :: xmax ! max value
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
It is possible to retrieve the IDs of specific grids discretizing a
coordinate. This is probably not how it will be done in applications,
which are more likely to find grids by starting from a profile ID.
Nevertheless, this public interface is supplied. The value of the input
argument "indx" should be between 1 and "ngrids", the upper limit being
the number of grids known to discretize the coordinate (see the
xplasma_coord_info call).
subroutine xplasma_coord_gridId(s,id,indx,idgrid,ier)
! get grid id from coordinate grid list index
type (xplasma), pointer :: s
integer, intent(in) :: id ! coordinate object id
integer, intent(in) :: indx ! which grid is desired
integer, intent(out) :: idgrid ! grid id of desired grid
integer, intent(out) :: ier ! completion code, 0=OK
This public interface is provided; the periodicity attribute can also
be obtained through an optional argument in the xplasma_coord_info call.
subroutine xplasma_coord_isPeriodic(s,id,periodic,ier)
! get information whether coordinate object is a periodic angle coord.
type (xplasma), pointer :: s
integer, intent(in) :: id ! coordinate object id
logical, intent(out) :: periodic ! T if this is a periodic coordinate
integer, intent(out) :: ier ! completion code, 0=OK
The GRID is one of the data types supported within xplasma.
Each grid is a strict ascending sequence of numbers covering a specific
range, and is associated with and discretizes a specific COORDINATE.
Some coordinates have a prescribed range-- e.g. the normalized radial
flux coordinate xplasma_rho_coord always covers the range 0 (the magnetic
axis) to 1 (the plasma boundary).
Grids over periodic coordinates cover a range of either [0,2pi] or [-pi,pi].
Some coordinates have their ranges defined by the first discretizing grid--
e.g. xplasma_R_coord and xplasma_Z_coord-- the dimensions of the [R,Z] box
defining a computational region that will vary from tokamak to tokamak.
With the exception of periodic coordinates, all grids discretizing a
given coordinate must cover exactly the same range-- i.e. the first and
last elements of the sequence of numbers defining the grid are constrained
by the coordinate discretized by the grid.
If the name of a grid is known and its ID is needed, the following call
can be used:
call xplasma_gridId(s,name,id) ! look up a GRID
Get the size of a grid...
subroutine xplasma_grid_size(s,id,nx,ier)
! get grid size
type (xplasma), pointer :: s
integer, intent(in) :: id ! grid object id
integer, intent(out) :: nx ! grid size
integer, intent(out) :: ier ! completion code, 0=OK
Get the grid itself. The array "x" must precisely match the size of
the grid.
subroutine xplasma_grid(s,id,x,ier, ccwflag)
! get grid
type (xplasma), pointer :: s
integer, intent(in) :: id ! grid id object
real*8, intent(out) :: x(:) ! the grid returned
integer, intent(out) :: ier ! completion code, 0=OK
logical, intent(in), optional :: ccwflag ! orientation flag
! default value is .TRUE., signifying counter-clockwise orientation
If the grid is periodic, ccwflag=.FALSE. causes the grid to be returned
in reverse order, corresponding e.g. to a clockwise drawn angle coordinate.
The internal grid is always stored with clockwise orientation. The
ccwflag=.FALSE. transform of a periodic grid theta(...) with ntheta
points covering range [-pi:pi] is:
theta_reverse(j) = -pi + (pi - theta(ntheta+1-j))
for j=1:ntheta
This routine returns information on a grid and its associated coordinate.
Other than the xplasma object pointer, the ID, and return status code, all
arguments are optional, so, the caller can select the information desired.
subroutine xplasma_grid_info(s,id,ier, &
size,xmin,xmax,perio,coord,nrefs,name,label,units,author)
! get grid reference count (# of profile items using this grid)
type (xplasma), pointer :: s
integer, intent(in) :: id ! grid id object
integer, intent(out) :: ier ! completion code, 0=OK
integer, intent(out), optional :: size ! size of grid
real*8, intent(out), optional :: xmin ! minimum grid value
real*8, intent(out), optional :: xmax ! maximum grid value
logical, intent(out), optional :: perio ! T for periodic grids
integer, intent(out), optional :: coord ! coordinate associated w/grid
integer, intent(out), optional :: nrefs ! no. of references to grid
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
The PROFILE is one of the data types supported within xplasma. Indeed,
xplasma was largely invented to support the storage, retrieval, and
interpolation of profiles used in plasma simulations.
Profiles are defined over grids (and hence over coordinates as well).
For purposes of interpolation, it is generally enough to know the
coordinates over which a profile is defined, but, it is of course
possible to retrieve the underlying grids, and the original un-
interpolated data, if desired.
Profiles can be 1d, 2d, or 3d-- i.e. defined over 1, 2, or 3 grids
(coordinates).
The interpolation method for a profile is defined by the creator of
the profile. The possibilities are the methods supported by the
PSPLINE NTCC module-- piecewise linear (continuous), cubic Hermite
(continuous and continuously once differentiable), and cubic spline
(continuous and continuously twice differentiable along any coordinate).
In addition, XPLASMA allows zonal step function data to be defined.
For more information on interpolation, see the PSPLINE NTCC web page,
https://w3.pppl.gov/NTCC/PSPLINE.
There is a generic interface for profile interpolation: xplasma_eval_prof.
Using this interface, retrieval of profile information basically comes
down to making an appropriate xplasma_eval_prof call. This method will
be described in detail.
If the name of a profile is known, the following xplasma call retrieves
its ID:
call xplasma_profId(s,name,id) ! look up a PROFILE
The following call allows access to detailed information on a given
profile, as identified by its ID. Many of the arguments are self-
explanatory, but further information on those that might not be is
given below.
subroutine xplasma_prof_info(s,id,ier, &
rank,splineType,gridId1,gridId2,gridId3,profId1,gridType,counter, &
name,label,units,author)
! base argauments in/out
type (xplasma), pointer :: s
integer, intent(in) :: id ! profile object id
integer, intent(out) :: ier ! completion code, 0=OK
!---------------------------------------------------
! select desired information with optional arguments...
integer, intent(out), optional :: rank ! rank
integer, intent(out), optional :: splineType
! -1 for step function; 0 for piecewise linear,
! 1 for C1 Hermite, 2 for C2 cubic spline
integer, intent(out), optional :: gridId1 ! id of 1st grid
integer, intent(out), optional :: gridId2 ! id of 2nd grid (or zero)
integer, intent(out), optional :: gridId3 ! id of 3rd grid (or zero)
integer, intent(out), optional :: profId1 ! id of associated profile
! (sometimes f(R,Z) has an associated f(rho,theta) profile available,
! and vice versa).
integer, intent(out), optional :: gridType ! generic grid type code
! 0=unknown or mixed
! 1=flux coords (rho) or (rho,theta) [phi someday]
! 2=cylindric coordinates (R,Z) [phi someday]
integer, intent(out), optional :: counter ! profile counter
! each new version of this profile gets a new counter value.
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
Notes on argumentes:
rank -- the dimensionality of the profile: 1 for 1d, 2 for 2d, 3 for 3d.
profId1 -- ID for quantity defined over "alternate" grids. For some
profiles there are two versions-- one defined over flux coordinates
and one defined over [R,Z] coordinates. If for the input profile ID
an alternate version exists, the alternate version ID can be returned
with this argument. When interpolations are performed, if the
coordinates supplied are not appropriate for a given profile ID, the
profile's alternate ID, if available, will be tried before giving up.
gridType -- this can be used to determine if the profile is defined over
flux coordinates or cylindrical coordinates. The following public
parameters are available for reference:
integer, parameter, public :: xplasma_flux_coords = 1 ! generic flux coord.
integer, parameter, public :: xplasma_cyl_coords = 2 ! generic cyl. coord.
An alternate method of acquiring a profile's grid information is shown
here; xplasma_prof_info can also be used.
subroutine xplasma_prof_gridInfo(s,id,icoord,idgrid,ierr, &
id_actual)
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of profile about which info is sought
integer, intent(in) :: icoord ! coordinate sought
integer, intent(out) :: idgrid ! grid over this coordinate, or zero
! zero means profile identified by "id" is not a function of the
! specified coordinate; this is not considered an error.
integer, intent(out) :: ierr ! error status code returned, 0=OK
! errors: profile id is invalid; coordinate id is invalid...
integer, intent(out), optional :: id_actual ! "actual" profile id
! explanation: for some quantities there are two representations,
! e.g. Bphi(R,Z) and Bphi(rho,theta). The passed id can refer to
! either of these; the coordinate sought is found for the appropriate
! representation. Thus, if id points to Bphi(R,Z) and info on a rho
! grid is sought, id_actual (if present) will be set to the id of
! Bphi(rho,theta).
xplasma_eval_prof is a generic interface that can be used in any of the
following modes:
(1) interpolate 1d, 2d, or 3d profiles to given target coordinates.
(2) interpolate 1d, 2d, or 3d profiles using previously calculated
lookup information.
(3) retrieve the original profile data (no interpolation).
Each type of use is considered in the subtopics.
Interpolations are supported on 1d, 2d, and 3d profiles, and, in a
single call it is possible to perform interpolations on 1 profile
or multiple profiles, at 1 target location or at a vector of target
locations. Function values or derivatives can be returned. Angle
coordinates may optionally be reversed with transformations
theta -> 2pi - theta.
When the rank of the profiles being evaluated exceeds one, the
coordinate arguments can be given in any order, with grid or
coordinate ID information given to indicate which is which.
Generally for non-periodic coordinates it is an error to provide
target coordinate data that is out of bounds; this behavior can be
overridden with an optional argument "force_bounds". The number
of out of bounds target points can be returned in the optional
output argument "n_out_of_bounds".
The relative tolerance for out-of-bounds tests is "bdytol", available
with the call:
call xplasma_global_info(s,ierr, bdytol=bdytol);
interpolation target coordinates that exceed the coordinate minimum xmin
or maximum xmax by more than bdytol*[xmax-xmin] are considered out of
bounds.
The result "ans" returned is a scalar if there is a single profile id
and a scalar target point. It is a vector if there is either a list
of profile ids or a vector of target points. If there is both a list
of profile ids and a vector of target points, ans is a 2d array with
size(ans,1) matching the target vector dimension, and size(ans,2)
matching the number of profile ids; ans(:,1) will be the results of
the evaluation of the 1st profile; ans(2,:) will be the results for
all profiles at the 2nd target coordinate location.
Here are some example calls:
integer :: id1,id2(2)
real*8 :: x,xvec(3),yvec(3)
real*8 :: ans00,ans2(2),ans3(3),ans2d(3,2)
! 1 id, 1 target, eval 1st derivative
call xplasma_eval_prof(s,id1,x,ans00,ier, ideriv1=1)
! 2 ids, 1 target, eval profile value
call xplasma_eval_prof(s,id2,x,ans2,ier)
! 1 id, vector target, eval profile values
call xplasma_eval_prof(s,id,xvec,ans3,ier)
! 2 ids, vector target, eval value of 1st profile, derivative of 2nd
call xplasma_eval_prof(s,id2,xvec,ans32,ier, ideriv1s = (/0,1/))
! 2d profile evaluation (no derivatives; xvec is "rho"; yvec is "theta"):
call xplasma_eval_prof(s,id2, &
xplasma_rho_coord,xvec, xplasma_theta_coord,yvec, ans32, ier)
When the 2d or higher evaluation routine is called, it is possible for
some of the profiles evaluated to be 1d, provided that the coordinate
scalar or vector argument needed for evaluation of the 1d profile is
present.
What follows is the full interface for the calls mapped to when multiple
ids and a vector target is provided to xplasma_eval_prof.
For 1d profile evaluation:
subroutine xplasma_eval_1dprofsxs(s,ids,xs,ans,ier, &
ideriv1,ideriv1s,ccwflag1,force_bounds,n_out_of_bounds)
! evaluate multiple profiles f(x) 1d at a vector of points
type (xplasma), pointer :: s
integer, dimension(:), intent(in) :: ids ! profile ids
real*8, dimension(:), intent(in) :: xs ! evaluation vector
real*8, dimension(:,:), intent(out) :: ans ! result of evaluations
integer, intent(out) :: ier ! completion code 0=OK
! ans(1:size(xs),1) -- result of eval of profile ids(1)
! ans(1:size(xs),2) -- result of eval of profile ids(2) ...etc...
! default: 0= fcn value
integer, intent(in), optional :: ideriv1 ! derivative control
! 1 for df/dx; 2 for d2f/dx2; 3 for d3f/dx3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv1s
! as ideriv1 but specified separately for each profile
! default: T -- CCW orientation, no CW->CCW transform
logical, intent(in), optional :: ccwflag1
! default: F -- T to force all points in bounds with min & max
logical, intent(in), optional :: force_bounds
! optional output: return no. of target points out of range
integer, intent(out), optional :: n_out_of_bounds
..............
For 2d profile evaluation:
subroutine xplasma_eval_2dprofsxs(s,ids,idx1,x1s,idx2,x2s,ans,ier, &
ideriv1,ideriv1s,ideriv2,ideriv2s, &
ccwflag1,ccwflag2,force_bounds,n_out_of_bounds)
! evaluate multiple profiles f(x1,x2) 2d at a vector of points
type (xplasma), pointer :: s
integer, dimension(:), intent(in) :: ids ! profile ids
integer :: idx1 ! id of x1 grid or coord.
real*8, dimension(:), intent(in) :: x1s ! evaluation vector x1
integer :: idx2 ! id of x2 grid or coord.
real*8, dimension(:), intent(in) :: x2s ! evaluation vector x2
real*8, dimension(:,:), intent(out) :: ans ! result of evaluations
integer, intent(out) :: ier ! completion code 0=OK
! size(x1s)=size(x2s)=size(ans,1) expected...
! ans(1:size(x1s),1) -- result of eval of profile ids(1)
! ans(1:size(x1s),2) -- result of eval of profile ids(2) ...etc...
! default: 0= fcn value
integer, intent(in), optional :: ideriv1 ! derivative control
! 1 for df/d[x1]; 2 for d2f/d[x1]2; 3 for d3f/d[x1]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv1s
! as ideriv1, 1 per evaluation profile id
! default: 0= fcn value
integer, intent(in), optional :: ideriv2 ! derivative control
! 1 for df/d[x2]; 2 for d2f/d[x2]2; 3 for d3f/d[x2]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv2s
! as ideriv2, 1 per evaluation profile id
! default: T -- CCW orientation, no CW->CCW transform
logical, intent(in), optional :: ccwflag1 ! for x1
logical, intent(in), optional :: ccwflag2 ! for x2
! (these are ignored except for theta and phi angle coordinates
! default: F -- T to force all points in bounds with min & max
logical, intent(in), optional :: force_bounds
! optional output: return no. of target points out of range
integer, intent(out), optional :: n_out_of_bounds
For 3d profile evaluation:
subroutine xplasma_eval_3dprofsxs(s,ids,idx1,x1s,idx2,x2s,idx3,x3s, &
ans,ier, &
ideriv1,ideriv1s,ideriv2,ideriv2s,ideriv3,ideriv3s, &
ccwflag1,ccwflag2,ccwflag3,force_bounds,n_out_of_bounds)
! evaluate multiple profiles f(x1,x2) 2d at a vector of points
type (xplasma), pointer :: s
integer, dimension(:), intent(in) :: ids ! profile ids
integer :: idx1 ! id of x1 grid or coord.
real*8, dimension(:), intent(in) :: x1s ! evaluation vector x1
integer :: idx2 ! id of x2 grid or coord.
real*8, dimension(:), intent(in) :: x2s ! evaluation vector x2
integer :: idx3 ! id of x3 grid or coord.
real*8, dimension(:), intent(in) :: x3s ! evaluation vector x3
real*8, dimension(:,:), intent(out) :: ans ! result of evaluations
integer, intent(out) :: ier ! completion code 0=OK
! size(x1s)=size(x2s)=size(x3s)=size(ans,1) expected...
! ans(1:size(x1s),1) -- result of eval of profile ids(1)
! ans(1:size(x1s),2) -- result of eval of profile ids(2) ...etc...
! default: 0= fcn value
integer, intent(in), optional :: ideriv1 ! derivative control
! 1 for df/d[x1]; 2 for d2f/d[x1]2; 3 for d3f/d[x1]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv1s
! as ideriv1, but separately specified for each evaluation
! default: 0= fcn value
integer, intent(in), optional :: ideriv2 ! derivative control
! 1 for df/d[x2]; 2 for d2f/d[x2]2; 3 for d3f/d[x2]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv2s
! as ideriv2, but separately specified for each evaluation
! default: 0= fcn value
integer, intent(in), optional :: ideriv3 ! derivative control
! 1 for df/d[x3]; 2 for d2f/d[x3]2; 3 for d3f/d[x3]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv3s
! as ideriv3, but separately specified for each evaluation
! default: T -- CCW orientation, no CW->CCW transform
logical, intent(in), optional :: ccwflag1 ! for x1
logical, intent(in), optional :: ccwflag2 ! for x2
logical, intent(in), optional :: ccwflag3 ! for x3
! (these are ignored except for theta and phi angle coordinates
! default: F -- T to force all points in bounds with min & max
logical, intent(in), optional :: force_bounds
! optional output: return no. of target points out of range
integer, intent(out), optional :: n_out_of_bounds
Every interpolation involves two steps: zone lookup, and evaluation.
Especially in the case of profiles defined over unevenly spaced grids,
the zone lookup is a significant portion of the computational cost of
the interpolation.
Sometimes it is possible, and worth the effort, to pre-evaluate zone
lookup and reuse this information through several interpolation calls.
Xplasma provides a way to do this. In order for the user application
to take advantage of this capability, it needs to declare special data
structures, given public definitions in the xplasma modules, to store
the results of a zone lookup calculation in such a way that it can be
passed on to an xplasma interpolation routine built for this purpose.
Such a declaration will look something like this:
type (xpeval) :: x1_intrp,x2_intrp,x3_intrp
Then, a generic interface xplasma_x_lookup can be used which will
accept evaluation of a lookup either for a single scalar value or
for a vector of values-- see the subtopic for information on the
lookup pre-evaluation routines "xplasma_x_lookup".
Once the lookups have been evaluated, xplasma_eval_prof can be
called, supplying this information, to perform actual interpolations.
Each xpeval item contains the lookup information either for a single
point on a single coordinate, or a vector of points on a single coordinate.
The evaluation routines expect to do a vector evaluation; at least one
of the xpeval items must contain a vector of n lookup results. Those
with only scalar lookup data will be replicated n times on evaluation.
xplasma_eval_prof will make the following call for interpolation of
a 1d profile:
subroutine xplasma_xpeval_1dprof(s,id,xinfo,ans,ier, ideriv)
! evaluate a single 1d profile function f(x) at a vector of
! target points for which lookup has already been executed
! and stored in "xinfo".
! the x axis grid id of the profile and of the xinfo must match
! the size of the output and the no. of points in the xinfo must
! match.
! xinfo was set up by a prior xplasma_x_lookup call.
type(xplasma), pointer :: s
integer, intent(in) :: id ! 1d function to evaluate
type (xpeval) :: xinfo ! prepared interpolation information
real*8, dimension(:), intent(out) :: ans ! interpolation result
integer, intent(out) :: ier ! status code, 0=OK
integer, intent(in), optional :: ideriv ! derivative selection
The following call is generated for interpolation of a 2d profile:
subroutine xplasma_xpeval_2dprof(s,id,xinfo1,xinfo2,ans,ier, &
ideriv1, ideriv2)
type(xplasma), pointer :: s
integer, intent(in) :: id ! 2d function to evaluate
type (xpeval) :: xinfo1,xinfo2 ! prepared interpolation information sets
real*8, dimension(:), intent(out) :: ans ! interpolation result
integer, intent(out) :: ier ! status code, 0=OK
integer, intent(in), optional :: ideriv1 ! derivative selection, x1
integer, intent(in), optional :: ideriv2 ! derivative selection, x2
And the following call is generated for interpolation of a 3d profile:
subroutine xplasma_xpeval_3dprof(s,id,xinfo1,xinfo2,xinfo3,ans,ier, &
ideriv1, ideriv2, ideriv3)
type(xplasma), pointer :: s
integer, intent(in) :: id ! 3d function to evaluate
type (xpeval) :: xinfo1,xinfo2,xinfo3
! prepared interpolation information sets
real*8, dimension(:), intent(out) :: ans ! interpolation result
integer, intent(out) :: ier ! status code, 0=OK
integer, intent(in), optional :: ideriv1 ! derivative selection, x1
integer, intent(in), optional :: ideriv2 ! derivative selection, x2
integer, intent(in), optional :: ideriv3 ! derivative selection, x3
In all of these calls, the optional arguments default to zero-- meaning,
interpolate the function value, not a derivative.
ideriv*=1 means to evaluate the 1st derivative along the indicated coordinate.
ideriv*=2 means to evaluate the 2nd derivative along the indicated coordinate.
With these methods one interpolates one profile at a time.
These routines must be called to load the xpeval structures BEFORE calling
one of the xplasma_xpeval routines (via xplasma_eval_prof):
When done it is important to FREE MEMORY associated with the xpeval
structure, called "xinfo" in the interfaces below. Failure to free memory
after use will result in memory leaks (pointers are involved).
!------------------------------------------------------
subroutine xpeval_free(xinfo)
type(xpeval) :: xinfo
! deallocate & clean up xinfo
!------------------------------------------------------
interface xplasma_x_lookup
module procedure xplasma_x_lookup1
module procedure xplasma_x_lookupn
end interface
subroutine xplasma_x_lookup1(s,idx,x,xinfo,ier, &
ccwflag,force_bounds,n_out_of_bounds)
! do x lookup -- scalar x value
type (xplasma), pointer :: s
integer, intent(in) :: idx ! x grid (grid) id
real*8, intent(in) :: x ! single scalar x value
type (xpeval) :: xinfo ! *** lookup results
integer, intent(out) :: ier ! status code, 0=OK
! default: T -- CCW orientation, no CW->CCW transform
logical, intent(in), optional :: ccwflag
! default: F -- T to force all points in bounds with min & max
logical, intent(in), optional :: force_bounds
! optional output: return no. of target points out of range
integer, intent(out), optional :: n_out_of_bounds
...............
subroutine xplasma_x_lookupn(s,idx,x,xinfo,ier, &
ccwflag,force_bounds,n_out_of_bounds)
! do x lookup -- scalar x value
type (xplasma), pointer :: s
integer, intent(in) :: idx ! x grid (grid) id
real*8, intent(in), dimension(:) :: x ! vector of x values
type (xpeval) :: xinfo ! *** lookup results
integer, intent(out) :: ier ! status code, 0=OK
! default: T -- CCW orientation, no CW->CCW transform
logical, intent(in), optional :: ccwflag
! default: F -- T to force all points in bounds with min & max
logical, intent(in), optional :: force_bounds
! optional output: return no. of target points out of range
integer, intent(out), optional :: n_out_of_bounds
...............
Some further notes on arguments:
idx -- this must be a grid ID, not a coordinate ID.
x -- if scalar, the generic interface maps to xplasma_x_lookup1;
if vector, the generic interface maps to xplasma_x_lookupn.
xinfo -- dummy argument name for xpeval object, where the results
of the interpolation lookup are stored.
optional arguments:
ccwflag -- if idx points to a grid over an angle coordinate, e.g.
the poloidal angle flux coordinate, setting this flag to .FALSE.
will cause the input data to be transformed by the formula
theta -> 2pi - theta
which would be appropriate if the caller's code wants to treat
the poloidal angle coordinate as drawn clockwise around the
flux surface (inside xplasma, the internal representation is
always drawn counterclockwise).
force_bounds -- force all input arguments to be in range using min
and max function calls. This has no effect on periodic coordinate
arguments, which always brought in range with 2*n*pi shifts as
needed.
n_out_of_bounds (out) -- number of x values not in bounds.
Normally it is an error for out of bounds x values to be provided.
The original profile data can be retrieved using xplasma_eval_prof
with the arguments shown for the following non-generic interfaces.
In all cases, the arrays receiving the data must EXACTLY match the
dimensioning of the profile being retrieved. The dimensioning can
be reconstructed (i.e. use xplasma_prof_info to retrieve rank and
grid IDs; use xplasma_grid_size to get sizes of individual grids,
which are also the correct profile array dimensions).
Error codes will be set if there is a mismatch in array dimension
sizes or array rank; if the return code "ier" is set to a non-zero
value, use:
call xplasma_error(s,ier,6)
to cause the error report to be printed on fortran unit 6 (stdout).
Optional arguments "ccwflag*" are used to specify orientation of
periodic coordinates. These have the same meaning as the corresponding
arguments used when the profile is created. Setting ccwflag<n> to .FALSE.
will reverse the indexing order of the data along the <n>th dimension, if
that dimension corresponds to a periodic coordinate. If the <n>th
coordinate is not periodic, ccwflag<n> is ignored.
---------------
Retrieve 1d profile (this method in the xplasma_eval_prof generic
interface):
subroutine xplasma_getprof_1data(s,id,zdata,ier, &
ccwflag1)
! return the original data associated with this profile
type (xplasma), pointer :: s
integer, intent(in) :: id ! profile id
real*8, dimension(:), intent(out) :: zdata ! data returned
integer, intent(out) :: ier ! completion code 0=OK
logical, intent(in), optional :: ccwflag1 ! CCW flag (default T)
Retrieve 2d profile (this method in the xplasma_eval_prof generic
interface):
subroutine xplasma_getprof_2data(s,id,zdata,ier, &
ccwflag1,ccwflag2)
! return the original data associated with this profile
type (xplasma), pointer :: s
integer, intent(in) :: id ! profile id
real*8, dimension(:,:), intent(out) :: zdata ! data returned
integer, intent(out) :: ier ! completion code 0=OK
logical, intent(in), optional :: ccwflag1 ! dim. 1 CCW flag (default T)
logical, intent(in), optional :: ccwflag2 ! dim. 2 CCW flag (default T)
Retrieve 3d profile (this method in the xplasma_eval_prof generic
interface):
subroutine xplasma_getprof_3data(s,id,zdata,ier, &
ccwflag1,ccwflag2,ccwflag3)
! return the original data associated with this profile
type (xplasma), pointer :: s
integer, intent(in) :: id ! profile id
real*8, dimension(:,:,:), intent(out) :: zdata ! data returned
integer, intent(out) :: ier ! completion code 0=OK
logical, intent(in), optional :: ccwflag1 ! dim. 1 CCW flag (default T)
logical, intent(in), optional :: ccwflag2 ! dim. 2 CCW flag (default T)
logical, intent(in), optional :: ccwflag3 ! dim. 3 CCW flag (default T)
This is a specialized routine for evaluating R(rho,theta) and/or
Z(rho,theta), with automatic failover to extrapolated profiles of R and Z
for rho > 1, when these are available.
In general, if xplasma has scrap off layer information, i.e. limiter
locations, it will, when updating the MHD equilibrium, create
extrapolated (rho,theta) -> (R,Z) map which however is not differentiable
across the rho=1 boundary.
This routine gives access to both the original and extrapolated R and Z
in the context of a single call.
subroutine xplasma_RZeval_2d(s,ids,idx1,x1s,idx2,x2s,ans,ier, &
ideriv1,ideriv1s,ideriv2,ideriv2s, &
ccwflag1,ccwflag2,force_bounds,n_out_of_bounds)
! use xplasma_eval_2dprofsxs to
! evaluate multiple profiles f(x1,x2) 2d at a vector of points
! IF all the profiles are "R" and "Z",
! and IF there is a scrape-off region,
! split the evaluation so that points inside the plasma use
! the standard R & Z, and points outside use the extrapolated
! profiles.
type (xplasma), pointer :: s
integer, dimension(:), intent(in) :: ids ! profile ids
integer :: idx1 ! id of x1 grid or coord.
real*8, dimension(:), intent(in) :: x1s ! evaluation vector x1
integer :: idx2 ! id of x2 grid or coord.
real*8, dimension(:), intent(in) :: x2s ! evaluation vector x2
real*8, dimension(:,:), intent(out) :: ans ! result of evaluations
integer, intent(out) :: ier ! completion code 0=OK
! size(x1s)=size(x2s)=size(ans,1) expected...
! ans(1:size(x1s),1) -- result of eval of profile ids(1)
! ans(1:size(x1s),2) -- result of eval of profile ids(2) ...etc...
! default: 0= fcn value
integer, intent(in), optional :: ideriv1 ! derivative control
! 1 for df/d[x1]; 2 for d2f/d[x1]2; 3 for d3f/d[x1]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv1s
! as ideriv1, 1 per evaluation profile id
! default: 0= fcn value
integer, intent(in), optional :: ideriv2 ! derivative control
! 1 for df/d[x2]; 2 for d2f/d[x2]2; 3 for d3f/d[x2]3
! 2 & 3 available only for C2 spline fits
integer, intent(in), dimension(:), optional :: ideriv2s
! as ideriv2, 1 per evaluation profile id
! default: T -- CCW orientation, no CW->CCW transform
logical, intent(in), optional :: ccwflag1 ! for x1
logical, intent(in), optional :: ccwflag2 ! for x2
! (these are ignored except for theta and phi angle coordinates
! default: F -- T to force all points in bounds with min & max
logical, intent(in), optional :: force_bounds
! optional output: return no. of target points out of range
integer, intent(out), optional :: n_out_of_bounds
The BLACK BOX is one of the data types supported within xplasma.
A black box is a storage mechanism for data not fitting into other types.
It consists of the following:
a) a user defined integer "type" code;
b) a user specified 1d array or buffer of any size, of integers;
c) a user specified 1d array or buffer of any size, of 64 bit
(real*8) floating point numbers.
The black box is used within xplasma for a number of purposes-- caches for
flux surface integration, data to support rapid methods for evaluation of
coordinate transformations, "MCgrid" data, ...
Integer black box type codes in the range 0 to 101 are used by xplasma
library software; it is recommended that user defined black boxes stay
away from using type codes in this range.
In order to avoid unnecesary and costly copying of large arrays, the
black box data access allows pointers to the black box integer and
floating point array data to be returned. This means however that
the contents of black box datasets can be modified directly by
assignment of (pointer) array elements, without going through an
interface call. User caution is advised.
The black box type code, buffer sizes, and generic xplasma item labeling
can be retrieved with this call. Except for the status code, output
arguments are optional, allowing the caller to control what information
is to be returned.
subroutine xplasma_blackBox_info(s,id,ier, &
type,isize,r8size,name,label,units,author)
!
! get the size of an existing list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of black box item
integer, intent(out) :: ier ! completion code
integer, intent(out), optional :: type ! "type" of black box
! generally, meaning of iitype is user defined...
integer, intent(out), optional :: isize ! number of integer words
integer, intent(out), optional :: r8size ! number of floating pt words
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
Black box data, or pointers to data, can be retrieved with this call. As
usual, most output arguments are optional:
subroutine xplasma_blackBox_retrieve(s,id,ier, &
itype,iarray,r8array,ia_ptr,r8a_ptr,name,label,units,author)
! retrieve blackBox data
! optional arguments; user controls what data is wanted.
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of black box item
integer, intent(out) :: ier ! completion code, 0=OK
integer, intent(out), optional :: itype ! "type" of black box
! meaning is user defined.
! if copies of arrays are wanted:
integer, dimension(:), intent(out), optional :: iarray
real*8, dimension(:), intent(out), optional :: r8array
! if pointers to arrays are wanted:
integer, dimension(:), pointer, optional :: ia_ptr
real*8, dimension(:), pointer, optional :: r8a_ptr
! optional labeling info...
character*(*), intent(out), optional :: name ! item name
character*(*), intent(out), optional :: label ! item label
character*(*), intent(out), optional :: units ! item units label
character*(*), intent(out), optional :: author ! author
Xplasma supports the notion of an axisymmetric "limiter" or vessel wall.
The region between the limiter and the core plasma contains plasma and
neutral gas on open field lines and is referred to here as the scrape off
layer.
For reasons related to legacy code, xplasma supports two methods for
defining an axisymmetric limiter. Therefore, the routine for retrieving
limiter information supports both representations. However, the routines
"xplasma_bdycon" and "xplasma_limcon" support methods for returning the
plasma boundary and limiter represented as a closed contour (to good
approximation), regardless of the internal representation.
The interface xplasma_lim_distance allows quick access to the distance
from any point in space to the nearest limiter point.
Specific call interfaces are in the subtopics.
subroutine xplasma_lim_info(s,ier, &
itype, npts,rpts,zpts, &
dist, &
nlines, Rl, Zl, thl, &
ncircs, Rc, Zc, rad)
! retrieve limiter information
type(xplasma), pointer :: s
integer, intent(out) :: ier
integer, intent(out), optional :: itype
! itype=100 means piecewise linear closed contour (axisymmetric limiter)
! itype=101 means list of circles & infinite lines
integer, intent(out), optional :: npts ! #pts in contour (itype=100 only)
real*8, dimension(:), intent(out), optional :: rpts,zpts
! the closed contour; rpts(npts)=rpts(1) and zpts(npts)=zpts(1) (m).
real*8, intent(out), optional :: dist ! itype=101 limiters only...
! if .ge.0.0d0, the plasma boundary + dist is considered a limiter
! if .lt.0.0d0-- no plasma boundary based limiter.
integer, intent(out), optional :: nlines ! #lines in itype=101 limiter
real*8, dimension(:), intent(out), optional :: Rl,Zl,thl
! point (m) through which line passes, and line orientation in DEGREES
! e.g. 45.0 means up and to the right...
integer, intent(out), optional :: ncircs ! #circles in itype=101 limiter
real*8, dimension(:), intent(out), optional :: Rc,Zc,rad
! center location and radius of each circle (m)
subroutine xplasma_lim_rzminmax(s,zRmin,zRmax,zZmin,zZmax,ier, &
itype)
! get the Rmin,Rmax,Zmin,Zmax of the vacuum region
! enclosed by the mechanical limiter
! optional: get a limiter type code also.
type (xplasma), pointer :: s
real*8, intent(out) :: zRmin,zRmax,zZmin,zZmax ! R & Z min & max (m)
integer, intent(out) :: ier
integer, intent(out), optional :: itype ! limiter representation
! within xplasma: 100 for piecewise contour; 101 for "circles and
! lines" possibly augmented by a fixed distance from the plasma
! boundary.
subroutine xplasma_limcon(s,rlim,zlim,inum_got,ier, &
tol,maptype)
! return a closed contour of length size(rlim)=size(zlim) or less
! describing the limiter.
! if tol.le.0.0d0 -- the length of the description is size(rlim) exactly
! if tol.gt.0.0d0 -- length may be shortened to remove colinear points.
! also, if the limiter was originally given in contour form, and,
! size(rlim) is sufficient, the original specification will be
! returned.
type (xplasma), pointer :: s
real*8, dimension(:), intent(out) :: rlim,zlim ! limiter contour
! the sizes of these vectors must be the same.
! unless the original specification is being returned, the code
! requires a minimum size of 30 points.
integer, intent(out) :: inum_got ! contour length returned
! if inum_got.lt.size(rlim), rlim(inum_got+1:size(rlim))=0 on exit.
! similarly for zlim.
integer, intent(out) :: ier ! status code, 0=OK
real*8, intent(in), optional :: tol ! tolerance specification
! if zero or negative, no shortening of the limiter description is
! attempted. If .gt. 0.0, adjacent segments that are colinear to within
! tol*[Rmax] are replaced by a single segment. DEFAULT: s%bdytol
integer, intent(in), optional :: maptype ! mapping option
! 1=slowest but most exact; 2=intermediate; 3=bilinear interpolation
! 2 or 3 recommended. DEFAULT: 2
Return a closed contour describing the plasma boundary; length of contour
taken from size of vector arguments provided:
subroutine xplasma_bdycon(s,rbdy,zbdy,ier)
! return a closed contour around the plasma boundary, similar to
! xplasma_limcon...
type (xplasma), pointer :: s
real*8, dimension(:), intent(out) :: rbdy,zbdy ! bdy contour
! the sizes of these vectors must be the same.
integer, intent(out) :: ier
interface xplasma_lim_distance
module procedure xplasma_lim_dist1
module procedure xplasma_lim_distn
end interface
subroutine xplasma_lim_dist1(s,r,z,d,ier, maptype, rlim1,zlim1)
! scalar version of xplasma_lim_distn -- axisymmetry assumed.
type (xplasma), pointer :: s
real*8, intent(in) :: R,Z ! input location (m)
real*8, intent(out) :: d ! output distance from nearest point
! on limiter or wall (m).
integer, intent(out) :: ier
integer, intent(in), optional :: maptype ! see xplasma_lim_distn
real*8, intent(out), optional :: rlim1,zlim1 ! nearest limiter point
end subroutine xplasma_lim_dist1
subroutine xplasma_lim_distn(s,rvec,zvec,dist,ier, &
maptype,rlim,zlim)
! find distance from each element of a vector of (R,Z) pairs
! to the nearest point on a limiter.
! optionally return the location of that point on the limiter
! in (R,Z).
! the routine assumes axisymmetry. Optional phi arguments may
! be added someday.
type (xplasma), pointer :: s
real*8, intent(in), dimension(:) :: rvec,zvec ! (R,Z) input vector
real*8, intent(out), dimension(:) :: dist ! distance values returned
integer, intent(out) :: ier
integer, intent(in), optional :: maptype ! distance map option
! if it is required to compute the distance from the (R,Z) point to
! the plasma boundary, this specifies the option (for xplasma_bdfind).
! Default is the circle-fit option (2). (1) is slower but more
! precise; (3) is less accurate but would be faster if a bilinear
! distance map has already been computed.
real*8, intent(out), dimension(:), optional :: rlim,zlim ! locations
! of nearest contact points on limiter, for each
! input location.
The MCgrid, or "Monte Carlo grid", is an irregular 2d spatial grid that
was developed originally for capture of 2d binned data in a Monte Carlo
calculation (NUBEAM). It is used for fast ion distribution functions and
for certain spatially 2d profiles output by NUBEAM-- such as, beam halo
thermal neutral sources, beam-target and beam-beam fusion rates.
The grid is aligned with flux coordinates. It is constructed of "zone
rows" that are equally spaced in rho=sqrt(Psi_tor/Psi_tor_at_bdy); each
zone row is subdivided into a different number of zones equispaced in
the equilibrium poloidal angle coordinate theta, with fewer subdivisions
for zone rows near the axis, and more for zone rows in the edge. The
result is a set of zones all with roughly equal volume and cross sectional
area, as is desirable for consistency of Monte Carlo summation statistics.
The number of poloidal zones per zone row is linear in the zone row index:
typically, 4 in the 1st zone row adjacent to the magnetic axis, 4+4=8 in the
next row out, 8+4=12 in the next row, etc.
There are two variants, according as the underlying MHD equilibrium is
updown symmetric, or updown asymmetric.
As internally stored, the first row layout is this for the updown symmetric
variant:
_____
/ | \
/ 2 | 1 \
|___|___|
with theta=0 on the large major radius side; increasing to theta=pi on
the large major radius side covering the upper half of the plasma cross
section above the midplane. The next row out would have 4 zones, the
next 8 zones, etc.
And this for the updown asymmetric variant:
_____
/ | \
/ 4 | 3 \
|___|___|
| | |
\ 1 | 2 /
\__|__/
with theta=-pi on the lower branch on the small major radius side, theta=0
on the large major radius side, and theta=pi on the upper branch on the small
major radius side. The next zone row contains 4+4=8 zones, the next 8+4=12 zones,
and so on.
Poloidal zones are stored contiguously, with the poloidal zone index
increasing with increasing theta coordinate, theta being oriented
counter-clockwise in the plasma cross section drawn to the right of
the machine axis of symmetry.
This is how the data is stored internally.
Note, however, that on retrieval of MCgrid data, there are options to
reverse the storage order to be consistent with a clockwise oriented
poloidal angle coordinate (as preferred by some codes), and, for both
updown symmetric and updown asymmetric data, the start point for theta
zone indexing can be specified as either 0, -pi, or the default (which
is 0 for updown symmetric data and -pi for updown asymmetric data).
These choices are controlled by optional arguments in the MCgrid data
access routines.
The MCgrid itself is a named "black box" item in the xplasma dataset;
in principle there could be multiple MCgrids defined, but in practice
there is usually only one.
Each profiles defined over an MCgrid is itself a named "black box" item.
When a MCgrid is set up, the volume of each MCgrid zone is computed,
and this is itself stored as a profile over the MCgrid; the name is
the MCgrid name with the suffix "_DVOL" appended.
These integer parameters define the black box type codes used for MCgrids
and their associated profiles:
integer, parameter, public :: xplasma_bbgtype=17 ! MC grid type
integer, parameter, public :: xplasma_bbftype=18 ! MC profile type
Although information on these grids and profile could be obtained by
using generic black box data access calls, it is better to use the
calls specifically provided to support MCgrid items, which are described
in the subtopics. Calls for access to MCgrid data are shown here; calls
to create MCgrid data are given under the section heading
Modifying_F95_Xplasma_Data.
Usually there is only one MCgrid per dataset, so, the optional argument
id_mcgrid1 can be used in the public interface shown below. If the MCgrid
is known to have been created by a named author, the search can be
constrained by that name: e.g "author_only='NUBEAM'".
subroutine xplasma_mcgrid_find(s,ierr, author_only, &
id_mcgrid1,num_mcgrids,id_mcgrids)
! find MCgrid grid definitions that are available in the current
! xplasma-- usually there will be only one we think.
type(xplasma), pointer :: s
integer, intent(out) :: ierr ! status code returned (0=OK)
character*(*), intent(in), optional :: author_only
! restrict search to MCgrid objects written by this author or code
integer, intent(out), optional :: id_mcgrid1 ! MCgrid id returned, if...
! if exactly one exists. If none exist, id_mcgrid1=0 is returned; if
! >1 exists,id_mcgrid1=-<the number of MCgrids> is returned.
integer, intent(out), optional :: num_mcgrids ! number of MCgrids found.
integer, dimension(:), optional :: id_mcgrids ! MCgrid ids returned.
The following interface enables access to details on the MCgrid. It should
be noted that for historical reasons the MCgrid supports an extension beyond
the plasma boundary, but, known applications of the MCgrid are restricted
to the plasma core at present.
Other than the error code, the return arguments are optional, so, the
caller can select just the information that is needed.
subroutine xplasma_mcgrid_info(s,id_mcgrid,ierr, &
nzrow,nzrow_ext, nzons,nzons_ext, nth0,nphi, nths, udsym, &
name,label)
! fetch information on xplasma_mcgrid grid
type(xplasma), pointer :: s
integer, intent(in) :: id_mcgrid ! id of MCgrid object to be queried
integer, intent(out):: ierr ! status code 0=OK
integer, intent(out), optional :: nzrow ! # of zone rows in plasma
integer, intent(out), optional :: nzrow_ext ! # inside and outside
integer, intent(out), optional :: nzons ! # of zones in plasma
integer, intent(out), optional :: nzons_ext ! # inside and outside
integer, intent(out), optional :: nth0 ! # of zones @axis cover [0:pi]
integer, intent(out), optional :: nphi ! # of phi zones (1:axisymmetry)
integer, dimension(:), intent(out), optional :: nths
! number of theta zones in each zone row
logical, intent(out), optional :: udsym
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: author
-----------
It is important to notice that nth0 gives only the number of zones in the
upper (or lower) halfplane of the first zone row, regardless of whether
the underlying MCgrid is updown symmetric or updown asymmetric. Thus,
an updown symmetric grid with 2 zones in the first row, and an updown
asymmtric grid with 4 zones in the first row, would both return a value
of 2 for nth0.
[A working example is in xplasma_debug_module.f90]
integer :: id_mcgrid
real*8 :: rho = 0.375d0 ! rho where index is wanted
real*8 :: th = 0.2d0 ! theta angle where index is wanted
real*8, parameter :: rhomax = 1.0d0 ! only looking at core plasma region...
integer :: nrow,irow ! zone rows
integer, dimension(:), allocatable :: nths,nthsum
logical :: udsym ! updown symmetry flag
integer :: indx
call xplasma_find_mcgrid(s,ierr, id_mcgrid1=id_mcgrid)
if(ierr.ne.0) [handle xplasma error]
if(id_mcgrid.eq.0) [no MCgrid exists]
if(id_mcgrid.lt.0) [multiple MCgrids exist]
call xplasma_mcgrid_info(s,id_mcgrid,ierr, nzrow=nrow, udsym=udsym)
allocate(nths(nrow),nthsum(0:nrow))
call xplasma_mcgrid_info(s,id_mcgrid,ierr, nths=nths)
nthsum(0)=0
do irow=1,nrow
nthsum(irow)=nthsum(irow-1)+nths(irow)
enddo
! find row index using subroutine. The indexing is counting from -pi,
! regardless of the symmetry flag-- there is an implicit assumption that
! MCgrid profile data is fetched with this option!!!
call mcindx(rho,th,udsym,nrows,rhomax,nths,nthsum,indx)
!-------------------------
subroutine mcindx(rho,th,udsym,nrow,rhomax,nths,nthsum,indx)
! private
! this routine assumes that the theta indexing is oriented
! counterclockwise and that it starts at -pi for each row,
! *even* for updown symmetric data.
real*8, intent(in) :: rho,th ! flux coords in
logical, intent(in) :: udsym ! updown symmetry flag
integer, intent(in) :: nrow ! #rows
real*8, intent(in) :: rhomax ! max rho in range
integer, intent(in) :: nths(nrow) ! # theta zones / row
integer, intent(in) :: nthsum(0:nrow) ! # in prior rows
integer, intent(out) :: indx
!----------------------------------------
integer :: irho,ith
real*8 :: zth,zthlim
real*8, parameter :: CPI = 3.1415926535897931D+00
real*8, parameter :: ZERO = 0.0d0
!----------------------------------------
indx = 0
if(udsym) then
zth = -abs(th)
zthlim = ZERO
else
zth = th
zthlim = CPI
endif
irho = 1 + rho*nrow/rhomax
if((irho.ge.1).and.(irho.le.nrow)) then
ith = 1 + (zth+CPI)*nths(irho)/(zthlim+CPI)
if((ith.ge.1).and.(ith.le.nths(irho))) then
indx = nthsum(irho-1) + ith
endif
endif
end subroutine mcindx
The following interface allows retrieval of the volumes of all the
MCgrid individual zones:
subroutine xplasma_mcgrid_volumes(s,id_mcgrid,vols,ierr, &
lstart0_th,ccwflag_th)
! return the zone volumes associated with a MCgrid. Compute them
! if necessary.
type(xplasma), pointer :: s
integer, intent(in) :: id_mcgrid ! MC grid id
real*8, dimension(:) :: vols ! the array of volume elements
integer, intent(out) :: ierr ! status code (0=OK)
! theta indexing options
logical, intent(in), optional :: lstart0_th ! default depends on symmetry
logical, intent(in), optional :: ccwflag_th ! default: T
! lstart0_th=T means user's first theta zone lower bdy is 0; F means it
! is -pi. If defaulted: updown symmetric data assumed to start at 0;
! updown asymmetric data assumed to start at -pi.
! ccwflag_th=T means zone index increases as one goes along a row of
! zones at fixed radial index in a counter-clockwise direction; F means
! the opposite. T is the default.
The following routine allows retrieval of a list of IDs of available profiles
defined over a MCgrid. In typical usage, a first call might get the number
of such profiles, followed by allocation of an integer array to hold the
list of profile IDs, followed by a second call to actually fetch the IDs.
subroutine xplasma_mcgrid_findProfs(s,id_mcgrid,ierr, &
author_only, num_profs, id_profs)
! find profiles defined over given MCgrid
type(xplasma), pointer :: s
integer, intent(in) :: id_mcgrid ! MC grid id
integer, intent(out) :: ierr ! status code returned (0=OK)
character*(*), intent(in), optional :: author_only
! restrict search to MCgrid profiles written by this author or code
integer, intent(out), optional :: num_profs ! number of profiles found.
integer, dimension(:), optional :: id_profs ! profile ids returned.
This routine returns information on an individual MCgrid profile. Except
for the status code, all output arguments are optional, allowing the caller
to select the desired information:
subroutine xplasma_mcgrid_profInfo(s,id_mcprof,ierr, &
id_mcgrid,nzons,irank,idim1,idim2, &
gridId1,gridId2,normcode, &
name,label,units,author)
! fetch information on a single profile defined over an MCgrid...
type (xplasma), pointer :: s
integer, intent(in) :: id_mcprof ! profile ID
integer, intent(out) :: ierr ! status code returned: 0=OK
integer, intent(out), optional :: id_mcgrid ! MCgrid ID
integer, intent(out), optional :: nzons ! #spatial zones
integer, intent(out), optional :: irank ! #non-spatial dimensions
integer, intent(out), optional :: idim1,idim2 ! non-spatial dim. sizes
! grid IDs for non-spatial dimensions (if available)
integer, intent(out), optional :: gridId1
integer, intent(out), optional :: gridId2
! normalization code
integer, intent(out), optional :: normcode
!-------------
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: label
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
For multidimensional profiles with velocity space coordinates (i.e.
distribution functions), the arguments gridId1 and gridId2 can be used
to retrieve the IDs of the velocity space coordinates used.
This routine retrieves the actual profile data into a user provided
array. An array with correct dimensioning must be supplied by the user.
The last dimension of the user supplied array is the spatial dimension,
matching the MCgrid size-- the value of the "nzons" argument returned by
xplasma_mcgrid_info(...), for profiles defined just over the core plasma
region.
subroutine xplasma_mcgrid_getobj(s,id_p,ierr, &
lstart0_th,ccwflag_th, &
data_1d,data_2d,data_3d,label,units)
! retrieve previously stored MCgrid profile
! (see xplasma_mcgrid_putobj)
type (xplasma), pointer :: s
integer, intent(in) :: id_p ! MCgrid profile ID
integer, intent(out) :: ierr ! exit status code (0=OK)
! theta indexing options
logical, intent(in), optional :: lstart0_th ! default depends on symmetry
logical, intent(in), optional :: ccwflag_th ! default: T
! lstart0_th=T means user's first theta zone lower bdy is 0; F means it
! is -pi. If defaulted: updown symmetric data assumed to start at 0;
! updown asymmetric data assumed to start at -pi.
! ccwflag_th=T means zone index increases as one goes along a row of
! zones at fixed radial index in a counter-clockwise direction; F means
! the opposite.
!-----------------
! one of the optional arguments data_1d, data_2d, data_3d must
! be provided to receive the data.
! the provided data array sizes must match the stored data.
real*8, dimension(:), intent(out), optional :: data_1d
real*8, dimension(:,:), intent(out), optional :: data_2d
real*8, dimension(:,:,:), intent(out), optional :: data_3d
!-----------------
character*(*), intent(out), optional :: name
character*(*), intent(out), optional :: labels
character*(*), intent(out), optional :: units
character*(*), intent(out), optional :: author
The previous sections discussed methods for accessing data in an xplasma
object that was already created. This and the following sections describe
how to build up an xplasma object from scratch, and how to evolve it in a
time dependent simulation.
The following pieces of an xplasma dataset normally do not evolve in time;
rather, they are initialized once at the start of the lifetime of an xplasma
object:
(a) the flux coordinate grids "rho" and "theta" over which equilibrium
profiles are defined:
flux surface label "rho" = sqrt(toroidal_flux/toroidal_flux_at_bdy)
poloidal angle "theta" -- in the internal xplasma representation,
moving in the direction of +theta at fixed rho traces out a
flux surface in the counter-clockwise direction, when the flux
surface cross section is drawn to the right of the tokamak axis
of symmetry (R=0).
(b) the location of an axisymmetric limiter or vacuum vessel wall, usually
specified as a closed contour sequence of [R,Z] pairs.
(c) the R and Z grids covering a rectangle in space that encloses the
limiters and will enclose the core plasma at all times.
(d) additional coordinates and grids as may be defined by the user, e.g.
in velocity space; multiple grids may discretize the same coordinates.
The items that do evolve over the lifetime of an xplasma object are:
(e) the equilibrium over flux coordinates
R(theta,rho), Z(theta,rho) -- the geometry of flux surfaces (m).
g(rho) = R*|B_phi|, a flux surface constant, m*T;
B_phi = bphi_ccw*(g/R);
bphi_ccw=+1 if toroidal field points counter-clockwise
-1 if clockwise, viewed from above
Psi(rho) = poloidal flux function, Wb/rad, zero on axis,
dPsi/drho > 0, with relation to the poloidal field
Bpol = -jphi_ccw*(1/R)*grad(Psi);
jphi_ccw=+1 if toroidal current flows counter-clockwise
-1 if clockwise, viewed from above
P(rho) = a profile representing the pressure used in the Grad-
Shafranov equation to produce the equilibrium
The derived profile BR(theta,rho), BZ(theta,rho), BPHI(theta,rho),
BMOD(theta,rho) give spline approximations to the field components.
(f) the equilibrium over (R,Z) coordinates
Psi(R,Z), Wb/rad, Bpol = -jphi_ccw*(1/R)*grad(Psi)
BR_RZ(R,Z), BZ_RZ(R,Z), BPHI_RZ(R,Z), BMOD_RZ(R,Z) are provided as
spline approximations to the field components over (R,Z) coordinates.
(g) any collection of named data items--
* profiles-- zonal data, piecewise linear data, or splines over
up to 3 independent coordinates
* lists -- each containing its own collection of named, labeled
elements with associated scalar integer and floating
point values.
* black boxes -- generic storage of "unstructured" integer and
floating point data.
The INITIALIZATION of an xplasma object consists of the definition of
the elements that do not (normally) vary in time.
The most direct method of initialization is to start from an EFIT
G-eqdsk file containing a free boundary equilibrium reconstruction:
use xplasma_obj_instance
character*32 :: author_name = 'My_code'
character*60 :: filename = <path to G-eqdsk file>
call xoi_init(ier) ! init xplasma pointers in module
call xplasma_author_set(s,author_name,ier)
call xplasma_fromgeqdsk(s,filename,ier, &
nrho = <number of numerical flux surfaces including axis>, &
ntheta = <number of poloidal grid points covering 0:2pi>)
! also a good idea:
real*8 :: curtime
curtime = <current time of simulation>
call xplasma_time_set(s,curtime,ier)
call xplasma_author_clear(s,author_name,ier)
The optional arguments specify the desired equilibrium flux coordinate
grid resolutions; if defaulted, the code uses nrho=51, ntheta=101.
Loading xplasma from a G-eqdsk file for the first time, items (a)-(c),
are defined, and initial values for items (e)-(f) are also provided.
The equilibrium can subsequently be modified with later
calls to xplasma_fromgeqdsk, reading modified G-eqdsk files, but then
the limiter and [R,Z] grids found must be the same as were given in
the first G-eqdsk file read; this is checked and an error will be
reported if there is a mismatch.
Further initializations may involve the definition of additional
coordinates and grids, e.g. in velocity space, which are application
dependent.
The xplasma public interfaces include the following routines likely to
be used in initialization:
xplasma_create_coordinate xplasma_create_grid
which will be described below.
The alternative to using a G-eqdsk file is to use a series of calls to
spell out each of the components of the equilibrium. In this context,
initialization consists in the definition of the flux coordinate grids,
the limiters, and the [R,Z] grids. Code to do this is sketched below.
Details on the interfaces used will be given below.
!--------------------------
! on the FIRST call, the rho and theta grids need to be defined...
character*32 :: author_name = 'My_code'
call xplasma_author_set(s,author_name,ier)
allocate(rho(inrho),th(inth))
<fill in rho values; rho(1)=0.0; rho(inrho)=1.0>
<fill in th values over range -pi:pi or 0:2pi>
call xplasma_create_grid(s,'my_rho',xplasma_rho_coordinate,rho,id_rho,ier, &
label='my rho grid for the equilibrium')
call xplasma_create_grid(s,'my_theta',xplasma_theta_coordinate,th,id_th, &
ier, label='my theta grid for the equilibrium')
! the field SIGNS also have to be set, once, at the beginning of the
! lifetime of the xplasma object:
! bphi_ccw=1 means the toroidal field points counter clockwise;
! jphi_ccw=-1 means the toroidal current flows clockwise
! these orientations as viewed from above the tokamak
! values of +/-1 are allowed for each of these arguments
call xplasma_field_ccw_set(s,ier, bphi_ccw=1, jphi_ccw=-1) ! example
! also on the first call, a LIMITER should be defined...
allocate(rpts(nlim),zpts(nlim))
<fill in with sequence of (R,Z) pairs defining a closed contour>
<rpts(1)=rpts(nlim) and zpts(1)=zpts(nlim) are expected>
call xplasma_mklim_contour(s,rpts(1:nlim),zpts(1:nlim),iwarn,ier)
! and an [R,Z] rectangular space covering the plasma and limiter should be
! defined:
allocate(rgrid(nrgrid),zgrid(nzgrid))
<fill in desired R and Z grids which should at least span the limiter>
< i.e. rgrid(1) <= minval(rpts); rgrid(nrgrid) >= maxval(rpts) >
< i.e. zgrid(1) <= minval(zpts); zgrid(nzgrid) >= maxval(zpts) >
call xplasma_create_rzgrid(s,rgrid(1:nrgrid),zgrid(1:nzgrid), &
id_rgrid,id_zgrid,ier) ! these arguments returned...
! done for now...
call xplasma_author_clear(s,author_name,ier)
In this section, the new Fortran-95 Xplasma interface is described,
with emphasis on methods for adding data to an existing dataset, or,
creating one from scratch.
The xplasma pointers (s,s1) defined in xplasma_obj_instance are presumed
to be available in the coding examples.
The argument "ierr" is always a return status code, 0=normal.
For simplicity of examples, error checking / error handling code is
usually omitted -- don't try this at home!
Since xplasma is envisioned as a means of communication between disparate
code modules or even separately executing processes, it is useful to be
able to trace the origin of xplasma data items to specific codes or
modules.
In practice, this means a code must "identify itself" to access xplasma.
A code that writes into xplasma must provide an "author id", which is any
alphanumeric name, not starting with a digit, up to 32 characters long.
Failure to do this will usually result in an "xplasma locked" error report.
Codes which write to xplasma should use the following pair of calls:
character*32 author_name
author_name = "my_code" ! or whatever name you choose...
call xplasma_author_set(s,author_name,ier) ! push my name onto author stack
<write stuff into xplasma>
call xplasma_author_clear(s,author_name,ier) ! pop my name off the stack
An error could conceivably occur on the latter call, if an intervening
call (after xplasma_author_set) was made to another module, which issued
another xplasma_author_set but failed to issue a matching call to
xplasma_author_clear; the software would then detect that the passed name
does not match the name at the top of the author stack.
To prevent such errors, it is important to always pair these calls.
FREE BOUNDARY MHD equilibria are best read into xplasma from a G-eqdsk
file:
call xplasma_author_set(s,author_name,ier)
call xplasma_fromgeqdsk(s,filename,ier, [...optional arguments...])
! also a good idea:
real*8 :: curtime
curtime = <current time of simulation>
call xplasma_time_set(s,curtime,ier)
call xplasma_author_clear(s,author_name,ier)
The first time this is done, the resolution of the flux coordinate "rho"
and "theta" grids can be specified-- note that only evenly spaced grids
are allowed for equilibria built from G-eqdsk at present. If the grid
size optional arguments "nrho" and "nth" are omitted, the default values
nrho=51 and nth=101 are used-- sufficient for many purposes.
On subsequent calls, the equilibrium flux coordinate grids are reused
from the prior state.
PRESCRIBED BOUNDARY MHD equilibria are updated through a sequence of
calls which redefine the equilibrium profiles-- as described in the
subtopic.
The full xplasma_fromgeqdsk interface is copied below.
Except possibly for specifying "nrho" and "nth" on the first call, most
users should leave the optional arguments unspecified-- the default values
are well tested and reliable.
The argument "filename" can be either a unix file path to a read-accessible
G-eqdsk file in the standard GA-documented EFIT format, or, it can be a
path to an MDS+ tree (access to experimental database).
MDS+ path strings take the following form:
MDS+[/<time-option>]:<server>[:<port>]:<tree>(<shot>;t=<time>)
items in square brackets [,] are optional (though sometimes a port number
is required to access a particular server); items in angle brackets <,>
are to be replaced with their correct values; everything else is literal.
<time-option> can be REDUCE (the default) or NOREDUCE.
REDUCE means: select the nearest available time; read only that time
from the MDS+ database.
NOREDUCE means: read all available times from the database; interpolate
linearly between available times to get to the <time> requested.
In applications which loop over all times in an experiment, it is more
efficient to use NOREDUCE (fewer network data accesses).
<server> or <server>[:<port>] is the network address of the MDS+ server.
Examples: alcdata.psfc.mit.edu (CMOD); europa.pppl.gov:8501 (NSTX).
<tree> is the name of the tree containing time dependent EFIT G-eqdsk data.
Typical examples: EFIT01 (NSTX, DIII-D), ANALYSIS (CMOD).
<shot> is the integer shot or pulse number of the experimental data.
<time> is time in seconds in any decodable floating point format.
Example: MDS+/REDUCE:EUROPA.PPPL.GOV:8501:EFIT06(116313;t=0.9)
----------------------
CAUTION: MDS+ server access may be restricted, depending on the policy
of the experimental project that owns the server.
----------------------
subroutine xplasma_fromgeqdsk(s,filename,ier, &
new_device, nrho, nth, lhermite, &
bdy0, cratio_min, rzspace, rhobrk_adj_axis, mom_rtol, &
equal_arc, &
gs_errmax)
! (re)build equilibrium inverse represenation from EFIT g-eqdsk data
! R(rho,theta), Z(rho,theta), g(rho),q(rho),P(rho).
type (xplasma), pointer :: s
character*(*), intent(in) :: filename ! Geqdsk filename or MDS+ path
! ...or...EFIT_INDEX:filename(t=<time>) specification
! see comments in source/xplasma/eqm_fromgeqdsk.f90
integer, intent(out) :: ier ! integer status code (returned; 0=OK)
!---------------
! optional...
logical, intent(in), optional :: new_device ! flag, .TRUE. if reading
! equilibrium for a new device. Default: .TRUE. when reading the
! first equilibrium in the history of the xplasma object; .FALSE.
! subsequently. When this is not .TRUE., the limiter data is presumed
! already known to xplasma.
integer, intent(in), optional :: nrho ! no. of flux surfaces
! covering rho = [0:1]
! rho = normalized(sqrt(Psi-tor/Psi-tor-at-bdy))
! for 20 zones specify nrho=21 -- count includes axis and plasma
! boundary-- evenly spaced in rho
! DEFAULT: nrho=51 if new_device is .TRUE.; otherwise the value from
! the old equilibrium is taken.
integer, intent(in), optional :: nth ! no. of theta points -- no. of
! evenly spaced points spanning 0:2pi inclusive.
! DEFAULT: nth=101 if new_device is .TRUE.; otherwise the value from
! the old equilibrium is taken.
logical, intent(in), optional :: lhermite ! flag, .TRUE. for Hermite
! interpolation of {R,Z}(rho,theta) -- by default, bicubic spline
! interpolation is used.
real*8, intent(in), optional :: bdy0 ! initial Psi_bdy/Psi_sep
! (default 1.0d0) fraction of Psi to enclose in mapped region
! The issue here is that a separatrix surface generally cannot be
! used as a boundary in an inverse representation, due to q=infinity
! and the kink in the surface shape. So instead one backs off from
! the Psi_sep flux, defining a boundary just inside the separatrix.
! Recommendation: the default. "cratio_min" causes the code to
! iterate to find a suitable boundary surface.
! Accepted range: 0.95d0 to 1.00d0
real*8, intent(in), optional :: cratio_min ! minimum curvature ratio
! (default 0.080d0) minimum acceptable local curvature radius,
! normalized to midplane half width. A separatrix kink has a curvature
! radius of zero. This is tested by fitting a circle through successive
! sequences of 3 points on the boundary grid [1:nth] and looking at this
! circle's radius compared to an estimate of the plasma midplane half-
! width.
! Recommendation: the default, based on experience.
! Accepted range: 0.01d0 to 0.15d0
logical, intent(in), optional :: rzspace ! .TRUE. to save (R,Z)
! grids using the EFIT grids, and compute B(R,Z). Default = .TRUE.
! Setting this .FALSE. may speed some applications.
integer, intent(in), optional :: kmom ! number of Fourier moments
! to use in Fourier Spline representation of equilibrium
! (default: not set here; xplasma default is 16).
real*8, intent(in), optional :: rhobrk_adj_axis ! delta(rho) for
! moments trimming operations, and, the maximum rho where flux
! surfaces can be touched, in order to assure that d/drho[R0,Z0] --> 0
! as rho--> 0, an attributed needed by codes (e.g. RF solvers) that
! use the inverted equilibrium.
! Default value: 0.15d0. To disable this feature, set
! rhobrk_adj_axis = -1.0d0 or any other negative number
real*8, intent(in), optional :: mom_rtol ! moments trimming ratio
! (default 1.0d-6). The EFIT Psi(R,Z) inversion involves a Fourier
! representation; this control specifies a method for trimming away
! higher order moments that are very small over wide regions of space.
! Accepted range: 0.0d0 to 1.0d-4
logical, intent(in), optional :: equal_arc ! .TRUE. to use an equal
! arc definition for the poloidal angle coordinate; .FALSE. (default)
! for VMEC/descur moments power spectrum minimization definition, the
! traditional definition for past usage.
real*8, intent(out), optional :: gs_errmax ! normalized measure of
! Grad Shafranof (GS) error in the EFIT equilibrium.
Immediately after reading an xplasma file or MDS+ tree (i.e. after an
xplasma_fromgeqdsk call) it is possible to rewrite the G-eqdsk data into
a separately named standard EFIT G-eqdsk file.
This is used mainly to extract MDS+ tree data into files, so that legacy
codes which only know to read G-eqdsk as files, not as MDS+ records, can
work:
subroutine xplasma_geqdsk_rewrite(filename,ier)
! rewrite g-eqdsk file e.g. under new filename, after reading
! (e.g. inside an eqi_fromgeqdsk call).
! WARNING: the information written is based on the most recent
! read of g-eqdsk information. Information to reconstruct the
! g-eqdsk from the data as originally read is NOT saved with each
! xplasma object. Hence: no s pointer argument.
character*(*), intent(in) :: filename
integer, intent(out) :: ier
NOTE that there is no xplasma object pointer-- this simply rewrites the
most recently read G-eqdsk data.
The xplasma library is using a subsidiary, non-object-oriented library
for EFIT G-eqdsk I/O. So, this method should be used with care.
After an equilibrium has been created within xplasma, by whatever means,
it can be written as a G-file.
The method here differs from xplasma_geqdsk_rewrite as follows:
(a) the output [R,Z] domain and resolution can be redefined;
(b) the equilibrium could have been created by means other than an
"xplasma_fromgeqdsk" call.
subroutine xplasma_wr_geqdsk(s,ierr, &
lun_geqdsk, filename, label, Rmin, Rmax, Zmin, Zmax, &
cur, id_qprof, id_pprof, &
nh, nv, nbdy)
use eqi_geq_mod
! Build and write a G-eqdsk file (disk file) from current xplasma
! contents. This differs from "xplasma_geqdsk_rewrite" as the latter
! just echoes data read in from another G-eqdsk file or MDSplus data
! structure. This actual constructs the information from current
! xplasma contents -- interpolation is involved.
! The xplasma object must contain a complete free boundary or
! extrapolated equilibrium, so that Psi(R,Z) covering a rectangle
! enclosing the plasma is defined.
!-----------------
! required:
type (xplasma), pointer :: s ! XPLASMA object containing equilibrium
integer, intent(out) :: ierr ! status code on exit: 0=OK
! optional:
integer, intent(in), optional :: lun_geqdsk
! FORTRAN LUN on which to write file. If omitted, the LUN used for
! reading G-eqdsk files, in the geqdsk_mds library, is used.
! (call geq_getlun(ilun)) (geqdsk_mod default value: 77 as of 7/2006).
character*(*), intent(in), optional :: filename
! default " "; if non-blank, it is the
! name of the file to write. If blank or omitted, the code simply
! writes to lun_geqdsk; it would be up to the caller to OPEN a file.
character*(*), intent(in), optional :: label
! default " "; default means: use xplasma global label;
! if non-blank, the 1st 48 characters are used as a label string in the
! G-eqdsk file being written.
real*8, intent(in), optional :: Rmin,Rmax, Zmin,Zmax
! [R,Z] domain over which Psi(R,Z) is written in the G-eqdsk file.
! default: use the [R,Z] grid limits already stored in xplasma. If
! explicit limits are provided, overriding the defaults, these must
! not extend beyond the grid limits.
real*8, intent(in), optional :: cur
! total plasma current. Default: use value implied by equilibrium data.
integer, intent(in), optional :: id_qprof
! ID of profile f(rho) defining q(rho). Default: use value derived from
! equilibrium-- calculated here, it will be named Q_EQDSK
integer, intent(in), optional :: id_pprof
! ID of profile f(rho) defining equilibrium pressure. Default: use value
! previously tagged as equilibrium pressure. If this is not available,
! the code will attempt to compute a pressure using the surface averaged
! GS equation JxB=grad(P). This could lead to a non-physical result if
! there are errors in the equilibrium data already provided, or if the
! assumption of a scalar P is inappropriate. If a pressure profile is
! calculated, it will be named P_EQDSK-- done using an xplasma_gen_p
! call.
integer, intent(in), optional :: nh,nv
! number of horizontal and vertical grid points, respectively. If
! defaulted, the sizes of the [R,Z] grids are used. NOTE that nh also
! controls the size of the 1d profiles f,ff',P,P',and q written in the
! G-eqdsk profiles. These 1d profiles are written over an implied
! evenly spaced Psi grid going from Psi(min) at the axis to Psi(max)
! at the boundary.
integer, intent(in), optional :: nbdy
! number of points to use to described plasma boundary and limiter.
! default: 200.
A working example is given in the xplasma test program xplasma_fsp_test.
Here a sketch of the code is provided. Details on individual calls are
provided in other parts of this document.
Prescribed boundary codes like TRANSP use this method.
Many details must be specified-- one reason why using a G-eqdsk instead is
often more convenient...
call xplasma_author_set(s,author_name,ier)
!--------------------------
! on the FIRST call, the rho and theta grids need to be defined...
! this is presumed done already; the rho and theta grid IDs are
! presumed known and stored in integers "id_rho" and "id_th".
integer :: id_rho, id_th ! grid IDs
integer :: inrho, inth ! grid sizes
! can retrieve this grid IDs e.g. as follows:
integer :: id_R
call xplasma_common_ids(s,ier, id_R=id_R) ! retrieve R(theta,rho) ID
call xplasma_prof_info(s,id_R,ier, gridId1=id_th, gridId2=id_rho)
! can get grid sizes this way:
call xplasma_grid_size(s,id_rho,inrho,ier)
call xplasma_grid_size(s,id_th,inth,ier)
! can also fetch actual grids with "xplasma_grid" calls.
!--------------------------
! on calls other than the FIRST call, the following call will DELETE
! the old equilibrium profiles:
logical :: rzonly ! TRUE to delete R,Z only; FALSE to delete {R,Z,g,p,Psi}
<set rzonly to desired value>
call xplasma_eqClear(s,rzonly,ier)
!--------------------------
allocate(R(inth,inrho),Z(inth,inrho)) ! dimensions match equilibrium
! flux coordinate grids
<update R and Z data arrays to desired new geometry>
! define the geometry
! optional arguments allow specification of dR/drho and dZ/drho
! boundary conditions if desired.
call xplasma_rzmag(s,id_th,id_rho,R,Z,id_R,id_Z,ier, [options...])
allocate(g(inrho),psi(inrho),p(inrho),q(inrho))
<update G, psi, P, q data arrays>
call xplasma_create_prof(s,'G',id_rho,g,id_g,ier)
call xplasma_create_prof(s,'Psi',id_rho,psi,id_psi,ier)
call xplasma_create_prof(s,'Q',id_rho,q,id_q,ier) ! or...
call xplasma_create_prof(s,'P',id_rho,p,id_p,ier)
! (Q can also be computed by xplasma, once G and Psi have been provided)
ispline=2
call xplasma_gen_q(s,'Q',ispline,id_q,ier)
! enough information has now been specified to check the equilibrium and
! compute field component splines
iforce=.TRUE. ! do the computation unconditionally
call xplasma_eqCheck(s,iforce,icount,ier)
! the error code should be checked!! if the (R,Z) geometry contains
! metric singularities, it will be detected. Such errors cannot be
! ignored as these would otherwise cause a zoo of failures later on...
! finally, to create an environment similar to the one provided by
! a free boundary equilibrium, based however only on a rough extrapolation
! of the fields to a [R,Z] rectangle covering the core and scrapeoff plasma
! DO NOT USE this if a free boundary Psi(R,Z), defining a real external
! field, is available...
call xplasma_brz_extrap(s,ier, [options...])
! also a good idea:
real*8 :: curtime
curtime = <current time of simulation>
call xplasma_time_set(s,curtime,ier)
call xplasma_author_clear(s,author_name,ier)
Xplasma routines never write messages to files or to stdout-- all error
handling is by means of a status code. Almost all xplasma subroutine
calls return an integer status code "ierr". If the value of ierr returned
is 0, there is no error, completion status is normal.
(NOTE: spelling variant: some xplasma routines spell this "ier" in
their argument lists).
Codes using xplasma need to check for and handle errors. Xplasma "tries
hard" to avoid errors; if it fails the error is generally not ignorable.
It is expected that the normal action after an xplasma error will be to
shut down the software as gracefully as possible with appropriate
explanatory messages.
Messages associated with errors are created and stored within a small
message buffer in the xplasma object. It is up to the code using xplasma
to control the disposition of these messages.
If an f95 xplasma routine returns a non-zero error code, the following
call should be used:
call xplasma_error(s,ierr,ilun)
with "s" the xplasma object pointer,
(integer) ierr the status code, *input* to this routine, and
(integer) ilun the fortran I/O unit on which to write messages
associated with this error.
In addition, the user software should generally write some message
such as perhaps "unexpected xplasma error in subroutine XYZ, exiting...";
good messages of this type are a tremendous help in debugging large codes.
Generally, specific error codes are associated with specific errors.
For example, ierr=101 indicates that a NULL s pointer was passed.
A major change to F95 xplasma compared to the original version, is the
ability to update the xplasma equilibrium on the fly, while preserving
other profiles. (In the original F77 xplasma version, an equilibrium
change required reconstruction of the entire xplasma contents).
Some items are marked as "derived from the equilibrium"-- these are
generally built internally within xplasma and have the following
author ID:
character*32, parameter, public :: xplasma_xmhd = '__XPLASMA_MHDEQ_DERIVED'
These items will be deleted and rebuilt, each time the equilibrium
changes. Examples of such items are:
-- list giving magnetic axis parameters R_axis, Z_axis, B_axis
-- list giving R, Z, B extrema of boundary surface
-- data related to flux surface averages
-- data related to coordinate transformations
----------------------------------------------------------------
There are some structural limits on changes over time.
The contraints are as follows. Although profiles, including those
describing the equilibrium, can be updated or replaced at any time,
underlying grids are not so easily changed.
In particular, the software does not allow a grid to be deleted or
replaced, if it detects that any existing profiles are using that grid.
If it were necessary for a time dependent code to regrid to a finer
mesh, all affected profiles would need to be reinterpolated accordingly.
This can be done, but there is no standard method at present. Some code
would have to be written.
Because F95 xplasma supports multiple instantiation of xplasma objects, it
would not be too difficult.
A sketch of the code to carry this out would look like this:
integer :: ier,ii,idg,inum_profs,inum_modify
integer :: idg1,idg2,idg3,icoord
integer, dimension(:), allocatable :: ids, ids_modify
character*32 gname
idg = [id of grid that will be changed]
! retrieve name and associated coordinate ID
! (in a real code these are probably known already)
call xplasma_grid_info(s,idg,ier, name=gname, coord=icoord)
! make a copy of the xplasma object "s" to "s2"
call xplasma_copy(s,s2,ier)
! get the full list of profiles
call xplasma_num_items(s,ier, num_profs=inum_profs)
allocate(ids(inum_profs),ids_modify(inum_profs))
call xplasma_contents(s,ier, id_profs=ids)
! delete profiles which use "idg"; build list of deleted profiles
inum_modify = 0
do ii=1,inum_profs
call xplasma_prof_info(s,ids(ii),ier, &
gridId1=idg1,gridId2=idg2,gridId3=idg3)
if((idg1.eq.idg).or.(idg2.eq.idg).or.(idg3.eq.idg)) then
! this profile uses the marked grid
inum_modify = inum_modify + 1
ids_modify(inum_modify) = ids(ii)
call xplasma_remove_item(s,ids(ii),ier)
endif
enddo
! remove the old idg grid-- this should succeed now, since all the profiles
! using idg were removed from "s".
call xplasma_remove_item(s,idg,ier); [check ier status]
<form new grid data real*8 x(1:n)> -- same name, same id will result.
call xplasma_create_grid(s,gname,icoord,s,idg,ier,label='...')
do ii=1,inum_modify
<using s2 copy of profile, form a local array copy reinterpolated to
the new grid>
<for multidimensional profiles this involves a loop over each
of the other coordinates>
call xplasma_create_prof(s,...) ! Insert interpolated profile into "s".
enddo
call xplasma_free(s2,ier) ! done with "s2"-- free the memory.
Coordinates, grids, profiles, lists, and "black boxes" can be created or
replaced by the user, within the following constraints:
(a) When a grid is created, the coordinate to which it belongs must be
identified. Conversely, coordinates cannot be removed or replaced
if there are any grids currently associated with the coordinate.
(b) When a profile is created, the grid or grids over which it is
defined must be specified. Conversely, grids cannot be removed
or redefined when they are "in use" by any profile.
Every item within xplasma has a unique name and integer ID. Since the
name translation and lookup is somewhat expensive, most calls which refer
to an item requires its integer ID. Full names are used when items are
created, and when their IDs are retrieved, per the following table:
item type CREATE call LOOKUP call
coordinate xplasma_create_coordinate xplasma_coordId
grid xplasma_create_grid xplasma_gridId
profile xplasma_create_prof xplasma_profId
list xplasma_create_list xplasma_listId
black box xplasma_create_blackBox xplasma_blkbxId
The lookup calls all take the arguments (s,name,id) where s is the
xplasma object pointer, name is the item name, id is the integer id
returned.
The interfaces for the CREATE routines are described in the subtopics.
Some specialized methods are available. These are:
xplasma_irhofun -- create a smoothed integrated profile
xplasma_rzprof -- create a profile f(R,Z), e.g. from f(rho).
xplasma_rzprof_fun -- xplasma_rzprof with a user provided function call
xplasma_mcgrid_define -- create an irregular "Monte Carlo" grid (MCgrid).
xplasma_mcgrid_putobj -- create a profile over a MCgrid.
--------------
All blocks of code using the CREATE methods should be bracked by the
pair of calls requesting and releasing write authority:
call xplasma_author_set(s,author_name,ier)
[create stuff]
call xplasma_author_clear(s,author_name,ier)
The label and physical units of an item are generally optional arguments
in the xplasma_create subroutines. These can be provided later, by means
of this call:
character*50 :: label = <very brief description of item>
character*16 :: units = <physical units label, should be MKS>
call xplasma_label_item(s,id,ier, label=label, units=units)
!-------------------
subroutine xplasma_label_item(s,id,ier, &
label,units)
! label an existing item as identified by "id"...
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of existing item
integer, intent(out) :: ier ! status code returned
character*(*), intent(in), optional :: label ! label for item
character*(*), intent(in), optional :: units ! units label for item
subroutine xplasma_create_coord(s,coordname,periodic,id,ier, &
label,units)
! create a coordinate with the specified name; return new coord id
type (xplasma), pointer :: s
character*(*), intent(in) :: coordname
logical, intent(in) :: periodic ! T if named coordinate is periodic
integer, intent(out) :: id ! id of coordinate object
integer, intent(out) :: ier ! completion code
character*(*), intent(in), optional :: label
character*(*), intent(in), optional :: units
subroutine xplasma_create_grid(s,gridname,icoord,x,id,ier, &
ccwflag,label)
! create a new grid with the specified name; return new grid id
type (xplasma), pointer :: s
character*(*), intent(in) :: gridname
integer, intent(in) :: icoord ! id of coordinate object to which the
! grid object belongs
! icoord can also be a grid id, in which case the coordinate associated
! with this new grid is the one associated with grid (icoord)
real*8, intent(in) :: x(:) ! the grid, x(j).lt.x(j+1) required.
integer, intent(out) :: id ! id of grid object
integer, intent(out) :: ier ! completion code
! specify if toroidal or poloidal angle grid is drawn counterclockwise:
logical, intent(in), OPTIONAL :: ccwflag
! theta: when viewing plasma cross section to right of machine
! centerline
! phi: when viewing machine from above
! Default: .TRUE.
character*(*), intent(in), optional :: label
! units label inherited from associated coordinate object
! NOTE: xplasma saves all angle grids with CCW orientation; a CW
! oriented grid will be redefined with its direction reversed:
! angle -> 2pi - angle
Generally, the following arguments are required:
xplasma object pointer
profile name
grid ID(s)
profile data -- array dimensions must match gridsizes EXACTLY.
profile ID returned
error status returned
Optional arguments:
ispline -- type of interpolation
-1 -- zonal data (step function):
this is the default if the data dimension sizes are 1 less
than the corresponding grid sizes.
0 -- piecewise linear (the default)
1 -- C1 Akima Hermite
2 -- C2 Cubic Spline
Boundary conditions (for ispline=1 or ispline=2). For rank N data,
each explicit boundary condition data array is of rank N-1; array
dimensions must match appropriate gridsizes EXACTLY. But-- there
are many cases where explicit boundary conditions are not required;
it depends on the application of the data.
ccwflag arguments -- in effect these specify that the storage order
of the data along a periodic grid dimension is reversed-- but, the
end points must match the referenced grid.
assoc_id -- pairs of profiles can be associated. This is to support
the idea that a physical quantity, e.g. a B field component, might
have both a flux coordinate representation and an [R,Z] representation.
When profiles are associated, the IDs for the flux coordinate versions
and [R,Z] coordinate versions of the data can be used interchangeably,
i.e. providing the flux coordinate profile ID to an [R,Z] interpolation
call will not result in an error.
label & units
Verbatim interfaces for 1d 2d and 3d profile create routines follow...
interface xplasma_create_prof
module procedure xplasma_create_1dprof
module procedure xplasma_create_2dprof
module procedure xplasma_create_3dprof
end interface
subroutine xplasma_create_1dprof(s,profname,idx,data,idf,ier, &
ispline,ibca,zbca,ibcb,zbcb,ccwflag,assoc_id,label,units)
! create a new profile with the specified name
!--------
! standard INPUT arguments...
type (xplasma), pointer :: s
character*(*), intent(in) :: profname ! name of profile
integer, intent(in) :: idx ! id of grid over which it is defined
real*8, dimension(:), intent(in) :: data ! the profile data
! (NOTE: size of data must match size of grid)
!--------
! standard OUTPUT arguments...
integer, intent(out) :: idf ! id of profile now being (re)defined.
integer, intent(out) :: ier ! completion code (0=normal)
!--------
! optional input arguments...
! defaults: pc linear; for splines: "not a knot" BC
! angle grid: normal CCW orientation.
integer, intent(in), optional :: ispline ! order of fit
! 0 -- pc linear; 1 -- Hermite; 2 -- cubic spline;
integer, intent(in), optional :: ibca ! BC control @ LHS of grid
real*8, intent(in), optional :: zbca ! BC data @ LHS of grid
integer, intent(in), optional :: ibcb ! BC control @ RHS of grid
real*8, intent(in), optional :: zbcb ! BC data @ RHS of grid
! ibca=ibcb=0 = default: "not a knot" for splines; Akima for Hermite
! for periodic functions BC args are ignored; periodic BC is used.
! ibc[*]=1 -- df/dx specified in zbc[*] (0 if absent)
! ibc[*]=2 -- d^2f/dx^2 specified in zbc[*] (0 if absent)
logical, intent(in), optional :: ccwflag ! T means data vs. CCW angle
! grid; F means CW. Default=T. ccwflag is ignored if the x grid of
! the profile is not an angle coordinate.
integer, intent(in), optional :: assoc_id
character*(*), intent(in), optional :: label
character*(*), intent(in), optional :: units
!------------------------------------------
subroutine xplasma_create_2dprof(s,profname, &
idx1,idx2,data,idf,ier, &
ispline,ibcx1a,zbcx1a,ibcx1b,zbcx1b, &
ibcx2a,zbcx2a,ibcx2b,zbcx2b, ccwflag1,ccwflag2, &
assoc_id, label,units)
! create a new profile with the specified name
!--------
! standard INPUT arguments...
type (xplasma), pointer :: s
character*(*), intent(in) :: profname ! name of profile
integer, intent(in) :: idx1 ! id of 1st grid over which it is defined
integer, intent(in) :: idx2 ! id of 2nd grid over which it is defined
real*8, dimension(:,:), intent(in) :: data ! the profile data
! (NOTE: sizes of dimensions MUST match sizes of identified grids)
!--------
! standard OUTPUT arguments...
integer, intent(out) :: idf ! id of container for this profile.
integer, intent(out) :: ier ! completion code (0=normal)
!--------
! optional input arguments...
! defaults: pc linear; for splines: "not a knot" BC
! angle grid: normal CCW orientation.
integer, intent(in), optional :: ispline ! order of fit
! 0 -- pc linear; 1 -- Hermite; 2 -- cubic spline;
integer, intent(in), optional :: ibcx1a ! BC control @ LHS of grid x1
real*8, intent(in), dimension(:), optional :: zbcx1a ! BC data @ LHS of grid x1
integer, intent(in), optional :: ibcx1b ! BC control @ RHS of grid x1
real*8, intent(in), dimension(:), optional :: zbcx1b ! BC data @ RHS of grid x1
integer, intent(in), optional :: ibcx2a ! BC control @ LHS of grid x2
real*8, intent(in), dimension(:), optional :: zbcx2a ! BC data @ LHS of grid x2
integer, intent(in), optional :: ibcx2b ! BC control @ RHS of grid x2
real*8, intent(in), dimension(:), optional :: zbcx2b ! BC data @ RHS of grid x2
! ibc*a=ibc*b=0 = default: "not a knot" for splines; Akima for Hermite
! for periodic functions BC args are ignored; periodic BC is used.
! ibc[*]=1 -- df/dx specified in zbc[*] (0 if absent)
! ibc[*]=2 -- d^2f/dx^2 specified in zbc[*] (0 if absent)
logical, intent(in), optional :: ccwflag1 ! T means data vs. CCW angle
! grid; F means CW. Default=T.
logical, intent(in), optional :: ccwflag2 ! T means data vs. CCW angle
! grid; F means CW. Default=T.
integer, intent(in), optional :: assoc_id ! id of associated profile
character*(*), intent(in), optional :: label
character*(*), intent(in), optional :: units
!------------------------------------------
subroutine xplasma_create_3dprof(s,profname, &
idx1,idx2,idx3,data,idf,ier, &
ispline,ibcx1a,zbcx1a,ibcx1b,zbcx1b, &
ibcx2a,zbcx2a,ibcx2b,zbcx2b, &
ibcx3a,zbcx3a,ibcx3b,zbcx3b, &
ccwflag1,ccwflag2,ccwflag3, assoc_id,label,units)
! create a new profile with the specified name
!--------
! standard INPUT arguments...
type (xplasma), pointer :: s
character*(*), intent(in) :: profname ! name of profile
integer, intent(in) :: idx1 ! id of 1st grid over which it is defined
integer, intent(in) :: idx2 ! id of 2nd grid over which it is defined
integer, intent(in) :: idx3 ! id of 3rd grid over which it is defined
real*8, dimension(:,:,:), intent(in) :: data ! the profile data
! (NOTE: sizes of dimensions MUST match sizes of identified grids)
!--------
! standard OUTPUT arguments...
integer, intent(out) :: idf ! id of newly created profile.
integer, intent(out) :: ier ! completion code (0=normal)
!--------
! optional input arguments...
! defaults: pc linear; for splines: "not a knot" BC
! angle grid: normal CCW orientation.
integer, intent(in), optional :: ispline ! order of fit
! 0 -- pc linear; 1 -- Hermite; 2 -- cubic spline;
integer, intent(in), optional :: ibcx1a ! BC control @ LHS of grid x1
real*8, intent(in), dimension(:,:), optional :: zbcx1a ! BC data @ LHS of grid x1
integer, intent(in), optional :: ibcx1b ! BC control @ RHS of grid x1
real*8, intent(in), dimension(:,:), optional :: zbcx1b ! BC data @ RHS of grid x1
integer, intent(in), optional :: ibcx2a ! BC control @ LHS of grid x2
real*8, intent(in), dimension(:,:), optional :: zbcx2a ! BC data @ LHS of grid x2
integer, intent(in), optional :: ibcx2b ! BC control @ RHS of grid x2
real*8, intent(in), dimension(:,:), optional :: zbcx2b ! BC data @ RHS of grid x2
integer, intent(in), optional :: ibcx3a ! BC control @ LHS of grid x3
real*8, intent(in), dimension(:,:), optional :: zbcx3a ! BC data @ LHS of grid x3
integer, intent(in), optional :: ibcx3b ! BC control @ RHS of grid x3
real*8, intent(in), dimension(:,:), optional :: zbcx3b ! BC data @ RHS of grid x3
! ibc*a=ibc*b=0 = default: "not a knot" for splines; Akima for Hermite
! for periodic functions BC args are ignored; periodic BC is used.
! ibc[*]=1 -- df/dx specified in zbc[*] (0 if absent)
! ibc[*]=2 -- d^2f/dx^2 specified in zbc[*] (0 if absent)
logical, intent(in), optional :: ccwflag1 ! T means data vs. CCW angle
! grid; F means CW. Default=T.
logical, intent(in), optional :: ccwflag2 ! T means data vs. CCW angle
! grid; F means CW. Default=T.
logical, intent(in), optional :: ccwflag3 ! T means data vs. CCW angle
! grid; F means CW. Default=T.
! ccwflag1 applies to x1 if it is periodic; otherwise it is ignored.
! ccwflag2 applies to x2 if it is periodic; otherwise it is ignored.
! ccwflag3 applies to x3 if it is periodic; otherwise it is ignored.
integer, intent(in), optional :: assoc_id ! id of associated profile
character*(*), intent(in), optional :: label
character*(*), intent(in), optional :: units
A linear transformation f -> a*f + b can be applied to an existing profile:
subroutine xplasma_prof_lintrans(s,id,ierr, factor, offset)
type (xplasma), pointer :: s
integer, intent(in) :: id ! profile ID
integer, intent(out) :: ierr ! status code returned 0 = normal
real*8, intent(in), optional :: factor ! multiplicative factor
real*8, intent(in), optional :: offset ! additive offset
where a is specified as "factor" and b is specified as "offset". If
neither optional argument is specified, the subroutine returns without
doing anything, since the default value for "factor" is a=1 and the
default value for "offset" is b=0.
Note: the current author (enabled to write xplasma data) must own the
profile being transformed; i.e. use xplasma_author_set(...) and
xplasma_author_clear(...) calls if necessary to modify and restore
access control settings.
When a list is created, its elements must all be named. Corresponding
values can either be given at create time, or may be supplied later.
subroutine xplasma_create_list(s,listname,enames,id,ier, &
label, units, ivals, r8vals, chvals)
!
! create list & define list elements
!
type (xplasma), pointer :: s
character*(*), intent(in) :: listname ! name of list being created
character*(*), intent(in), dimension(:) :: enames ! names of elements
integer, intent(out) :: id ! id of list item
integer, intent(out) :: ier ! completion code
character*(*), intent(in), optional :: label ! label for entire list
character*(*), intent(in), optional :: units ! units label for list
integer,intent(in), dimension(:), optional :: ivals ! integer values
real*8, intent(in), dimension(:), optional :: r8vals ! real*8 values
character*(*), intent(in), dimension(:), optional :: chvals ! str values
Methods for setting the list element values are given in the subtopics.
subroutine xplasma_setList_r8vals(s,id,r8vals,ier)
!
! set real*8 values in list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
real*8, dimension(:), intent(in) :: r8vals ! the real*8 values
integer, intent(out) :: ier ! completion code
subroutine xplasma_setList_ivals(s,id,ivals,ier)
!
! set integer values in list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
integer, dimension(:), intent(in) :: ivals ! the integer values
integer, intent(out) :: ier ! completion code
subroutine xplasma_setList_chvals(s,id,chvals,ier)
!
! set character string values in list
!
type (xplasma), pointer :: s
integer, intent(in) :: id ! id of list item
character*(*), dimension(:), intent(in) :: chvals
! the character string values
integer, intent(out) :: ier ! completion code
A black box is basically a pair of buffers (one for integers, one for
real*8 floating point) in which the data interpretation is completely
left to the user.
Xplasma internally makes use of black boxes for various purposes-- caches
for numerical integration, data to speed coordinate transformations, and
implementation of MCgrids.
Each black box has a type code "itype". Types between 0 and 101 are used
by xplasma. User defined types should select a type code outside this
range. The item name should also be chosen to give a hint as to the
meaning of the data.
subroutine xplasma_create_blackBox(s,iname,itype,id,ier, &
iarray,r8array,iasize,r8asize,label,units, &
ia_ptr,r8a_ptr)
! create a blackBox -- a structure which contains the following
! -- a scalar integer TYPE (user defined meaning)
! -- an integer array
! -- a real*8 array
! Xplasma provides a mechanism for storage and retrieval of
! Black boxes; they are included in Xplasma NetCDF state files.
! The only methods supported are:
! xplasma_create_blackBox -- create the item; store the data
! xplasma_blackBox_info -- retrieve info on item (e.g. array sizes)
! xplasma_blackBox_retrieve -- retrieve black box item data
! Xplasma makes internal use of blackboxes, e.g. to define limiters
! or set up data for efficient numerical integration for flux surface
! averages. But, there are no reserved TYPE codes-- the significance
! of the blackBox TYPE, like the data itself, is entirely up to the
! user.
type (xplasma), pointer :: s
character*(*), intent(in) :: iname ! name of black box item
integer, intent(in) :: itype ! Black Box type code (user defined).
integer, intent(out) :: id ! id of newly created black box item
integer, intent(out) :: ier ! completion code
! independently for the integer and floating point arrays,
! --either data must be provided, or, data sizes must be provided.
integer, dimension(:), intent(in), optional :: iarray ! integer data
real*8, dimension(:), intent(in), optional :: r8array ! real*8 data
! --but not both!
integer, intent(in), optional :: iasize ! size of integer data
integer, intent(in), optional :: r8asize ! size of real*8 data
! optional labels...
character*(*), intent(in), optional :: label,units
! optional output: pointers to blackbox arrays (null if ierr.ne.0)
integer, dimension(:), pointer, optional :: ia_ptr
real*8, dimension(:), pointer, optional :: r8a_ptr
This subroutine creates a lightly smoothed interpolating profile for
an "integrated profile" function defined over a grid of the caller's
choosing.
An example of such a function would be:
P_NBI(x) = Integrated NBI power, watts, from the plasma magnetic
axis to flux surface "x".
The input gives a grid identification, the data, i.e. the integrated value
to each point on the grid, and the name to be used for identifying the
function. The output is an identifier for the interpolating function.
The control "iflag" is 1 for volume integrated density profiles; 2 for
area integrated density profiles such as current drive profiles.
NOTE-- smoothing can produce unexpected artifacts-- for example, regions of
very small negative power density in a heating profile. But, the integrated
power will be faithful to the original within half a zone width...
!------------------------------------------
subroutine xplasma_irhofun(s,id_axis,zlbl,inprof,zprof,iflag,id,ierr)
! make XPLASMA profile -- integrated quantity; smooth by 1/2 zone width
! to insure smooth derivative from spline
type (xplasma), pointer :: s
integer, intent(in) :: id_axis ! axis id -- must be rho or akin to rho
character*(*), intent(in) :: zlbl ! name for profile function to create
integer, intent(in) :: inprof ! size of the integrated data profile
real*8, intent(in) :: zprof(inprof) ! the integrated data provided
! if inprof = size(x_axis) zprof(1)=0 is expected
! if inprof = size(x_axis)-1 the axial data point
! is presumed to be omitted.
integer, intent(in) :: iflag ! =1 -- volume normalization;
! derivative evaluations -> W/m^3, #/sec/m^3, etc.
! =2 -- area normalization;
! derivative evaluations -> A/m^2 (current density).
integer, intent(out) :: id ! id of stored profile (if successful)
integer, intent(out) :: ierr ! completion code, 0=OK.
! if an error occurs and ierr is set, id=0 will be returned.
!-----------------------------------------------------------------
subroutine xplasma_rzprof(s,fname,id_out,ier, &
id_Rgrid,id_Zgrid, &
ispline,sm_edge, &
id_fun_in,lamda, &
label,units,data)
! create a profile f(R,Z) from existing data with extrapolation,
! or with array data provided.
! either an existing function (id_fun_in) or input data (data) or
! both must be provided.
! modes of use:
! (id_fun_in omitted, data(:,:) provided) -- just use the data given
! (id_fun_in provided, data(:,:) omitted) -- use existing profile
! at id_fun_in to give variation inside plasma; use extrapolation
! based on distance map: f_outside = f_bdy*exp(-d/lamda) where
! d is the distance from the plasma
! (both id_fun_in and data(:,:) provided) -- use existing profile
! at id_fun_in to give variation inside plasma: use data to
! specify the variation beyond the plasma.
! ispline -- 0 = Bilinear, 1 = C1 Hermite, 2 = C2 Spline
! sm_edge -- Meters -- gives smoothing in vicinity of boundary
! a hat function convolution of half width sm_edge is applied
! in the vicinity of the boundary
type(xplasma), pointer :: s
character*(*), intent(in) :: fname ! name of profile to create...
integer, intent(out) :: id_out ! ID of function just created
integer, intent(out) :: ier ! completion code 0=OK
!------
integer, intent(in), optional :: id_Rgrid,id_Zgrid ! R & Z grids to use
! (default: __RGRID & __ZGRID)
integer, intent(in), optional :: ispline ! interpolation order
! -1: zonal step function; 0: piecewise linear;
! 1: C1 Hermite; 2: C2 Spline
real*8, intent(in), optional :: sm_edge ! edge smoothing control
! default: no smoothing
integer,intent(in), optional :: id_fun_in ! f(rho) from which to
! form f(R,Z) (default: 0, none)
real*8, intent(in), optional :: lamda ! scrape off distance
! default: huge, making for flat extrapolation
character*(*), intent(in), optional :: label,units ! labeling info
real*8, intent(inout), dimension(:,:), optional :: data ! f(R,Z) data
! default: NONE
! data must be defined in extrapolated region-- internal region
! will be filled in...
!-------------------------------------------------------
subroutine xplasma_rzprof_fun(s,fname,user_fcn,user_iarg,id_out,ier, &
id_Rgrid,id_Zgrid,ispline,sm_edge,id_fun_in,no_extrap, &
label,units)
! create a profile f(R,Z) from a user provided callable function
! possibly combined with existing data.
! the callable function covers the entire region inside the limiters
! if id_fun_in is omitted; it covers only the region outside the plasma
! and inside the limiters if id_fun_in is included.
! modes of use:
! (id_fun_in omitted) -- just use the given callable function.
! (id_fun_in provided)-- use existing profile
! at id_fun_in to give variation inside plasma; use the given
! function to define the variation beyond the plasma boundary.
! the region beyond the limiters is defined by a simple extrapolation.
! unless no_extrap is set, in which case the function is used even
! beyond the limiters.
! sm_edge -- Meters -- gives smoothing in vicinity of boundary
! a hat function convolution of half width sm_edge is applied
! in the vicinity of the boundary
type(xplasma), pointer :: s
character*(*), intent(in) :: fname ! name of profile to create...
real*8, external :: user_fcn ! user provided function...
integer, intent(in) :: user_iarg ! argument for user_fcn:
! real*8 function user_fcn(user_iarg,R,Z,Phi,ier)
integer, intent(out) :: id_out ! ID of function just created
integer, intent(out) :: ier ! completion code 0=OK
!------
integer, intent(in), optional :: id_Rgrid,id_Zgrid ! R & Z grids to use
! (default: __RGRID & __ZGRID)
integer, intent(in), optional :: ispline ! interpolation order
! -1: zonal step function; 0: piecewise linear;
! 1: C1 Hermite; 2: C2 Spline
real*8, intent(in), optional :: sm_edge ! edge smoothing control
! default: no smoothing
integer,intent(in), optional :: id_fun_in ! f(rho) from which to
! form f(R,Z) (default: 0, none)
logical, intent(in), optional :: no_extrap ! T to suppress extrapolation
! beyond limiter. In this case user_fcn calls are used for (R,Z) even
! for points outside the limiter.
character*(*), intent(in), optional :: label,units ! labeling info
This routine is used to create an irregular "Monte Carlo grid" or MCgrid.
This is a grid used by the NUBEAM fast ion Monte Carlo code.
The description of the MCgrid won't be repeated here, it is well described
elsewhere in the document (look for "mcgrid"), including in the f77 section.
subroutine xplasma_mcgrid_define(s,mcname,inth0,inrow,id,ierr, &
nphi,udsym,label,nrow_ext_in,nrow_ext_out)
type(xplasma), pointer :: s
character*(*), intent(in) :: mcname ! name of MC grid
integer, intent(in) :: inth0 ! no. of zones covering [0,pi]
! in 1st row at axis
integer, intent(in) :: inrow ! no. of rows covering plasma
integer, intent(out) :: id ! MC grid id returned (0 if error)
integer, intent(out) :: ierr ! status code returned (0=OK)
integer, intent(in), optional :: nphi ! number of Phi=const zones
! default:1 (axisymmetry)
logical, intent(in), optional :: udsym ! updown symmetry flag
! default:.FALSE. (do not assume updown symmetric MHD equilibrium).
character*(*), intent(in), optional :: label ! optional label for MCgrid
integer, intent(in), optional :: nrow_ext_in ! no. of external rows
! requested (default: 0, meaning: use internal calculation). If non-
! zero value is given, inrow >= nrow_ext_in >= 3 will be enforced.
integer, intent(out), optional :: nrow_ext_out ! actual no. of external
! zone rows created.
Use this method to create a profile over an MCgrid. The sister routine
xplasma_mcgrid_getobj is used to retrieve the profile.
subroutine xplasma_mcgrid_putobj(s,id_mcgrid,pname,id_p,ierr, &
lstart0_th,ccwflag_th, &
data_1d,data_2d,data_3d, &
gridId1,gridId2,normcode, &
label,units)
! define a profile "object" over a MCgrid. This can define
! a single data value per grid zone (use data_1d), or a vector of
! of data values per grid zone (use data_2d) or a 2d array of values
! per grid zone (use data_3d).
type(xplasma), pointer :: s
integer, intent(in) :: id_mcgrid ! MCgrid over which profile is defined.
character*(*), intent(in) :: pname ! name of profile
integer, intent(out) :: id_p ! xplasma ID of profile returned
integer, intent(out) :: ierr ! status code (0=OK)
! theta indexing options
logical, intent(in), optional :: lstart0_th ! default depends on symmetry
logical, intent(in), optional :: ccwflag_th ! default: T
! lstart0_th=T means user's first theta zone lower bdy is 0; F means it
! is -pi. If defaulted: updown symmetric data assumed to start at 0;
! updown asymmetric data assumed to start at -pi.
! ccwflag_th=T means zone index increases as one goes along a row of
! zones at fixed radial index in a counter-clockwise direction; F means
! the opposite. T is the default.
! NOTE: similar options for phi mapping could be defined but that has
! not yet been done; may be an issue if/when we get beyond axisymmetry
! in XPLASMA...
! Although following arguments are optional, exactly one must be
! provided. The last dimension of the array must match the size
! of the entire grid, or of the subset of the grid covering the
! plasma (either or)
real*8, intent(in), dimension(:), optional :: data_1d
real*8, intent(in), dimension(:,:), optional :: data_2d
real*8, intent(in), dimension(:,:,:), optional :: data_3d
! if data_2d or data_3d is specified-- it is desirable to specify
! a grid which corresponds to the 1st dimension of the data (default
! 0, meaning no grid was specified).
integer, intent(in), optional :: gridId1
! if data_3d is specified-- it is desirable to specify a grid which
! corresponds to the 2nd dimension of the data
integer, intent(in), optional :: gridId2
! (the actual data_2d or data_3d array dimensions will be one less
! than the corresponding grid sizes, for zone oriented "binned" data).
!-------------------
! normalization code
! 0 - none -- default -- no information
! -1 - divide by vol(...) to get a density
! +1 - multiply by vol(...) to integrate a density
! +2 -- multiply by vol(...)*d[grid1]*d[grid2]/2 (FBM normalization)
integer, intent(in), optional :: normcode
!-----------------
character*(*), intent(in), optional :: label
character*(*), intent(in), optional :: units
The following interfaces allow various xplasma global attributes or data
items to be updated. Generally the values are retrieved with a call to
xplasma_global_info using optional arguments
subroutine xplasma_time_set(s,ztime,ier)
! set the time (seconds) affiliated with xplasma object "s".
type (xplasma), pointer :: s
real*8, intent(in) :: ztime
integer, intent(out) :: ier
retrieval:
call xplasma_time_get(s,ztime,ier) ...or...
call xplasma_global_info(s,ier, time=ztime)
-------------------------------------------------------------
subroutine xplasma_bdytol_set(s,zbtol,ier)
! set the out-of-bounds tolerance for profile interpolations
! a maximum value of 1.0d-4 is allowed...
type (xplasma), pointer :: s
real*8, intent(in) :: zbtol
integer, intent(out) :: ier
retrieval:
call xplasma_global_info(s,ier, bdytol=zbtol)
-------------------------------------------------------------
subroutine xplasma_kmom_set(s,kmom,ier)
! set the # of Fourier moments for the optional Fourier representation
! of the current equilibrium. Default value is 16 not counting the
! 0'th moment. Minimum value is 2, max is 64.
type (xplasma), pointer :: s
integer, intent(in) :: kmom
integer, intent(out) :: ier
retrieval:
call xplasma_global_info(s,ier, kmom=kmom)
-------------------------------------------------------------
subroutine xplasma_ajac_maxvar_set(s,zvar,ier)
! set the amount of variation of det[J] to be allowed on a flux
! surface-- numerical control for detection of singular equilibria
! det J is det | dR/drho dR/dtheta |
! | dZ/drho dZ/dtehta |
type (xplasma), pointer :: s
real*8, intent(in) :: zvar
integer, intent(out) :: ier
retrieval:
call xplasma_global_info(s,ier, ajac_maxVar=zvar)
-------------------------------------------------------------
subroutine xplasma_field_ccw_set(s,ier, bphi_ccw,jphi_ccw)
! set the Bphi and/or Jphi orientations (for tokamak fields)
! CCW -- counter-clockwise
! 1 means (B or J is pointed) CCW looking down on machine from above
! -1 means CW, clockwise orientation.
type (xplasma), pointer :: s
integer, intent(out) :: ier ! status code, 0=OK
! for the toroidal magnetic field:
integer, intent(in), optional :: bphi_ccw ! CCW value for Bphi
! for the toroidal plasma current:
integer, intent(in), optional :: jphi_ccw ! CCW value for Jphi
retrieval:
call xplasma_global_info(s,ier, bphi_ccw=bphi_ccw, jphi_ccw=jphi_ccw)
-------------------------------------------------------------
subroutine xplasma_rzOrder_set(s,irzOrder,ier)
! set the default fit order for R(rho,theta),Z(rho,theta)
! equilibrium specification
! min value: 1 -- C1 Hermite fit will be used
! max value: 2 -- C2 Spline fit will be used (default)
type (xplasma), pointer :: s
integer, intent(in) :: irzOrder
integer, intent(out) :: ier
retrieval:
call xplasma_global_info(s,ier, rzOrder=irzOrder)
The entire contents of an xplasma object "s" can be written to a NetCDF file,
with the following call:
character*(*) filename ! i.e. unix path to file
call xplasma_write(s,filename,ier)
Other codes can load "s" (replacing any prior contents), with:
call xplasma_read(s,filename,ier)
(With this section, original xplasma1 (f77 xplasma) documentation resumes).
(old f77 xplasma 1 documentation)
Note: the R4 interface is available for the fortran-77 xplasma
routines only. The fortran-90 interface uses strictly REAL*8 precision
throughout. The REAL*8 precision is compatible with fortran-90 floating
point KINDs which map to this (8-byte 64 bit) precision which is available
on all modern hardware. Advice: code with IMPLICIT NONE to avoid
inadvertant creation of 4-byte reals; be careful with handling of hard
coded constants.
The xplasma software is written in REAL*8 precision.
Routines described in the sections "Setup_Routines" and
"Data_Access_Routines" are coded for REAL*8 floating point
arguments. To call a fortran-77 style XPLASMA routine <name>
instead with REAL arguments, use R4_<name>.
For example,
real*8 rhoR8,zeffR8 ! rhoR8 input, zeffR8 output
real rhoR4,zeffR4 ! rhoR4 input, zeffR4 output
integer id_Zeff ! Zeff profile id (input)
integer ierr ! completion code, 0=OK (output)
! pass REAL*8 argument, get REAL*8 result
rhoR8=0.5d0
call eq_rgetf(1,rhoR8,id_Zeff,0,zeffR8,ierr)
! pass REAL(*4) argument, get REAL(*4) result
rhoR4=rhoR8
call r4_eq_rgetf(1,rhoR4,id_Zeff,0,zeffR4,ierr)
--or--
fortran-90 codes can
use xplasma_calls
which defines "eq_rgetf" as a generic subroutine name. This means
that if it the compiler sees that the routine has REAL*8 arguments
it will call the usual eq_rget subroutine, but, if the compiler
sees REAL*4 arguments, r4_eq_rgetf gets called. The compiler can
also check that all arguments are of the right type and all arrays
of the correct rank.
The xplasma_calls module and the R4_ interfaces are automatically
generated from the standard interfaces by means of a script which
makes use of the "ftoken" tools in the NTCC modules library.
CAUTION: for routines which take an external subroutine or
function as an argument, the passed routine MUST use a REAL*8
precision interface.
(old f77 xplasma 1 documentation)
FROZEN in xplasma Version 2-- C interface routines exist for most
of the Version 1 xplasma fortran-77 style interface, but the
development has been discontinued, due to lack of evidence of use.
PLEASE: if you are interested in further development of the xplasma
C interface, please contact the authors (email dmccune@pppl.gov).
The following is an edited description of the original C interface
for Version 1 xplasma f77-style subroutine calls:
All arguments for f77 xplasma (v1) routines were INTEGER, REAL*8,
or CHARACTER*(*). C routines can use int* and double* to access
the INTEGER and REAL*8 arguments.
Generally, routines which have character arguments should have
explicit C interface routines provided, as well. These routines
will have names generated from the standard names by prepending
"C_".
fortran routine C callable routine
eq_save F77NAME(c_eq_save)
eqm_msgs F77NAME(c_eqm_msgs)
(where F77NAME is a macro provided in NTCC module FPREPROC to
"decorate" the names as needed to meet the requirements of the
current system's ld or binary loader software).
Where the standard fortran routines accept fortran CHARACTER
data, the c routine will take a null terminated byte string
instead.
NTCC "Portlib" module routines are used for converting C-strings
to fortran-strings. The C_ interfaces were generated automatically
from standard xplasma sources, using tools from the ftoken module
in the NTCC modules library. (These tools are not extensible to
the fortran-90 interfaces of XPLASMA v2).
(old f77 xplasma 1 documentation)
[This is the original xplasma 1.0 documentation].
The xplasma setup routines are used to load the plasma field
equilibrium and plasma parameters into the xplasma module, with
generation of spline or Hermite coefficients as needed for later
use by the Data Access Routines
(Note the subsections describe only the standard routines, although
the R4_ and C_ interfaces are also available).
By default, xplasma error messages are written to stdout via
FORTRAN i/o unit number 6.
To override this:
! input
integer ilun_msgs ! FORTRAN i/o unit number for messages
character*(*) msg_file ! file to receive messages
! output
integer ierr ! completion code, returned, 0=OK
call eqm_msgs(ilun_msgs,msg_file,ierr)
...subroutine eqm_msgs tries to open `msg_file' on unit ilun_msgs,
if the `msg_file' character string is non-blank. If it is blank,
the error message i/o unit number is still changed to ilun_msgs.
If a file open is attempted and fails, ierr.ne.0 is returned.
(old f77 xplasma 1 documentation)
This module defines the fortran-77 xplasma calls only.
Included with "xplasma" is an "interface" module for fortran-90
code, which can be accessed by inserting the line
use xplasma_calls
at the top of each fortran-90 subroutine that makes xplasma
subroutine calls. This module does the following:
(1) allows generic names (e.g. eq_rgetf and eqm_frhochi) to be
used for both REAL*8 and REAL(*4) codes. Without the module,
REAL(*4) codes must call the R4 interface routines (e.g.
r4_eq_rgetf, r4_eqm_frhochi) by their full names.
(2) allows the compiler to check all subroutine arguments at
compile time.
The use of this module is optional. There are situations where
functionally correct calls to xplasma routines will not survive
the compiler's argument check.
To delete the contents of xplasma, e.g. as a first step to
storing plasma parameters and equilibrium at a new time:
! input
character*(*) model_string ! name of equilibrium model
integer ilaxi ! axisymmetry flag (1 for TRUE).
call eqm_select(model_string,ilaxi)
`model_string' contains a short string which may identify something
about the current or intended contents of xplasma. This string is
used only as a prologue for certain messages.
Caution: as of May 2000, ilaxi=1 is required. Non-axisymmetry
is not yet supported in xplasma.
As xplasma is meant to contain a timeslice representing the current
plasma equilibrium and profiles, the user may choose to associate a
time with the current contents of xplasma, by means of the call:
real*8 ztime ! physical time of xplasma data
call eqm_time(ztime)
(old f77 xplasma 1 documentation)
The call to `eqm_select' sets default values for certain
user adjustable model characteristics:
(a) fit order for equilibrium representation
{R(rho,chi),Z(rho,chi)}. Default is iorder=2 (splines).
However iorder=1 (Akima Hermite) is also allowed.
(b) underlying Fourier representation-- highest order
retained Fourier moments. Default is 8; for some
applications it may be desirable to reduce this.
Details:
Axisymmetric "xplasma", based on pspline [1], can use either
2nd order (piecewise bicubic spline) or 1st order (piecewise
bicubic Hermite) interpolation functions to represent flux
surface geometry {R(rho,chi),Z(rho,chi)}. The default is
to use splines (iorder=2). This produces a smoother output
for smooth input data, but for input data that is not smooth
there is a potential for "ringing", an oscillatory propagation
of a sharp feature in the input data. By using Hermite
(iorder=1), such ringing can be suppressed at a cost to
overall smoothness.
integer iorder ! 2 for R,Z splines (default)
! 1 for R,Z bicubic Hermite representation
call eqm_rzordr_set(iorder)
Later, use the following call to determine what setting was chosen
call eq_rzordr_get(iorder)
** This routine should be called right after eqm_select is
called.
[1] https://w3.pppl.gov/NTCC/PSPLINE/pspline.html
When "xplasma" is built from a psi(R,Z) representation (e.g. a
G-EQdsk file), part of the process involves a Fourier analysis
and recomposition. By default, 8 moments are retained, but this
number can be changed by the call:
kmom=5 ! retain moments 0:5 only in Fourier composition
call xmoments_kmom_set(kmom)
In systems with known well behaved boundaries, the higher order
moments may not be needed and can be a source of "noise".
The call to "xmoments_kmom_set" will effect the results of
subsequent calls to "xmoments_kmom_get" and "eqmom_Rcos" and
similar routines, after the xplasma setup is complete, as described
in the subsection "Fourier Spline Representation" under the
description of Data Access Routines.
! fit order for (R,Z) and B(rho,chi) components
integer, parameter :: iordr_rz = 2 ! 2 for spline 1 for Hermite
! axisymmetry flag (axisymmetry required, ilaxi=1 REQUIRED).
integer, parameter :: ilaxi = 1
! maximum retained Fourier Moments specification
integer, parameter :: kmom = 5
...
call eqm_select('your_xplasma',ilaxi)
call eqm_rzordr_set(iorder_rz)
call xmoments_kmom_set(kmom)
(old f77 xplasma 1 documentation)
To erase xplasma contents and free up the memory used in the
xplasma module:
call eqm_free
(old f77 xplasma 1 documentation)
The full setup of an xplasma MHD equilibrium representation is
fairly complicated, involving as it does the requirement to
define a large number of data items.
Common sources for equilibrium data are: TRANSP runs in files
or MDS+, and EFIT (G-EQDSK) data in files or MDS+.
Canned routines, encapsulating the appropriate sequence of
xplasma "eqm" calls, are provided for the construction of xplasma
representations from these standard data sources.
call trx_connect(...) ! connect to a TRANSP run
call trx_time(...) ! set time of interest
call trx_init_xplasma(...) ! xplasma initialization
call trx_mhd(...) ! build xplasma from TRANSP MHD data
call trx_wall_rz(...) ! acquire limiter locations (optional)
call trx_scal(...) ! acquire any TRANSP scalar data item
call trx_prof(...) ! acquire any TRANSP profile data item
Xplasma "vacuum field extrapolation" software allows an environment
to be created in which full field information is available in (R,Z)
space or in magnetic coordinates.
Details are given in the documentation of the NTCC module "trxplib".
(old f77 xplasma 1 documentation)
Use this routine to acquire all xplasma MHD data from an EFIT G-EQDSK
file. The software finds closed contours of psi(R,Z), equally spaced
in sqrt(toroidal flux), from which to construct the magnetic-coordinate
based equilibrium representation, at the same time preserving the (R,Z)
information: psi(R,Z), all components of B vs. (R,Z) as bicubic Hermite
interpolating functions.
To use this routine:
! identifying information
character*30 label ! xplasma label
character*150 zfile ! G-EQDSK file path or MDS+ path
! or "index file" expression...
! resolution of mag. coordinates representation
integer ns ! abs(ns) = number of flux surfaces
integer nth ! number of theta grid points
! specify ns.lt.0 for Hermite instead of Spline fits of
! R(rho,chi),Z(rho,chi),BR(rho,chi),BZ(rho,chi),[mod(B)](rho,chi).
! Adjustment factor for psi(bdy)
real*8 fbdy ! psi(bdy(mag)) = fbdy*psi(bdy(efit))
! Alternate bdy adjustment option: minimum curvature parameter
real*8 cbdy ! require curv(bdy)/a .gt. cbdy
! (R,Z) extension flag
integer irz ! =1: extend xplasma representation to
! cover EFIT's (R,Z) box; define
! psi(R,Z) & B(R,Z) as xplasma objects.
! =0: do not extend (less work).
! error status code returned
integer ierr ! returns 0 = OK
... ...
call eqm_fromgeqdsk(label,zfile,ns,nth,fbdy,cbdy,irz,ierr)
The factor "fbdy" is allowed to vary between 0.95 and 1.00. This may
be needed because some G-EQDSK data may give a psi(bdy) value which
corresponds to an open field line or the exact last closed flux
surface which has a shape that is kinked through a separatrix. For
codes requiring a smooth boundary, fbdy = ~ 0.99 may be needed.
BUT: read on.
The factor "cbdy" gives a semi-automatic way to select a boundary
contour that is suitable. The software will reject a boundary
contour if any of the following conditions arise:
-- contour reverses direction or escapes (R,Z) box (open field line)
-- minimum curvature divided by midplane minor radius is less than
"cbdy".
The result is effective in preventing a "kinked" contour just inside
a separatrix, which might cause numerical problems for inverted
coordinate systems or equilibrium representations downstream.
The maximum allowed value of cbdy is 0.2; we recommend:
fbdy = 1.000
cbdy = 0.080
fbdy = 1.000
cbdy = 0.080
ns = 51
nth = 76
! read from file "geqdsk.dat"; create xplasma "G-EQDSK-file"
call eqm_fromgeqdsk('G-EQDSK-file','geqdsk.dat',ns,nth,fbdy,cbdy, &
ierr)
if(ierr.ne.0) then ... [ process error ]
! or, read from MDSplus
zfile = 'MDS+:EUROPA.PPPL.GOV:8501:EFIT01(103875;t=.25)'
call eqm_fromgeqdsk('G-EQDSK-MDSplus',zfile,ns,nth,fbdy,cbdy,ierr)
if(ierr.ne.0) then ... [ process error ]
The string specification "zfile" contains the following components:
MDS+[/NOREDUCE]:<server-name[:port]>:<tree-name>(<shot-no>;t=<time>)
Generally, each experiment supports an MDSplus server for its MDSplus
data; in the case of the NSTX experiment at PPPL, the server to use
is EUROPA.PPPL.GOV (port 8501). The data is taken from tree "EFIT01",
NSTX shot 103875, at time t=0.25. Because the option "/REDUCE" is set
by default, the eqm_fromgeqdsk call only caches the single time point
of EFIT data closest to 0.25 seconds; with "/NOREDUCE", it would cache
the entire time variation of the EFIT tree in memory, making subsequent
calls to eqm_fromgeqdsk on the same shot but at different times much
faster, since a networked data access operation would be avoided.
Generally, collaborators need to obtain permission to read data from
MDSplus servers maintained by tokamak experiments, and, the user should
consult with responsible scientists about the suitability of selected
data.
Note that if <server-name> is given as "LOCAL" or "CONNECTED", then,
the eqm_fromgeqdsk call does not attempt to connect to and later
disconnect from the MDSplus server. This is desirable e.g. if the
calling code is taking care of MDSplus server connect/disconnect
operations.
Note that if <tree-name> is given as "OPENED", then, eqm_fromgeqdsk
does not attempt to open and later close the EFIT MDS+ tree. This
is desirable if the calling code is taking care of the MDS+ tree
open and close operations.
eqm_fromgeqdsk can be used to access G-EQDSK data over the net
from an MDSplus server. To do this, one simply uses an MDSplus
path instead of a file path in the subroutine's "zfile" argument.
Here are comments from the code on this subject:
c
c zfile - a file path, e.g. /u/efit/nstx/g123456_6800.eqdsk
c --or--
c an MDSplus path:
c
c MDS+[/reduce|/noreduce]:<server[:port]|LOCAL>:<tree>(<shot>;t=<time>)
c
c where the initial MDS+ specifies MDSplus not a file;
c
c /reduce means just read the specified timeslice (this is the default)
c /noreduce means read and cache data from all available EFIT times
c
c <server...> specifies the MDSplus server, can be "LOCAL"
c
c <tree> specifies the MDSplus tree, e.g. "EFIT01" or "EFIT02"
c
c <shot> specifies the MDSplus (or experiment) shot number
c <time> is an ascii encoding of the currently desired time of interest
c
c examples of valid MDSplus paths:
c
c MDS+:LOCAL:EFIT01(104370;t=0.15)
c MDS+/reduce:europa.pppl.gov:8501:EFIT01(104370;t=1.0e-1)
c MDS+/noreduce:cmoda.psfc.mit.edu:EFIT09(123456789;t=2.3)
c
The /reduce option (default) is recommended for codes which are really
only going to look at one timepoint.
For a code that is going to loop over time, the /noreduce option allows
all the data to be read in and cached, obviating the need for repeated
connections to the remote MDSplus server.
Note: option names, server names and tree-names are case-insensitive.
Thus for example, "/reduce" and "/REDUCE" have the same effect. Also,
the time keyword after the shot number can be "t", "T", "time" or "TIME"
or "TiMe" or any combination which when mapped to uppercase yields "T"
or "TIME".
In order to support access to time dependent EFIT datasets without
having to use MDS+, an alternative file-based access scheme is
supported: access indirectly via an "EFIT_INDEX" file. This is a
file that correlates G-eqdsk filenames with physical times in
seconds. For example:
[tshare@dhcp-dmccune scrunch2]$ cat 8500index.txt
ntimes=4
path='/pub/outgoing/pshare'
time=0.15 filename=g008500.00150
time=0.20 filename=g008500.00200
time=0.25 filename=g008500.00250
time=0.294 filename=g008500.00294
G-eqdsk files can be referred to directly by filename, or, indirectly
via an index file. Thus in the above example, the same result would
follow from an eqm_fromgeqdsk call with argument
zfile = "g008500.00294"
or with
zfile = "EFIT_INDEX:8500index.txt( 4)"
(blanks are allowed between the parentheses in which the time index is
given). Note that the "path" field is ignored; the g-eqdsk files are
expected to be found in the same directory in which the index file itself
is found.
This provides a mechanism for correlating files in g-eqdsk file collections
with specific times, without having to parse time information out of the
filename, a procedure which tends to be different at each EFIT user site.
(old f77 xplasma 1 documentation)
If EFIT data is acquired via MDSplus, then there is an option to
write a local G-EQDSK file from the MDSplus data:
character*(*) file_out ! name of file to write
integer ier ! completion code returned, 0=OK
call eqm_wr_geqdsk(file_out,ier)
This provides a mechanism for generating G-EQDSK files from remote
MDSplus trees. The data is as acquired from the MDSplus tree, i.e.
not modified by xplasma.
There is another routine eq_geqdsk, which can generate and write a
G-EQDSK file from any XPLASMA equilibrium, howsoever obtained. But
this method involves interpolation and so is less accurate than
using eqm_wr_geqdsk if the purpose at hand is to write an MDSplus
time slice to disk.
(old f77 xplasma 1 documentation)
The radial flux coordinate is designated as "rho" in xplasma
documentation.
The poloidal angle flux coordinate is designated as "chi" in
xplasma documentation.
Before any spline, Hermite, or piecewise linear f(rho) can be defined,
rho itself must be given; before any bicubic spline, Hermite, or
bilinear f(chi,rho) can be defined, both rho and chi must be given.
The "rho" coordinate grid is to be surface oriented, covering
the entire plasma from magnetic axis to numeric boundary near
the last closed flux surface.
The "chi" coordinate is periodic with period 2pi, i.e.
chi(nchi)-chi(1)=2pi. Usually the coordinate as given spans
[0,2pi] or [-pi,pi].
All grids x must be strict ascending, i.e. x(j).gt.x(j-1).
Even spacing is not required.
Once the "basic" rho, chi grids are defined, alternate
rho, chi grids can also be given. This gives a foundation
for support, within "xplasma", of objects with different
radial and/or angular resolution.
(old f77 xplasma 1 documentation)
The following call sets the grid for the radial flux coordinate.
It is recommended to use the following flux coordinate:
normalized sqrt(toroidal flux),
range 0 to 1
The problem with using a flux coordinate psi directly, is that
grad(psi) -> 0 on axis, which means, for any f such that
grad(f) does NOT -> 0 on axis, df/dpsi -> +/-infinity on axis;
this singularity leads to numerical problems e.g. "ringing" in
spline interpolation.
! input
integer nsurf ! no. of flux surfaces, including axis
real*8 zrho(nsurf) ! flux coordinate, strict ascending
real*8 ztol ! d(zrho) variance tolerance
! ztol -- if the deviation of actual rho grid spacing varies
! from the average spacing by less than ztol, then, the
! grid is deemed evenly spaced.
! ...average spacing is (zrho(nsurf)-zrho(1))/(nsurf-1)
! ...actual spacing is (zrho(j+1)-zrho(j))
! with j in {1,2,...,(nsurf-1)}
! output
integer id_rho ! xplasma id code for rho grid
integer ierr ! completion code, 0=OK
...
call eqm_rho(zrho,nsurf,ztol,id_rho,ierr)
(old f77 xplasma 1 documentation)
The following call sets up the grid for the poloidal angle
coordinate. Note that the grid array is either input or output,
depending how the automatic generation switch is set.
The xplasma software makes no restriction about the type of
poloidal angle coordinate-- "Hamada", "Boozer", "Vmec", all
are acceptable. However, for a plasma poloidal cross section
to the right of the axis of symmetry, an increasing poloidal
angle parameter is expected to describe the flux surface going
around in a counterclockwise direction, and, chi=0.0 is by
convention associated with the large major radius side of the
plasma. The first and last points in the chi grid must differ
by exactly 2pi. Typically the given grid covers the range
[0,2pi] or [-pi,+pi].
! input
integer inum_chi ! no. of chi grid points
integer iauto ! =1 to generate automatic chi grid
real*8 ztol ! d(zchi) variance tolerance
! ztol -- if the deviation of actual chi grid spacing varies
! from the average spacing by less than ztol, then, the
! grid is deemed evenly spaced. If iauto=1, ztol is
! ignored; the iauto=1 grid is always evenly spaced.
! input/output
real*8 zchi(inum_chi) ! chi grid, output if iauto=1
! output
integer id_chi ! xplasma id code for chi grid
integer ierr ! completion code, 0=OK
...
call eqm_chi(zchi,iauto,inum_chi,ztol,id_chi,ierr)
Although the poloidal angle grid is always stored internally with the
counter-clockwise orientation, clockwise grids can be provided as input
using this call:
call eqm_chi_setccw(-1,zchi,iauto,inum_chi,ztol,id_chi,ierr)
The grid orientation will then be reversed prior to storage.
Note however that XPLASMA does not "remember" that a clockwise oriented
grid was provided as input. Subsequent calls involving objects defined
over a clockwise grid must indicate this explicitly each time. In the
XPLASMA f77 interface this is done by specifying a negative value for
an array size, as documented on a routine-by-routine basis below. In
the f90 interface, this is indicated by setting a flag in an optional
argument.
(old f77 xplasma 1 documentation)
Once the basic "rho" and "chi" coordinate grids have been established,
or indeed once any coordinate grid is established, the user can define
alternate grids for these same coordinates. An alternate grid has the
following properties:
* strict ascending sequence
* spans exactly the same range as the original grid
* need not contain the same number of grid points.
Thus, for example, if zrho(1:nsurf) is the real*8 array of values
used to define "rho" in a call to eqm_rho, zrho2(1:nsurf2) can be
a regridding of zrho(1:nsurf) if zrho2(j).lt.zrho(j+1) for j = 1
to nsurf2-1, and, zrho(1)=zrho2(1), and, zrho(nsurf)=zrho2(nsurf2).
To establish an alternate grid, use subroutine `eqm_uaxis':
! input:
character*(*) aname ! name for alt. grid, not "RHO" or "CHI"
integer id_orig ! id of original grid
! for which new grid is an alternate,
! or, zero if this is a new "user grid".
integer iper ! =1 if this is a periodic dimension,
! 0 if not periodic
! -1 if periodic with clockwise orientation
integer inum ! #of points in alternate grid
real*8 zaxis(inum) ! the alternate grid points
real*8 ztol ! even spacing tolerance
!
! output:
integer id ! xplasma id code for this new grid
integer ierr ! completion code, 0=OK
...
call eqm_uaxis(aname,id_orig,iper,zaxis,inum,ztol,id,ierr)
! notes:
! * aname should be unique & specific, i.e. not "RHO" or "CHI"
! or "R" or "Z". NAMES ARE CASE INSENSITIVE.
! * iper=1 should be set for angle-type coordinate grids
! * id_orig should be the value returned from eqm_rho or eqm_chi
! or eqm_rzgrid.
! * inum.ge.4 is required.
! * zaxis must be strict ascending and its endpoints must match
! the endpoints of the "id_orig" axis.
! * ztol sets a tolerance for endpoint matching and also for
! determining if this grid is evenly spaced: if
! dzavg = (zaxis(inum)-zaxis(1))/(inum-1), and,
! maxval(abs(zaxis(2:inum)-zaxis(1:inum-1)-dzavg)).lt.ztol
! then the grid is declared evenly spaced, and the faster
! even spacing algorithm can be used for zone lookup during
! interpolation.
(old f77 xplasma 1 documentation)
A grid of ascending mod(B) values can be defined. This might be
used e.g. to gather data binned by field strength. If this is
needed, use the following...
! input:
integer inum ! #of mod(B) grid points wanted
integer iauto ! =1 for automatic grid generation
! input or output:
real*8 zbb(inum) ! mod(B) grid, input if iauto=0
! output if iauto=1
! input:
real*8 ztol ! even spacing tolerance
! output:
integer id_Bgrid ! xplasma axis id for mod(B) grid
! output:
integer ierr ! completion status code, 0=OK
...
call eqm_bbin(zbb,iauto,inum,ztol,id_Bgrid,ierr)
! note: if the variance of delta(zbb) = zbb(j+1)-zb(j) from
! <delta(zbb)> = (zbb(inum)-zbb(1))/(inum-1) is .lt. ztol,
! then the grid is considered evenly spaced, allowing use
! of a faster lookup algorithm
(old f77 xplasma 1 documentation)
The eqm_uaxis routine can also be used to establish an arbitrary
"user grid", i.e. an ascending sequence of numbers spanning some
specified range. For example, an energy grid, pitch angle grid,
or mod(B) grid might be defined.
To establish a user grid, use eqm_uaxis (cf Alternate_grids section),
but set id_orig=0 to indicate a new grid type unrelated to grids
previously defined.
Data Access routines are provided for efficient binning into user
defined axis grids.
(old f77 xplasma 1 documentation)
! set up rho grid
subroutine eqm_rho(zrho,nsurf,ztol,id_rho,ierr)
! set up chi grid
subroutine eqm_chi(zchi,iauto,inum_chi,ztol,id_chi,ierr)
! set up alternate grid (axis)
subroutine eqm_uaxis('grid-name',id_orig,iper,zaxis,inum,ztol,
> id,ierr)
(old f77 xplasma 1 documentation)
The following item types can be defined:
f(rho) e.g. psi(rho),q(rho),Te(rho), ...
f(rho,chi) e.g. R(rho,chi), Z(rho,chi)
f(R,Z) e.g. Te(R,Z): Te(rho) mapped and extrapolated into the
scrape-off layer.
Generally, data for items can be "updated" by calling the function
set-up routine with iorder=99 set. However, R(rho,chi) and
Z(rho,chi) cannot be updated in this manner -- xplasma must be
re-initialized if the flux surface geometry is to change.
Caution: xplasma generally cannot enforce consistency of data
items just because one is updated. If "G" or "PSI" are updated,
then B(rho,chi) will be updated as well. However, an update of
B(R,Z) would require a separate call by the user.
Although there are some situations where updates of profiles in
the context of a fixed geometry might be useful, such methods
should be used with care. More typically, as plasma profiles
change the flux surface geometry also changes; in this case,
xplasma must be re-initialized and reconstructed from scratch
for each new timeslice.
(old f77 xplasma 1 documentation)
(old f77 xplasma 1 documentation)
A special routine is provided for setting up a profile function f(rho).
This profile can be a piecewise cubic spline, piecewise cubic Hermite,
or piecewise linear. Thus:
! input:
integer iorder ! "order" of interpolating function
integer id_rho ! rho coordinate grid id (orig. or alt.)
character*(*) fname ! unique name of profile function
real*8 zdata(*) ! the function data (#pts: id_rho axis)
integer ibc1 ! boundary condition code, at rho(1)
real*8 zbc1 ! boundary condition data, at rho(1)
integer ibc2 ! boundary condition code, at rho(nrho)
real*8 zbc2 ! boundary condition data, at rho(nrho)
! output:
integer id_rhofun ! unique xplasma id for this function
integer ierr ! completion code, 0=OK
...
call eqm_rhofun(iorder,id_rho,fname,zdata,
> ibc1,zbc1,
> ibc2,zbc2,
> id_rhofun,ierr)
if(ierr.ne.0) then
[...handle error...]
endif
! notes:
! * the amount of data expected in zdata(...) depends on the selected
! axis (id_rho) and the interpolation order (iorder). Let nrho=
! the number of data points in the "id_rho" grid. Then,
!
! => iorder = 0 ... piecewise linear function, zdata(1:nrho)
! must be defined;
! => iorder = 1 ... piecewise cubic Hermite fcn, zdata(1:nrho)
! must be defined, Akima Hermite is used;
! => iorder = 2 ... piecewise spline function, zdata(1:nrho)
! must be defined;
! for iorder.ge.0, the interpolating function f(rho) will satisfy
! f(rho(i))=zdata(i) for i=1:nrho.
!
! note: set iorder = 99 to update data for an already defined
! item. The original interpolation method is retained and
! the axis id must match.
!
! * the name 'fname' must uniquely identify the function. The
! names "R","Z","RHO,"CHI","G","PSI", "BR", "BZ", "BPHI" and
! "BMOD" are reserved to xplasma. NAMES ARE CASE INSENSITIVE.
!
! * boundary conditions -- ignored if iorder.le.0
! ibc1,zbc1 -- controls at rho(1), i.e. the "magnetic axis"
! ibc2,zbc2 -- controls at rho(nrho), i.e. the "plasma bdy".
! options:
! ibc1|2 = 0 -- "not a knot" or default at axis | bdy
! ibc1|2 = 1 -- zbc1|2 contains df/drho at the axis | bdy
! ibc1|2 = 2 -- splines only -- zbc1|2 contains d2f/drho2 at
! the axis | bdy.
!
! * id_rhofun -- on normal exit (ierr.eq.0), id_rhofun contains the
! xplasma index to the interpolating function. This may be worth
! saving for efficient access to the interpolating function.
(old f77 xplasma 1 documentation)
This subroutine creates a lightly smoothed interpolating profile for
an "integrated profile" function defined over a grid of the caller's
choosing.
An example of such a function would be:
P_NBI(x) = Integrated NBI power, watts, from the plasma magnetic
axis to flux surface "x".
The input gives a grid identification, the data, i.e. the integrated value
to each point on the grid, and the name to be used for identifying the
function. The output is an identifier for the interpolating function.
The control "iflag" is 1 for volume integrated density profiles; 2 for
area integrated density profiles such as current drive profiles.
subroutine eqm_irhofun(id_axis,zlbl,inprof,zprof,iflag,id,ierr)
! make XPLASMA profile -- integrated quantity; smooth by 1/2 zone width
! to insure smooth derivative from spline
implicit NONE
integer, intent(in) :: id_axis ! axis id -- must be rho or akin to rho
character*(*), intent(in) :: zlbl ! name for profile function to be created
integer, intent(in) :: inprof ! size of the integrated data profile
real*8, intent(in) :: zprof(inprof) ! the integrated data provided
! if inprof = size(x_axis) zprof(1)=0 is expected
! if inprof = size(x_axis)-1 the axial data point
! is presumed to be omitted.
integer, intent(in) :: iflag ! =1 -- volume normalization;
! derivative evaluations -> W/m^3, #/sec/m^3, etc.
! =2 -- area normalization;
! derivative evaluations -> A/m^2 (current density).
integer, intent(out) :: id ! id of stored profile (if successful)
integer, intent(out) :: ierr ! completion code, 0=OK.
! if an error occurs and ierr is set, id=0 will be returned.
(old f77 xplasma 1 documentation)
In order for (axisymmetric) xplasma to form a complete
specification of the magnetic field, two 2d profile functions
must be provided:
g -- name must be "g" or "G" -- g(rho) is the toroidal field
in the form R*Btor. MKS units would be Tesla*m. Units
given must be consistent with other data given to xplasma.
psi-- name must be "psi" or "PSI" -- poloidal flux function
psi(rho) -- 1/(2*pi) * area integral of poloidal field
through flux zones. MKS units would be Webers/radian.
Units given must be consistent with other data given to
xplasma.
The field specification will be completed by giving the flux surface
geometry R(chi,rho) and Z(chi,rho). Then,
Btor(chi,rho) = g(rho)/R(chi,rho)
Bpol(chi,rho) = grad(psi(rho))/R(chi,rho)
= [d(psi)/d(rho)]*grad(rho)/R(chi,rho)
-------------
use eqm_rhofun to set up the g and psi profiles in xplasma.
(old f77 xplasma 1 documentation)
Another profile associated with MHD equilibrium is the plasma pressure.
There are some variations as to how this may be defined, and the name
for the pressure profile within xplasma is not standardized. However,
a specific profile can be "tagged" as "the" MHD equilibrium pressure
profile by calling this routine:
integer :: id_p ! MHD pressure profile id (1d profile f(rho)) (input)
integer :: ier ! status code returned (0=normal) (output)
call eqm_mark_pmhd(id_p,ier)
(old f77 xplasma 1 documentation)
This routine is provided for setting up a general 1d profile function f(x);
x is a user defined axis with a presumption of no particular relationship
to magnetic or cylindric coordinates. This profile can be a piecewise
cubic spline, piecewise cubic Hermite, or piecewise linear. Thus:
! input:
integer iorder ! "order" of interpolating function
integer id_axis ! user specified x axis
character*(*) fname ! unique name of profile function
real*8 zdata(*) ! the function data (#pts: id_rho axis)
integer ibc1 ! boundary condition code, at rho(1)
real*8 zbc1 ! boundary condition data, at rho(1)
integer ibc2 ! boundary condition code, at rho(nrho)
real*8 zbc2 ! boundary condition data, at rho(nrho)
! output:
integer id_f1d ! unique xplasma id for this function
integer ierr ! completion code, 0=OK
...
call eqm_f1d(iorder,id_axis,fname,zdata,
> ibc1,zbc1,
> ibc2,zbc2,
> id_f1d,ierr)
if(ierr.ne.0) then
[...handle error...]
endif
These arguments are a 1-to-1 analog to the arguments of eqm_rhofun.
The notes to the description of arguments for that routine apply here
as well.
(old f77 xplasma 1 documentation)
To set up a profile f(rho)...
call eqm_rhofun(iorder,id_rho,'f-name',zdata,
> ibc1,zbc1,ibc2,zbc2,
> id_rhofun,ierr)
if(ierr.ne.0) then
[...handle the error...]
endif
The "G" and "PSI" profiles are required, if xplasma is to generate
a continuous differentiable field representation.
To set up a profile f(x), for a user defined x axis...
call eqm_f1d(iorder,id_axis,'f-name',zdata,
> ibc1,zbc1,ibc2,zbc2,
> id_f1d,ierr)
if(ierr.ne.0) then
[...handle the error...]
endif
(old f77 xplasma 1 documentation)
This section discusses methods for defining 2d profile functions
f(chi,rho). To have an (axisymmetric) equilibrium, at least
R(chi,rho) and Z(chi,rho) must be defined.
R(chi,rho) and Z(chi,rho) must be defined before any other
f(chi,rho), and one of the special purpose routines (eqm_rzmag,
eqm_rzmagb, eqm_rzmag2) must be used for this.
Once R and Z are defined, additional functions f(chi,rho) can
be defined using eqm_frhochi.
(old f77 xplasma 1 documentation)
!
! establish R(chi,rho) and Z(chi,rho) bicubic splines.
! periodic in chi, not-a-knot BC @ rho bdys
! (for more control of rho BCs use eqm_rzmagb)
!
! ***note the R & Z arrays must be defined on the same grids as
! were specified by earlier eqm_rho and eqm_chi calls.
!
! input arguments:
!
integer id1,id2 ! R,Z array dimensions
real*8 Rarr(id1,id2) ! R array - (1:nchi)x(1:nrho) points used
real*8 Zarr(id1,id2) ! Z array - (1:nchi)x(1:nrho) points used
!
integer idrho ! =1: id1 is "rho" dimension; =2: id2 is.
!
! if idrho.eq.1, id1.ge.nrho & id2.ge.nchi
! R(1:nrho,1:nchi) & Z(1:nrho,1:nchi) are used
! if idrho.eq.2, id1.ge.nchi & id2.ge.nrho
! R(1:nchi,1:nrho) & Z(1:nchi,1:nrho) are used
!
! output arguments:
!
integer id_R ! xplasma id for "R" bicubic spline (returned)
integer id_Z ! xplasma id for "Z" bicubic spline (returned)
!
integer ierr ! completion code (0 = OK)
!
....
call eqm_rzmag(Rarr,Zarr,id1,id2,idrho, id_R,id_Z,ierr)
(old f77 xplasma 1 documentation)
!
! establish R(chi,rho) and Z(chi,rho) bicubic splines.
! periodic in chi, not-a-knot BC @ rho bdys
! use the BC controls to specify the boundary conditions at
! rho(1) (mag. axis) and rho(nrho) (plasma boundary); there
! are separate BC controls for R and for Z.
!
! ***note the R & Z arrays must be defined on the same grids as
! were specified by earlier eqm_rho and eqm_chi calls.
!
! input arguments:
!
integer id1,id2 ! R,Z array dimensions
real*8 Rarr(id1,id2) ! R array - (1:nchi)x(1:nrho) points used
real*8 Zarr(id1,id2) ! Z array - (1:nchi)x(1:nrho) points used
!
integer idrho ! =1: id1 is "rho" dimension; =2: id2 is.
!
! if idrho.eq.1, id1.ge.nrho & id2.ge.nchi
! R(1:nrho,1:nchi) & Z(1:nrho,1:nchi) are used
! if idrho.eq.2, id1.ge.nchi & id2.ge.nrho
! R(1:nchi,1:nrho) & Z(1:nchi,1:nrho) are used
!
! ** boundary conditions **
!
! BC controls are as in "pspline": 0 -- default, 1 -- fix df/drho
! 2 -- fix d2f/drho2 ...f is R or Z
!
! for BC types 1 & 2, data must be supplied: nchi 1st or 2nd
! derivative values, repsectively; for other types the "zbc"
! arrays are ignored.
!
integer ibcR0 ! R BC type @ rho(1)
real*8 zbcR0(*) ! R BC data @ rho(1) (if needed)
integer ibcR1 ! R BC type @ rho(nrho)
real*8 zbcR1(*) ! R BC data @ rho(nrho) (if needed)
!
integer ibcZ0 ! Z BC type @ rho(1)
real*8 zbcZ0(*) ! Z BC data @ rho(1) (if needed)
integer ibcZ1 ! Z BC type @ rho(nrho)
real*8 zbcZ1(*) ! Z BC data @ rho(nrho) (if needed)
!
! output arguments:
!
integer id_R ! xplasma id for "R" bicubic spline (returned)
integer id_Z ! xplasma id for "Z" bicubic spline (returned)
!
integer ierr ! completion code (0 = OK)
!
....
call eqm_rzmagb(Rarr,Zarr,id1,id2,idrho, &
& ibcR0,zbcR0,ibcR1,zbcR1, &
& ibcZ0,zbcZ0,ibcZ1,zbcZ1, &
& id_R,id_Z,ierr)
(old f77 xplasma 1 documentation)
This routine sets up R(chi,rho) and Z(chi,rho) from a user supplied
mapping subroutine rzmapper:(rho,chi) -> (R,Z). R and Z are defined
as bicubic splines. `rzmapper' is the name of the mapping subroutine
as given in the documentation and source code comments, but of course
the actual programmer can choose any name.
In detail:
The user will supply a subroutine with the following interface:
subroutine rzmapper(rho,chi,R,Z,ierr)
real*8 rho,chi ! magnetic coordinates (in)
real*8 R,Z ! cylindric coordinates (out)
integer ierr ! completion code, 0=OK
...
[compute (R,Z) from (rho,chi)...]
...
end
And then call eqm_rzmag2 to create the bicubic splines (with "not
a knot" rho boundary conditions) for R,Z:
! input:
external rzmapper ! mapping routine
! output:
integer id_R ! xplasma index to R spline
integer id_Z ! xplasma index to Z spline
integer ierr ! completion code, 0=OK
...
call eqm_rzmag2(rzmapper,id_R,id_Z,ierr)
...
Note that the rho and chi grids used to drive rzmapper have to
have already been defined by prior calls to eqm_rho and eqm_chi.
These axes determine the size of the bicubic spline "objects"
created.
(old f77 xplasma 1 documentation)
The eqm_rzmag* routines test the orientation of the chi parameter.
The xplasma internal representation is standardized on a "counter-
clockwise" increasing poloidal angle parameter. If the R and Z
data imply the reverse orientation, the data is "reversed" so that
a counter-clockwise internal representation is used.
To reference data with respect to the original, clockwise oriented
poloidal angle coordinate, use appropriate "ccwflag" optional logical
arguments (fortran-90 interface) or set vector lengths to negative
values (fortran-77 interface).
Xplasma does not "remember" the orientation of the original R & Z
flux surface data.
(old f77 xplasma 1 documentation)
A special routine is provided for setting up a 2d profile function
f(chi,rho). This profile can be a piecewise bicubic spline,
piecewise bicubic Hermite (Akima), or piecewise bilinear. Thus:
! input:
integer iorder ! "order" of interpolating function
integer id_axis1 ! rho or chi coordinate grid id
integer id_axis2 ! chi or rho coordinate grid id
! these axes determine the size and layout requirements for
! zdata(:,:)
character*(*) fname ! unique name of profile function
!
integer idim1 ! size of 1st dimension of zdata
real*8 zdata(idim1,*) ! the function data
!
integer ibcrho0 ! boundary condition code, at rho(1)
real*8 zbcrho0(*) ! boundary condition data, at rho(1)
integer ibcrho1 ! boundary condition code, at rho(nrho)
real*8 zbcrho1(*) ! boundary condition data, at rho(nrho)
! output:
integer id_fun ! unique xplasma id for this new function
integer ierr ! completion code, 0=OK
...
call eqm_frhochi(iorder,id_axis1,id_axis2,fname,
> zdata,idim1,
> ibc1,zbc1,
> ibc2,zbc2,
> id_fun,ierr)
if(ierr.ne.0) then
[...handle error...]
endif
! notes:
! * the amount and ordering of data expected in zdata(...) depends
! on the selected axes (id_axis1, id_axis2), and the interpolation
! order (iorder). Let nrho = the number of data points in the
! "id_rho" grid; let nchi = the number of data points in the chi
! grid. Let's suppose id_axis1.eq.id_rho & id_axis2.eq.id_chi.
! then:
!
! => iorder = 0 ... piecewise bilinear interpolating function;
! zdata(1:nrho,1:nchi) or zdata(1:nchi,1:nrho)
! according as id_axis1 and id_axis2 are set.
! => iorder = 1 ... piecewise bicubic Akima Hermite function;
! zdata(1:nrho,1:nchi) or zdata(1:nchi,1:nrho)
! according as id_axis1 and id_axis2 are set.
! => iorder = 2 ... piecewise bicubic spline function vs. chi,rho.
! zdata(1:nrho,1:nchi) or zdata(1:nchi,1:nrho)
! according as id_axis1 and id_axis2 are set.
!
! for iorder.ge.0, the interpolating function f(rho) will satisfy
! f(rho(i),chi(j))=zdata(i,j) for i=1:nrho, j=1:nchi.
!
! note: set iorder = 99 to update data for an already defined
! item. The original interpolation method is retained and
! the axis ids must match. ** "R" and "Z" cannot be updated
! by this method.
!
! * the name 'fname' must uniquely identify the function. The
! names "R","Z","RHO,"CHI","G","PSI", "BR", "BZ", "BPHI" and
! "BMOD" are reserved to xplasma. NAMES ARE CASE INSENSITIVE.
!
! * the double data array zdata(idim1,*) has a first dimension idim1;
! idim1 must be .ge.nrho, or, .ge.nchi, depending on how
! id_axis1 and id_axis2 are set.
!
! * boundary conditions -- ignored if iorder.le.0
! ibc1,zbc1(*) -- controls at rho(1), i.e. the "magnetic axis"
! ibc2,zbc2(*) -- controls at rho(nrho), i.e. the "plasma bdy".
! options:
! ibc1|2 = 0 -- "not a knot" or default at axis | bdy; zbc1|2
! ignored.
! ibc1|2 = 1 -- zbc1|2 contains df/drho vs. chi at the axis|bdy
! ibc1|2 = 2 -- splines only -- zbc1|2 contains d2f/drho2 vs. chi
! at the axis|bdy.
!
! * id_fun -- on normal exit (ierr.eq.0), id_fun contains the
! xplasma index to the interpolating function. This may be worth
! saving for efficient access to the interpolating function.
!
! * after the subroutine returns, the value of ierr should be
! examined and appropriate action taken if an error is
! indicated.
(old f77 xplasma 1 documentation)
Input profiles to eqm_frhochi are assumed to be defined w.r.t. a
standard counter-clockwise oriented poloidal angle coordinate. If
this is not the case, there is an alternate call that may be used
to indicate a profile defined over a clockwise oriented poloidal
angle coordinate:
iccw=0
call eqm_frhochi_ccw(iccw,
> iorder,id_axis1,id_axis2,fname,
> zdata,idim1,
> ibc1,zbc1,
> ibc2,zbc2,
> id_fun,ierr)
if(ierr.ne.0) then
[...handle error...]
endif
Except for "iccw" the arguments are the same as for eqm_frhochi above.
(old f77 xplasma 1 documentation)
The profiles R(chi,rho) and Z(chi,rho) must be provided in order
to give the flux surface geometry.
Use one of the special purpose routines eqm_rzmag, eqm_rzmagb, or
eqm_rzmag2 to define R and Z. Then, use eqm_frhochi to define
additional functions f(chi,rho).
The general purpose routine eqm_frhochi for constructing f(chi,rho)
2d profiles may be used, or, the special purpose routine eqm_rzmag2
might be used as a more convenient alternative.
To set up R(chi,rho) and Z(chi,rho) from user supplied data arrays
using default "not a knot" spline boundary conditions:
To set up R(chi,rho) and Z(chi,rho) from user supplied data arrays
with full control of boundary conditions at the rho extrema:
To set up R(chi,rho) and Z(chi,rho) splines from a user supplied
subroutine:
subroutine eqm_rzmag2(rzmapper,id_R,id_Z,ierr)
! in out out out
...where rzmapper stands for a user supplied routine with
the interface
subroutine rzmapper(rho,chi,R,Z,ierr)
real*8 rho,chi ! magnetic coordinates (in)
real*8 R,Z ! cylindric coordinates (out)
integer ierr ! completion code (out) 0=OK
To set up any function f(chi,rho):
subroutine eqm_frhochi(iorder,id_axis1,id_axis2,'fname',
> zdata,idim_1,
> ibcrho0,zbcrho0,ibcrho1,zbcrho1,
> id_fun,ierr)
where the first dimension of zdata(idim_1,*) is rho or chi (as
specified by id_axis1), and the 2nd dimension is chi or rho (as
specified by id_axis2). The fit order is controlled by "iorder"
but is forced to be a bicubic spline if the data is named "R" or
"Z". Boundary condition controls specify treatment of the data
at rho(1) and rho(nrho). After the interpolating function is
successfully set up, the xplasma identifer is returned as id_fun.
(old f77 xplasma 1 documentation)
(for axisymmetric tokamak equilibria...)
To compute the magnetic field in the region mapped by magnetic
coordinates, the following quantities need to be defined:
-> 1d profiles: g(rho), psi(rho) (...see _1d_Profiles_f(rho)...)
-> 2d profiles: R(chi,rho), Z(chi,rho) (...see _2d_profiles...)
-> sign factors: given
integer isnccwb,isnccwi
! isnccwb = +1 if Bphi is counterclockwise (CCW) looking down
! on the torus
! = -1 if Bphi is clockwise (CW) looking down on the
! torus.
!
! isnccwi = +1 if Ip (plasma current) is CCW looking down on
! the torus
! -1 if Ip is CW looking down on the torus.
call eqm_bset(isnccwb,isnccwi)
! use eqm_bset to inform xplasma of the sign orientation of
! the toroidal field and plasma current (hence poloidal field).
Subsequently, one may use
call eq_bchk_sign(isnccwb,isnccwi)
to retrieve the sign factors as previously set; if the field has not
been initialized then this routine will return zeroes.
When all of the above information is known to xplasma, it will
go ahead and compute
BR(chi,rho) -- R component of B field -- bicubic spline
BZ(chi,rho) -- Z component of B field -- bicubic spline
BMOD(chi,rho) -- mod(B) -- bicubic spline
(note Bphi(chi,rho) = isnccwb*g(rho)/R(chi,rho) is known implicitly).
(note also that the signs of g(rho) and d(psi)/d(rho) will not
be used to compute B. Instead, "G" as stored by xplasma as a
positive definite function, and "PSI" is standardized such that
psi(rho(1))=0.0d0 and psi(rho) is strictly increasing. The call
to eqm_bset is required to set the signs of the field vector
components.)
A sketched example of code that would supply enough information for
xplasma to evaluate the B field vs. magnetic coordinates:
call eqm_rho(...) ! set up rho grid
call eqm_chi(...) ! set up chi grid
call eqm_rhofun(...,'g',...) ! set up g(rho)
call eqm_rhofun(...,'psi',...) ! set up psi(rho)
call eqm_frhochi(...,'R',...) ! set up R(chi,rho)
call eqm_frhochi(...,'Z',...) ! set up Z(chi,rho)
call eqm_bset(isnccwb,isnccwi) ! specify sign conventions
In xplasma the "limiter" refers to traditional limiters, vacuum
vessel walls, divertor throats-- any mechanical bounding surface
that faces the plasma or the vacuum region surrounding the plasma.
Generally, the (R,Z) cartesian grid is set up at the same time as
the limiter, because the limiter location determines the range of
(R,Z) rectangular grid required to cover both the core plasma and
the scrape off plasma between the core and the limiters.
xplasma supports 3 types of limiter specifications:
* fixed distance "dlim" from plasma boundary, but constrained by
a box [Rmin,Rmax]x[Zmin,Zmax]. dlim.ge.zero, and the box contains
(and does not cut into) the core plasma. If dlim=0, the limiter
and the plasma boundary are the same.
=> use eqm_dbdy_grid or eqm_rzgrid followed by eqm_dbdy
* a list of infinite lines and circles. Each line j divides space
into two half-planes one of which contains the plasma, call this
half plane Hj. Each circle k divides space into the region inside
the circle and the region outside, one of which contains the
plasma; call this region Ck. The intersection I of all Hj and Ck
then defines a space containing the plasma. The boundary of I,
which consists of segments of the given lines and arcs of the
given circles, defines the limiter. It is an error if one of
the limiter lines or circles cuts into the plasma.
=> use eqm_tbdy_grid or eqm_tbdy followed by eqm_rzgrid
* a contour defined by a closed sequence of points {(R(i),Y(i))},
i=1:nc, R(1)=R(nc),Y(1)=Y(nc), and the contour does not cross
itself, and the contour does not cut into the plasma.
=> use eqm_cbdy_grid or eqm_cbdy followed by eqm_rzgrid
(old f77 xplasma 1 documentation)
If evenly spaced (R,Z) grids are OK, use eqm_dbdy_grid, as
follows:
To define the limiter (input to eqm_dbdy_grid or eqm_dbdy)
real*8 dlim ! fixed distance out from plasma bdy
real*8 rmin,rmax ! Rmin and Rmax of constraining box
real*8 zmin,zmax ! Zmin and Zmax of constraining box
To define the (R,Z) grids (input to eqm_dbdy_grid or eqm_rzgrid)
integer inumR ! no. of R grid points
integer inumZ ! no. of Z grid points
Information returned (from eqm_dbdy_grid or eqm_rzgrid)
integer id_R ! xplasma R grid id code
integer id_Z ! xplasma Z grid id code
integer ierr ! completion code 0 = OK
...
call eqm_dbdy_grid(dlim,rmin,rmax,zmin,zmax,
> inumR,inumZ,
> id_R,id_Z,ierr)
-----------------------------------------------------
Or...
To explicitly control the (R,Z) grids...
Add the declarations...
real*8 Rgrid(inumR) ! user's desired R grid for eqm_rzgrid
real*8 Zgrid(inumZ) ! user's desired Z grid for eqm_rzgrid
! *** Rgrid and Zgrid must be strict ascending, and,
! [Rgrid(1),Rgrid(inumR)]x[Zgrid(1),Zgrid(inumZ)] must
! cover the plasma
integer iauto ! if iauto.le.0 user supplies grid
! if iauto.eq.0 the grid is padded with
! two "gaurd rows" on each edge
! if iauto.eq.-1 the grid is not padded.
real*8 ztol ! (R,Z) grid even-spacing tolerance
! recommend about 1.0d-6*Rmax
...
! set up grid FIRST
call eqm_rzgrid(Rgrid,Zgrid,iauto,iauto,inumR,inumZ,ztol,
> id_R,id_Z,ierr)
! THEN set up limiter.
call eqm_dbdy(dlim,Rgrid(1),Rgrid(inumR),Zgrid(1),Zgrid(inumZ),
ierr)
-- or --
call eqm_dbdy(dlim,Rmin,Rmax,Zmin,Zmax,
ierr)
! but note:
! if Rmin.lt.Rgrid(1), Rgrid(1) is used,
! if Rmax.gt.Rgrid(inumR), Rgrid(inumR) is used,
! if Zmin.lt.Zgrid(1), Zgrid(1) is used,
! if Zmax.gt.Zgrid(inumZ), Zgrid(inumZ) is used,
(old f77 xplasma 1 documentation)
If evenly spaced (R,Z) grids are OK, use eqm_tbdy_grid, as
follows:
To define the line limiters (input to eqm_tbdy_grid or eqm_tbdy):
integer ilines ! number of infinite line "limiters"
real*8 Rl(ilines),Zl(ilines) ! 1 point given on each line
real*8 thl(ilines) ! orientation angle in DEGREES
! thl(j) = 0 or 180 or -180 -- line j is horizontal
! thl(j) = +/- 90 -- line j is vertical
! thl(j) = 45 -- line runs in (+1,+1) or (-1,-1) direction
along the line in (R,Z) space
! thl(j) = -45 -- line runs in (-1,+1) or (+1,-1) direction
along the line in (R,Z) space
To define the circle limiters (input to eqm_tbdy_grid or eqm_tbdy):
integer icircs ! number of circle "limiters"
real*8 Rc(icircs),Zc(icircs) ! (R,Z) centers of circles
real*8 rad(icircs) ! radii of circles
To define the (R,Z) grids (input to eqm_tbdy_grid or eqm_rzgrid)
integer inumR ! no. of R grid points
integer inumZ ! no. of Z grid points
Information returned (from eqm_tbdy_grid or eqm_rzgrid)
integer id_R ! xplasma R grid id code
integer id_Z ! xplasma Z grid id code
integer ierr ! completion code 0 = OK
...
call eqm_tbdy_grid(ilines,Rl,Zl,thl,
> icircs,Rc,Zc,rad,
> inumR,inumZ,
> id_R,id_Z,ierr)
-----------------------------------------------------
Or...
To explicitly control the (R,Z) grids...
Add the declarations...
! for eqm_rzgrid:
real*8 Rgrid(inumR) ! desired R grid
real*8 Zgrid(inumZ) ! desired Z grid
! if Rgrid and Zgrid are not generated, then,
! *** Rgrid and Zgrid must be strict ascending, and,
! [Rgrid(1),Rgrid(inumR)]x[Zgrid(1),Zgrid(inumZ)] must
! cover the plasma AND scrapeoff layer
integer iautoR = 0 ! user supplies Rgrid if iautoR=0
integer iautoZ = 0 ! user supplies Zgrid if iautoZ=0
real*8 ztol ! (R,Z) grid even-spacing tolerance
! recommend about 1.0d-6*Rmax
...
integer itype ! limiter type code from eq_bdlims
real*8 zRmin,zRmax ! limiter Rmin & Rmax from eq_bdlims
real*8 zZmin,zZmax ! limiter Zmin & Zmax from eq_bdlims
! set up limiter FIRST
call eqm_tbdy(ilines,Rl,Zl,thl,
> icircs,Rc,Zc,rad, ierr)
if(ierr.ne.0) then
[...handle error...]
endif
! find Rmin,Rmax, Zmin,Zmax of limiters (use eq_bdlims subroutine)
call eq_bdlims(itype,zRmin,zRmax,zZmin,zZmax,ierr)
! generate a grid which covers the necessary range + some margin
! **for example** code to generate an evenly spaced (R,Z) grid:
zRmid=0.5d0*(zRmin+zRmax)
zZmid=0.5d0*(zZmin+zZmax)
zdelR=(inumR-1)*0.5d0*(zRmax-zRmin)/(inumR-4) ! 1.5 zone margin
zdelZ=(inumZ-1)*0.5d0*(zZmax-zZmin)/(inumZ-4) ! 1.5 zone margin
do i=1,inumR
zRgrid(i)=zRmid-zdelR + (i-1)*2.0d0*zdelR/(inumR-1)
enddo
do i=1,inumZ
zZgrid(i)=zZmid-zdelZ + (i-1)*2.0d0*zdelZ/(inumZ-1)
enddo
! but note an evenly spaced grid is not required.
! finally, call eqm_rzgrid to set up the (R,Z) grid
call eqm_rzgrid(Rgrid,Zgrid,iautoR,iautoZ,inumR,inumZ,ztol,
> id_R,id_Z,ierr)
(old f77 xplasma 1 documentation)
If evenly spaced (R,Z) grids are OK, use eqm_cbdy_grid, as
follows:
To define the limiter contour, an ordered, closed sequence of
(R,Z) points, for eqm_cbdy_grid or eqm_cbdy:
integer ipts ! number of points in piecewise linear
! limiter contour
real*8 Rlim(ipts),Zlim(ipts) ! count (R,Z) point data
! Rlim(1)=Rlim(ipts) and Zlim(1)=Zlim(ipts) is required, and,
! the arrays must describe a simple closed contour that does not
! cross itself, and which does not cut into the core plasma.
To define the (R,Z) grids (input to eqm_cbdy_grid or eqm_rzgrid)
integer inumR ! no. of R grid points
integer inumZ ! no. of Z grid points
Information returned (from eqm_cbdy_grid or eqm_rzgrid)
integer id_R ! xplasma R grid id code
integer id_Z ! xplasma Z grid id code
integer ierr ! completion code 0 = OK
...
call eqm_cbdy_grid(ipts,Rlim,Zlim,
> inumR,inumZ,
> id_R,id_Z,ierr)
-----------------------------------------------------
Or...
To explicitly control the (R,Z) grids...
Add the declarations...
! for eqm_rzgrid:
real*8 Rgrid(inumR) ! desired R grid
real*8 Zgrid(inumZ) ! desired Z grid
! if Rgrid and Zgrid are not generated, then,
! *** Rgrid and Zgrid must be strict ascending, and,
! [Rgrid(1),Rgrid(inumR)]x[Zgrid(1),Zgrid(inumZ)] must
! cover the plasma AND scrapeoff layer
integer iautoR = 0 ! user supplies Rgrid if iautoR=0
integer iautoZ = 0 ! user supplies Zgrid if iautoZ=0
real*8 ztol ! (R,Z) grid even-spacing tolerance
! recommend about 1.0d-6*Rmax
...
integer itype ! limiter type code from eq_bdlims
real*8 zRmin,zRmax ! limiter Rmin & Rmax from eq_bdlims
real*8 zZmin,zZmax ! limiter Zmin & Zmax from eq_bdlims
! set up limiter FIRST
call eqm_cbdy(ipts,Rlim,Zlim, ierr)
if(ierr.ne.0) then
[...handle error...]
endif
! find Rmin,Rmax, Zmin,Zmax of limiters (use eq_bdlims subroutine)
call eq_bdlims(itype,zRmin,zRmax,zZmin,zZmax,ierr)
! generate a grid which covers the necessary range + some margin
! **for example** code to generate an evenly spaced (R,Z) grid:
zRmid=0.5d0*(zRmin+zRmax)
zZmid=0.5d0*(zZmin+zZmax)
zdelR=(inumR-1)*0.5d0*(zRmax-zRmin)/(inumR-4) ! 1.5 zone margin
zdelZ=(inumZ-1)*0.5d0*(zZmax-zZmin)/(inumZ-4) ! 1.5 zone margin
do i=1,inumR
zRgrid(i)=zRmid-zdelR + (i-1)*2.0d0*zdelR/(inumR-1)
enddo
do i=1,inumZ
zZgrid(i)=zZmid-zdelZ + (i-1)*2.0d0*zdelZ/(inumZ-1)
enddo
! but note an evenly spaced grid is **not** required.
! finally, call eqm_rzgrid to set up the user (R,Z) grid
call eqm_rzgrid(Rgrid,Zgrid,iautoR,iautoZ,inumR,inumZ,ztol,
> id_R,id_Z,ierr)
(old f77 xplasma 1 documentation)
After the limiter and (R,Z) grids have been established, the user
can define profiles over the (R,Z) grid. These functions will
cover both the core plasma and the scrape-off plasma. For (R,Z)
points which are behind the limiter, a smooth automatic extrap-
olation is provided.
Interpolating profile functions f(R,Z) are characterized by
an "order" which corresponds to fitting method:
order description
0 bilinear interpolation, continuous
1 Akima Hermite interpolation, continuous & differentiable
2 Spline interpolation, continuous & twice differentiable
However, bicubic splines should be used with care, as noise in
the data can cause "ringing" artifacts. If this is in the
plasma edge region, a smoothing option is available.
Three subroutines are available for defining functions f(R,Z):
eqm_rzfun -- from an existing flux surface function f(rho),
define f(R,Z) using f(rho) in the core region
and f(rho(bdy))*exp(-d/lamda) in the scrape
off region, where d = distance beyond plasma
boundary. The parameter "lamda", the scrape-
off length, is a subroutine argument.
eqm_rzfunf-- from an existing flux surface function f(rho),
define f(R,Z) using f(rho) in the core region
and using a user supplied function (passed as
a subroutine argument) for the scrape-off layer.
The user supplied function is only called for
(R,Z) values which are outside the plasma but
inside the limiter.
eqm_rzfun2-- load f(R,Z) from a user supplied function
which is passed as a subroutine argument.
The user supplied function is called for
all (R,Z) values which are inside the
limiter, including inside the core
plasma.
eqm_rzfunda- load f(R,Z) from a user supplied data array.
As with f(rho) & f(rho,chi) routines, an existing item can
receive a "data update" by calling the appropriate routine with
iorder=99 specified. However, it is up to the programmer to
enforce consistency of profiles in the context of updates.
(old f77 xplasma 1 documentation)
Use this subroutine to define a profile f(R,Z) from an existing
flux surface function profile f(rho), using a "scrape-off length"
parameter to specify the variation outside the core plasma
boundary.
Input arguments:
integer id_fun ! xplasma id for existing function f(rho)
! if necessary: call eq_gfnum('TE',id_fun) to fetch a function
! id given the function name; here 'TE' might be the label for
! "electron temperature".
real*8 lamda ! scrape-off length
! outside the plasma boundary, f(R,Z)=f(rho(bdy))*exp(-d/lamda),
! where f(rho(bdy)) is the value of f(rho) at the plasma boundary,
! and d(R,Z) gives the distance from the plasma boundary.
integer iorder ! interpolation method, 0=bilinear,
! 1=Hermite, 2=Spline
real*8 delta ! smoothing parameter (0.0d0 for none)
Output argument:
integer ierr ! completion code, 0=OK
...
call eqm_rzfun(id_fun,lamda,iorder,delta,ierr)
if(ierr.ne.0) then [...handle error...]
After a successful call to eqm_rzfun, the interpolation object
indexed by "id_fun" is a "compound object" containing both
representations f(rho) and f(R,Z), which are designed to co-exist.
(old f77 xplasma 1 documentation)
Use this subroutine to define a profile f(R,Z) from an existing
flux surface function profile f(rho), using a user supplied
function "userfcn" to specify the profile variation outside the
core plasma but inside the limiter. "userfcn" is the name used
for the function dummy argument and in this documentation, but
in practice for any particular call to eqm_rzfunf the user
chooses an appropriate specific name for this function. The
"userfcn" routine must have the following interface:
real*8 function "userfcn"(iarg,R,Z,phi,ierr)
with inputs:
integer iarg ! integer argument, pass through eqm_rzfunf
real*8 R,Z,phi ! cylindric coordinates (for axisymmetric
! cases phi will not be used). Points passed
! to userfcn will be outside the core plasma
! but inside the limiter
and outputs:
(function value) ! the value of the function "f" at (R,Z,phi)
integer ierr ! completion code, 0=OK, else an error.
Assuming "userfcn" exists, then, the arguments to eqm_rzfunf are
as follows.
Input arguments:
character*(*) zname ! name of new function, must be unique.
! if necessary: call eq_gfnum('TE',id_fun) to fetch a function
! id given the function name; here 'TE' might be the label for
! "electron temperature".
external <your-userfcn> ! the name of your "userfcn" routine
integer iarg ! pass-through argument for "userfcn"
integer iorder ! interpolation method, 0=bilinear,
! 1=Hermite, 2=Spline
real*8 delta ! smoothing parameter (0.0d0 for none)
Output arguments:
integer id_fun ! xplasma id for new function (returned).
integer ierr ! completion code, 0=OK
...
call eqm_rzfunf(id_fun,<your-userfcn>,iarg,iorder,delta,ierr)
if(ierr.ne.0) then [...handle error...]
(old f77 xplasma 1 documentation)
Use this subroutine to define a new profile function f(R,Z),
using a user supplied function "userfcn" to specify the profile
variation both inside the core plasma and also outside the
core plasma but inside the limiter. "userfcn" is the name used
for the function dummy argument and in this documentation, but
in practice for any particular call to eqm_rzfun2 the user chooses
an appropriate specific name for this function. The "userfcn" must
have the following interface:
real*8 function "userfcn"(iarg,R,Z,phi,ierr)
with inputs:
integer iarg ! integer argument, pass through eqm_rzfun2
real*8 R,Z,phi ! cylindric coordinates (for axisymmetric
! cases phi will not be used). Points passed
! to userfcn will be inside the limiter
and outputs:
(function value) ! the value of the function "f" at (R,Z,phi)
integer ierr ! completion code, 0=OK, else an error.
Assuming "userfcn" exists, then, the arguments to eqm_rzfun2 are
as follows.
Input arguments:
character*(*) zname ! name of function
external <your-userfcn> ! the name of your "userfcn" routine
integer iarg ! pass-through argument for "userfcn"
integer iorder ! interpolation method, 0=bilinear,
! 1=Hermite, 2=Spline
real*8 delta ! smoothing parameter (0.0d0 for none)
Output arguments:
integer id_fun ! xplasma id for function
integer ierr ! completion code, 0=OK
...
call eqm_rzfun2(zname,id_fun,<your-userfcn>,iarg,iorder,delta,
> ierr)
if(ierr.ne.0) then [...handle error...]
After a successful call to eqm_rzfun, the interpolation object
indexed by "id_fun" is a "compound object" containing both
representations f(rho) and f(R,Z), which are designed to co-exist.
(old f77 xplasma 1 documentation)
Use this subroutine to define a new profile function f(R,Z),
using array data supplied by the user. The data must be dimensioned
f(nR,nZ) where nR and nZ are the R and Z grids, respectively. The
function covers the entire R-Z box, including points beyond the
limiters.
Input arguments:
character*(*) zname ! name of function
integer inR,inZ ! array dimensions -- must match R, Z grids
real*8 zdata(inR,inZ) ! data array-- R,Z grid dimensions.
integer iorder ! interpolation method, 0=bilinear,
! 1=Hermite, 2=Spline
real*8 delta ! smoothing parameter (0.0d0 for none)
Output arguments:
integer id_fun ! xplasma id for function
integer ierr ! completion code, 0=OK
...
call eqm_rzfunda(zname,id_fun,zdata,inR,inZ,iorder,delta,ierr)
if(ierr.ne.0) then [...handle error...]
After a successful call to eqm_rzfun, the interpolation object
indexed by "id_fun" is a "compound object" containing both
representations f(rho) and f(R,Z), which are designed to co-exist.
(old f77 xplasma 1 documentation)
Often noise occurs in the vicinity of the plasma boundary;
to ameliorate this there is a smoothing parameter "delta" given
in units of (R,Z). Data at points (Rd,Zd) within distance
0.5*delta of the plasma boundary are smoothed by a weighted
convolution over data in the region
[Rd-delta,Rd+delta]x[Zd-delta,Zd+delta]
with the weighting a pyramid shaped function favoring the points
close to the original (Rd,Zd). For points in the bands from
0.5*delta to 1.5*delta away from the boundary, reduced smoothing
is done, the less smoothing the further away, until at 1.5*delta
remove no smoothing is done at all.
The smoothing parameter "delta" is a calling argument in
the subroutines eqm_rzfun* used for defining profile functions
f(R,Z). To disable smoothing, specify delta = 0.0D0 ...
(old f77 xplasma 1 documentation)
The xplasma package permits formation of a representation of the
magnetic field vector over the (R,Z) grid, and therefore covering
the scrape-off region as well as the core plasma.
To load this representation, the user calls subroutine "eqm_brz"
which itself takes a field specification subroutine as an argument.
For this field specification subroutine, the user must either
(a) provide a user written subroutine "userbvec", with the
required interface, which specifies he field everywhere, or,
(b) if B(chi,rho) is available in the core region but there is
no data for B in the scrape-off region, use the xplasma
supplied extrapolator for B(R,Z):
eqm_brz_adhoc -- ad hoc extrapolation, fast and smooth.
(the old method eqm_brzx -- div(B)=curl(B)=0 extrapolation
has been continued; eqm_brzx and eqm_brz_adhoc now both
yield the same result).
(more details below).
(c) if data specifying psi(R,Z) is available, enter this into
xplasma using e.g. eqm_rzfun with iorder=1 or iorder=2,
then use "eqm_bpsi", which will use grad(phi) x grad(psi)
for BR and BZ, and g/R for Bphi.
Option (b) should only be used in cases where an independent data
source for reconstruction of B(R,Z) outside the core plasma is
unavailable. For example, if reading data from a source such as
a TRANSP run, which lacks outside-the-plasma data, option (b) may
have to be used. But, if reading from a source such as an EFIT
G EQDSK file which gives psi(R,Z), options (a) or (c) should be
used.
The call to eqm_brz takes the following form:
input arguments:
external <your-userbvec | eqm_brz_adhoc | eqm_bpsi >
! your B(R,Z) extrapolation data subroutine
real*8 delta ! edge smoothing parameter
output argument:
integer ierr ! completion code, 0=OK
...
call eqm_brz(<your B(R,Z) extrapolation data subroutine>,
> delta, ierr)
if(ierr.ne.0) then [... handle error ...]
A successful call to eqm_brz results in the creation of functions
BR(R,Z), BZ(R,Z) and BPHI(R,Z), interpolations on which are usually
done by one of the eq_get* data access subroutines. If
"eqm_brz_adhoc" is used, the poloidal flux function psi(R,Z) is
also formed and associated with psi(rho) from inside the plasma.
Note: at present this system is only valid for axisymmetric plasma
field configurations. Non-axisymmetry will require significant
new options.
(old f77 xplasma 1 documentation)
If you write your own routine for specifying the field, it must
have the following interface:
subroutine <your-userbvec>(
> ivec,R,Z,phi,
> init,
> BR,BZ,BPHI,
> Psi,ipsi,
> ierr)
! input arguments:
integer ivec ! vector dimensioning
real*8 R(ivec),Z(ivec),phi(ivec) ! coordinates at which
! ! to evaluate BR,BZ,Bphi
integer init ! call flag
! init=1 -- initialization call (or just set ipsi)
! init=2 -- cleanup call (or do nothing)
! init=0 -- standard call
!
! the purpose of init is to allow the subroutine to perform
! initializations before it is first used, and to perform
! cleanup operations after the last time it is used. Of
! course some <your-userbvec> routines might not require
! initialization or cleanup.
!
! within an eqm_brz routine, the first call to <your-userbvec>
! has init=1, subsequent calls have init=0, and then there is
! a final call with init=2.
! output arguments:
! if init=1, only ipsi need be output
! if init=2, no output is required
! if init=0, all output is required
!
real*8 BR(ivec),BZ(ivec),BPHI(ivec) ! the field components
! ! at the given coordinates
!
! optional output:
real*8 Psi(ivec) ! the poloidal flux
integer ipsi ! =1 if poloidal flux is
! ! output
!
! status code
!
integer ierr ! exit code, 0=OK
! notes:
! * the phi(ivec) argument is provided for compatibility, in case
! the software is extended for non-axisymmetry in the future.
! For an axisymmetric code it can be ignored.
! * ipsi must be set =1 if the routine returns psi(R,Z) in addition
! to the field components. Otherwise set ipsi=0.
! * the routine is expected to return BR(j),BZ(j),BPHI(j) and
! possibly psi(j), i.e. the field, at R(j),Z(j),phi(j),
! for j = 1 to ivec. If the field cannot be evaluated at
! any one or more of these points, the status code ierr
! should be set to a non-zero number.
!
! it may be helpful to look at the source code for eqm_brz_adhoc
! and eqm_bpsi -- two routines which satisfy these interface
! requirements.
(old f77 xplasma 1 documentation)
CORRECTION: eqm_brzx has been remapped to point to eqm_brz_adhoc. For
stability/robustness reasons, the original eqm_brzx described here has
been discontinued.
Xplasma supplies 2 B(R,Z) extrapolator routines. Either
subroutine eqm_brz_adhoc(ivec,zR,zZ,zphi,init,BR,BZ,BPHI,zpsi,
> ipsi,ierr)
or
subroutine eqm_brzx(ivec,zR,zZ,zphi,init,BR,BZ,BPHI,zpsi,
> ipsi,ierr)
...can be passed to eqm_brz as the external subroutine argument.
Eqm_brz_adhoc uses a fast, approximate ad hoc method for extrapolating
B(R,Z), described in detail below. In summary, the external toroidal
field is taken as g(rho_bdy)/R, while the external poloidal field is
assumed to allign with an ad hoc numerical extrapolation of the
(rho,chi) flux coordinate system. The poloidal variation of the
poloidal field occurring at the boundary is preserved, but the
magnitude is scaled down by the ratio of the poloidal path length
at the extrapolated rho surface to the poloidal path length of the
boundary surface (at rho_bdy). An extrapolation of the poloidal
flux function psi is also provided and is roughly consistent with
the extrapolated poloidal field.
The original Eqm_brzx used a block-tridiagonal finite difference
solver to extrapolate the core field, assuming axisymmetry and
div(B)=0, and, initially, curl(B)=0. As the extrapolation advances,
instabilities are detected and repaired by introducing current
(curl(b).ne.0). Physically, this can be thought of as "guessing"
where the coils are that hold the field in place in the actual
experiment. This stabilized extrapolation will result in a
field estimate whose deviation from the actual experimental
field will increase, the further one moves from the core plasma
region where the field is known. However, the field should have
roughly the right behaviour close to the core plasma. On the
other hand, the stabilization method is purely ad hoc.
The purpose of eqm_brz_adhoc and eqm_brzx is to provide an (R,Z)
extrapolation when the original field data is unavailable outside
the core plasma. If the original field data is available, it should
be used in preference to these routines.
A sketch of the actual eqm_brz_adhoc algorithm executed by xplasma:
1. compute an ad hoc extrapolation of the (chi,rho) magnetic
coordinate system. This extrapolation produces a set of
nested surfaces but not flux surfaces; outside the core
plasma, psi is a function of (chi,rho), not just (rho).
2. for rho>rho_bdy compute
Lpol(rho) = poloidal path length around rho "flux" surface.
and then take
mod(Bpol(rho,chi)) = mod(Bpol(rho_bdy,chi)) * L_ratio
where L_ratio = Lpol(rho_bdy)/Lpol(rho) < 1.
while the direction of Bpol is tangent to the rho surface
with sign consistent with the interior Bpol field.
also take
Bphi(rho,chi) = nsnccwb*g(rho_bdy)/R(rho,chi)
i.e. use the "1/R" vacuum toroidal field beyond the plasma
boundary.
3. form a poloidal variation spline
fRB(chi)) = (R*Bpol(rho_bdy,chi))/(Rmax*Bpol(rho_bdy,Rmax))
(Bpol(rho_bdy,Rmax) is found by finding chi_Rmax = the poloidal
angle at which the max R on the rho surface occurs, then
Bpol(rho_bdy,Rmax)=Bpol(rho_bdy,chi_Rmax) is used).
4. Along the contour (rho,chi_Rmax(rho)) compute the extrapolated
poloidal field and poloidal flux function differential
d(psi) = Rmax(rho)*d(Rmax)*Bpol(rho,chi_Rmax(rho))
and integrate this to form psi(rho,chi_Rmax(rho))
5. Take psi(rho,chi) = fRB(chi)*psi(rho,chi_Rmax(rho))
The result is quickly calculated, numerically well behaved, gives
reasonable results near the boundary, but, it does not precisely
satisfy physics constraints (del.B=0, curl.B=0, Bpol=grad(psi)/R
are NOT satisfied accurately).
A sketch of the original eqm_brzx algorithm:
*** NO LONGER AVAILABLE ***
1. compute an ad hoc extrapolation of the (chi,rho) magnetic
coordinate system. This extrapolation produces a set of
nested surfaces but not flux surfaces; outside the core
plasma, psi is a function of (chi,rho), not just (rho).
2. on this ad hoc coordinate system, advance from rho(j-1)
to rho(j), for all chi, using a block tridiagonal solver
for BR(1:nchi,j), BZ(1:nchi,j), with the triagonal system
derived by finite differences from div(B)=0, curl(B)=0.
3. detect and repair fluctuations in Bchi, by adding current
(in the form curl(B)) where needed to keep Bchi smooth.
(this is an entirely ad hoc method).
4. move on to the next surface-- keep going until the (R,Z)
grid rectangle is entirely covered.
The main computations are done on the init=1 call to eqm_brzx.
The init=0 calls return the resulting field components to
eqm_brz, and the init=2 call is used to clean up memory
management related to the calculation.
Bottom line: eqm_brz_adhoc is faster and more stable. Both
extrapolators have "arbitrary" ad hoc features. If a calculation
requires accurate B(R,Z) beyond the plasma boundary, there is no
substitute for providing the actual data based on coil positions,
coil currents, and a free boundary equilibrium reconstruction (e.g.
EFIT) that actually covers the vacuum region. An EFIT-based or
equivalent psi(R,Z) profile can also be used (see the eqm_bpsi
B(R,Z) data extrapolation routine).
Since many datasets (e.g. TRANSP results) do not retain the
necessary vacuum field data, the extrapolation tools are provided
as a convenience.
(old f77 xplasma 1 documentation)
subroutine eqm_bpsi(ivec,zR,zZ,zphi,init,BR,BZ,BPHI,zpsi,ipsi,
> ierr)
...can be passed to eqm_brz as the external subroutine argument.
This routine can be used when psi(R,Z) has been defined as an
xplasma function (e.g. with eqm_rzfun). The routine computes
Bphi from g(rho)/R, where rho=rho(R,Z) inside the core plasma,
and, rho=rhobdy at the edge and outside the core plasma.
The routine computes BR and BZ from grad(phi) x grad(psi)
which looks like
BR(i) = nsnccwi*(dpsi/dZ)/R
BZ(i) = -nsnccwi*(dpsi/dR)/R
where nsnccwi (+/-1) specifies the direction of the toroidal plasma
current.
If core field data BR(chi,rho) and BZ(chi,rho) are available,
xplasma will use this in preference to grad(psi)/R.
If necessary, edge smoothing can be employed to improve the
blending of the core and external regions.
Often noise occurs in the vicinity of the plasma boundary;
to ameliorate this there is a smoothing parameter "delta" given
in units of (R,Z). Data at points (Rd,Zd) within distance
0.5*delta of the plasma boundary are smoothed by a weighted
convolution over data in the region
[Rd-delta,Rd+delta]x[Zd-delta,Zd+delta]
with the weighting a pyramid shaped function favoring the points
close to the original (Rd,Zd). For points in the bands from
0.5*delta to 1.5*delta away from the boundary, reduced smoothing
is done, the less smoothing the further away, until at 1.5*delta
remove no smoothing is done at all.
The smoothing parameter "delta" is a calling argument in
the subroutines eqm_rzfun* used for defining profile functions
f(R,Z). To disable smoothing, specify delta = 0.0D0 ...
XPLASMA now supports a generic facility for creating and accessing lists.
Each list has a name (max 32 characters, one alphanumeric word, treated
as uppercase), a label (max 60 characters), and a size which specifies
the number of elements in the list.
Each list element has a name (max 32 characters, one alphanumeric word,
treated as uppercase), and the following associated data:
a character string (max 60 characters) in addition to the name
a floating point number
an integer.
Lists can be used to communicate such information as:
-> a list of plasma spacies and the Z and A values of each species:
name of example list: TR_THERMAL_SPECIES, size = 3
(label: "list of thermal ion species")
name character data floating data integer data
H (blank) 1.000 1
D (blank) 2.000 1
HE4 (blank) 4.000 2
interpretation: floating data gives "A", integer gives "Z".
-> a timestep
name of example list: TR_TIMESTEP, size = 2
(label: "timestep for TRANSP sources")
name character data floating data integer data
TR_TS1 S 1.010 0
TR_TS2 S 1.020 0
interpretation: character data gives physical units, floating data
gives actual time values.
-> a collection of related profile data items
name of example list: TR_EHEAT, size = 5
(label: "integrated electron heating profiles")
name character data floating data integer data
TR_PBE W 3.073e4 310
TR_PFE W 0.000 311
TR_PEICRF W 0.000 312
TR_PEECRF W 0.000 313
TR_PELH W 0.000 314
interpretation: a list of heating profiles. Character data gives
physical units, floating data gives total amount (power, W, in this
context), and the integer gives XPLASMA id of the interpolating
function f(rho).
The methods for creation of lists are described here. Methods for
finding and accessing lists and list contents are described under
"Data Access Routines".
(old f77 xplasma 1 documentation)
! inputs:
character*32 :: list_name ! 1 word, treated as uppercase in XPLASMA
character*60 :: list_label ! any label string
integer :: list_size ! number of elements in list
character*32 :: element_names(list_size) ! names of elements (1 word each)
! outputs:
integer :: list_id ! list id -- used to refer to list in subsequent calls.
integer :: ierr ! completion code, 0=OK
...
call eqm_list_create(list_name,list_label,list_size,element_names, &
& list_id,ierr)
(old f77 xplasma 1 documentation)
! inputs:
integer :: list_id
integer :: list_size ! should match size in prior eqm_list_create call
character*60 chvals(list_size) ! character data
! output:
integer :: ierr ! completion code, 0=OK
call eqm_list_chval(list_id,list_size,chvals,ierr)
(old f77 xplasma 1 documentation)
! inputs:
integer :: list_id
integer :: list_size ! should match size in prior eqm_list_create call
integer :: ivals(list_size) ! integer data
! output:
integer :: ierr ! completion code, 0=OK
call eqm_list_ival(list_id,list_size,ivals,ierr)
(old f77 xplasma 1 documentation)
! inputs:
integer :: list_id
integer :: list_size ! should match size in prior eqm_list_create call
real*8 r8vals(list_size) ! 8 byte floating point data
! output:
integer :: ierr ! completion code, 0=OK
call eqm_list_r8val(list_id,list_size,r8vals,ierr)
(old f77 xplasma 1 documentation)
! inputs:
integer :: list_id
integer :: list_size ! should match size in prior eqm_list_create call
real*4 r4vals(list_size) ! 4 byte floating point data
! output:
integer :: ierr ! completion code, 0=OK
call eqm_list_r4val(list_id,list_size,r4vals,ierr)
(old f77 xplasma 1 documentation)
-- max 32 characters.
-- no imbedded blanks.
-- alphanumeric characters + "$" + "_" only.
-- all alphabetic characters treated as uppercase.
-- first character should not be a numerical digit [0-9].
(old f77 xplasma 1 documentation)
A subroutine to numerically compute the q(rho) profile, using the
formula
q = d(Phi)/d(Psi) = [d(Phi)/d(rho)]/[d(Psi)/d(rho)]
is provided:
character*10 qname -- desired XPLASMA name of q profile (input)
id_q -- id of XPLASMA profile created (output)
ierr -- completion code (0 = normal) (output)
call eqm_gen_q(qname,id_q,ierr)
The profile is a piecewise cubic Hermite function.
[d(Psi)/d(rho)] > 0 near the axis is forced, but, the numerical
value of q near axis can still jump, in cases with low axial
current density.
(old f77 xplasma 1 documentation)
[This is the original xplasma 1.0 documentation].
After xplasma setup has been completed, the xplasma module contains
a list of items identifiable by unique names and integer id codes.
Data Access Routines can be used. The following is a partial list
of the types of routines that are provided:
* coordinate transformations between cartesian, cylindric, and
magnetic coordinate systems.
* interpolation of plasma parameters and field vector, and their
gradients, to target locations given in cartesian, cylindric
or magnetic coordinates.
* determination of location inside or outside the core plasma
boundary and/or limiter, and by what distance, given target
points in cartesian, cylindric or magnetic coordinates.
* metric tensor information.
* flux surface oriented statistics, such as Rmin, Rmax, Zmin, Zmax,
Bmin and Bmax on a given flux surface.
Many routines are **vectorized**, i.e. they accept vector arguments
and return vector results. Taking advantage of vector features is
good for code performance, on modern workstations as well as
traditional vector supercomputers.
(old f77 xplasma 1 documentation)
Associated with each xplasma dataset is a (30 character) label,
an "axisymmetry" flag, and a "scrape-off layer" flag. Optionally,
a timeslice time can also be associated with the dataset.
To retrieve the label and flags:
character*30 xplasma_label
integer axisymm_flag ! axisymmetry flag, =1 means axisymmetric
integer scrapeoff_flag ! scrapeoff layer flag, =1 means defined
call eq_ident(xplasma_label,axisymm_flag,scrapeoff_flag)
Notes:
Any meaning or interpretation of the label is up to the user (it
was set by a prior eqm_select(...) xplasma initialization call);
xplasma only uses the label for informational messages.
As of April 2003-- axisymm_flag=1 always.
The setting scrapeoff_flag=1 means that xplasma has a limiter or
vacuum vessel wall description, and, an (R,Z) grid that extends
beyond the plasma boundary is defined.
If a time has been associated with the current xplasma dataset, it
can be retrieved with the call:
real*8 ztime ! xplasma time, seconds.
call eq_time(ztime)
The xplasma time is user information only-- it is not used internally
by the xplasma library code.
(old f77 xplasma 1 documentation)
There are two types of named "xplasma" entities: "grids" and
"profiles". Each of these entities carries a unique name and
id number. Most of the xplasma software requires the id number
to be supplied. Applications should keep frequently used xplasma
id numbers on hand, for performance reasons, i.e. to minimize
time spent doing name lookup and translation.
Even so, the following name translation routines are available.
All names are case insensitive.
character*(*) zname ! name of entity
integer id ! integer entity id code
integer nlist ! length of list
character*(*) znames(nlist) ! list of names
integer idlist(nlist) ! list of entity id codes
integer ierr ! completion code, 0=OK
...
! get a grid id
call eq_ganum(zname,id) ! e.g.: call eq_ganum("rho",id)
! id=0 if translation fails
! get a profile function id
call eq_gfnum(zname,id) ! e.g.: call eq_gfnum("psi",id)
! id=0 if translation fails
! get a list of profile function ids
call eq_gflist(nlist,znames,idlist,ierr)
! ...ierr.ne.0 if any translation fails...
Reverse translations can also be done: given the id return the
name string:
call eq_get_aname(id,zname) ! get name of axis grid
call eq_get_fname(id,zname) ! get name of profile function
(old f77 xplasma 1 documentation)
The xplasma module contains a set of grids, each identifiable
! size of a grid
integer id_grid ! xplasma grid id (in)
integer isize ! size (no. of points) in grid (out)
call eq_ngrid(id_grid,isize)
! the grid itself
integer imax ! max size of grid (in)
integer igot ! actual size of grid (out)
real*8 zdata(imax) ! array for grid data (out)
integer ierr ! completion code, 0=OK (out)
call eq_grid(id_grid,zdata,imax,igot,ierr)
! grid "zone centers" -- if grid contains N pts, this is a
! sequence of N-1 values; the jth value is half way point j and
! point j+1 in the original (boundary oriented) grid.
call eq_grid_zc(id_grid,zdata,imax,igot,ierr)
! sizes of "common grids"...
integer inumrho ! no. of flux surface pts
integer inumchi ! no. of poloidal angle pts
integer inumphi ! no. of toroidal angle pts
integer inumR ! no. of R grid pts
integer inumZ ! no. of Z grid pts
call eq_ngrids(inumrho,inumchi,inumphi,inumR,inumZ)
! inumphi=1 is returned for axisymmetric cases. Otherwise, if
! any grid is undefined, a zero size is returned.
(old f77 xplasma 1 documentation)
A vectorized zone lookup routines is provided:
eq_nindx -- for a vector of values along a given grid, return
a vector of zone indices, with a warning flag set
if any of the values are out of range.
eq_nindx is intended for "binning". A returned index value of j
means that the corresponding input value is between grid
point j and grid point j+1.
eq_nindx returns j=0 for points that are out of range low, and
j=N for points that are out of range high.
Note that, if an axis is periodic, then, no point will ever be out
of range, as the software will apply 2*pi*n shifts as necessary to
bring the target value into the selected grid's normal range.
Periodicity is an attribute of the grid which is defined at setup
time (cf eqm_rho, eqm_chi, eqm_bbin, or eqm_uaxis).
further details: see the subtopic.
(old f77 xplasma 1 documentation)
Details on the "eq_nindx" subroutine call:
! input:
integer id_grid ! grid id code
integer ivec ! vector size
real*8 zvals(ivec) ! vector of input values
!
! the "zvals" are along the "id_grid" axis.
!
! output:
integer kindx(ivec) ! corresponding zone indices
integer iwarn ! warning flag, 0=normal
...
call eq_nindx(id_grid,ivec,zvals,kindx,iwarn)
Note that iwarn=0 indicates normal completion, no warning.
iwarn=+M means M points were out of range relative to the
grid indicated by id_grid.
For each value zvals(i), j=kindx(i) specifies the zone in
which zvals(i) lies, i.e. x(j).le.zvals(i).le.x(j+1), where
x(...) is the grid indicated by id_grid. If N is the size
of the grid, 1 <= j < N for all points zvals(i) that are
in range.
If zvals(i) is out of range low, kindx(i)=0 is returned
and the warning flag is incremented.
If zvals(i) is out of range high, kindx(i)=N is returned
and the warning flag is incremented.
The caller should check the warning flag value. However,
if "id_grid" is a "periodic" axis such as a poloidal or
toroidal angle coordinate, out-of-range errors will never
occur.
(old f77 xplasma 1 documentation)
The xplasma module supports 3 coordinate systems:
cylindric: (R,Z,phi)
cartesian: (x,y,z)
magnetic: (rho,chi,phi),
rho and chi as defined during setup
(xplasma makes no assumption about any
possible physical meaning of rho and chi).
A collection of coordinate transformation routines are
supported. The following is a summary:
(R,phi) -> (x,y)
subroutine eq_xcyl(ivec,R,phi,x,y)
subroutine eq_xcylcs(ivec,R,phi,x,y,cosphi,sinphi)
x=R*cos(phi), y=R*sin(phi), (z=Z)
(x,y) -> (R,phi)
subroutine eq_rcyl(ivec,x,y,R,phi)
subroutine eq_rcylcs(ivec,x,y,R,phi,cosphi,sinphi)
R=sqrt(x**2+y**2), phi=atan2(y,x), (z=Z)
In the following:
To have "chi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
(rho,chi,phi) -> (R,Z,phi)
subroutine eq_rzget(ivec,rho,chi,phi,R,Z,nregion,ierr)
R=R(rho,chi,phi) & Z=Z(rho,chi,phi)
by interpolation (phi ignored for axisymmetric
cases).
(rho,chi,phi) -> (x,y,z)
subroutine eq_xget(ivec,rho,chi,phi,x,y,z,nregion,ierr)
(call eq_rzget then eq_xcyl)
(R,Z,phi) -> (rho,chi,phi)
subroutine eq_inv(ivec,R,Z,phi,tol,rho,chi,nregion,ierr)
Newton's method with "fast map" initial guess
generator and using d[R,Z,phi]/d[rho,chi,phi].
Accuracy: if one uses eq_rzget to compute
(R',Z') from (rho,chi,phi), then,
max(|R-R'|,|Z-Z'|) <= tol*max(R,|Z|)
(x,y,z) -> (rho,chi,phi)
subroutine eqx_inv(ivec,x,y,z,tol,rho,chi,phi,nregion,ierr)
(call eq_rcyl, then eq_inv)
(old f77 xplasma 1 documentation)
All of the coordinate transformation routines have similar
arguments, drawn from the following set. Which are input and
which are output depend on context, except where noted.
Note that except for ivec and ierr, all arguments are vectors.
integer ivec ! (always input) vector dimensioning
real*8 R(ivec) ! major radius (cylindrical coordinate)
real*8 Z(ivec) ! elevation (cylindrical coordinate
! or cartesian coordinate) (z=Z).
real*8 phi(ivec) ! toroidal angle (cylindrical coordinate)
real*8 cosphi(ivec) ! cos(phi) (always output)
real*8 sinphi(ivec) ! sin(phi) (always output)
real*8 x(ivec) ! cartesian coordinate, x axis along phi=0
real*8 y(ivec) ! cartesian coordinate, perp to x & Z.
real*8 rho(ivec) ! radial flux coordinate
real*8 chi(ivec) ! poloidal angle coordinate
integer nregion(ivec) ! region code (always output)
! nregion(i)=1 means input position (i)
! is in the space mapped by (rho,chi,phi)
! nregion(i).gt.1 means it is outside the
! region mapped by magnetic coordinates.
integer ierr ! (always output) completion code, 0=OK
! ierr.ne.0 means a serious error.
! A point (i) being outside the magnetic
! coordinate mapped region does NOT cause
! ierr to be set.
(old f77 xplasma 1 documentation)
Several routines are available for finding useful minima and maxima:
(a) minimum and maximum "rho" coordinate
! output:
real*8 rho_axis ! "rho" at magnetic axis
real*8 rho_bdy ! "rho" at plasma boundary
...
call eq_rholim(rho_axis,rho_bdy)
(b) minimum and maximum R and Z ocurring on a flux surface
! input:
real*8 rho ! "rho" where min/max info is wanted
! output:
real*8 Rmin,Rmax ! minimum and maximum R on "rho" surface
real*8 Zmin,Zmax ! minimum and maximum Z on "rho" surface
integer ierr ! completion code (0=OK)
...
call eq_glimrz(rho,Rmin,Rmax,Zmin,Zmax,ierr)
(c) minimum and maximum B ocurring on a flux surface
! output:
real*8 Bmin,Bmax ! minimum and maximum mod(B)
...
call eq_glimb(rho,Bmin,Bmax,ierr)
...or...
real*8 phi ! input; use e.g. phi=0 for axisymmetric case
real*8 chi_Bmin,chi_Bmax ! ouput: angles of min & max B
call eq_glimb1(rho,phi,Bmin,chi_Bmin,Bmax,chi_Bmax,ierr)
(d) minimum and maximum R and Z of space enclosed by limiters
(i.e. minimum and maximum R and Z of scrape-off plasma).
! output:
integer itype ! xplasma modules limiter type
! =1: same as plasma boundary
! =2: circles & lines
! =3: piecewise linear contour
! =4: in box, fixed distance from bdy
! (for more information on limiters, see section on limiters in
! the description of xplasma setup routines).
...
call eq_bdlims(itype,Rmin,Rmax,Zmin,Zmax,ierr)
(e) minimum and maximum R and Z of (R,Z) grids defined in xplasma
module.
call eq_rzlims(Rmin,Rmax,Zmin,Zmax,ierr)
This gives the space over which B(R,Z), psi(R,Z), f(R,Z), etc.
are defined.
(f) elongation,triangularity,indentation,midplane intercepts for a
set of flux surfaces
The midplane intercepts are defined as the major radiaus at the height of the
centroid of the flux surface.
The rho value will be forced to be larger then 1.e-5*(rho_bdy-rho_axis).
Date: Wed, 22 Sep 1999 11:59:40 -0400
From: Glenn Bateman <bateman@fusion.physics.lehigh.edu>
Consider a toroidal magnetic surface (also called a flux surface).
Use the following variables
R_out = major radius to the outboard edge of the flux surface
that is, the largest value of major radius anywhere on the surface
R_in = major radius to the inboard edge (ie, smallest value of major
radius)
R_top = major radius to the top edge of the flux surface
that is, to the highest point on the flux surface cross section
R_bottom = major radius to the lowest point on the flux surface
height = distance between the elevations of the highest point
and the lowest point
R_dent_in = major radius to the most indented inboard part of the surface
in cases where the flux surface is bean shaped
with a dent on the inboard edge
Note: R_dent_in = R_in whenever the flux surface is not indented
R_dent_out = major radius to the most indented outboard part of the surface
r_minor = half width = (R_out - R_in ) / 2 (usually called minor radius)
r_major = ( R_out + R_in ) / 2 (usually called major radius)
elongation: kappa = height / ( R_out - R_in )
tiangularity: delta = ( r_major - min ( R_top, R_bottom ) ) / r_minor
indentation: indent = max ( ( R_dent_in - R_in ) / rminor ,
( R_dent_out - R_out ) / rminor )
Note:
For normal convex surfaces, indent = 0.0
For bean-shaped surfaces with indentation on the inboard edge, indent > 0
For bean-shaped surfaces with indentation on the outboard edge, indent < 0
For surfaces with the point of the triangle facing outward,
delta > 0.0, while if the triangle points inward, delta < 0.0
For vertically elongated surfaces, kappa > 1.0
For horizontally elongated surfaces, kappa < 1.0.
! input:
integer, intent(in) :: ivec ! number of surfaces
real*8, intent(in) :: rho(ivec) ! radial coordinate of flux surface
real*8, intent(in) :: phi(ivec) ! toroidal coordinate of flux surface
integer, intent(in) :: ntheta ! number of poloidal points to use around
! the flux surface or 0 for the default of 400
! output:
real*8, intent(out) :: elong(ivec) ! surface elongation
real*8, intent(out) :: triang(ivec) ! triangularity
real*8, intent(out) :: indent(ivec) ! indentation
real*8, intent(out) :: zmidp (ivec) ! height of flux surface centroid
real*8, intent(out) :: rmnmidp(ivec) ! inner midplane intercept at centroid height
real*8, intent(out) :: rmjmidp(ivec) ! outer midplane intercept at centroid height
real*8, intent(out) :: limits(9,ivec) ! surface limits used for elong,triang,indent
! 1 -> R_in
! 2 -> R_out
! 3 -> R_top
! 4 -> Z_top
! 5 -> R_bottom
! 6 -> Z_bottom
! 7 -> R_dent_in
! 8 -> R_dent_out
! 9 -> R_centroid
integer, intent(out) :: ier ! nonzero on error
...
call eq_surfgeo(ivec, rho, phi, ntheta, &
elong, triang, indent, zmidp, rmnmidp, rmjmidp, limits, ier)
(old f77 xplasma 1 documentation)
When an xplasma geometry is defined, splines are set up containing
estimates of enclosed plasma volume and enclosed cross-sectional area
as a function of flux surface. These estimates are accurate to machine
precision at the rho grid points, and well behaved inbetween.
Method: Gauss's theorem is exploited to derive accurate expressions
for enclosed volume (area) as 1d integrals at each flux surface rather
than using 2d integrals convering the volume (area).
To access the results of these fits (Volume, d[Volume]/drho,
d2[Volume]/drho2, Area, d[Area]/drho, d2[Area]/drho2):
! input:
integer ivec ! input data vector dimension
real*8 x(ivec) ! vector of "x" values
integer iwant ! 0: value, 1: df/drho, 2: d2f/drho2
! output:
real*8 zval(ivec) ! interpolation results, returned
integer ierr ! completion code, 0=OK
...
call eq_volume(ivec,rho,iwant,zval,ierr) ! Volume info. (m**3)
call eq_area(ivec,rho,iwant,zval,ierr) ! Xsect. Area info. (m**2)
(old f77 xplasma 1 documentation)
(for information on translating an integer id code into a name, or
vice-versa, see "name_translation")
Given a function id integer code, the "eq_fun" call can be used to get
a description of the available fundation data: fits to magnetic
coordinates and dimenstionality; fits to cylindric coordinates and
dimensionality, as follows.
! input:
integer id_fun ! function id code
! output:
integer magdim ! dimensionality vs. mag. coordinates
integer magfit ! type of fit
integer cyldim ! dimensionality vs. cyl. coordinates
integer cylfit ! type of fit
integer iwarn ! 0=OK, set if function is "internal".
integer ierr ! 0=OK, set if invalid id code is passed
...
call eq_fun(id_fun,magdim,magfit,cyldim,cylfit,iwarn,ierr)
the output arguments should be interpreted as follows:
magdim=0 => no fit to magnetic coordinates
magdim=1 => f(rho) fit available -- flux function
magdim=2 => f(rho,chi) fit available -- f(flux & pol. angle)
magdim=3 => f(rho,chi,phi) fit available.
magfit=0 => piecewise linear fit, f continuous
magfit=1 => piecewise cubic Hermite, f' continuous
magfit=2 => piecewise cubic spline, f'' continuous
cyldim=0 => no fit to cylindric coordinates
cyldim=2 => f(R,Z) fit available
cyldim=3 => f(R,Z,phi) fit available
cylfit=0 => piecewise linear fit, f continuous
cylfit=1 => piecewise cubic Hermite, f' continuous
cylfit=2 => piecewise cubic spline, f'' continuous
(old f77 xplasma 1 documentation)
Given an appropriate function id, these routines return the
indicated independent coordinate grid (axis) id. In all cases,
if the function does not exist or does not have the type of
independent coordinate grid requested, 0 is returned.
input:
integer id_fun ! function id code
output:
integer id_grid ! indep. coord. grid id, or zero (0)
! 0 returned if no such grid or function
call eq_fun_rhogrid(id_fun,id_grid) ! rho grid id
! OK for f(rho), f(rho,chi), f(rho,chi,phi)
call eq_fun_chigrid(id_fun,id_grid) ! chi grid id
! OK for f(rho,chi), f(rho,chi,phi)
call eq_fun_Rgrid(id_fun,id_grid) ! R grid id
! OK for f(R,Z), f(R,Z,phi)
call eq_fun_Zgrid(id_fun,id_grid) ! Z grid id
! OK for f(R,Z), f(R,Z,phi)
if xplasma is extended for non-axisymmetric applications
some day, then:
call eq_fun_phigrid(id_fun,id_grid) ! phi grid id
! OK for f(R,Z,phi), f(rho,chi,phi)
(old f77 xplasma 1 documentation)
The following simplified routines may be useful for interpolation
of profile functions of "rho" only:
(a) for interpolation on a single profile f(rho) prepared previously
by an eqm_rhofun call:
! input:
integer ivec ! input data vector dimension
real*8 rho(ivec) ! vector of "rho" values
integer id_fun ! xplasma id of profile function f(rho)
integer iwant ! 0: value, 1: df/drho, 2: d2f/drho2
! output:
real*8 zval(ivec) ! interpolation results, returned
integer ierr ! completion code, 0=OK
...
call eq_rgetf(ivec,rho,id_fun,iwant,zval,ierr)
The error code will be set if either an invalid function id is
passed or if some of the "rho" values are out of range.
Note: iwant=2 is valid only if f(rho) is a cubic spline.
(b) for interpolation on a multiple profiles f(rho). arguments are as
above, except:
! input:
integer nlist ! length of list of xplasma ids
integer id_funs(nlist) ! list of xplasma function ids
integer ivecd ! output vector dimension
! output:
real*8 zvals(ivecd,nlist) ! interpolation results, all fcns
...
call eq_rgetff(ivec,rho,nlist,id_funs,iwant,ivecd,
> zvals,ierr)
(c) for interpolation on a single profile f(x) prepared previously
by an eqm_f1d call:
! input:
integer ivec ! input data vector dimension
real*8 x(ivec) ! vector of "x" values
integer id_fun ! xplasma id of profile function f(x)
integer iwant ! 0: value, 1: df/dx, 2: d2f/dx2
! output:
real*8 zval(ivec) ! interpolation results, returned
integer ierr ! completion code, 0=OK
...
call eq_xgetf(ivec,rho,id_fun,iwant,zval,ierr)
The error code will be set if either an invalid function id is
passed or if some of the "x" values are out of range.
Note: iwant=2 is valid only if f(x) is a cubic spline.
(old f77 xplasma 1 documentation)
Two combined interpolation/smooth routines are provided, one for
ordinary profiles, and one for integrated profiles (such as are output
by several NTCC physics modules).
For ordinary profiles: eq_rget_sm
For integrated profiles: eq_rget_binsm
subroutine eq_rget_sm(inrho,zrho,id,zdelta,zval,ierr)
!
! return a profile interpolated and then smoothed.
! caution: not recommended for volume- or area-integrated profiles; see
! eq_rget_bin(...) or eq_rget_binsm(...)
!
implicit NONE
integer, intent(in) :: inrho ! target gridsize, must be .gt.1
real*8, intent(in) :: zrho(inrho) ! target grid-- strict ascending order
integer, intent(in) :: id ! function id of f(rho) profile
real*8, intent(in) :: zdelta ! smoothing parameter
! zdelta defines the base half-width of a triangular weighting function
! used for smoothing. The smoothed output profile at each rho is
! weighted average of the original function over the range
! (rho-zdelta,rho+zdelta) with weight 1/zdelta at rho linearly
! declining to weight 0 at the end points. (area of weighting
! triangle is unity).
real*8, intent(out) :: zval(inrho) ! results of smoothed interpolation
! zval(rho) is a weighted average of the original profile over the
! range [rho-zdelta,rho+zdelta] with a triangular weighting
! function whose weight goes to 0 at the interval enpoints.
integer ierr ! completion code 0=OK
subroutine eq_rget_bin(inrho,zrho,id,iint,zval,ierr)
!
! given an integrated profile function id,
! return an integrated profile on the user's grid.
!
! if the interpolation order is >1, just do an interpolation.
! if the interpolation order is =1 (piecewise linear), do an integration
! of the step function of the inferred density
!
implicit NONE
integer, intent(in) :: inrho ! target gridsize, must be .gt.1
real*8, intent(in) :: zrho(inrho) ! target grid-- strict ascending order
integer, intent(in) :: id ! function id of f(rho) profile
integer, intent(in) :: iint ! data type id
! iint=1 -- area integral profile being smoothed --
! Examples: current density profiles
! iint=2 -- volume integral profile being smoothed --
! Examples: density, energy density, ptcl/power/momentum source
! functions, ...
!
real*8, intent(out) :: zval(inrho) ! results of interpolation/integration
integer ierr ! completion code 0=OK
subroutine eq_rget_binsm(inrho,zrho,id,zdelta,iint,zval,ierr)
!
! given an integrated profile function id,
!
! return an integrated profile, smoothed onto the user's grid
! from the step-function defined by binning the density on the function's
! original grid.
!
implicit NONE
integer, intent(in) :: inrho ! target gridsize, must be .gt.1
real*8, intent(in) :: zrho(inrho) ! target grid-- strict ascending order
integer, intent(in) :: id ! function id of f(rho) profile
real*8, intent(in) :: zdelta ! smoothing parameter
! if <0, zdelta=delta(rho) assumed.
integer, intent(in) :: iint ! data type id
! iint=1 -- area integral profile being smoothed --
! Examples: current density profiles
! iint=2 -- volume integral profile being smoothed --
! Examples: density, energy density, ptcl/power/momentum source
! functions, ...
!
! for iint > 0, the data is binned on the original grid, but smoothed
! on the user grid. The unsmoothed unintegrated profile (e.g. density
! profile) becomes a step function with step widths matching the original
! grid of the data. It is smoothed using a triangular weighting function
! whose base half-width (smoothing parameter) is zdelta.
!
! If the smoothing parameter is too small, the step-
! function-like behavior of the binned density profile will remain
! evident in the result
!
! zdelta =~ delta(rho)
!
! is recommended, where delta(rho) is the stepsize of the function's
! underlying rho grid. The recommended value is used if the passed
! value for zdelta is < 0. If zdelta=0, no smoothing occurs.
! if zdelta > 0, smoothing occurs using the user specified zdelta.
!
! note that the result is independent of the interpolating function
! xplasma has for the (integrated) profile function #id.
!
! NOTE: smoothing integrated profiles directly, e.g. with eq_rget_sm,
! is hazardous, because, if d/dV of the smoothed result is evaluated
! near the axis it will generally have a pole singularity there.
real*8, intent(out) :: zval(inrho) ! results of smoothed interpolation
! zval(rho) is a weighted average of the original profile over the
! range [rho-zdelta,rho+zdelta] with a triangular weighting
! function whose weight goes to 0 at the interval endpoints.
integer ierr ! completion code 0=OK
(old f77 xplasma 1 documentation)
When a given function or set of functions is known to be represented
in f(rho,chi) form, the fastest interpolation routines are:
eq_frhochi -- for a given set of (rho,chi) target points,
return the values of all functions on the function
list.
eq_grhochi -- for a given set of (rho,chi) target points, return
the df/drho and df/dchi of all functions on the
function list.
eq_hrhochi -- for a given set of (rho,chi) target points, return
a user specified combination of values and derivatives
(up to 2nd order derivatives). The user specifies what
is wanted via the "ictwant" integer array.
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 zrho(ivec) ! rho of target points
real*8 zchi(ivec) ! chi of target points
integer nlist ! number of functions in function list
integer ifcns(nlist) ! function ids of functions wanted
!
integer ivecd ! 1st (vector) dimension of output buffer
! eq_frhochi output:
real*8 fans(ivecd,nlist) ! function values returned
! fans(j,k) = value of k'th function
! at zrho(j),zchi(j)
! eq_grhochi output:
real*8 gans(ivecd,2,nlist) ! df/drho, df/dchi values returned
! gans(j,1,k) = df/drho of k'th function
! at zrho(j),zchi(j)
! gans(j,2,k) = df/dchi of k'th function
! at zrho(j),zchi(j)
! output (for both):
integer ierr ! completion code, 0=OK
... ...
call eq_frhochi(ivec,zrho,zchi,nlist,ifcns,ivecd,fans,ierr)
... ...
call eq_grhochi(ivec,zrho,zchi,nlist,ifcns,ivecd,gans,ierr)
To have "zchi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
(old f77 xplasma 1 documentation)
This routine has the same arguments as "eq_frhochi" and "eq_grhochi"
except this input array:
integer ictwant(6) ! selector array
!
! each element of ictwant(1:6) should be 0 or 1
! use 1 to indicate a desired item; use 0 otherwise:
!
! ictwant(1) -- function values
! ictwant(2) -- functions' df/drho values
! ictwant(3) -- functions' df/dchi values
! ictwant(4) -- functions' d2f/drho2 values
! ictwant(5) -- functions' d2f/dchi2 values
! ictwant(6) -- functions' d2f/drhodchi values
and the output array is interpreted differently:
REAL*8 zans(ivecd,nlist*max(1,sum(ictwant))) ! results
!
! ** let ictnum = # of non-zero elements of ictwant.
! (ictnum=sum(ictwant)). Then...
! zans(1:ivec,1) = value or derivative for function id iflist(1)
! indicated by first non-zero value of ictwant(...)
! zans(1:ivec,2) = value or derivative for function id iflist(1)
! indicated by second non-zero value of ictwant(...)
! ...
! zans(1:ivec,ictnum) = value or derivative for function id iflist(1)
! indicated by last non-zero value of ictwant(...)
!
! or generally,
! for 1 .le. j .le. nlist
! for 1 .le. k .le. ictnum,
! ...
! zans(1:ivec,(j-1)*ictnum+k) = value or derivative for function
! id iflist(j), corresponding to k'th
! non-zero element of ictwant(...)
!
... ...
call eq_hrhochi(ivec,zrho,zchi,nlist,ifcns, &
& ictwant,ivecd,gans,ierr)
To have "zchi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
(old f77 xplasma 1 documentation)
When a given function or set of functions is known to be represented
in f(R,Z) form, the fastest interpolation routines are:
eq_fRZ -- for a given set of (R,Z) target points, return the
values of all functions on the function list.
eq_gRZ -- for a given set of (R,Z) target points, return
the df/dR and df/dZ of all functions on the function
list.
eq_hRZ -- for a given set of (R,Z) target points, return
a user specified set of values and derivatives up
to 2nd order.
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 zR(ivec) ! R of target points
real*8 zZ(ivec) ! Z of target points
integer nlist ! number of functions in function list
integer ifcns(nlist) ! function ids of functions wanted
!
integer ivecd ! 1st (vector) dimension of output buffer
! eq_fRZ output:
real*8 fans(ivecd,nlist) ! function values returned
! fans(j,k) = value of k'th function
! at zR(j),zZ(j)
! eq_gRZ output:
real*8 gans(ivecd,2,nlist) ! function df/dR, df/dZ values returned
! gans(j,1,k) = df/dR of k'th function
! at zR(j),zZ(j)
! gans(j,2,k) = df/dZ of k'th function
! at zR(j),zZ(j)
! output (for both):
integer ierr ! completion code, 0=OK
... ...
call eq_fRZ(ivec,zR,zZ,nlist,ifcns,ivecd,fans,ierr)
... ...
call eq_gRZ(ivec,zR,zZ,nlist,ifcns,ivecd,gans,ierr)
(old f77 xplasma 1 documentation)
This routine has the same arguments as "eq_fRZ" and "eq_gRZ"
except this input array:
integer ictwant(6) ! selector array
!
! each element of ictwant(1:6) should be 0 or 1
! use 1 to indicate a desired item; use 0 otherwise:
!
! ictwant(1) -- function values
! ictwant(2) -- functions' df/dR values
! ictwant(3) -- functions' df/dZ values
! ictwant(4) -- functions' d2f/dR2 values
! ictwant(5) -- functions' d2f/dZ2 values
! ictwant(6) -- functions' d2f/dRdZ values
and the output array is interpreted differently:
REAL*8 zans(ivecd,nlist*max(1,sum(ictwant))) ! results
!
! ** let ictnum = # of non-zero elements of ictwant.
! (ictnum=sum(ictwant)). Then...
! zans(1:ivec,1) = value or derivative for function id iflist(1)
! indicated by first non-zero value of ictwant(...)
! zans(1:ivec,2) = value or derivative for function id iflist(1)
! indicated by second non-zero value of ictwant(...)
! ...
! zans(1:ivec,ictnum) = value or derivative for function id iflist(1)
! indicated by last non-zero value of ictwant(...)
!
! or generally,
! for 1 .le. j .le. nlist
! for 1 .le. k .le. ictnum,
! ...
! zans(1:ivec,(j-1)*ictnum+k) = value or derivative for function
! id iflist(j), corresponding to k'th
! non-zero element of ictwant(...)
!
... ...
call eq_hRZ(ivec,zR,zZ,nlist,ifcns, &
& ictwant,ivecd,zans,ierr)
(old f77 xplasma 1 documentation)
A set of routines exists to find values and gradients of a
set of functions, whose representation might be known a priori
to the caller.
There are 3 series of functions:
eqxyz_*get -- given vectors of (x,y,z) coordinates, return
function values, gradients (d/dx,d/dy,d/dz),
Bfield vector, and Bfield gradient tensor.
(rho,chi,phi) vectors can also be obtained.
eqrz_*get -- given vectors of (R,Z,phi) coordinates, return
function values, gradients (d/dR,d/dZ,1/R*d/dphi),
Bfield vector, and Bfield gradient tensor.
(rho,chi,phi) vectors can also be obtained.
eq_*get -- given vectors of (rho,chi,phi), return function
values, gradients, Bfield vector, and Bfield
gradient tensor. (R,Z,phi) vectors can also
be obtained. The gradients and field can be
returned in cylindric- or flux-surface aligned
coordinates:
c if irzmode=1: (df/dR,df/dZ,(1/R)*df/dphi) @ (rho,chi,phi)
c if irzmode=0: (df/dsperp,df/dschi,(1/R)*df/dphi) @ (rho,chi,phi)
where sperp denotes ds in a flux durface normal
direction, and dschi denotes ds tangential to the
surface in the poloidal plane. Similarly, the
the field will be returned with irzmode=1 as
(BR,BZ,Bphi), or (0,Bchi,Bphi) for irzmode=0.
(old f77 xplasma 1 documentation)
(1) it is faster to use eq_frz or eq_frhochi, if the function
representation is known a priori, then to use eq*get*. However,
eq*get* have the "smarts" to define the field and gradients in
various coordinate systems, whereas eq_frz and eq_frhochi just
return function values and derivatives.
(2) vectorization and function grouping...
Performance is improved both by using vectors of target points
rather than individual target points, and, by evaluating groups
of functions together, rather than each function individually.
On cache oriented workstations, for a fixed total number of
evaluations, we have seen factors of 25 (!) performance difference
by evaluating individual functions one target point at a time, vs.
evaluating groups of 10 functions in a single calls with target
vectors of length 100. (This of course gives a 1000:1 difference
in the amount of data returned by a single interpolation call).
We think we are seeing the power of workstation data and
instruction caches in these performace figures.
Tests were done on an Alpha 500au running DEC UNIX / compaq
fortran, and on an Intel P-II 400MHz Linux running fujitsu
fortran -- with similar scaling results.
(DEC alpha 500au results):
jupiter.pppl.gov 7 Jun 2000:
500,000 function evaluations (10 functions x 50,000 target pts):
cpu time, seconds
function evaluations per cal
vector Nfcn=1 Nfcn=10
size
1 15.24 2.52
10 4.88 .77
100 4.07 .61
(old f77 xplasma 1 documentation)
generic interface (f90):
For routines involving flux coordinates:
To have "zchi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
use xplasma_get
...
call eqxyz_get(...)
specific interfaces (f77/f90):
arguments used by this series of routines:
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 xx(ivec) ! x coordinate, each vector element
real*8 yy(ivec) ! y coordinate, each vector element
real*8 zz(ivec) ! z coordinate, each vector element
! output:
real*8 zrho(ivec) ! rho coordinate, each vector element
real*8 zchi(ivec) ! chi coordinate, each vector element
real*8 zphi(ivec) ! phi coordinate, each vector element
! input:
real*8 ztol ! rel. tolerance for coord. mapping
! (rho,chi,phi) maps to (x',y',z')
! with max(|x-x'|,|y-y'|,|z-z'|)
! .le. tol*[R of mag. axis].
! output:
real*8 bvec(3,ivec) ! field vector at each target point
real*8 gbtensr(3,3,ivec) ! gbtensr(1:3,j,k)=grad(bvec(j,k))
! input:
integer ifcn ! a single fcn id, or...
integer nlist ! no. of fcns to evaluate
integer ifcns(nlist) ! list of fcn ids
integer ivecd ! vector dimension for output arrays
! output:
real*8 fval(ivec) ! function values, (single function)
real*8 fgrad(3,ivec) ! function gradient (single function)
! ..or..
real*8 fvals(ivecd,nlist) ! function values returned
real*8 fgrads(3,ivecd,nlist) ! function gradients returned.
integer nregion(ivec) ! region code for each target point
! for target point j:
! nregion(j)=1: core plasma
! nregion(j)=2: scrape off layer
! nregion(j)=3: outside limiter
! nregion(j)=4: beyond (R,Z) grid
integer ierr ! completion code, 0=OK
! ierr.ne.0 means a "serious error".
! note: B field is [Bx,By,Bz].
! all gradients are: [d/dx,d/dy,d/dz].
--->(return values of a single function)
(f only)
CALL eqxyz_fget(ivec,xx,yy,zz,ztol,ifcn,fval,nregion,ierr)
(f, grad(f))
CALL eqxyz_fgrad(ivec,xx,yy,zz,ztol,ifcn,
> fval,fgrad,nregion,ierr)
(f, (rho,chi,phi))
CALL eqxyzmap_fget(ivec,xx,yy,zz,zrho,zchi,zphi,ztol,
> ifcn,fval,nregion,ierr)
(f, grad(f), (rho,chi,phi))
CALL eqxyzmap_fgrad(ivec,xx,yy,zz,zrho,zchi,zphi,ztol,
> ifcn,fval,fgrad,nregion,ierr)
-->(return values of multiple functions)
(f only)
CALL eqxyz_ffget(ivec,xx,yy,zz,ztol,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f))
CALL eqxyz_ffgrad(ivec,xx,yy,zz,ztol,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(f, (rho,chi,phi))
CALL eqxyzmap_ffget(ivec,xx,yy,zz,zrho,zchi,zphi,
> ztol,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), (rho,chi,phi))
CALL eqxyzmap_ffgrad(ivec,xx,yy,zz,zrho,zchi,zphi,
> ztol,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
-->(return values of B field only)
(B only)
CALL eqxyz_bget(ivec,xx,yy,zz,ztol,bvec,nregion,ierr)
(B, grad(B))
CALL eqxyz_bgrad(ivec,xx,yy,zz,ztol,bvec,gbtensr,nregion,
> ierr)
(B, (rho,chi,phi))
CALL eqxyzmap_bget(ivec,xx,yy,zz,zrho,zchi,zphi,ztol,
> bvec,nregion,ierr)
(B, grad(B), (rho,chi,phi))
CALL eqxyzmap_bgrad(ivec,xx,yy,zz,zrho,zchi,zphi,ztol,
> bvec,gbtensr,nregion,ierr)
-->(return values of B field & multiple functions)
(f, B only)
CALL eqxyz_bffget(ivec,xx,yy,zz,ztol,bvec,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), B, grad(B))
CALL eqxyz_bffgrad(ivec,xx,yy,zz,ztol,bvec,gbtensr,
> nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(f, B, (rho,chi,phi))
CALL eqxyzmap_bffget(ivec,xx,yy,zz,zrho,zchi,zphi,
> ztol,bvec,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), B, grad(B), (rho,chi,phi))
CALL eqxyzmap_bffgrad(ivec,xx,yy,zz,zrho,zchi,zphi,
> ztol,bvec,gbtensr,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(old f77 xplasma 1 documentation)
generic interface (f90):
use xplasma_get
...
call eqrz_get(...)
For routines using flux coordinate:
To have "zchi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
specific interfaces (f77/f90):
arguments used by this series of routines:
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 zR(ivec) ! R coordinate, each vector element
real*8 zZ(ivec) ! Z coordinate, each vector element
real*8 zphi(ivec) ! phi coordinate, each vector element
! output:
real*8 zrho(ivec) ! rho coordinate, each vector element
real*8 zchi(ivec) ! chi coordinate, each vector element
! input:
real*8 ztol ! rel. tolerance for coord. mapping
! (rho,chi,phi) maps to (x',y',z')
! with max(|x-x'|,|y-y'|,|z-z'|)
! .le. tol*[R of mag. axis].
! output:
real*8 bvec(3,ivec) ! field vector at each target point
real*8 gbtensr(3,3,ivec) ! gbtensr(1:3,j,k)=grad(bvec(j,k))
! input:
integer ifcn ! a single fcn id, or...
integer nlist ! no. of fcns to evaluate
integer ifcns(nlist) ! list of fcn ids
integer ivecd ! vector dimension for output arrays
! output:
real*8 fval(ivec) ! function values, (single function)
real*8 fgrad(3,ivec) ! function gradient (single function)
! ..or..
real*8 fvals(ivecd,nlist) ! function values returned
real*8 fgrads(3,ivecd,nlist) ! function gradients returned.
integer nregion(ivec) ! region code for each target point
! for target point j:
! nregion(j)=1: core plasma
! nregion(j)=2: scrape off layer
! nregion(j)=3: outside limiter
! nregion(j)=4: beyond (R,Z) grid
integer ierr ! completion code, 0=OK
! ierr.ne.0 means a "serious error".
! note: B field is [BR,BZ,Bphi].
! all gradients are: [d/dR,d/dZ,(1/R)d/dphi].
--->(return values of a single function)
(f only)
CALL eqrz_fget(ivec,zR,zZ,zphi,ztol,ifcn,fval,nregion,ierr)
(f, grad(f))
CALL eqrz_fgrad(ivec,zR,zZ,zphi,ztol,ifcn,
> fval,fgrad,nregion,ierr)
(f, (rho,chi))
CALL eqrzmap_fget(ivec,zR,zZ,zphi,zrho,zchi,ztol,
> ifcn,fval,nregion,ierr)
(f, grad(f), (rho,chi))
CALL eqrzmap_fgrad(ivec,zR,zZ,zphi,zrho,zchi,ztol,
> ifcn,fval,fgrad,nregion,ierr)
-->(return values of multiple functions)
(f only)
CALL eqrz_ffget(ivec,zR,zZ,zphi,ztol,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f))
CALL eqrz_ffgrad(ivec,zR,zZ,zphi,ztol,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(f, (rho,chi))
CALL eqrzmap_ffget(ivec,zR,zZ,zphi,zrho,zchi,
> ztol,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), (rho,chi))
CALL eqrzmap_ffgrad(ivec,zR,zZ,zphi,zrho,zchi,
> ztol,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
-->(return values of B field only)
(B only)
CALL eqrz_bget(ivec,zR,zZ,zphi,ztol,bvec,nregion,ierr)
(B, grad(B))
CALL eqrz_bgrad(ivec,zR,zZ,zphi,ztol,bvec,gbtensr,nregion,
> ierr)
(B, (rho,chi))
CALL eqrzmap_bget(ivec,zR,zZ,zphi,zrho,zchi,ztol,
> bvec,nregion,ierr)
(B, grad(B), (rho,chi))
CALL eqrzmap_bgrad(ivec,zR,zZ,zphi,zrho,zchi,ztol,
> bvec,gbtensr,nregion,ierr)
-->(return values of B field & multiple functions)
(f, B only)
CALL eqrz_bffget(ivec,zR,zZ,zphi,ztol,bvec,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), B, grad(B))
CALL eqrz_bffgrad(ivec,zR,zZ,zphi,ztol,bvec,gbtensr,
> nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(f, B, (rho,chi))
CALL eqrzmap_bffget(ivec,zR,zZ,zphi,zrho,zchi,
> ztol,bvec,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), B, grad(B), (rho,chi))
CALL eqrzmap_bffgrad(ivec,zR,zZ,zphi,zrho,zchi,
> ztol,bvec,gbtensr,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(old f77 xplasma 1 documentation)
generic interface (f90):
use xplasma_get
...
call eq_get(...)
To have "zchi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
specific interfaces (f77/f90):
arguments used by this series of routines:
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 zrho(ivec) ! rho coordinate, each vector element
real*8 zchi(ivec) ! chi coordinate, each vector element
real*8 zphi(ivec) ! phi coordinate, each vector element
! output:
real*8 zR(ivec) ! R coordinate, each vector element
real*8 zZ(ivec) ! Z coordinate, each vector element
! input:
integer irzmode ! =1: output B & grads in (R,Z,phi)
! =0: output B & grads in (sperp,chi,phi)
! output:
real*8 bvec(3,ivec) ! field vector at each target point
real*8 gbtensr(3,3,ivec) ! gbtensr(1:3,j,k)=grad(bvec(j,k))
! input:
integer ifcn ! a single fcn id, or...
integer nlist ! no. of fcns to evaluate
integer ifcns(nlist) ! list of fcn ids
integer ivecd ! vector dimension for output arrays
! output:
real*8 fval(ivec) ! function values, (single function)
real*8 fgrad(3,ivec) ! function gradient (single function)
! ..or..
real*8 fvals(ivecd,nlist) ! function values returned
real*8 fgrads(3,ivecd,nlist) ! function gradients returned.
integer nregion(ivec) ! region code for each target point
! for target point j:
! nregion(j)=1: core plasma
! nregion(j)=2: scrape off layer
! nregion(j)=3: outside limiter
! nregion(j)=4: beyond (R,Z) grid
integer ierr ! completion code, 0=OK
! ierr.ne.0 means a "serious error".
! note: if irzmode.eq.1:
! B field is [BR,BZ,Bphi].
! all gradients are: [d/dR,d/dZ,(1/R)d/dphi].
! if irzmode.eq.0:
! B field is [0,Bchi,Bphi]
! all gradients are: [d/dsperp,d/dschi,(1/R)d/dphi]
! where dsperp is ds in the direction normal to the
! flux surface, and,
! dschi is ds in the direction tangential to
! the flux surface in the poloidal plane.
--->(return values of a single function)
(f only)
CALL eq_fget(ivec,zrho,zchi,zphi,irzmode,ifcn,fval,nregion,
> ierr)
(f, grad(f))
CALL eq_fgrad(ivec,zrho,zchi,zphi,irzmode,ifcn,
> fval,fgrad,nregion,ierr)
(f, (R,Z))
CALL eqmap_fget(ivec,zrho,zchi,zphi,zR,zZ,irzmode,
> ifcn,fval,nregion,ierr)
(f, grad(f), (R,Z))
CALL eqmap_fgrad(ivec,zrho,zchi,zphi,zR,zZ,irzmode,
> ifcn,fval,fgrad,nregion,ierr)
-->(return values of multiple functions)
(f only)
CALL eq_ffget(ivec,zrho,zchi,zphi,irzmode,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f))
CALL eq_ffgrad(ivec,zrho,zchi,zphi,irzmode,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(f, (R,Z))
CALL eqmap_ffget(ivec,zrho,zchi,zphi,zR,zZ,
> irzmode,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, (R,Z))
CALL eqmap_ffgrad(ivec,zrho,zchi,zphi,zR,zZ,
> irzmode,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
-->(return values of B field only)
(B only)
CALL eq_bget(ivec,zrho,zchi,zphi,irzmode,bvec,nregion,ierr)
(B, grad(B))
CALL eq_bgrad(ivec,zrho,zchi,zphi,irzmode,bvec,gbtensr,nregion,
> ierr)
(B, (R,Z))
CALL eqmap_bget(ivec,zrho,zchi,zphi,zR,zZ,irzmode,
> bvec,nregion,ierr)
(B, grad(B), (R,Z))
CALL eqmap_bgrad(ivec,zrho,zchi,zphi,zR,zZ,irzmode,
> bvec,gbtensr,nregion,ierr)
-->(return values of B field & multiple functions)
(f, B only)
CALL eq_bffget(ivec,zrho,zchi,zphi,irzmode,bvec,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), B, grad(B))
CALL eq_bffgrad(ivec,zrho,zchi,zphi,irzmode,bvec,gbtensr,
> nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(f, B, (R,Z))
CALL eqmap_bffget(ivec,zrho,zchi,zphi,zR,zZ,
> irzmode,bvec,nlist,ifcns,
> ivecd,fvals,nregion,ierr)
(f, grad(f), B, grad(B), (R,Z))
CALL eqmap_bffgrad(ivec,zrho,zchi,zphi,zR,zZ,
> irzmode,bvec,gbtensr,nlist,ifcns,
> ivecd,fvals,fgrads,nregion,ierr)
(old f77 xplasma 1 documentation)
The fortran-90 module
use xplasma_get
can be employed to allow the generic names
call eq_get(...)
call eqrz_get(...)
call eqxyz_get(...)
for all of the interpolation routines described in the neighbouring
sections, and, the R4_ versions of the routines as well.
The xplasma_get module contains a description of all the specific
calls. The compiler decides which one to call by analyzing the
calling arguments.
Caution#1: "xplasma_get" and "xplasma_calls" cannot both be used
in the same routine;
Caution#2: there exist functionally correct calls to the software
for which the fortran-90 compiler will fail to match up the proper
routine, giving a compile-time error instead.
The use of this module is optional. In its absence, full
subroutine names must be used.
(old f77 xplasma 1 documentation)
XPLASMA now supports a generic facility for creating and accessing lists.
Each list has a name (max 32 characters, one alphanumeric word, treated
as uppercase), a label (max 60 characters), and a size which specifies
the number of elements in the list.
Each list element has a name (max 32 characters, one alphanumeric word,
treated as uppercase), and the following associated data:
a character string (max 60 characters) in addition to the name
a floating point number
an integer.
Lists can be used to communicate such information as:
-> a list of plasma spacies and the Z and A values of each species:
name of example list: TR_THERMAL_SPECIES, size = 3
(label: "list of thermal ion species")
name character data floating data integer data
H (blank) 1.000 1
D (blank) 2.000 1
HE4 (blank) 4.000 2
interpretation: floating data gives "A", integer gives "Z".
-> a timestep
name of example list: TR_TIMESTEP, size = 2
(label: "timestep for TRANSP sources")
name character data floating data integer data
TR_TS1 S 1.010 0
TR_TS2 S 1.020 0
interpretation: character data gives physical units, floating data
gives actual time values.
-> a collection of related profile data items
name of example list: TR_EHEAT, size = 5
(label: "integrated electron heating profiles")
name character data floating data integer data
TR_PBE W 3.073e4 310
TR_PFE W 0.000 311
TR_PEICRF W 0.000 312
TR_PEECRF W 0.000 313
TR_PELH W 0.000 314
interpretation: a list of heating profiles. Character data gives
physical units, floating data gives total amount (power, W, in this
context), and the integer gives XPLASMA id of the interpolating
function f(rho).
The methods for finding and accessing lists are described here. Methods
for creating lists and list contents are described under "Setup Routines".
(old f77 xplasma 1 documentation)
To get the number of lists currently known to XPLASMA:
integer :: num_lists ! value returned by subroutine call
call eq_nlist(num_lists)
(old f77 xplasma 1 documentation)
To get the names and ids of *all* lists currently known to XPLASMA:
! input:
integer :: num_lists ! should match value returned by eq_nlists
! output:
character*32 :: list_names(num_lists) ! names of lists
integer :: list_ids(num_lists) ! list id codes
integer :: ierr ! completion code (0=OK)
call eq_lists(num_lists,list_names,list_ids,ierr)
The list of names is retunred in alphabetic order.
To refer to a specific list, its id code is needed.
(old f77 xplasma 1 documentation)
To get the number of lists currently known to XPLASMA which contain
XPLASMA profile data:
integer :: num_lists ! value returned by subroutine call
call eq_nlists(num_lists)
(old f77 xplasma 1 documentation)
To get the names and ids of *all* lists containing XPLASMA profile data:
! input:
integer :: num_lists ! should match value returned by eq_nlists
! output:
character*32 :: list_names(num_lists) ! names of lists
integer :: list_ids(num_lists) ! list id codes
integer :: ierr ! completion code (0=OK)
call eq_lists(num_lists,list_names,list_ids,ierr)
The list of names is retunred in alphabetic order.
To refer to a specific list, its id code is needed.
(old f77 xplasma 1 documentation)
To find the list_id associated with a specific list name:
! input:
character*32 :: list_name ! name of a specific list
! output:
integer :: list_id ! corresponding id code
call eq_glistnum(list_name,list_id)
If there is no such list in the current XPLASMA, list_id = 0 is returned.
(old f77 xplasma 1 documentation)
To find the size (number of elements) of a specific list:
! input:
integer :: list_id ! id of an existing list
! output:
integer :: isize ! list size (no. of elements)
call eq_list_size(list_id,list_size)
If the id is invalid, list_size = 0 is returned.
(old f77 xplasma 1 documentation)
To find them maximum number of elements (isze) of any list:
! output:
integer :: max_size
call eq_list_maxSize(max_size)
If there are no lists, max_size = 0 is returned.
(old f77 xplasma 1 documentation)
To fetch the label for a list:
! input:
integer :: list_id ! id of an existing list
! output:
character*60 alabel ! list label (arb. string) returned
call eq_list_label(list_id,alabel)
(old f77 xplasma 1 documentation)
To get list element names:
! input:
integer :: list_id ! id of an existing list
integer :: list_size ! size of list (should match eq_list_size value)
! output:
character*32 :: enames(list_size) ! list element names returned.
integer :: ierr ! completion code, 0=OK
call eq_list_enames(list_id,list_size,enames,ierr)
ierr is set, and enames = " " returned, if the list id is invalid or if the
list_size value is less than the actual size of the list.
(old f77 xplasma 1 documentation)
To get integer data associated with each list element:
! input:
integer :: list_id ! id of an existing list
integer :: list_size ! size of list (should match eq_list_size value)
! output:
integer :: ivals(list_size) ! integer list data
integer :: ierr ! completion code, 0=OK
call eq_list_ivals(list_id,list_size,ivals,ierr)
ierr is set, and ivals = 0 returned, if the list id is invalid or if the
list_size value is less than the actual size of the list.
(old f77 xplasma 1 documentation)
To get character data associated with each list element:
! input:
integer :: list_id ! id of an existing list
integer :: list_size ! size of list (should match eq_list_size value)
! output:
character*60 :: chvals(list_size) ! character data with each list element
integer :: ierr ! completion code, 0=OK
call eq_list_chvals(list_id,list_size,chvals,ierr)
ierr is set, and chvals = " " returned, if the list id is invalid or if the
list_size value is less than the actual size of the list.
(old f77 xplasma 1 documentation)
To get floating point data associated with each list element:
! input:
integer :: list_id ! id of an existing list
integer :: list_size ! size of list (should match eq_list_size value)
! output:
real*8 :: r8vals(list_size) ! floating point list data
integer :: ierr ! completion code, 0=OK
call eq_list_r8vals(list_id,list_size,r8vals,ierr)
ierr is set, and r8vals = 0 returned, if the list id is invalid or if the
list_size value is less than the actual size of the list.
(old f77 xplasma 1 documentation)
To get floating point data associated with each list element:
! input:
integer :: list_id ! id of an existing list
integer :: list_size ! size of list (should match eq_list_size value)
! output:
real :: r4vals(list_size) ! floating point list data
integer :: ierr ! completion code, 0=OK
call eq_list_r4vals(list_id,list_size,r4vals,ierr)
ierr is set, and r4vals = 0 returned, if the list id is invalid or if the
list_size value is less than the actual size of the list.
(old f77 xplasma 1 documentation)
for irzmode=0:
This routine finds the jacobian matrix d[x,y,z]/d[rho,chi,phi]
for each point from a set of (rho,chi,phi) vectors.
for irzmode=1:
this routine returns the jacobian matrix
d[R,Z,Lphi]/d[rho,chi,phi];
Lphi=(spatial displacement in phi direction -- R*phi)
in axisymmetric case: [(dR/drho) (dR/dchi) 0 ]
[(dZ/drho) (dZ/dchi) 0 ]
[ 0 0 R ]
and det(J)=
-R*((dR/drho)*(dZ/dchi)-(dR/dchi)*(dZ/drho))
(-sign as (R,Z,phi) is left-handed)
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 rho(ivec) ! rho coordinate, each vector element
real*8 chi(ivec) ! chi coordinate, each vector element
real*8 phi(ivec) ! phi coordinate, each vector element
! output:
real*8 jtens(3,3,ivec) ! Jacobian
real*8 detj(ivec) ! det(jtens), determinant
! **** det(jtens) not dependent on irzmode
!
integer ierr ! completion code, 0=OK
! notes for (irzmode=0):
! jtens(1:3,1,1:ivec) = (dx/drho,dx/dchi,dx/dphi), all elements
! jtens(1:3,2,1:ivec) = (dy/drho,dy/dchi,dy/dphi), all elements
! jtens(1:3,3,1:ivec) = (dz/drho,dz/dchi,dz/dphi), all elements
!
! notes for (irzmode=1):
! jtens(1:3,1,1:ivec) = (dR/drho,dR/dchi,dR/dphi), all elements
! jtens(1:3,2,1:ivec) = (dZ/drho,dZ/dchi,dZ/dphi), all elements
! jtens(1:3,3,1:ivec) = R*(dphi/drho,dphi/dchi,1), all elements
!
...
...
call eq_getjac(ivec,rho,chi,phi,irzmode,jtens,detj,ierr)
To have "chi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
(old f77 xplasma 1 documentation)
This routine returns the determinant of the jacobian matrix
d[R,Z,Lphi]/d[rho,chi,phi];
Lphi=(spatial displacement in phi direction -- R*phi)
in axisymmetric case: [(dR/drho) (dR/dchi) 0 ]
[(dZ/drho) (dZ/dchi) 0 ]
[ 0 0 R ]
and det(J)=
-R*((dR/drho)*(dZ/dchi)-(dR/dchi)*(dZ/drho))
(-sign as (R,Z,phi) is left-handed)
! input:
integer ivec ! vector dimension (vector of coordinates)
real*8 rho(ivec) ! rho coordinate, each vector element
real*8 chi(ivec) ! chi coordinate, each vector element
real*8 phi(ivec) ! phi coordinate, each vector element
real*8 zscale ! scale factor to apply to result (e.g. 1.0)
! output:
real*8 detj(ivec) ! det(jtens), determinant
!
integer ierr ! completion code, 0=OK
call eq_getdetj(ivec,rho,chi,phi,zscale,detj,ierr)
To have "chi" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
(old f77 xplasma 1 documentation)
The following routines can be used to determine the distance from
any point in space to a specified surface on the plasma (usually
the plasma boundary). These routines have the convention that if
the returned distance is positive, the point is outside the surface
by the given amount; if the distance is negative, the point is inside
by the given amount.
(a) distance of cartesian coordinate point(s) (x,y,z) to plasma
boundary --fast, approximate solution
subroutine eqx_dfast(ivec,x,y,z,dist,ierr)
(b) distance of cylindrical coordinate point(s) (R,Z,phi) to plasma
boundary --fast, approximate solution
subroutine eq_dfast(ivec,R,Z,phi,dist,ierr)
To have "chi_out" interpreted as lying on a reversed (clockwise oriented)
poloidal angle grid, substitute a negative number -ivec for ivec in the
calling argument list.
(c) distance of cartesian coordinate point(s) (x,y,z) to any "rho"
surface in plasma --slow, precise solution
subroutine eqx_bdfind(ivec,x,y,z,rho_in,chi_out,phi_out,dist,ierr)
(d) distance of cylindrical coordinate point(s) (R,Z,phi) to any "rho"
surface in plasma --slow, precise solution
subroutine eq_bdfind(ivec,R,Z,phi,rho_in,chi_out,phi_out,dist,ierr)
The fast routines function by using a bilinear lookup on a table of
precomputed exact solutions; the precise routines solve the distance
minimization problem to high accuracy with a root finder, and also
give the (chi,phi) of the nearest point on the requested rho surface.
Note: the data for the fast evaluations are only available after the
limiters and (R,Z) grid have been set up. If this setup is not done,
the code reverts to the accurate but slow method.
The routines share many arguments:
! input:
integer ivec ! vector dimension, no. of pts to solve
real*8 x(ivec),y(ivec),z(ivec) ! cartesian coordinate points
! or...
real*8 R(ivec),Z(ivec),phi(ivec) ! cylindrical coordinate points
!
! extra input, bdfind routines:
!
real*8 rho_in ! "rho" of desired surface
!
! output:
real*8 dist(ivec) ! distance to bdy or selected surface
! .gt.0 -- outside, .lt.0 -- inside
! .eq.0 -- the point is on the surface.
integer ierr ! completion code, 0=OK
!
! extra output, bdfind routines:
!
real*8 chi_out(ivec) ! "chi" of nearest point on target surface
real*8 phi_out(ivec) ! "phi" of nearest point on target surface
(old f77 xplasma 1 documentation)
The following routines compute the distance from given points to the
nearest limiter. If the distance is positive, the point is behind the
limiter by the given amount; if the distance is negative, the point is
on the plasma side of the limiter by the given amount.
(a) distance of cartesian coordinate point(s) (x,y,z) to limiter
subroutine eqx_dist(ivec,x,y,z,dist,ilopt,Rlim,Zlim,philim,ierr)
(b) distance of cylindrical coordinate point(s) (R,Z,phi) to limiter
subroutine eq_rzdist(ivec,R,Z,phi,dist,ilopt,Rlim,Zlim,philim,ierr)
For both of these routines, if the control flag "iopt" is set, then,
the cylindrical coordinates of nearest limiter point are also returned.
The arguments are:
! input:
integer ivec ! vector dimension, no. of pts to solve
real*8 x(ivec),y(ivec),z(ivec) ! cartesian coordinate points
! or...
real*8 R(ivec),Z(ivec),phi(ivec) ! cylindrical coordinate points
! output:
real*8 dist(ivec) ! distance to limiter
! .gt.0 -- outside, .lt.0 -- inside
! .eq.0 -- the point is on the limiter.
! input:
integer ilopt ! if ilopt=1, compute Rlim,Zlim,philimm
! output, only if ilopt.eq.1 ...
real*8 Rlim(ivec) ! R of nearest limiter point
real*8 Zlim(ivec) ! Z of nearest limiter point
real*8 philim(ivec) ! phi of nearest limiter point
! general output:
integer ierr ! completion code, 0=OK
(old f77 xplasma 1 documentation)
It sometimes happens that one has a line segment AB, such that
A is outside and B is inside a boundary (either the limiter
boundary or the core plasma boundary), and the need is to find
the point on AB which intersects the boundary in question.
The vectorized routine eq_fxcep can be used to find such segment
boundary intersections:
! input:
integer ivec ! vector dimension (# of segments)
integer itype ! =1: want plasma bdy; =2: limiter
integer idinit ! =1: endpoint distances are known
! more input:
real*8 ra(ivec),za(ivec) ! "A" endpoints of segments
real*8 phia(ivec) ! "A" endpoints phi locations
! input (if idinit.eq.1) or output (if idinit.ne.1):
real*8 da(ivec) ! distance, "A" to chosen bdy
! more input:
real*8 rb(ivec),zb(ivec) ! "B" endpoints of segments
real*8 phib(ivec) ! "B" endpoints phi locations
! input (if idinit.eq.1) or output (if idinit.ne.1):
real*8 db(ivec) ! distance, "B" to chosen bdy
! input:
real*8 tol ! accuracy tolerance
! approximate accuracy of intercept positions:
! tol*[system size], system size = ~midplane half width
! of plasma (approx).
! output:
real*8 rx(ivec),zx(ivec) ! segment bdy intercept pts returned
real*8 phix(ivec) ! segment bdy intercept phi values
!
integer istat(ivec) ! segment status code (see below)
!
integer ierr ! completion code, 0=OK
! ierr is only set for "serious" errors.
! segment status codes: on output,
!
! istat(j)=2 -- both endpoints outside bdy
! istat(j)=1 -- 1st endpoint outside, 2nd endpoint inside
! istat(j)=-1 -- 1st endpoint inside, 2nd endpoint outside
! istat(j)=-2 -- both endpoints inside bdy
! (rx(j),zx(j)) contains bdy intercept only if abs(istat(j))=1.
... ...
call eq_fxcep(ivec,itype,idinit,
> ra,za,phia,da, rb,zb,phib,db,
> tol,
> rx,zx,phix, istat, ierr)
! the routine can be used to define a start point for RF rays
! or neutral atom tracks or diagnostic sightline - plasma
! intersections.
(old f77 xplasma 1 documentation)
The following routine returns R, Z, and mod(B) at the magnetic
axis, for given phi value. (For axisymmetric equilibria, the
phi value has no effect; one can use ivec=1 and phi(1)=0.0d0).
! input:
integer ivec ! vector dimension
real*8 phi(ivec) ! toroidal angle coordinate
! output:
real*8 B_axis(ivec) ! mod(B) on axis
real*8 R_axis(ivec) ! R of magnetic axis
real*8 Z_axis(ivec) ! Z of magnetic axis
...
call eq_gaxis(ivec,phi,B_axis,R_axis,Z_axis)
--> simplified routine for axisymmetric case:
call eq_sgaxis(b_axis,r_axis,z_axis)
returns the single axial values for B, R, and Z; the arguments can
be real*8 scalars.
(old f77 xplasma 1 documentation)
The (R,Z) MHD equilibrium may be retrieved in Fourier Spline form,
i.e. the coeffcient profiles Rc[i](x), Rs[i](x), Zc[i](x), Zs[i](x)
satisfying
R(x,theta) = Rc[0](x) + sum(Rc[i](x)*cos(i*theta)+Rs[i](x)*sin(i*theta))
Z(x,theta) = Zc[0](x) + sum(Zc[i](x)*cos(i*theta)+Zs[i](x)*sin(i*theta))
! generally x is normalized sqrt(toroidal_flux), aka "rho".
where the sum is from i=1 to kmom, where kmom is the maximum Fourier
coefficient retained.
To get kmom:
integer :: kmom
call xmoments_kmom_get(kmom)
The moments profile retrieval routines can return either the moments
coefficients themselves, or, a scaled representation
Rcx[i] = Rc[i]/x**j, j=min(i,kmaxe)
which has been constructed in a way such that the behavior as x->0 is
kept smooth, i.e. Rc[i] is proportional to x**i in the limit as x->0.
(kmaxe can be fetched by an undocumented xplasma routine-- work in
progress-- for now users should just get the unnormalized coefficient).
Each moment coefficient is fetched by a separate call, using one of
the following four subroutines:
eqmom_Rcos(...) for Rc[i]
eqmom_Rsin(...) for Rs[i]
eqmom_Zcos(...) for Zc[i]
eqmom_Zsin(...) for Zs[i]
All of the above routines have the following arguments:
for input:
integer nx ! number of x points at which the coefficient is desired.
real*8 x(nx) ! the actual x points (x = sqrt(phi/philim) = "rho").
integer inorm ! =0 to get the coefficient, =1 to get coeff with
! normalization-- context dependent, do not use...
integer i ! the moment index of the desired coefficient
for output:
real*8 momarr(nx) ! the coefficients (or coeff/x**i) returned.
integer iwarn ! =0: OK; =-1: "i" not in range 0 to kmom.
if the warning flag is set, momarr=0.0 is returned. The form of the
call is then:
call eqmom_Rcos(x,nx,inorm,i,momarr,iwarn) ! inorm=0 recommended
and a sample fragment showing the use of these routines follows:
integer kmom
real*8, dimension(:,:), allocatable :: rc,rs,zc,zs
! x(1:nx) contains the user's grid.
call xmoments_kmom_get(kmom)
allocate(rc(0:kmom,nx),rs(0:kmom,nx))
allocate(zc(0:kmom,nx),zs(0:kmom,nx))
do i=0,kmom
call eqmom_Rcos(x,nx,0,i,rc(i,1:nx),iwarn) ! get the coeffs.
call eqmom_Rsin(x,nx,0,i,rs(i,1:nx),iwarn)
call eqmom_Zcos(x,nx,0,i,zc(i,1:nx),iwarn)
call eqmom_Zsin(x,nx,0,i,zs(i,1:nx),iwarn)
enddo
The Fourier Representation is available after xplasma has been
initialized from any data source.
(old f77 xplasma 1 documentation)
The poloidal moments of a geometric factor used to compute the Pfirsch-Schluter
contribution of the viscosity for NCLASS as described in
Houlberg,Shaing,Hirshman,Zarnstorff, "Bootstrap current and neoclassical
transport in tokamaks of arbitray collisionality and aspect ratio",
Phys. Plasmas 4 (9), September 1997, pg 3230-3242
are returned by the eq_psmom function. The Fm moments from this paper are
returned in the PSMOM argument. For rho<RHOMIN, axis values are returned.
The relative error returned by
ZERROR =|sum(Fm) - <(n.grad(B))**2>/<B**2>| / max(<(n.grad(B))**2>/<B**2>)
should go to zero for increasing number of moments.
! input
integer :: ivec ! number of surfaces
real*8 :: rho(ivec) ! radial coordinate
real*8 :: rhomin ! rho values below this are considered on axis
integer :: nmom ! number of moments to compute
real*8 :: phi1 ! toroidal coordinate, same one used for all surfaces
integer :: idmom ! dimension of the psmom array
!
real*8 :: psmom(idmom,ivec) ! PS moments
real*8 :: gamma(ivec) ! wayne's gamma = 2*pi/integral_0_2pi[Bmod/B.grad(theta)]_dtheta
real*8 :: ngrdb2(ivec) ! <(n.grad(B))**2>
real*8 :: b2(ivec) ! <B**2>
real*8 :: zerror(ivec) !|sum(Fm)-<(n.grad(B))**2>/<B**2>|/max(<(n.grad(B))**2>/<B**2>)
integer :: ier ! nonzero on error
call eq_psmom(ivec, rho, rhomin, nmom, phi1, idmom, psmom, gamma, ngrdb2, b2, zerror, ier)
(old f77 xplasma 1 documentation)
(dmc: 20 Sept 2000. Although these interfaces are written with
eventual extension to 3d non-axisymmetric in mind, currently only
axisymmetric geometry is supported).
(old f77 xplasma 1 documentation)
Once the magnetic coordinates (rho,chi) are established, the
spline functions g(rho), psi(rho), R(rho,chi), Z(rho,chi),
and the vector B(rho,chi) have been constructed, it becomes
possible to evaluate metric and field flux surface / flux zone
averages and integrals.
Generally the integrands are algebraic combinations of spline
functions, optionally weighted by the determinant of the Jacobian,
which is also an algebraic combination of splines.
Integrals are evalaluated numerically using an 11-point Gauss
Legendre method within each spline grid zone, and summing
across multiple zones. For typical integrands the (bi)cubic
pieces within spline-grid zones are C-infinity, which means that
components of the sum forming the integral are estimated to an
accuracy within REAL*8 machine precision, as is readily verified
by comparison to higher order Gauss Legendre formulae.
Integrations are performed on a set of flux surfaces-- not
necessarily the same set of surfaces as given by the rho
spline grid -- or over flux zones between successive flux
surfaces. Integrations are performed over entire surfaces
or zones (poloidal angle chi varying from 0 to 2pi), or,
over a user defined discreet set of chi subzones, again not
necessarily the same zones as defined by the chi spline
grid.
Six types numerical integrations are available.
In these expressions:
"f" is the user selected integrand function
"det(J)" is the absolute value of the determinant of the
Jacobian (i.e. R*[(dR/drho)*(dZ/dchi)-(dZ/drho)*(dR/dchi)]
in axisymmetric geometry).
When det(J) is used, the software verifies that sign(det(J))
does not change; an error will be reported if it does.
a) surface integral:
Integral[phi = 0 to 2pi] dphi *
Integral[chi = 0 to 2pi] dchi * f
= 2pi * Integral[chi = 0 to 2pi] dchi * f
(in axisymmetric geometry)
--computed at each of N rho surface (see setup routines)
--also available split into chi subzones
b) volume weighted surface integral:
Integral[phi = 0 to 2pi] dphi *
Integral[chi = 0 to 2pi] dchi * f * det(J)
= 2pi * Integral[chi = 0 to 2pi] dchi * f * det(J)
(in axisymmetric geometry)
--computed at each of N rho surface (see setup routines)
--also available split into chi subzones
c) volume weighted surface average:
A type(b) integral, normalized by
Integral[phi = 0 to 2pi] dphi *
Integral[chi = 0 to 2pi] dchi * det(J)
--computed at each of N rho surface (see setup routines)
--also available split into chi subzones
d) zone integral:
Integral[phi = 0 to 2pi] dphi *
Integral[chi = 0 to 2pi] dchi *
Integral[rho = rho(j) to rho(j+1)] drho * f
= 2pi * Integral[chi = 0 to 2pi] dchi *
Integral[rho = rho(j) to rho(j+1)] drho * f
(in axisymmetric geometry)
--computed in the N-1 zones between the N rho surfaces
--also available split into chi subzones
e) volume weighted zone integral
Integral[phi = 0 to 2pi] dphi *
Integral[chi = 0 to 2pi] dchi *
Integral[rho = rho(j) to rho(j+1)] drho * f * det(J)
= 2pi * Integral[chi = 0 to 2pi] dchi *
Integral[rho = rho(j) to rho(j+1)] drho * f * det(J)
(in axisymmetric geometry)
--computed in the N-1 zones between the N rho surfaces
--also available split into chi subzones
Note that if f=1 the integral result is the zone volume,
which (for chi = 0 to 2pi) we'll call delta_volume(j).
f) volume weighted zone average
a type (e) integral, normalized in each zone j by dividing
by delta_volume(j).
--computed in the N-1 zones between the N rho surfaces
--also available split into chi subzones
(old f77 xplasma 1 documentation)
A rho grid for integrations needs to be defined. This can be but
need not necessarily be the same rho grid as was used for setting
up the g, psi, R, Z, and B field splines.
The rho grid identifies an ascending sequence of rho surfaces,
at which / between which numerical integrations are to be performed.
Optionally, a chi grid can also be defined, to allow breaking up
an integration into zones [rho(j),rho(j+1)]x[chi(k),chi(k+1)], or
surface intervals [chi(k),chi(k+1)] at each surface rho(j), for
surface integrals.
The integration rho grid (chi grid) can be redefined any number of
times, so that, for a given xplasma geometry and field, the call
sequence
setup rho-grid-A
(optionally set up chi-grid-A)
evaluate metric integrals on rho-grid-A (chi-grid-A)
setup rho-grid-B
(optionally set up chi-grid-B)
evaluate metric integrals on rho-grid-B (chi-grid-B)
is readily done.
(old f77 xplasma 1 documentation)
The basic setup call defines the integration rho grid:
integer iauto ! =1 for automatic generation of rho sequence
! =0 for user supplied rho sequence
integer inumrhos ! number of rho points (each identifies a surface)
real*8 zrho(inumrhos) ! the actual rho values
! if iauto=1, evenly spaced values from rho(axis)
! to rho(bdy), inclusive, are output.
! if iauto=0, user supplied values must lie within
! the range defined by the splines.
real*8 zrhomin ! minimum safe rho (approach to axial singularity)
! det(J) --> 0 as rho --> rho(axis).
! a typical safe choice:
! rho(axis)+1.0d-7*(rho(bdy)-rho(axix))
!
! output:
integer ierr ! completion code, 0=OK
........
call eq_flxint_init(iauto,inumrhos,zrho,zrhomin,ierr)
The setup evaluates the zone volumes between successive rho surfaces;
it uses this integration as a test to set "ineed".
An example of the use of eq_flxint_init can be found in xplasma in
the source code: eqdbg_flxint.for
(old f77 xplasma 1 documentation)
This optional further setup call defines the integration chi grid:
integer iauto ! =1 for automatic generation of chi sequence
! =0 for user supplied chi sequence
integer inumchis ! number of chi points
! each identifies a chi interval boundary.
real*8 zchi(inumchis) ! the actual chi values
! if iauto=1, evenly spaced values from chi(min)
! to chi(max), inclusive, are output; the extrema
! are taken from the chi spline grid.
! if iauto=0, user supplied values must be given
! in increasing order, and must lie within one
! period of the periodic chi grid.
!
! output:
!
integer ierr ! completion code, 0=OK
........
call eq_flxint_chinit(iauto,inumchis,zchi,ierr)
Note that this call must come *after* an eq_flxint_init call.
Note also, in related documentation of the integration routines,
the quantity "inumchi" refers to the number of chi intervals, which
is (inumchis - 1).
(old f77 xplasma 1 documentation)
Certain "canned" integrations are available by calling "eq_flxint"...
(old f77 xplasma 1 documentation)
! eq_flxint input:
character*20 iname ! name-of-desired-integral
integer noption ! accuracy control (noption=1 recommended)
!
! noption=0 -- evaluate quadrature to near machine precision accuracy;
! a message is generated recommending a non-zero
! noption value that would be sufficiently accurate.
! noption=1 -- use 11-point Gauss-Legendre integration on each segment
! =2 -- use 21-point Gauss-Legendre integration on each segment
! =3 -- use 43-point Gauss-Legendre integration on each segment
! (each segment is a continuous segment btw spline grid pts)
!
! **(generally noption=1 is sufficient real*8 machine precision,
! and it is the fastest by far)
!
inteter inumchi ! chi dimension of results array
! --if inumchi=1, do whole flux surface
! integrals.
! --if inumchi.ge.(# of chi intervals
! specified via a prior eq_flxint_chinit
! call), break up the integral into the
! chi intervals and store the partial
! results
! --if inumchi.gt.1 but less than the
! no. of intervals: this is an error.
integer inumrho ! rho dimension of results array
! --must be .ge. the number of zones or
! number of surfaces, depending on the
! type of integral.
! eq_flxint output:
real*8 result(inumchi,inumrho) ! integration results
integer ierr ! completion code, 0=OK
....
call eq_flxint(name,noption,result,inumchi,inumrho,ierr)
Examples:
call eq_flxint('DVOL',1,result,30,20,ierr)
--valid if no. of rho surfaces is .le.21
--valid if chi intervals are defined and there are at least
30 of them (i.e. 31 chi interval boundaries).
result(j,k) = the volume in the region
[chi(j),chi(j+1)]x[rho(k),rho(k+1)]
j = 1 to (#of chi intervals)
k = 1 to (#of rho zones)
call eq_flxint('<1/R^2>',1,result,1,20,ierr)
--valid if no. of rho surfaces is .le.21
result(k) = volume weighted average of 1/R**2, integrating
chi = 0 to 2pi, between surfaces at rho(k) and
rho(k+1).
k = 1 to (#of rho zones)
More realistic code examples of the use of eq_flxint can be found
in xplasma in the source code: eqdbg_flxint.for
The minimum required inum, inumchi was determined by preceding
initialization calls (eq_flxint_init, eq_flxint_chinit); the values
can be retrieved as follows:
call fluxav_nzones_get(inumchi,inumrho) ! inumrho = #of rho zones
--or--
call fluxav_nsurfs_get(inumchi,inumrho) ! inumrho = #of rho surfaces
! = (#of rho zones) + 1
! inumchi = #of chi intervals
! (defined by prior eq_flxint_chinit call)
! ( =1 if no prior eq_flxint_chinit call)
(old f77 xplasma 1 documentation)
These are the character string names passed to "eq_flxint". All
names are case-insensitive.
Basic metrics:
inumrho = #of rho zones
inumchi = 1 --or-- #of rho zones * #of chi zones
DVOL -- zone volumes (units of R,Z)^3
DAREA -- cross sectional area btw successive rho surfaces
(units of R,Z)^2
inumrho = #of rho surfaces
inumchi = 1 --or-- #of rho surfaces * #of chi zones
SURF -- surface area of each rho surface (units of R,Z)^2
LPOL -- poloidal path length around each rho surface
(units of R,Z)
DVDRHO -- dV/drho at each rho surface (units of R,Z)^3
Zonal volume averages
--integrands are volume integrated; then zone volume is divided out
inumrho = #of rho zones
inumchi = 1 --or-- #of chi zones
moments of R:
<R^2> <R> <1/R> <1/R^2> <1/R^3>
moments of |grad(rho)|
<grad(rho)> <grad(rho)^2>
moments involving |grad(rho)| and R
<grad(rho)^2/R^2> <grad(rho)^2/R^3> <R^2*grad(rho)^2>
<1/(R*grad(rho))>
moment involving |grad(rho)| and mod(B)
<grad(rho)^2/B^2>
moments of B (mod(B)) or vertical component of B, BZ
<B^2> <B> <1/B> <1/B^2> <BZ^2>
Volume weighted surface averages
inumrho = #of rho surfaces
inumchi = 1 --or-- #of chi zones
--names as above, but with "S" appended, e.g.
<R^2>S or <grad(rho)^2/R^2>S
These quantities are are the flux surface integrals normalized
by DVDRHO. Since at the axis DVDRHO=0.0, the averages there are
just the local values of the integrand.
(note: dmc 21 Sept 2000: several additional metric averages will
be added soon, to complete the set needed by NCLASS. Also,
suggestions for additional canned integrations are welcome, send
to dmccune@pppl.gov).
(old f77 xplasma 1 documentation)
Any quantity that can be represented as a bicubic spline can
be integrated, with the following routines. The procedure is
as follows:
1. construct an array f(nchi,nrho) containing the integrand
function values f(j,k) = f(chi(j),rho(k)), where the
{chi(j)} and {rho(k)} values are from the *original* grid
used to set up the R and Z splines, *not* the integration
zone grids.
2. call the integrator routine, which will
a) construct a temporary (bicubic) spline from f
b) perform the requested integral
c) discard the temporary spline
Four types of integrations are available:
c *** iwant *** specifies the type of integration desired:
c here "f" denotes the interpolating function generated from the
c passed array data.
c
c iwant=1: surface integral:
c inumchi=1:
c int[phi=0 to 2pi] dphi * {
c int[chi=0 to 2pi] dchi * f }
c
c result(1:inumrho) computed; inumrho = #of surfaces
c result(1,j) -- integrated at rho(j)
c
c inumchi=(# of chi intervals)
c result(1:inumchi,1:inumrho) computed;
c result(i,j) -- integrated from chi(i) to chi(i+1) at rho(j).
c
c iwant=5: volume weighted surface integral:
c inumchi=1:
c int[phi=0 to 2pi] dphi * {
c int[chi=0 to 2pi] dchi * f * det(Jacobian) }
c
c result(1:inumrho) computed; inumrho = #of surfaces
c result(1,j) -- integrated at rho(j)
c
c inumchi=(# of chi intervals)
c result(1:inumchi,1:inumrho) computed;
c result(i,j) -- integrated from chi(i) to chi(i+1) at rho(j).
c
c **caution** if rho(1) corresponds to the mag. axis, a
c det(Jacobian)=0 and a value of 0.0 is always returned!
c
c iwant=6: volume weighted surface average:
c inumchi=1:
c
c int[phi=0 to 2pi] dphi * {
c int[chi=0 to 2pi] dchi * f * det(Jacobian)}
c -----------------------------------------------
c int[phi=0 to 2pi] dphi * {
c int[chi=0 to 2pi] dchi * det(Jacobian)}
c
c result(1:inumrho) computed; inumrho = #of surfaces
c result(1,j) -- integrated at rho(j)
c
c inumchi=(# of chi intervals)
c result(1:inumchi,1:inumrho) computed;
c result(i,j) -- integrated from chi(i) to chi(i+1) at rho(j).
c
c **caution** if rho(1) corresponds to the mag. axis,
c the value "f" evaluated at the axis is returned there.
c
c zonal integrals: result(1:inumchi,1:inumrho) are computed
c
c iwant=2: integrate f*drho*dchi*dphi:
c inumchi=1:
c int[rho=rho(j) to rho(j+1)] drho * {
c int[phi=0 to 2pi] dphi * {
c int[chi=0 to 2pi] dchi * f } }
c
c result(1:inumrho) computed; inumrho = #of surfaces - 1
c result(1,j) -- integrated from rho(j) to rho(j+1).
c
c inumchi=(# of chi intervals)
c result(1:inumchi,1:inumrho) computed;
c result(i,j) -- doubly integrated from chi(i) to chi(i+1),
c rho(j) to rho(j+1)
c
c iwant=3: integrate f*dV (use det(Jacobian))
c
c int[rho=rho(j) to rho(j+1)] drho * {
c int[phi=0 to 2pi] dphi * {
c int[chi=0 to 2pi] dchi * f * det(Jacobian) } }
c
c result(1:inumrho) computed; inumrho = #of surfaces - 1
c result(1,j) -- integrated from rho(j) to rho(j+1).
c
c inumchi=(# of chi intervals)
c result(1:inumchi,1:inumrho) computed;
c result(i,j) -- doubly integrated from chi(i) to chi(i+1),
c rho(j) to rho(j+1)
c
c iwant=4: compute <f> = (integrated f*dV)/dVol
c where dVol is the volume btw rho(j) and rho(j+1)
c
c result(1:inumrho) computed; inumrho = #of surfaces - 1
c result(1,j) -- integrated from rho(j) to rho(j+1).
c
c inumchi=(# of chi intervals)
c result(1:inumchi,1:inumrho) computed;
c result(i,j) -- doubly integrated from chi(i) to chi(i+1),
c rho(j) to rho(j+1)
c
c **caution** in axisymmetric case there is no variation with phi,
c but a factor of 2pi still arises from the phi integration...
c
To control the boundary conditions on the temporary spline, use
"eq_flxint_arr2s"
For default boundary conditions (not-a-knot) use
"eq_flxint_arr2".
(old f77 xplasma 1 documentation)
input:
real*8 zdata(nchi,nrho) ! the integrand function data
integer ibcrho0 ! rho(1) BC option
real*8 zbcrho0(*) ! rho(1) BC data
integer ibcrho1 ! rho(nrho) BC option
real*8 zbcrho1(*) ! rho(nrho) BC data
c
c BC type values: for ibcrho0:
c =0 -- use "not a knot", zbcrho0(...) ignored
c =1 -- match slope, given at x(1),chi(ichi) by zbcrho0(ichi)
c =2 -- match 2nd deriv., given at x(1),chi(ichi) by zbcrho0(ichi)
c =3 -- boundary condition is slope=0 (df/dx=0) at x(1), all chi(j)
c (zbcrho0(...) ignored).
c and similarly for (ibcrho1,zbcrho1(...)).
c
c if zbcrho0,zbcrho1 are used, nchi points must be defined.
integer iwant ! type of integration
! =1 -- surface integral of f*dchi*dphi
! =5 -- surface integral of f*det(Jacobian)*dchi*dphi
! =6 -- surface average:
! int(f*det(Jacobian)*dchi*dphi)/
! int(det(Jacobian)*dchi*dphi)
! =2 -- zone integral of f*dchi*dphi*drho
! =3 -- volume integeral, f*det(Jacobian)*dchi*dphi*drho
! =4 -- volume average: vol. integral / delta_volume
! ** caution: dphi integration -> factor of 2pi
integer inumchi,inumrho ! results dimensions
! *** see eq_flxint ***
output:
real*8 result(inumrho,inumchi) ! integration results
integer ierr ! completion code, 0=OK
....
call eq_flxint_arr2s(zdata,
> ibcrho0,zbcrho0,ibcrho1,zbcrho1,
> iwant,result,inumchi,inumrho,ierr)
(old f77 xplasma 1 documentation)
input:
real*8 zdata(nchi,nrho) ! the integrand function data
integer iwant ! type of integration
! =1 -- surface integral of f*dchi*dphi
! =5 -- surface integral of f*det(Jacobian)*dchi*dphi
! =6 -- surface average:
! int(f*det(Jacobian)*dchi*dphi)/
! int(det(Jacobian)*dchi*dphi)
! =2 -- zone integral of f*dchi*dphi*drho
! =3 -- volume integeral, f*det(Jacobian)*dchi*dphi*drho
! =4 -- volume average: vol. integral / delta_volume
! ** caution: dphi integration -> factor of 2pi
integer inumchi,inumrho ! results dimensions
! *** see eq_flxint ***
output:
real*8 result(*) ! integration results
integer ierr ! completion code, 0=OK
....
call eq_flxint_arr2(zdata,
> iwant,result,inumchi,inumrho,ierr)
(old f77 xplasma 1 documentation)
(real codes should check the return status code ierr).
! set up rho integration zones
call eq_flxint_init(0,nsurfs,rho_surfs,rho_min,ierr)
! (optional) set up chi integration zones
call eq_flxint_chinit(0,nchbdys,chi_bdys,ierr)
!
! (re)fetch flux integration zone grid sizes
!
call fluxav_nzones_get(nchi_zones,nrho_zones)
! compute standard metric integrals -- chi = 0 to 2pi
ichizons=1
allocate(result(ichizons,nrho_zones))
iaccuracy=1
call eq_flxint('DVOL',iaccuracy,result,ichizons,nrho_zones,ierr)
...
call eq_flxint('<1/R^2>',iaccuracy,result,ichizons,nrho_zones,ierr)
...
call eq_flxint('<grad(rho)^2/R^2>',iaccuracy, &
result,ichizons,nrho_zones,ierr)
! compute standard metric integrals -- discrete chi zones
ichizons=nchi_zones
allocate(result(ichizons,nrho_zones))
call eq_flxint('DVOL',iaccuracy,result,ichizons,nrho_zones,ierr)
! given an array f(1:nchi,1:nrho) on the (R,Z) original spline grid
! (NOT the integration grid)... compute various integrals
iwant=1 ! surface integrals f*dphi*dchi
nrho_surfs = nrho_zones+1
allocate(result(ichizons,nrho_surfs))
call eq_flxint_arr2(f,iwant,result,ichizons,nrho_surfs,ierr)
iwant=2 ! zone integrals f*drho*dphi*dchi
allocate(result(ichizons,nrho_zones))
call eq_flxint_arr2(f,iwant,result,ichizons,nrho_zones,ierr)
iwant=3 ! volume integrals f*det(J)*drho*dphi*dchi
allocate(result(ichizons,nrho_zones))
call eq_flxint_arr2(f,iwant,result,ichizons,nrho_zones,ierr)
iwant=4 ! volume avgs (as iwant=3, div. by dVol)
! dVol = integral det(J)*drho*dphi*dchi
allocate(result(ichizons,nrho_zones))
call eq_flxint_arr2(f,iwant,result,ichizons,nrho_zones,ierr)
! these calls create temporary spline fits. to control boundary
! conditions use eq_flxint_arr2s.
(old f77 xplasma 1 documentation)
eq_flxint_init, eq_flxint_chinit -- initialization
eq_flxint -- canned integrations
eq_flxint_arr2, eq_flxint_arr2s -- user defined integrations
(old f77 xplasma 1 documentation)
(eq_limcon)
(Axisymmetric geometry only...)
Regardless of the method chosen to represent the limiter, the
routine "eq_limcon" can be used to return a piecewise linear
contour representation of the limiter:
! input:
integer imax ! max no. of points in contour returned
! output:
integer inum ! actual no. of points in contour
real*8 rlim(inum),zlim(inum) ! the contour itself
! input:
real*8 dtol ! colinearity tolerance (R,Z units)
! output:
integer ierr ! completion status code, 0=OK
...
call eq_limcon(imax,inum,rlim,zlim,dtol,ierr)
The contour returned is a closed finite sequence of (R,Z)
points. Closed means rlim(1)=rlim(inum) & zlim(1)=zlim(inum).
The colinearity tolerance "dtol" controls the deletion of
extraneous contour points: If 3 consecutive points A,B,C
are colinear to tolerance dtol, i.e. B is .le.dtol away
from the segment AC, then, B is eliminated, reducing the
size of the contour returned.
Sample use of eq_limcon: create a limiter contour sequence
suitable for writing into a G EQDSK file.
(old f77 xplasma 1 documentation)
(eq_rhopsi, eq_rhopsin) -- these routines can be used to generate
the sequence of "rho" values corresponding to a specified sequence
of psi-poloidal values. "eq_rhopsin" will generate the sequence of
rho values corresponding to an equispaced psi grid going from the
magnetic axis to the plasma boundary; "eq_rhopsi" will find the
sequence of rho values {rho[j]} satisfying
psi(rho[j]) = psi[j] (j = 1 to npsi)
for any given sequence of psi values {psi[j]}.
To use these eq_rhopsin:
integer npsi ! no. of pts in psi sequence (input)
real*8 rhovals(npsi) ! corresponding rho values (output)
integer ierr ! completion code, 0=OK (output)
...
call eq_rhopsin(npsi,rhovals,ierr)
! rhovals(1:npsi) is the set of rho values which map
! to an equispaced psi grid ranging from the plasma
! magnetic axis to the plasma boundary
! the accuracy tolerance is
! 10*[machine epsilon]*[rho(bdy)]
To use eq_rhopsi: npsi, rhovals, ierr as above, and...
real*8 psivals(npsi) ! psi values for which rho values
! are sought...
real*8 ztol ! accuracy tolerance (in rho)
! for the root finder
...
call eq_rhopsi(npsi,psivals,rhovals,ztol,ierr)
(old f77 xplasma 1 documentation)
XPLASMA supports a legacy 2d spatial grid mechanism that derives
from TRANSP and NUBEAM: the "Monte Carlo" grid, or MCgrid.
This is the spatial grid used for NUBEAM spatially 2d outputs, including
the fast ion distribution function, fusion rate profiles, etc.
This grid consists of radial zone rows each of which are partitioned
into evenly spaced poloidal zones-- with the number of zones proportional
to radius, i.e. fewer poloidal partitions at the center, more at the
edge. This yields a grid where each zone has roughly equivalent volume
and cross-sectional area, which is well suited to Monte Carlo uses, i.e.
there are no "tiny" zones near the axis as would be the case with a
regularly structured polar mesh. Such tiny zones would be hard to find
by Monte Carlo parameter summation methods, leading to high statistical
variance or noise in output. Using MCgrid avoids this.
A typical MCgrid has 10 radial zone rows inside the plasma. For historical
reasons, MCgrids also support a set of extrapolated zone rows mapping a
region somewhat beyond the plasma boundary. A MCgrid supports the notion
of updown symmetry, allowing a smaller grid to be used in the case of
symmetric equilibrium.
Although the interface has been coded to allow eventual generalization to
3d, actual use has been limited to axisymmetric geometries so far.
The main parameters defining the grid are:
iudsym -- =1 for updown symmetry =2 for updown asymmetry
inznbmri -- no. of zone rows inside the plasma (typical value: 10)
inth0 -- no. of theta zones in first (centermost) zone row
spanning the range poloidal angle range [0,pi]
(typical value: 2).
The number of zones in the n'th zone rows is n*iudsym*inth0.
The following chart illustrates the zone counts in the "typical"
MCgrid -- this is the grid historically used for most NUBEAM
calculations in TRANSP over the years.
no. of poloidal zones per zone row
updown symmetric updown asymmetric
[0,pi] [-pi,pi]
zone row
1 2 4
2 4 8
3 6 12
4 8 16
5 10 20
6 12 24
7 14 28
8 16 32
9 18 36
10 20 40
----------------------------------------------------------plasma bdy
11 22 44
12 24 48
13 26 52
zones
inside
plasma: 110 220
total
zones: 182 364
The radial zone rows are evenly spaced in the XPLASMA radial coordinate
"rho".
(old f77 xplasma 1 documentation)
Input variables (traditional/suggested values in parentheses):
integer :: inphi ! no. of phi zones -- toroidal variation (1)
integer :: iudsym ! updown symmetry 1=yes 2=no (1 or 2)
integer :: inth0 ! no. of poloidal zones subtending [0,pi] at axis (2)
integer :: inznri ! no. of radial zone rows inside plasma (10)
Output variables
integer :: inznrx ! no. of radial zones including extrapolated region
integer :: id_mcgrid ! XPLASMA id for MC grid just defined.
call mcgrid_define(inphi,iudsym,inth0,inznri, &
inznrx, id_mcgrid)
If there is an error, id_mcgrid = 0 will be returned, and messages will
be written on the i/o unit open for XPLASMA messages.
(old f77 xplasma 1 documentation)
Each of these routines retrieves information from the definition. In
each case, the XPLASMA id "id_mcgrid" is input, and the grid description
parameters are output. All arguments are integers.
! get symmetry information:
call mcgrid_getsym(id_mcgrid,iudsym)
! iudsym=1 means updown symmetric; 2 means updown asymmetric.
! get number of zone rows:
call mcgrid_getnumr(id_mcgrid,inznrx,inznri)
! inznrx -- *total* number of zone rows inside and outside plasma
! inznri -- number of zone rows inside plasma
! ** note order: inznrx then inznri **
! get number of zone rows:
call mcgrid_getnumz(id_mcgrid,inzonx,inzoni)
! inzonx -- *total* number of zones inside and outside plasma
! inzoni -- number of zones inside plasma
! ** note order: inzonx then inzoni **
! get number of zones covering [0,pi] on axis; get no. of phi zones.
call mcgrid_getnuma(id_mcgrid,inth0,inphi)
! inth0 -- no. of poloidal zones covering [0,pi] on axis
! inphi -- no. of phi zones (1 means axisymmetry).
! get array of numbers of poloidal zones per zone row
integer, dimension(:), allocatable :: inthzns
allocate inthzns(inznrx)
call mcgrid_getnumzns(id_mcgrid,inthzns)
! for this it is also possible to use the simple formula
! inthzns(k)=iudsym*inth0*k
(old f77 xplasma 1 documentation)
These routines are used to create data objects defined over an existing
MCgrid. For a scalar function use "mcgrid_putobj" (REAL*8 precision) or
"mcgrid_putobj_r4" (REAL precision).
There is also a call (used for specifying the beam distribution function)
which allows a 2d array of data to be associated with each MCgrid zone.
This is "mcgrid_putarr2" (REAL*8 precision) or "mcgrid_putarr2_r4" (REAL
precision).
The data is stored in XPLASMA in REAL*8 precision, regardless of which
call is used.
Specific functions are defined by names. Each defined MCgrid supports
its own "name space".
Example of use: the NUBEAM NTCC module uses these routines, to make
fast ion distribution functions, and other 2d profiles, such as neutron
emission rates, available externally.
The REAL*8 routine is shown; the REAL interface is identical except
for the floating point precision of zmks and zdata arguments.
input:
integer :: id_mcgrid ! MCgrid id
character*(*) :: zname ! (unique) name to use for this function
integer :: ist_th ! theta grid start point indicator:
! =0: grid starts at theta=0;
! =-1: grid starts at -pi
integer :: ist_ph ! phi grid start point indicator (for
! axisymmetric case choose either value):
! =0: grid starts at theta=0;
! =-1: grid starts at -pi
integer :: iextend ! flag if data is defined beyond plasma boundary
integer :: idim ! zdata array dimension
real*8 :: zmks ! multiply data by this to put it in MKS units
real*8 :: zdata(idim) ! the data to be stored in XPLASMA.
output:
integer :: ierr ! status code 0=normal
call mcgrid_putobj(id_mcgrid, zname, ist_th, ist_ph, iextend, &
zmks, zdata, idim, ierr)
For the distribution function definition, the above arguments are
extended to define the 3 array dimensions of the input data-- size of
array, and the subset of numbers actually used:
integer :: idim1,idim2,idim3 ! array dimensions
! zdata(idim1,idim2,idim3)
integer :: in1,in2 ! data used: zdata(1:in1,1:in2,:)
call mcgrid_putarr2(id_mcgrid, zname, ist_th, ist_ph, iextend, &
zmks, zdata, in1, in2, idim1, idim2, idim3, &
ierr)
(old f77 xplasma 1 documentation)
These routines are used to fetch data objects that have been defined
over an existing MCgrid. For a scalar function use "mcrid_getobj"
(REAL*8 precision) or "mcgrid_getobj_r4" (REAL precision). The
precision is that desired by the caller; XPLASMA internal storage is
always REAL*8.
There is also a call (used for fetching the beam distribution function)
which allows a 2d array of data to be associated with each MCgrid zone.
This is "mcgrid_getarr2" (REAL*8 precision) or "mcgrid_getarr2_r4" (REAL
precision). Again, the precision is that desired by the caller.
Specific functions are defined by names. Each defined MCgrid supports
its own "name space".
Example of use: the TRANSP code retrieves spatially 2d NUBEAM module
outputs, including fast ion distribution function data, using these
calls.
The REAL*8 routine is shown; the REAL interface is identical except
for the floating point precision of zmks and zdata arguments.
input:
integer :: id_mcgrid ! MCgrid id
character*(*) :: zname ! (unique) name to use for this function
integer :: ist_th ! caller's theta grid start point indicator:
! =0: grid starts at theta=0;
! =-1: grid starts at -pi
integer :: ist_ph ! caller's phi grid start point indicator (for
! axisymmetric case choose either value):
! =0: grid starts at theta=0;
! =-1: grid starts at -pi
integer :: idim ! zdata array dimension
real*8 :: zconv ! multiply data by this to put it into the
! physical units desired by caller. The data
! as stored is in MKS/KeV in XPLASMA.
output:
real*8 :: zdata(idim) ! the data to be returned XPLASMA.
integer :: ierr ! status code 0=normal
call mcgrid_getobj([-]id_mcgrid, zname, ist_th, ist_ph, &
zconv, zdata, idim, ierr)
For the distribution function definition, the above arguments are
extended to define the 3 array dimensions of the input data-- size of
array, and the subset of numbers actually used:
integer :: idim1,idim2,idim3 ! array dimensions
! zdata(idim1,idim2,idim3)
input/output:
subset of dimensioned array space actually in use. If in1>0 on
input, the actual space used must match this value. If in1<= 0,
the actual space usage will be returned; similarly for in2.
integer :: in1,in2 ! data used: zdata(1:in1,1:in2,:)
call mcgrid_getarr2([-]id_mcgrid, zname, ist_th, ist_ph, iextend, &
zmks, zdata, idim1, idim2, idim3, in1, in2, &
ierr)
Notes:
1. if the MCgrid is updown symmetric, only data covering the range
[0,pi] or [-pi,0] are copied, depending on the setting of ist_th.
I.e. the order of the poloidal indexing is reversed, within each
zone row.
2. if the non-existance of a function should not be treated as an
error, and the output data should be set to zero in this case,
pass -id_mcgrid instead of id_mcgrid as the first argument.
3. errors are flagged in case of argument values out of range, or
if array dimensions are too small to hold the available data.
Messages are written to the XPLASMA messages i/o unit.
(old f77 xplasma 1 documentation)
When an MCgrid is defined, a scalar function "ZONE_VOLUME" is
automatically defined along with it, giving the volume of each zone
in m**3.
This data can be fetched by the call
call mcgrid_getobj([-]id_mcgrid, 'ZONE_VOLUME', ist_th, ist_ph, &
zconv, zdata, idim, ierr)
where the arguments are as described in the preceding section.
(old f77 xplasma 1 documentation)
The mapping [(rho,theta) -> zone index] will depend on whether or
not the geometry is updown symmetric, and, e.g. in the case of
updown asymmetry, on whether the user's poloidal angle grid spans
[-pi,pi] or [0,2pi]. It will also depend on whether the user's code
makes use of the extrapolated zone rows or not. Here is some sample
code which could be adapted or optimized for a particular situation.
integer function izmap(id_mcgrid,rho,theta)
! MCgrid zone map, with assumptions:
! 1. axisymmetry
! 2. ignore zones outside plasma
! 3. updown symmetric theta range: [0,pi]
! 4. updown asymmetric theta range: [-pi,pi]
integer, intent(in) :: id_mcgrid ! grid id (axisymmetry assumed)
real*8, intent(in) :: rho,theta ! (rho,theta in)
!------------------------------------
! info on zone rows
logical, save :: init = .FALSE.
integer, dimension(:), allocatable, save :: izns,iztot
integer, save :: inznr,inznri,iudsym,inth0
integer, save :: id_mcgrid_int = 0
!------------------------------------
integer i,izr,inth,ith,idum
real*8 ztheta
real*8, parameter :: cpi = 3.1415926535897931D+00
real*8, parameter :: c2pi = 6.2831853071795862D+00
! check if id matches the one stored; if not, re-initialize...
if(id_mcgrid.ne.id_mcgrid_int) init = .FALSE.
if(.not.init) then
! ** initialize **
! get no. of zones per row; add up cumulative number
call mcgrid_getnumr(id_mcgrid,inznr,inznri)
if(inznr.eq.0) then
izmap=0 ! invalid MCgrid, evidently.
return
endif
call mcgrid_getsym(id_mcgrid,iudsym)
allocate(izns(inznr),iztot(0:inznr))
call mcgrid_getnumzns(id_mcgrid,izns)
iztot(0)=0
do i=1,inznr
iztot(i)=iztot(i-1)+izns(i)
enddo
init = .TRUE.
endif
izr = 1 + rho*inznri ! zone row index -- inznri are inside bdy
izr = max(1,min(inznri,izr)) ! forced inside plasma
inth = izns(izr) ! no. of poloidal zones, this row
ztheta = theta ! force theta in range [-pi,pi
do while ( ztheta < -cpi )
ztheta = ztheta + c2pi
enddo
do while ( ztheta > cpi )
ztheta = ztheta - c2pi
enddo
if(iudsym.eq.1) then
ztheta = abs(ztheta) ! updown symmetry: map to upper half-plane
ith=1 + ztheta*inth/cpi
ith = max(1,min(inth,ith))
else
ith=1 + (ztheta+cpi)*inth/c2pi
ith = max(1,min(inth,ith))
endif
izmap = iztot(izr-1)+ith
end function izmap
This sort of routine could also be generalized for piecewise linear
interpolation of functions defined over an MCgrid, but, higher-order
interpolation methods probably require mapping to a regular polar
mesh, and, care would have to be taken at the axis, boundary, and
poloidal angle branch cut.
The current contents of the xplasma module can be visualized via the
command line / menu driven interactive fortran subroutine:
(fortran-95): call xplasma_debug_access(s)
type (xplasma), pointer :: s
(fortran-77):
call eqdbg_plot
which use the NTCC modules SGLIB, UREADSUB, and TRGRAF.
The current contents of the xplasma module can be written to a netcdf
file:
character*(*) filename ! name of file to write
integer ierr ! completion code, 0=OK
[the filename must be set...]
(fortran-95):
call xplasma_write(s,filename,ierr)
type (xplasma), pointer :: s
(fortran-77):
call eq_save(filename,ierr) ! ierr should be checked
A file previously written by `eq_save' can be read back in using
character*(*) filename ! name of file to write
integer ierr ! completion code, 0=OK
[the filename must be set...]
(fortran-95):
call xplasma_read(s,filename,ierr)
type (xplasma), pointer :: s
(fortran-77):
call eq_restore(filename,ierr) ! ierr should be checked
these routines use the NTCC module EZCDF.
(old f77 xplasma 1 documentation)
The f77 xplasma interface writes messages to a fortran i/o unit
(logical unit number or LUN).
To fetch the fortran logical unit number currently used for messages:
integer ilun ! l.u.n. (Returned)
call eq_get_lunerr(ilun)
(old f77 xplasma 1 documentation)
To fetch the order of R(rho,chi), Z(rho,chi) and component B(rho,chi)
spline fits, use
integer iorder ! fit order
call eq_rzordr_get(iorder)
where the value returned is 1 for Hermite, 2 for spline.
(old f77 xplasma 1 documentation)
This routine will write a GEQDSK (EFIT-style) ascii file, with
format as per the notes of Lang Lao (General Atomic), Februrary 2000.
Psi(R,Z) and B(R,Z) must be defined. "q" and "P" profiles must also
be provided.
The calling routine is responsible for naming and opening the file
and providing a logical unit number on which the file may be written.
Input arguments:
integer lun_geqdsk ! logical unit number of open file
character*48 geqdsk_lbl ! label string, written into file.
The following define the limits of evenly spaced (R,Z) grids over
which psi(R,Z) will be written. These limits must cover the core
plasma but not exceed the extent of the (R,Z) grids defined in the
xplasma module.
real*8 Rmin,Rmax ! R extrema
real*8 Zmin,Zmax ! Z extrema
The total toroidal plasma current (amps) must be specified
real*8 zcur ! plasma current, amps
Xplasma id codes for the pressure and q profiles:
integer id_p ! xplasma id: Pressure profile
integer id_q ! xplasma id: q profile
The following integers specify the resolution of the grids
integer nh ! no. of horizontal (R) grid points.
! also the no. of flux grid points.
integer nv ! no. of vertical (Z) grid points.
integer nb ! no. of points in plasma bdy contour
! also, upper limit on limiter contour.
Output:
integer ierr ! completion code, 0=OK
...
call eq_geqdsk(lun_geqdsk,geqdsk_lbl,
> Rmin,Rmax, Zmin,Zmax, zcur,
> id_p, id_q, nh, nv, nb,
> ierr)
The GEQDSK file will contain:
Psi(R,Z) on evenly spaced (R,Z) grid covering [Rmin,Rmax]x[Zmin,Zmax]
and on an evenly spaced psi grid mapping from the plasma mag. axis out
the plasma boundary,
f(psi) = R*Bt f*f' P(psi) P' q
where the "'" denotes d/dpsi.
.....
The caller should check the error code; if it is non-zero, the
open file on lun_geqdsk should be deleted
(i.e. close(lun_geqdsk,status='delete')).
Given a file written by a prior call to eq_save (see the neighbouring
topic Save_and_Restore_Routines), this program can be used to examine
and visualize the contents. The program employs a simple command line
menu driven interface and vector graphics, using the NTCC modules
SGLIB, UREADSUB, and TRGRAF.
The program can be run in an xterm emulator window (must be a real
xterm, some clones like aixterm will not work). The environment
variable TERMINAL_TYPE must be set to XTERM. The environment variable
PLOT should usually be undefined.
To run the program, simply type its name and give the name of the
netcdf file previously written by the xplasma eq_save subroutine.
This program works with F95 xplasma as well.
This is a test program for the xplasma NTCC module. It's data source
is an ascii file derived from TRANSP data. (A general tool for building
an xplasma from TRANSP MDS+ or file data is available in the NTCC module
"trxplib"). The test_xplasma program comes with a controlling script,
script.ind so that
> test_xplasma @script
will produce a canned sequence of plots. This is the same set of
plots as are stored in "script_output.ps", which comes with the
xplasma distribution.
Details on building the software and running test_xplasma are in the
module's README file.
This program has been upgraded to F95 xplasma.
this is a 2nd test program for the xplasma NTCC module. This program
demonstrates construction of an xplasma from EFIT output, a G-EQDSK
file. The program comes with a controlling script, nstx_geqxpl.ind,
so that
> geqxpl @nstx_geqdsk
will produce a canned sequence of plots. This is the same set of
plots as are stored in "geqxpl_output.ps", which comes with the
xplasma distribution.
Details on building the software and running test_xplasma are in the
module's README file.
This program works with F95 xplasma.
This Document was created by hlptohtml
Written By:Manish Vachharajani(mvachhar@pppl.gov)