class CylMeshGenerator(BaseMeshGenerator, BaseCylMixin): """ Simple 3D cylindrical mesh """ csx = properties.Float("cell size in the x-direction", default=25.) # padding factors in each direction pfx = properties.Float("padding factor to pad to infinity", default=1.5) # number of extra cells horizontally, above the air-earth interface and # below the casing nch = properties.Integer( "number of cells to add on each side of the mesh horizontally", default=10.) # Instantiate the class with casing parameters def __init__(self, **kwargs): super(CylMeshGenerator, self).__init__(**kwargs) self._discretizePair = discretize.CylMesh # number of cells in each direction @property def ncx(self): if getattr(self, '_ncx', None) is None: self._ncx = int(np.ceil(self.domain_x / self.csx) + self.nch) return self._ncx # cell spacings in each direction @property def hx(self): if getattr(self, '_hx', None) is None: self._hx = utils.meshTensor([(self.csx, self.ncx), (self.csx, self.npadx, self.pfx)]) return self._hx
class RasterMetaData(properties.HasProperties): """An object to contain all the information for a single swath. """ #https://landsat.usgs.gov/how-do-landsat-8-band-combinations-differ-landsat-7-or-landsat-5-satellite-data # Metadata data_provider = properties.String('The data provider') satellite = properties.String('The satellite from which data was aquired') instrument = properties.String('The instrument on the satellite') acquisition_date = properties.String('The date of acquisition', required=False) scene_center_time = properties.String('Center time', required=False) level1_production_date = properties.String('Production date', required=False) solar_angles = properties.Instance('The solar angles', SolarAngle, required=False) earth_sun_distance = properties.Float('The earth-sun distance', required=False) product_id = properties.String('Data product ID', required=False) lpgs_metadata_file = properties.String('metadata file', required=False) wrs = properties.Instance('WRS', WRS, required=False) # TODO modis = properties.Instance('Modis', Modis, required=False) corner = properties.List('The corner points', prop=Corner) # Spatial Reference bounding_coordinates = properties.Instance('The bounding coordinates', BoundingCoordinates) projection_information = properties.Instance('The projection', Projection) orientation_angle = properties.Float('The orientation angle', min=-360.0, max=360.0)
class BaseWaveform(properties.HasProperties): hasInitialFields = properties.Bool( "Does the waveform have initial fields?", default=False ) offTime = properties.Float( "off-time of the source", default=0. ) eps = properties.Float( "window of time within which the waveform is considered on", default=1e-9 ) def __init__(self, **kwargs): Utils.setKwargs(self, **kwargs) def _assertMatchesPair(self, pair): assert isinstance(self, pair), ( "Waveform object must be an instance of a %s " "BaseWaveform class.".format(pair.__name__) ) def eval(self, time): raise NotImplementedError def evalDeriv(self, time): raise NotImplementedError # needed for E-formulation
class VTEMWaveform(BaseWaveform): offTime = properties.Float( "off-time of the source", default=4.2e-3 ) peakTime = properties.Float( "Time at which the VTEM waveform is at its peak", default=2.73e-3 ) a = properties.Float( "parameter controlling how quickly the waveform ramps on", default=3. ) def __init__(self, **kwargs): BaseWaveform.__init__(self, hasInitialFields=False, **kwargs) def eval(self, time): if time <= self.peakTime: return ( (1. - np.exp(-self.a*time/self.peakTime))/(1.-np.exp(-self.a)) ) elif (time < self.offTime) and (time > self.peakTime): return -1. / (self.offTime-self.peakTime) * (time - self.offTime) else: return 0.
class Layers(Halfspace): """ A model containing an arbitrary number of layers """ sigma_layers = properties.List( "list containing the conductivity of each of the layers (S/m)", properties.Float( "conductivity of the layer (S/m)", min=0. ), default=[SIGMA_BACK] ) layer_tops = properties.List( "top of each of the layers", properties.Float( "top of each of the layers, z (m)" ), default=[0.] ) # todo: sanity checking that sigma_layers and layer_tops the same size def sigma(self, mesh): """ Construct the conductivity model on a mesh :param discretize.BaseMesh mesh: mesh to put conductivity model on """ sigma = super(Layers, self).sigma(mesh) for z, sig in zip(self.layer_tops, self.sigma_layers): z_inds = mesh.gridCC[:, 2] < z sigma[z_inds] = sig return sigma
class CameraStandard(_BaseCamera): """Base class for Standard Cameras Both orthographic and perspective cameras are built on this """ mode = properties.StringChoice( 'View mode of camera', choices=['perspective', 'orthographic'], default='orthographic', ) target = properties.Vector3( 'Center of rotation of camera relative to the scene origin', default=lambda: [0., 0., 0.], ) radius = properties.Float( 'Distance of camera to target', default=5., ) zoom = properties.Float( 'Zoom level of camera', default=1., ) rotation = properties.List( 'Quaternion rotation of the camera relative to the scene', properties.Float(''), min_length=4, max_length=4, default=lambda: [0., 0., 0., 1.], ) up_direction = properties.Vector3( 'Up direction of camera', default='up', )
class CircularLoop(MagDipole): radius = properties.Float("radius of the loop source", default=1.0, min=0.0) current = properties.Float("current in the loop", default=1.0) N = properties.Float("number of turns in the loop", default=1.0) def __init__(self, receiver_list=None, **kwargs): super(CircularLoop, self).__init__(receiver_list, **kwargs) @property def moment(self): return np.pi * self.radius**2 * self.current * self.N def _srcFct(self, obsLoc, coordinates="cartesian"): # return MagneticLoopVectorPotential( # self.loc, obsLoc, component, mu=self.mu, radius=self.radius # ) if getattr(self, "_loop", None) is None: self._loop = CircularLoopWholeSpace( mu=self.mu, location=self.loc, orientation=self.orientation, radius=self.radius, current=self.current, ) return self._loop.vector_potential(obsLoc, coordinates)
class RockType(properties.HasProperties): mat_type = properties.String( 'The rock type name corresponding to ``MaterialComponent.mat_type``.') Kd = properties.List( 'Dimensionless solid sorption coefficient of the component', Param) KdFactor = properties.List( 'Dimensionless solid sorption coefficient of the component', Param) tort = properties.List('The Tortuosity parameters per phase.', EqnParams) pc = properties.List('The pressure parameters per phase.', EqnParams) kr = properties.List('The relative permeability/saturation per phase.', EqnParams) K0 = properties.Float('The Kx value') K1 = properties.Float('The Kx value') K2 = properties.Float('The Kx value') porosity = properties.Float('The fractional porosity', min=0., max=1.) solid_density = properties.Float('The solid density value.') #tcond #Cp @classmethod def _create(cls, mat_type, values, validate=True): if not isinstance(values, dict): raise RuntimeError('Input values must be a dictionary') props = cls() props.mat_type = mat_type for k, v in values.items(): if k in cls._props: # If the value is simple, set it! if not isinstance(cls._props[k], properties.List): p = props._props.get(k) props._set(k, p.from_json(v)) elif k in ['Kd', 'KdFactor']: # v is a dictionary of name value pairs of params props._set( k, [Param(name=n, value=float(p)) for n, p in v.items()]) elif k in ['tort', 'pc', 'kr']: phases = [] for phase, eqns in v.items(): opt = eqns.pop('option') params = [ Param(name=n, value=float(p)) for n, p in eqns.items() ] phases.append( EqnParams(phase=phase, equation=opt, params=params)) props._set(k, phases) else: #warnings.warn("({}:{}) property is not valid.".format(k, v)) pass else: print('WARN: {} not a property of this class.'.format(k)) if validate: props.validate() return props
def test_numbers(self): with self.assertRaises(TypeError): pi = properties.Integer('My int', max=0) pi.min = 10 with self.assertRaises(TypeError): pi = properties.Integer('My int', min=10) pi.max = 0 class NumOpts(properties.HasProperties): myint = properties.Integer("My int") myfloat = properties.Float("My float") myfloatmin = properties.Float("My min float", min=10.) myfloatmax = properties.Float("My max float", max=10.) myfloatrange = properties.Float("My max float", min=0., max=10.) nums = NumOpts() with self.assertRaises(ValueError): nums.myint = 1.5 with self.assertRaises(ValueError): nums.myfloat = [1.0, 2.0] with self.assertRaises(ValueError): nums.myfloatmin = 0. with self.assertRaises(ValueError): nums.myfloatmax = 20. with self.assertRaises(ValueError): nums.myfloatrange = -10. nums.myint = 1. assert nums.myint == 1 nums.myfloat = 1 assert nums.myfloat == 1. nums.myfloatmin = nums.myfloatmax = nums.myfloatrange = 10. nums.myfloat = np.float32(1) assert nums.myfloat == 1. assert properties.Integer.to_json(5) == 5 assert properties.Float.to_json(5.) == 5. assert properties.Float.to_json(np.nan) == 'nan' assert properties.Float.to_json(np.inf) == 'inf' assert properties.Integer.from_json(5) == 5 assert properties.Integer.from_json('5') == 5 assert properties.Float.from_json(5.0) == 5. assert properties.Float.from_json('5.0') == 5. assert np.isnan(properties.Float.from_json('nan')) assert np.isinf(properties.Float.from_json('inf')) serialized = {'myint': 1, 'myfloat': 1., 'myfloatmin': 10., 'myfloatmax': 10., 'myfloatrange': 10.} self.assertEqual(nums.serialize(include_class=False), serialized) assert NumOpts.deserialize(serialized).myfloatrange == 10. assert properties.Integer('').equal(5, 5) assert properties.Float('').equal(5, 5.) assert not properties.Float('').equal(5, 5.1) assert not properties.Float('').equal('hi', 'hi')
class BaseElectricDipole(BaseDipole): length = properties.Float(help='length of the dipole (m)', default=1.0, min=0.0) current = properties.Float(help='size of the injected current (A)', default=1.0, min=0.0)
class CasingMeshGenerator(BaseMeshGenerator, BaseCylMixin): """ Mesh that makes sense for casing examples """ # X-direction of the mesh csx1 = properties.Float("finest cells in the x-direction", default=2.5e-3) csx2 = properties.Float("second uniform cell region in x-direction", default=25.) pfx1 = properties.Float("padding factor to pad from csx1 to csx2", default=1.3) pfx2 = properties.Float("padding factor to pad to infinity", default=1.5) # Instantiate the class with casing parameters def __init__(self, **kwargs): super(CasingMeshGenerator, self).__init__(**kwargs) self._discretizePair = discretize.CylMesh @property def ncx1(self): """number of cells with size csx1""" return np.ceil(self.modelParameters.casing_b / self.csx1 + 2) @property def npadx1(self): """number of padding cells to get from csx1 to csx2""" return np.floor(np.log(self.csx2 / self.csx1) / np.log(self.pfx1)) @property def hx(self): """ cell spacings in the x-direction """ if getattr(self, '_hx', None) is None: # finest uniform region hx1a = Utils.meshTensor([(self.csx1, self.ncx1)]) # pad to second uniform region hx1b = Utils.meshTensor([(self.csx1, self.npadx1, self.pfx1)]) # scale padding so it matches cell size properly dx1 = sum(hx1a) + sum(hx1b) dx1 = np.floor(dx1 / self.csx2) hx1b *= (dx1 * self.csx2 - sum(hx1a)) / sum(hx1b) # second uniform chunk of mesh ncx2 = np.ceil((self.domain_x - dx1) / self.csx2) hx2a = Utils.meshTensor([(self.csx2, ncx2)]) # pad to infinity hx2b = Utils.meshTensor([(self.csx2, self.npadx, self.pfx2)]) self._hx = np.hstack([hx1a, hx1b, hx2a, hx2b]) return self._hx
class BaseElectricDipole(BaseDipole): """ Base class for electric current dipoles """ length = properties.Float("length of the dipole (m)", default=1.0, min=0.0) current = properties.Float("magnitude of the injected current (A)", default=1.0, min=0.0)
class ExponentialSinusoidSimulation(LinearSimulation): """ This is the simulation class for the linear problem consisting of exponentially decaying sinusoids. The rows of the G matrix are .. math:: \\int_x e^{p j_k x} \\cos(\\pi q j_k x) \\quad, j_k \\in [j_0, ..., j_n] """ n_kernels = properties.Integer( "number of kernels defining the linear problem", default=20 ) p = properties.Float("rate of exponential decay of the kernel", default=-0.25) q = properties.Float("rate of oscillation of the kernel", default=0.25) j0 = properties.Float("maximum value for :math:`j_k = j_0`", default=0.0) jn = properties.Float("maximum value for :math:`j_k = j_n`", default=60.0) def __init__(self, **kwargs): super(ExponentialSinusoidSimulation, self).__init__(**kwargs) @property def jk(self): """ Parameters controlling the spread of kernel functions """ if getattr(self, "_jk", None) is None: self._jk = np.linspace(self.j0, self.jn, self.n_kernels) return self._jk def g(self, k): """ Kernel functions for the decaying oscillating exponential functions. """ return np.exp(self.p * self.jk[k] * self.mesh.vectorCCx) * np.cos( np.pi * self.q * self.jk[k] * self.mesh.vectorCCx ) @property def G(self): """ Matrix whose rows are the kernel functions """ if getattr(self, "_G", None) is None: G = np.empty((self.n_kernels, self.mesh.nC)) for i in range(self.n_kernels): G[i, :] = self.g(i) * self.mesh.hx self._G = G return self._G
class BaseEM(properties.HasProperties): mu = properties.Float(help='Magnetic permeability.', default=mu_0, min=0.0) sigma = properties.Float(help='Electrical conductivity (S/m)', default=1.0, min=0.0) epsilon = properties.Float(help='Permitivity value', default=epsilon_0, min=0.0)
class Band(properties.HasProperties): """Contains raster metadata and data for a single band.""" # metadata attributes name = properties.String('Name of the band') data_type = properties.String('Band data type') nlines = properties.Integer('number of lines') nsamps = properties.Integer('number of samples') product = properties.String('Data product') # Not required app_version = properties.String('app version', required=False) production_date = properties.String('production date', required=False) resample_method = properties.String('resample method', required=False) category = properties.String('Band category', required=False) source = properties.String('Band source', required=False) qa_description = properties.String('QA description', required=False) # TODO: class_values percent_coverage = properties.Float('percent coverage', required=False) # metadata: All required short_name = properties.String('Short name') long_name = properties.String('Long display name') file_name = properties.String('Original file name') pixel_size = properties.Instance('The pixel size', PixelSize) # data information fill_value = properties.Integer('fill value', default=-9999) saturate_value = properties.Integer('Saturate value', required=False) add_offset = properties.Float('Add offset', required=False) data_units = properties.String('Data units', required=False) scale_factor = properties.Float('Scaling factor', required=False) valid_range = properties.Instance('The valid data range', ValidRange, required=False) radiance = properties.Instance('The radiance', Lum, required=False) reflectance = properties.Instance('The reflectance', Lum, required=False) thermal_const = properties.Instance('The thermal const', ThermalConst, required=False) bitmap_description = properties.Dictionary( 'band bitmap description (not always present)', required=False, key_prop=properties.String('Key value'), value_prop=properties.String('Bitmap value description')) # TODO: data validation causes a MAJOR slowdown. WAAAAYYY faster to not set # the data as a `properties` attribute. # data = properties.Array( # 'The band data as a 2D NumPy data', # shape=('*','*'), # ) data = None
class TriangularWaveform(TrapezoidWaveform): """ TriangularWaveform is a special case of TrapezoidWaveform where there's no pleateau """ offTime = properties.Float("off-time of the source") peakTime = properties.Float("Time at which the Triangular waveform is at its peak") def __init__(self, **kwargs): super(TriangularWaveform, self).__init__(**kwargs) self.hasInitialFields = False self.ramp_on = np.r_[0.0, self.peakTime] self.ramp_off = np.r_[self.peakTime, self.offTime]
class AddTask(Task): addend_a = properties.Float('First add argument') addend_b = properties.Float('Second add argument') class Result(BaseResult): value = properties.Float('Result of add operation') def __call__(self): self.report_status({'progress': 0., 'message': 'Starting'}) if self.addend_a == self.addend_b: raise PermanentTaskFailure() return self.Result(value=self.addend_a + self.addend_b)
class BaseEM(properties.HasProperties): """ Base class for electromanetics. Contains physical properties that are relevant to all problems that use Maxwell's equations """ mu = properties.Float("Magnetic permeability (H/m)", default=mu_0, min=0.0) sigma = properties.Float("Electrical conductivity (S/m)", default=1.0, min=0.0) epsilon = properties.Float("Permitivity value (F/m)", default=epsilon_0, min=0.0)
class CircularLoop(MagDipole): """ Circular loop magnetic source calculated by taking the curl of a magnetic vector potential. By taking the discrete curl, we ensure that the magnetic flux density is divergence free (no magnetic monopoles!). This approach uses a primary-secondary in frequency in the same fashion as the MagDipole. :param list rxList: receiver list :param float freq: frequency :param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`) :param string orientation: 'X', 'Y', 'Z' :param float moment: magnetic dipole moment :param float mu: background magnetic permeability """ radius = properties.Float("radius of the loop", default=1., min=0.) def __init__(self, rxList, freq, loc, **kwargs): super(CircularLoop, self).__init__( rxList, freq, loc, **kwargs ) def _srcFct(self, obsLoc, component): return emutils.MagneticLoopVectorPotential( self.loc, obsLoc, component, mu=self.mu, radius=self.radius, orientation=self.orientation )
class Volume(ObjectiveFunction.BaseObjectiveFunction): """ A regularization on the volume integral of the model .. math:: \phi_v = \frac{1}{2}|| \int_V m dV - \text{knownVolume} ||^2 """ knownVolume = properties.Float("known volume", default=0., min=0.) def __init__(self, mesh, **kwargs): self.mesh = mesh super(Volume, self).__init__(**kwargs) def __call__(self, m): return 0.5 * (self.estVol(m) - self.knownVolume)**2 def estVol(self, m): return np.inner(self.mesh.vol, m) def deriv(self, m): # return (self.mesh.vol * np.inner(self.mesh.vol, m)) return self.mesh.vol * (self.knownVolume - np.inner(self.mesh.vol, m)) def deriv2(self, m, v=None): if v is not None: return Utils.mkvc(self.mesh.vol * np.inner(self.mesh.vol, v)) else: # TODO: this is inefficent. It is a fully dense matrix return sp.csc_matrix(np.outer(self.mesh.vol, self.mesh.vol))
class SquareLoop(Point): """Square loop receiver Measurements with this type of receiver are the field, integrated over the area of the loop, then multiplied by the number of coils, then normalized by the dipole moment. As a result, the units for fields predicted with this type of receiver are the same as 'h', 'b', 'dhdt' and 'dbdt', respectively. """ width = properties.Float("Square loop width", min=1e-6) nTurns = properties.Integer("Number of loop turns", min=1, default=1) quadOrder = properties.Integer( "Order for numerical quadrature integration over loop", min=1, max=7, default=3) def __init__(self, locations, **kwargs): if locations.shape[1] != 3: raise ValueError( "Rx locations (xi,yi,zi) must be np.array(N,3) where N is the number of stations" ) super(SquareLoop, self).__init__(locations, **kwargs)
class ScalarColormap(ContentModel): """Length-128 color gradient with min/max values, used with ScalarData""" gradient = properties.Instance( 'length-128 ColorArray defining the gradient', ColorArray ) limits = properties.List( 'Data range associated with the gradient', prop=properties.Float(''), min_length=2, max_length=2, default=properties.undefined, ) @properties.validator('gradient') def _check_gradient_length(self, change): #pylint: disable=no-self-use """Ensure gradient is length-128""" if len(change['value']) != 128: raise ValueError('Colormap gradient must be length 128') @properties.validator('limits') def _check_limits_on_change(self, change): #pylint: disable=no-self-use """Ensure limits are valid""" if change['value'][0] > change['value'][1]: raise ValueError('Colormap limits[0] must be <= limits[1]') @properties.validator def _check_limits_on_validate(self): """Ensure limits are valid""" self._check_limits_on_change({'value': self.limits})
class LineCurrent(BaseFDEMSrc): """ Line current source. Given the wire path provided by the (n,3) loc array the cells intersected by the wire path are identified and integrated src terms are computed :param list rxList: receiver list :param float freq: src frequency :param (n,3) array locations: points defining src path """ location = properties.Array("location of the source", shape=("*", 3)) current = properties.Float("current in the line", default=1.0) def Mejs(self, simulation): if getattr(self, "_Mejs", None) is None: mesh = simulation.mesh locs = self.location self._Mejs = self.current * segmented_line_current_source_term( mesh, locs) return self._Mejs def getRHSdc(self, simulation): Grad = simulation.mesh.nodalGrad return Grad.T * self.Mejs(simulation) def s_m(self, simulation): return Zero() def s_e(self, simulation): if simulation._formulation != "EB": raise NotImplementedError( "LineCurrents are only implemented for EB formulations") return self.Mejs(simulation)
class SingleLayer(Halfspace): """ A model consisting of air, subsurface and a single subsurface layer """ sigma_layer = properties.Float( "conductivity of the layer (S/m)", default=1e-2 ) layer_z = properties.Array( "z-limits of the layer", shape=(2,), default=np.r_[-1000., -900.] ) def ind_layer(self, mesh): return ( (mesh.gridCC[:, 2] < self.layer_z[1]) & (mesh.gridCC[:, 2] > self.layer_z[0]) ) def sigma(self, mesh): sigma = super(self, sigma)(mesh) sigma[self.ind_layer(mesh)] = self.sigma_layer return sigma
class AnnotationInk(_BaseAnnotation): """Pen-drawn annotation on a slide""" path = properties.List( 'Ink path vertices, relative to position', properties.List('', properties.Float(''), min_length=2, max_length=2), max_length=2000, )
class HasDynamicProperty(properties.HasProperties): my_float = properties.Float('a float') @properties.Integer('a dynamic int') def my_doubled_int(self): return self.my_float * 2 my_doubled_int.deleter(5)
class BaseMagneticDipole(BaseDipole): """ Base class for magnetic dipoles """ moment = properties.Float("moment of the dipole (Am^2)", default=1.0, min=0.0)
class OptionsStaticOpacity(_BaseOptionsItem): """Option for a static opacity value""" value = properties.Float( 'Single opacity value', min=0.0, max=1.0, default=1.0, )
class Effect(BaseModel): """Represents an effect profile.""" euphoria = properties.Float( 'Euphoric effect.', default=0.0, ) creativity = properties.Float( 'Creativity effect', default=0.0, ) calming = properties.Float( 'Calming effect', default=0.0, ) numbness = properties.Float( 'Numbness effect', default=0.0, ) appetite_gain = properties.Float( 'Appetite gain effect', default=0.0, ) dry_mouth = properties.Float( 'Dry mouth effect', default=0.0, ) anxiety = properties.Float( 'Anxiety effect', default=0.0, )
class MenuItem(BaseModel): """Menu items for dispensaries.""" name = properties.String('Name of the item.', ) type = properties.StringChoice( 'Type of item.', choices=['strain', 'flower', 'extract', 'edible', 'product'], ) item = properties.Property('The strain, extract, edible, or product.', ) price = properties.Float( 'The price for the item. This is not set for strains and extracts.', ) price_half_gram = properties.Float( 'Price for one half gram of the item. This is not set for edibles ' 'and products.', ) price_gram = properties.Float( 'Price for one gram of this item. This is not set for edibles and ' 'products.', ) price_eighth = properties.Float( 'Price for one eighth ounce of this item. This is not set for ' 'edibles and products.', ) price_quarter = properties.Float( 'Price for one quarter ounce of this item. This is not set for ' 'edibles and products.', ) price_half_ounce = properties.Float( 'Price for one half ounce of this item. This is not set for ' 'edibles and products.', ) price_ounce = properties.Float( 'Price for one ounce of this item. This is not set for ' 'edibles and products.', )