def construct_eos_for_material(material_tag,units=None,etamin=0.94,etamax=100.0): """Return a spheral EOS object for a material identified by tag. construct_eos_for_material(mtag,units) calls the appropriate spheral eos constructor for the material identified by mtag, which must be one of the keys defined in the global shelpers.material_dictionary. This dictionary also includes additional arguments to be passed to the constructor, when necessary. The etamin and etamax optional arguments have slightly different meaning depending on which EOS constructor is actually used. Currently implemented constructors are: Tillotson : the value of etamin is passed to the etamin_solid parameter of the constructor. This is used to limit tensional pressure when the material is no longer solid. (Note that the spheral constructor also has an etamin parameter, which is used to prevent underflows in the pressure computation.) ANEOS : Not yet implemented. All pcs runs should use this method to create equations of state, instead of calling the spheral constructors directly, in order to allow automatic record keeping of what material was used in a given run. This also allows reusing "pre cooked" node lists in new runs. The file <pcs>/MATERIALS.md should contain a table of available material tags. See also: material_dictionary """ # Make sure we are not wasting our time. assert isinstance(material_tag,str) assert material_tag.lower() in material_dictionary.keys() if units is None: units = sph.PhysicalConstants(1,1,1) assert isinstance(units,sph.PhysicalConstants) assert isinstance(etamin,float) assert isinstance(etamax,float) # Build eos using our internal dictionary mat_dict = material_dictionary[material_tag.lower()] eos_constructor = mat_dict['eos_constructor'] eos_arguments = mat_dict['eos_arguments'] eos = None if mat_dict['eos_type'] == 'Tillotson': eos = eos_constructor(eos_arguments['materialName'], 1e-20, 1e20, units, etamin_solid=etamin) eos.uid = mat_dict['eos_id'] # Fix for LLNL ignoring the min eta requirement of Tillotson eos.minimumPressure = eos.pressure( eos.etamin_solid*eos.referenceDensity, 0) eos.minimumPressureType = 1 # 0: floor 1: zero pass else: print "EOS type {} not yet implemented".format(mat_dict['eos_type']) pass # And Bob's our uncle return eos
def __init__(self, R, eos, rho0=None, rmin=0, units=None, nbins=100): """Class constructor for quasi-incompressible density profile. Assuming a barely compressible, one-layer planet, a pressure profile in hydrostatic equilibrium can be found by integrating the hydrostatic equation with constant density. The equation of state can then be inverted to provide a density profile consistent with this pressure profile. Although the resulting pressure/density state is not strictly self consistent, it may be used as a good approximation for small planets that are not expected to be highly compressed. This class generates, in the constructor, a density profile: a vector of radii and a vector of corresponding densities. The __call__ method is used to extract a density for an arbitrary radius by interpolation. This is to provide the interface used by some of the existing node generators in SPHERAL. Parameters ---------- R : float > 0 Radius of uncompressed planet. eos : SolidSpheral3d.EquationOfState3d Equation-of-state of planet material. rho0 : float > 0, optional Guess for density at surface. If not provided eos.referenceDensity will be used. rMin : float >=0, optional Bottom of profile to be computed. Default is 0. units : SolidSpheral3d.PhysicalConstants, optional Units object if arguments are not in MKS. Must match constants member of eos. Default is SolidSpheral3d.PhysicalConstants(1,1,1). nbins : int >= 10, optional Number of interpolation points in [rMin,R]. """ # Minimal input checking assert np.isreal(R) and R > 0 assert np.isreal(rmin) and rmin < R assert isinstance(eos, sph.EquationOfState3d) assert type(nbins) is type(1) and nbins >= 10 if rho0 is None: rho0 = eos.referenceDensity assert type(rho0) is type(1.0) and rho0 > 0 if units is None: units = sph.PhysicalConstants(1,1,1) assert isinstance(units, sph.PhysicalConstants) assert units.G == eos.constants.G # Local variables rvec = np.linspace(rmin, R, num=nbins) dvec = np.ones(rvec.size)*np.NaN pvec = np.ones(rvec.size)*np.NaN # Step one - calculate pressure profile G = units.G for k in range(rvec.size): pvec[k] = 2*np.pi/3*G*rho0**2*(R**2 - rvec[k]**2) assert np.all(np.isfinite(pvec)) # Step two - lion hunt to invert eos and get a density def f(x): return pressure(eos,x,0) - p_hs for k in range(pvec.size): p_hs = pvec[k] x_hi = eos.referenceDensity*2 x_lo = eos.referenceDensity/2 while (x_hi - x_lo) > 1e-12*eos.referenceDensity: x_hs = (x_lo + x_hi)/2 if f(x_hs) > 0: x_hi = x_hs else: x_lo = x_hs pass pass dvec[k] = x_hs assert np.all(np.isfinite(dvec)) # Store object data self.rvec = rvec self.dvec = dvec self.pvec = pvec self.units = units # And Bob's our uncle. return
# currently used by pcs into the global workspace. There you can interactively # call the EOS methods and compare different materials. This script can also be # used to extract the code snippets needed to create EOS objects in spheral runs. #------------------------------------------------------------------------------- import sys, os import SolidSpheral3d as sph # The top-level spheral module importer #------------------------------------------------------------------------------- # Setup #------------------------------------------------------------------------------- # Show signs of life. print "Loading spheral equations of state..." # EOS constructors take a units object. I usually work in MKS. units = sph.PhysicalConstants(1.0, # Unit length in meters 1.0, # Unit mass in kg 1.0) # Unit time in seconds #------------------------------------------------------------------------------- # Tillotson EOS for common materials #------------------------------------------------------------------------------- mats = ['Granite', 'Basalt', 'Nylon', 'Pure Ice', '30% Silicate Ice', 'Water'] etamin, etamax = 0.94, 10.0 pext, pmin, pmax = 0.0, -1e200, 1e200 # these are actually the defaults EOSes = [sph.TillotsonEquationOfState(mat, 1e-20, 1e20, units, etamin_solid = etamin, externalPressure = pext, minimumPressure = pmin, maximumPressure = pmax) for mat in mats] granite = EOSes[0] basalt = EOSes[1] nylon = EOSes[2]
def __init__(self, R, rCore, eosMantle, eosCore, nbins = 100, units=None): """Class constructor for quasi-incompressible two-layer density profile.""" # Minimal input checking assert True if units is None: units = sph.PhysicalConstants(1,1,1) assert isinstance(units, sph.PhysicalConstants) assert units.G == eosMantle.constants.G == eosCore.constants.G # Local variables rvec = np.linspace(0, R, num=nbins) dvec = np.ones(rvec.size)*np.NaN pvec = np.ones(rvec.size)*np.NaN rc = rCore rhoc = eosCore.referenceDensity rhom = eosMantle.referenceDensity assert 0 < rc < R assert rhom <= rhoc r_inner = rvec[rvec <= rc] r_outer = rvec[rvec > rc] # Step one - calculate pressure profile G = units.G c2 = 4*np.pi/3*G*(0.5*rhom**2*R**2 - rhom*(rhoc - rhom)*rc**3/R) c1 = 4*np.pi/3*G*(0.5*rhoc**2 - 1.5*rhom**2 + rhoc*rhom)*rc**2 + c2 p_inner = np.ones(r_inner.size)*np.NaN p_outer = np.ones(r_outer.size)*np.NaN for k in range(r_inner.size): p_inner[k] = c1 - 4*np.pi/3*G*0.5*rhoc**2*r_inner[k]**2 for k in range(r_outer.size): p_outer[k] = c2 - 4*np.pi/3*G*(0.5*rhom**2*r_outer[k]**2 - rhom*(rhoc - rhom)*rc**3/r_outer[k]) assert np.all(np.isfinite(p_inner)) assert np.all(np.isfinite(p_outer)) pvec = np.concatenate((p_inner, p_outer)) # Step two - lion hunt to invert eos and get a density def f(x): return pressure(eos,x,0) - p_hs for k in range(rvec.size): p_hs = pvec[k] if rvec[k] <= rc: eos = eosCore else: eos = eosMantle x_hi = eos.referenceDensity*2 x_lo = eos.referenceDensity/2 while (x_hi - x_lo) > 1e-12*eos.referenceDensity: x_hs = (x_lo + x_hi)/2 if f(x_hs) > 0: x_hi = x_hs else: x_lo = x_hs pass pass dvec[k] = x_hs assert np.all(np.isfinite(dvec)) # Store object data self.rvec = rvec self.dvec = dvec self.pvec = pvec self.units = units # And Bob's our uncle. return
import mpi # Mike's simplified mpi wrapper import SolidSpheral3d as sph # The top-level spheral module importer from GenerateNodeDistribution3d import GenerateNodeDistribution3d # basic nl-gens from VoronoiDistributeNodes import distributeNodes3d # the load distributer pcsbase = '' # Edit this with full path to <pcs> if you see an ImportError. sys.path += ['..', pcsbase, os.getenv('PCSBASE', '')] import shelpers # My module of some helper functions #------------------------------------------------------------------------------- # Construct a minimal spheral simulation structure, consisting of a node list, a # node lists generator, a node list distributer, a physics package, an integrator, # and a controller. #------------------------------------------------------------------------------- # First, create an equation of state. units = sph.PhysicalConstants(1.0, 1.0, 1.0) eos = shelpers.construct_eos_for_material('h2oice', units) # Create an empty node list. nodes = sph.makeFluidNodeList('nodelist', eos) # Create a stock generator. generator = GenerateNodeDistribution3d(2, 2, 2, eos.referenceDensity, distributionType='lattice') # Distribute nodes to ranks (suppress with any cl arg to speed things up). if len(sys.argv) == 1: distributeNodes3d((nodes, generator))