def verifySettings(self): """ Verify that all (mandatory) settings are set and consistent. """ if self.type in [ TYPE_UNIFORM, TYPE_BIUNIFORM, TYPE_UNIFORM_THETA, TYPE_BIUNIFORM_THETA, TYPE_CUSTOM ]: if self.nxi is None or self.nxi <= 0: raise DREAMException( "XiGrid {}: Invalid value assigned to 'nxi': {}. Must be > 0." .format(self.name, self.nxi)) else: raise DREAMException( "XiGrid {}: Unrecognized grid type specified: {}.".format( self.name, self.type)) if self.type == TYPE_BIUNIFORM: if self.nxisep is not None and (self.nxisep <= 0 or self.nxisep >= self.nxi): raise DREAMException( "XiGrid {}: Invalid value assigned to 'nxisep': {}. Must be > 0 and < nxi." .format(self.name, self.nxisep)) if self.nxisep_frac is not None and (self.nxisep_frac <= 0 or self.nxisep_frac >= 1): raise DREAMException( "XiGrid {}: Invalid value assigned to 'nxisep_frac': {}. Must be > 0 and < 1." .format(self.name, self.nxisep)) elif self.nxisep is None and self.nxisep_frac is None: raise DREAMException( "XiGrid {}: Neither 'nxisep' nor 'nxisep_frac' have been specified." .format(self.name)) elif self.xisep is None or self.xisep <= -1 or self.xisep >= 1: raise DREAMException( "XiGrid {}: Invalid value assigned to 'xisep': {}. Must be > -1 and < 1." .format(self.name, self.xisep)) elif self.type == TYPE_BIUNIFORM_THETA: if self.nthetasep is not None and (self.nthetasep <= 0 or self.nthetasep >= self.nxi): raise DREAMException( "XiGrid {}: Invalid value assigned to 'nthetasep': {}. Must be > 0 and < nxi." .format(self.name, self.nthetasep)) elif self.nthetasep_frac is not None and ( self.nthetasep_frac <= 0 or self.nthetasep_frac >= 1): raise DREAMException( "XiGrid {}: Invalid value assigned to 'nthetasep_frac': {}. Must be > 0 and < 1." .format(self.name, self.nthetasep_frac)) elif self.nthetasep is None and self.nthetasep_frac is None: raise DREAMException( "XiGrid {}: Neither 'nthetasep' nor 'nthetasep_frac' have been specified." .format(self.name)) elif self.thetasep is None or self.thetasep <= 0 or self.thetasep >= np.pi: raise DREAMException( "XiGrid {}: Invalid value assigned to 'thetasep': {}. Must be > 0 and < pi." .format(self.name, self.thetasep))
def verifySettings(self): """ Verify that all (mandatory) settings are set and consistent. """ if self.type == TYPE_PXI: if self.enabled: self.pgrid.verifySettings() self.xigrid.verifySettings() elif self.type == TYPE_PPARPPERP: raise DREAMException("{}: No support implemented yet for 'ppar/pperp' grids.".format(self.name)) else: raise DREAMException("{}: Unrecognized momentum grid type specified: {}.".format(self.name, self.type))
def set(self, enabled=True, ttype=1, np=100, nxi=1, pmax=None): """ Set all settings for this hot-tail grid. """ self.enabled = enabled self.type = ttype if self.type == TYPE_PXI: self.pgrid = PGrid(self.name, np=np, pmax=pmax) self.xigrid = XiGrid(self.name, nxi=nxi) elif self.type == TYPE_PPARPPERP: raise DREAMException("No support implemented yet for 'ppar/pperp' grids.") else: raise DREAMException("Unrecognized momentum grid type specified: {}.".format(ttype))
def setNp(self, np): if np <= 0: raise DREAMException("{}: Invalid value assigned to 'np': {}. Must be > 0.".format(self.name, np)) elif np == 1: print("WARNING: {}: np = 1. Consider disabling the hot-tail grid altogether.".format(self.name)) self.pgrid.setNp(np)
def setPmax(self, pmax): if pmax <= 0: raise DREAMException( "PGrid {}: Invalid value assigned to 'pmax': {}. Must be > 0.". format(self.name, pmax)) self.pmax = float(pmax)
def fromdict(self, data): """ Load settings from the given dictionary. """ def scal(v): if type(v) == np.ndarray: return v[0] else: return v self.type = data['type'] if 'wall_radius' in data: self.b = data['wall_radius'] if type(self.b) == np.ndarray: self.b = float(self.b[0]) else: self.b = float(self.b) if self.type == TYPE_CYLINDRICAL or self.type == TYPE_ANALYTIC_TOROIDAL or self.type == TYPE_NUMERICAL: self.a = data['a'] self.nr = data['nr'] self.r0 = data['r0'] if 'r_f' in data: self.r_f = data['r_f'] if self.type == TYPE_CYLINDRICAL: self.B0 = data['B0'] elif self.type == TYPE_ANALYTIC_TOROIDAL: self.R0 = data['R0'] self.ntheta = data['ntheta'] self.Delta = data['Delta']['x'] self.Delta_r = data['Delta']['r'] self.delta = data['delta']['x'] self.delta_r = data['delta']['r'] self.GOverR0 = data['GOverR0']['x'] self.GOverR0_r = data['GOverR0']['r'] self.kappa = data['kappa']['x'] self.kappa_r = data['kappa']['r'] self.psi_p0 = data['psi_p0']['x'] self.psi_p0_r = data['psi_p0']['r'] elif self.type == TYPE_NUMERICAL: self.num_filename = data['filename'] self.ntheta = data['ntheta'] if 'fileformat' in data: self.num_fileformat = data['fileformat'] else: raise DREAMException( "RadialGrid: Unrecognized grid type specified: {}.".format( self.type)) if 'ripple' in data: self.ripple_ncoils = int(scal(data['ripple']['ncoils'])) self.ripple_deltacoils = float(scal(data['ripple']['deltacoils'])) self.ripple_m = data['ripple']['m'] self.ripple_n = data['ripple']['n'] self.ripple_dB_B = data['ripple']['x'] self.ripple_r = data['ripple']['r'] self.ripple_t = data['ripple']['t']
def setType(self, ttype): """ Set the type of p grid generator. """ if ttype == TYPE_UNIFORM or ttype == TYPE_BIUNIFORM: self.type = ttype else: raise DREAMException( "PGrid {}: Unrecognized grid type specified: {}.".format( self.name, self.type))
def setMajorRadius(self, R0): """ (Analytic toroidal) Set the tokamak major radius. """ if R0 <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to major radius 'R0': {}". format(R0)) self.R0 = float(R0)
def setType(self, ttype): """ Set the type of radial grid to use. """ types = [TYPE_CYLINDRICAL, TYPE_ANALYTIC_TOROIDAL, TYPE_NUMERICAL] if ttype in types: self.type = ttype else: raise DREAMException( "RadialGrid: Unrecognized grid type specified: {}.".format( ttype))
def setBiuniform(self, psep, npsep=None, npsep_frac=None): self.type = TYPE_BIUNIFORM self.psep = psep if npsep is not None: self.npsep = npsep self.npsep_frac = None elif npsep_frac is not None: self.npsep = None self.npsep_frac = npsep_frac else: raise DREAMException( "PGrid biuniform {}: npsep or npsep_frac must be set.")
def setNtheta(self, ntheta): """ (Analytic toroidal and numerical) Set the number of grid points to use for the poloidal grid on which bounce averages are calculated. """ if ntheta <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to 'ntheta': {}".format( ntheta)) self.ntheta = ntheta
def fromdict(self, name, data): """ Loads a momentum grid from the specified dictionary. """ self.name = name self.enabled = data['enabled'] self.type = data['type'] if self.enabled: if self.type == TYPE_PXI: self.pgrid = PGrid(name, data=data) self.xigrid = XiGrid(name, data=data) elif self.type == TYPE_PPARPPERP: raise DREAMException("No support implemented yet for loading 'ppar/pperp' grids.") else: raise DREAMException("Unrecognized momentum grid type specified: {}.".format(self.type)) else: # Set default grid self.set(enabled=False, ttype=self.type) self.verifySettings()
def setType(self, ttype): """ Set the type of xi grid generator. """ if ttype in [ TYPE_UNIFORM, TYPE_BIUNIFORM, TYPE_UNIFORM_THETA, TYPE_BIUNIFORM_THETA ]: self.type = ttype else: raise DREAMException( "XiGrid {}: Unrecognized grid type specified: {}.".format( self.name, self.type))
def verifySettingsShapeParameter(self, shapeparam): """ Verify the settings of the named shape parameter. :param str shapeparam: Name of shape parameter to verify settings for. """ v = getattr(self, shapeparam) r = getattr(self, shapeparam + '_r') if v is None or type(v) != np.ndarray: raise DREAMException( "RadialGrid: Invalid type of shape parameter '{}': {}.".format( shapeparam, type(v))) elif r is None or type(r) != np.ndarray: raise DREAMException( "RadialGrid: Invalid type of radial grid for shape parameter '{}': {}." .format(shapeparam, type(r))) if v.shape != r.shape: raise DREAMException( "RadialGrid: Dimensions mismatch between shape parameter '{}' {} and its radial grid {}." .format(shapeparam, v.shape, r.shape))
def todict(self, verify=True): """ Returns the settings in this object as a Python dictionary. """ if verify: self.verifySettings() data = {'type': self.type} if self.type == TYPE_CYLINDRICAL or self.type == TYPE_ANALYTIC_TOROIDAL or self.type == TYPE_NUMERICAL: data['a'] = self.a data['nr'] = self.nr data['r0'] = self.r0 data['wall_radius'] = self.b if self.r_f is not None: data['r_f'] = self.r_f if self.type == TYPE_CYLINDRICAL: data['B0'] = self.B0 elif self.type == TYPE_ANALYTIC_TOROIDAL: data['R0'] = self.R0 data['ntheta'] = self.ntheta data['Delta'] = {'x': self.Delta, 'r': self.Delta_r} data['delta'] = {'x': self.delta, 'r': self.delta_r} data['GOverR0'] = {'x': self.GOverR0, 'r': self.GOverR0_r} data['kappa'] = {'x': self.kappa, 'r': self.kappa_r} data['psi_p0'] = {'x': self.psi_p0, 'r': self.psi_p0_r} elif self.type == TYPE_NUMERICAL: data['filename'] = self.num_filename data['ntheta'] = self.ntheta if self.num_fileformat is not None: data['fileformat'] = self.num_fileformat else: raise DREAMException( "RadialGrid: Unrecognized grid type specified: {}.".format( self.type)) if self.ripple_ncoils > 0 or self.ripple_deltacoils > 0: data['ripple'] = { 'ncoils': self.ripple_ncoils, 'deltacoils': self.ripple_deltacoils, 'm': self.ripple_m, 'n': self.ripple_n, 'x': self.ripple_dB_B, 'r': self.ripple_r, 't': self.ripple_t } return data
def todict(self, verify=True): """ Returns a Python dictionary containing all settings of this MomentumGrid object. """ if verify: self.verifySettings() data = { 'enabled': self.enabled, 'type': self.type } if not self.enabled: return data if self.type == TYPE_PXI: data = {**data, **(self.pgrid.todict()), **(self.xigrid.todict())} elif self.type == TYPE_PPARPPERP: raise DREAMException("No support implemented yet for saving 'ppar/pperp' grids.") else: raise DREAMException("Unrecognized momentum grid type specified: {}.".format(self.type)) return data
def setNr(self, nr): """ (Cylindrical, Analytic toroidal) Set the number of radial grid points to use. """ if nr <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to 'nr': {}".format(nr)) if self.r_f is not None: print( "*WARNING* RadialGrid: Prescibing 'Nr' overrides the custom radial grid 'r_f'." ) self.r_f = None self.nr = int(nr)
def setBiuniform(self, xisep=None, nxisep=None, nxisep_frac=None, thetasep=None, nthetasep=None, nthetasep_frac=None): if xisep is not None: self.type = TYPE_BIUNIFORM self.xisep = float(xisep) if nxisep is not None: self.nxisep = int(nxisep) self.nxisep_frac = None elif nxisep_frac is not None: self.nxisep = None self.nxisep_frac = nxisep_frac else: raise DREAMException( "XiGrid biuniform {}: nxisep or nxisep_frac must be set.") elif thetasep is not None: self.type = TYPE_BIUNIFORM_THETA self.thetasep = float(thetasep) if nthetasep is not None: self.nthetasep = int(nthetasep) self.nthetasep_frac = None elif nthetasep_frac is not None: self.nthetasep = None self.nthetasep_frac = nthetasep_frac else: raise DREAMException( "XiGrid biuniform theta {}: nthetasep or nthetasep_frac must be set." ) else: raise DREAMException( "XiGrid biuniform {}: thetasep or xisep must be set.")
def setMinorRadius(self, a): """ (Cylindrical, Analytic toroidal) Set the plasma minor radius. """ if a <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to minor radius 'a': {}". format(a)) if self.r_f is not None: print( "*WARNING* RadialGrid: Prescibing 'Minor radius' a overrides the custom radial grid 'r_f'." ) self.r_f = None self.a = float(a)
def visualize(self, *args, ax=None, show=None, **kwargs): """ Visualize the current magnetic field. :param int nr: Number of flux surfaces to show. :param int ntheta: Number of poloidal angles per flux surface. """ # Ensure that settings are valid... self.verifySettings() if self.type == TYPE_ANALYTIC_TOROIDAL: self.visualize_analytic(*args, ax=ax, show=show, **kwargs) elif self.type == TYPE_NUMERICAL: self.num_magneticfield.visualize(*args, ax=ax, show=show, **kwargs) else: raise DREAMException( "RadialGrid: Can only visualize the analytic toroidal magnetic field." )
def setShapeParameter(self, name, data, r=0.0): """ (Analytic toroidal) Set a specific magnetic field shape parameter. """ if name not in ['Delta', 'delta', 'GOverR0', 'kappa', 'psi_p0']: raise DREAMException( "RadialGrid: Invalid name of shape parameter specified: '{}'.". format(name)) if type(data) in [float, int]: data = np.array([float(data)]) elif type(data) == list: data = np.array(data) if type(r) in [float, int]: r = np.array([float(r)]) elif type(r) == list: r = np.array(r) setattr(self, name, data) setattr(self, name + '_r', r)
def todict(self, verify=True): """ Returns a Python dictionary containing all settings of this XiGrid object. """ if verify: self.verifySettings() data = { 'xigrid': self.type, 'nxi': self.nxi, } if self.type == TYPE_BIUNIFORM: if self.nxisep is not None: data['nxisep'] = self.nxisep elif self.nxisep_frac is not None: #data['nxisep'] = int(round(self.nxi*self.nxisep_frac)) data['nxisep_frac'] = self.nxisep_frac else: raise DREAMException( "XiGrid {}: Neither 'nxisep' nor 'nxisep_frac' have been specified." .format(self.name)) data['xisep'] = self.xisep elif self.type == TYPE_BIUNIFORM_THETA: if self.nthetasep is not None: data['nxisep'] = self.nthetasep elif self.nthetasep_frac is not None: #data['nxisep'] = int(round(self.nxi*self.nthetasep_frac)) data['nxisep_frac'] = self.nthetasep_frac data['xisep'] = self.thetasep elif self.type == TYPE_CUSTOM: data['xi_f'] = self.xi_f return data
def verifySettings(self): """ Verify that all (mandatory) settings are set and consistent. """ if self.type == TYPE_UNIFORM or self.type == TYPE_BIUNIFORM or self.type == TYPE_CUSTOM: if self.np is None or self.np <= 0: raise DREAMException( "PGrid {}: Invalid value assigned to 'np': {}. Must be > 0." .format(self.name, self.np)) elif self.pmax is None or self.pmax <= 0: raise DREAMException( "PGrid {}: Invalid value assigned to 'pmax': {}. Must be > 0." .format(self.name, self.pmax)) else: raise DREAMException( "PGrid {}: Unrecognized grid type specified: {}.".format( self.name, self.type)) if self.type == TYPE_BIUNIFORM: if self.npsep is not None and (self.npsep <= 0 or self.npsep >= self.np): raise DREAMException( "PGrid {}: Invalid value assigned to 'npsep': {}. Must be > 0 and < np." .format(self.name, self.npsep)) elif self.npsep_frac is not None and (self.npsep_frac <= 0 or self.npsep_frac >= 1): raise DREAMException( "PGrid {}: Invalid value assigned to 'npsep_frac': {}. Must be > 0 and < np." .format(self.name, self.npsep_frac)) elif self.npsep is None and self.npsep_frac is None: raise DREAMException( "PGrid {}: Neither 'npsep' nor 'npsep_frac' have been set." .format(self.name)) elif self.psep is None or self.psep <= 0 or self.psep >= self.pmax: raise DREAMException( "PGrid {}: Invalid value assigned to 'psep': {}. Must be > 0 and < pmax." .format(self.name, self.psep))
def setNxi(self, nxi): if nxi <= 0: raise DREAMException("{}: Invalid value assigned to 'nxi': {}. Must be > 0.".format(self.name, nxi)) self.xigrid.setNxi(nxi)
def verifySettings(self): """ Verfiy that the RadialGrid settings are consistent. """ types = [TYPE_CYLINDRICAL, TYPE_ANALYTIC_TOROIDAL, TYPE_NUMERICAL] if self.type in types: if (self.a is None or self.a <= 0) and self.r_f is None: raise DREAMException( "RadialGrid: Invalid value assigned to minor radius 'a': {}" .format(self.a)) elif (self.r0 is None or self.r0 < 0) and self.r_f is None: raise DREAMException( "RadialGrid: Invalid value assigned to innermost simulated radius 'r0': {}" .format(self.r0)) elif self.b is None or self.b < self.a: raise DREAMException( "RadialGrid: Invalid value assigned to wall radius 'b' (must be explicitly set to >= 'a' using 'setWallRadius'): " .format(self.b)) if self.r0 >= self.a and self.r_f is None: raise DREAMException( "RadialGrid: 'r0' must be strictly less than 'a'.") if self.nr <= 0 and self.r_f is None: raise DREAMException( "RadialGrid: Invalid value assigned 'nr': {}. Must be > 0." .format(self.nr)) if not np.isscalar(self.b): raise DREAMException( "RadialGrid: The specified wall radius is not a scalar: {}." .format(self.b)) if self.type == TYPE_CYLINDRICAL: if self.B0 is None or self.B0 <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to 'B0': {}".format( self.B0)) elif self.type == TYPE_ANALYTIC_TOROIDAL: if self.R0 is None or self.R0 <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to tokamak major radius 'R0': {}" .format(self.R0)) elif self.ntheta <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to 'ntheta': {}. Must be > 0." .format(self.ntheta)) self.verifySettingsShapeParameter('Delta') self.verifySettingsShapeParameter('delta') self.verifySettingsShapeParameter('GOverR0') self.verifySettingsShapeParameter('kappa') self.verifySettingsShapeParameter('psi_p0') if np.size(self.Delta_r) > 1: if self.Delta_r[0] == 0 and self.Delta[0] != 0: print( "*WARNING* RadialGrid: Shape parameter 'Delta' (Shafranov shift) is non-zero at r=0, which is inconsistent (add Delta(0) to the major radius R0 instead)" ) elif self.Delta != 0: print( "*WARNING* RadialGrid: Shape parameter 'Delta' (Shafranov shift) is assigned a constant non-zero value. It is recommended to add its value to the major radius R0 instead" ) if np.size(self.delta_r) > 1: if self.delta_r[0] == 0 and self.delta[0] != 0: print( "*WARNING* RadialGrid: Shape parameter 'delta' (triangularity) is non-zero at r=0, which is inconsistent with Grad-Shafranov" ) elif self.type == TYPE_NUMERICAL: if type(self.num_filename) != str: raise DREAMException( "RadialGrid: No numerical magnetic field file specified.") elif not pathlib.Path(self.num_filename).is_file(): raise DREAMException( "RadialGrid: The specified numerical magnetic field file does not exist." ) elif self.ntheta <= 0: raise DREAMException( "RadialGrid: Invalid value assigned to 'ntheta': {}. Must be > 0." .format(self.ntheta)) formats = [FILE_FORMAT_LUKE] if (self.num_fileformat is not None) and (self.num_fileformat not in formats): raise DREAMException( "RadialGrid: Unrecognized file format specified for numerical magnetic field: {}." .format(self.num_fileformat)) else: raise DREAMException( "RadialGrid: Unrecognized grid type specified: {}.".format( self.type)) # Ripple settings if self.ripple_ncoils > 0 or self.ripple_deltacoils > 0: if type(self.ripple_m) != np.ndarray or self.ripple_m.ndim != 1: raise EquationException( "RadialGrid: Invalid type or shape of 'ripple_m'.") elif type(self.ripple_n) != np.ndarray or self.ripple_n.ndim != 1: raise EquationException( "RadialGrid: Invalid type or shape of 'ripple_n'.") elif self.ripple_m.size != self.ripple_n.size: raise EquationException( "RadialGrid: 'ripple_m' and 'ripple_n' must have the same number of elements." ) elif type(self.ripple_r) != np.ndarray or self.ripple_r.ndim != 1: raise EquationException( "RadialGrid: Invalid type or shape of 'ripple_r'.") elif type(self.ripple_t) != np.ndarray or self.ripple_t.ndim != 1: raise EquationException( "RadialGrid: Invalid type or shape of 'ripple_t'.") elif type(self.ripple_dB_B ) != np.ndarray or self.ripple_dB_B.shape != ( self.ripple_m.size, self.ripple_t.size, self.ripple_r.size): raise EquationException( "RadialGrid: Invalid type or shape of 'ripple_dB_B'.". format(self.ripple_dB_B))