def __init__(self): self.name = self.name_base SubductionCase.__init__(self,u(130,"Myr"),u(75,"Myr")) # Solve to imaginary model division to make # model outputs align with farallon-reheated case self.fake_underplating_time = u(24,'Myr') self.variable_term = 'qfric'
def run(self): plot_opts = dict( range=(0,1500)) crust = continental_crust.to_layer(interface_depth) solver = FiniteSolver(crust, constraints=(u(0,"degC"),u(600,"degC"))) # Assume arbitrarily that interface is at 600 degC crust_section = solver.steady_state() mantle = oceanic_mantle.to_layer(total_depth-interface_depth) mantle = Section([mantle]) # Set starting state section = stack_sections(crust_section, mantle) adiabat = AdiabatSolver( section, start_depth=self.underplating_depth, start_temp=asthenosphere_temperature) self.t = self.start_time self.section = adiabat() self.record("initial") self.do_underplating() self.solve_to_present()
def __init__(self, dT): Farallon.__init__(self) self.name = self.name_base+'-'+str(dT) self.underplating_duration = u(dT,'Myr') self.underplating_depth = u(80,'km') self.underplating_time = u(24,'Myr') self.variable_term = 'qfric'
def __init__(self, dT): ModelRunner.__init__(self) self.name = self.name_base+'-'+str(dT) self.underplating_duration = u(dT,'Myr') self.underplating_depth = u(30,'km') self.underplating_time = u(24,'Myr') self.start_time = self.underplating_time
def geobaric_gradient(pressure): rho0 = continental_crust.density g = u(9.8,'m/s^2') P = u(pressure,'GPa') rho1 = oceanic_mantle.density d0 = interface_depth a0 = P/g/rho0 if a0 < d0: return a0.into('km') a = P/g - rho0*d0 + rho1*d0 a/=rho1 return a.into('km')
def run(self): section = steady_state_section for flux in ((i+12)*5 for i in range(13)): q = u(flux,'mW/m^2') # Use steady-state model that assumes # a reasonable reduced heat flux from # the mantle, rather than that which # sums radiogenic heat. s = steady_state_mantle(section,q, characteristic_scale=u(10,'km'), heatflow_mantle_proportion=0.6) self.record(s,flux) echo("Saving section for " +style(str(q), fg='green') +" surface heat flux")
def on_step(solver, **kwargs): """ Function to change finite solver boundary conditions at each step """ self.step_function(solver, **kwargs) step = kwargs.pop("step") steps = kwargs.pop("steps") # How complete we will be at the # end of this step completion = (step + 1) / steps sz_depth = final_depth * completion # Decrease depth offset self.depth_offset = final_depth - sz_depth self._top_depth = sz_depth # Set temperature at the subduction # interface T = royden( (final_distance * completion).into("m"), sz_depth.into("m"), # Depth of interest sz_depth.into("m")) # Depth of subduction interface solver.set_constraints(upper=u(T, "degC"))
def do_underplating(self): self.record("before-underplating") dT = self.underplating_duration temp = asthenosphere_temperature adiabat = AdiabatSolver( self.section, start_depth=self.underplating_depth, start_temp=temp) # Apply adiabat to section self.section = adiabat() self.record("underplating-started", self.section) if dT.into('s') > 0: # We're holding the temperature # at the boundary for some length of time top, bottom = self.section.divide(self.underplating_depth) solver = FiniteSolver(top, constraints=(u(0,'degC'),temp), step_function=self.step_function) dTdz = adiabat.gradient[0] k = bottom.material_property('conductivity')[0] flux = -k*dTdz solver.set_constraints(lower=flux) res = solver(dT).profile self.section.profile[:len(res)] = res self.t -= dT self.record("underplating-ended") # Record a later timestep dT = u(6,'Myr') - dT if dT > u(0,'s'): self.finite_solve(self.t-dT) self.record("after-underplating")
def underplating(): name = "Underplating" print(name) start = u(20,"Myr") slab_window_upwelling = Section([ oceanic_mantle.to_layer(u(100,"km")) ], uniform_temperature=u(1300,"degC")) final_section = stack_sections( forearc, slab_window_upwelling) solver = AdvancedFiniteSolver( final_section, constraints=solver_constraints) return solver.solution( duration=start-present, steps=200, plotter=Plotter(range=(0,1400), title=name))
def finite_solve(self, end_time, **kwargs): defaults = dict( constraints = (u(0,"degC"), self.section.profile[-1]), step_function = self.step_function) for k,v in defaults.items(): if k not in kwargs: kwargs[k] = v self.log("Initializing finite solver") self.log("Constraints") labels = ('upper','lower') for l,c in zip(labels,kwargs['constraints']): self.log(" - "+l,c) duration = self.t - end_time solver = FiniteSolver(self.section, **kwargs) self.set_state(end_time,solver(duration))
def pre_subduction(self): """ Get an oceanic geotherm at emplacement and just before subduction begins. """ self.log("Start age", self.start_time) self.log("Subduction", self.subduction_time) oceanic = Section( [oceanic_mantle.to_layer(total_depth - interface_depth)]) ocean_model = GDHSolver(oceanic, T_max=solver_constraints[1]) t = u(0, "s") self.set_state(self.start_time, ocean_model(t)) self.record("initial") dt = self.t - self.subduction_time self.set_state(self.subduction_time, ocean_model(dt)) self.record("before-subduction")
def trace(self, depth, t=None): if self.depth_offset is None: _depth = depth else: _depth = depth - self.depth_offset assert _depth >= u(0,'km') if t is None: t = self.t # Because we are dealing with # final section only cell = int((_depth-self._top_depth).into('m')/self.dz) T = self.section.profile[cell] v = ModelTracer( run=self.__model, time=t.into("Myr"), final_depth=depth.into("km"), depth=_depth.into("km"), temperature=T.into("degC")) self.session.add(v)
def stepped_subduction(self, **kwargs): """ Method to subduct crust under a forearc with a geotherm modeled over a period of time, and capture a snapshot of underplating. The `velocity` option defines a subduction velocity that is used to move the subducting slab down the channel. If the `final_temperature` option is not set, the procedure will infer the final temperature of the subducted slab from a steady-state subduction geometery, using the method of Royden (1992). If the `final_temperature` option is set, the procedure will target that temperature at the final conditions of the interface. In this case, the Royden model parameters for this temperature will be found via optimization. """ final_distance = kwargs.pop("final_distance", underplating_distance) velocity = kwargs.pop("velocity", convergence_velocity) final_depth = kwargs.pop("final_depth", interface_depth) final_temperature = kwargs.pop("final_temperature", None) optimization_depth = kwargs.pop("optimization_depth", final_depth) # Thickness of foreland lithosphere # at the time of subduction # Presumably, foreland in this case refers # to the lower plate of the thrust advancing # into the subduction zone # The Royden model assumes that the foreland # is in thermal equilibrium, and the lithospheric # depth is being maintained, which is likely a # reasonable approximation for the timescales involved. self.log("Subduction velocity", velocity) dip = N.arctan(final_depth / final_distance).to("degrees") self.log("Dip of subduction zone", dip) # distance along subduction channel sub_distance = N.sqrt(final_distance**2 + final_depth**2) duration = (sub_distance / velocity).to("Myr") self.log("Duration of subduction", duration) echo("Beginning to subduct slab") kwargs.update(l=lithosphere_depth(self.section).into("m"), v=velocity.into("m/s")) self.log("Lithosphere depth", kwargs['l']) if final_temperature is None: royden = forearc_solver(**kwargs) else: # Optimize on rate of accretion royden = optimized_forearc( final_temperature, final_distance, # Optimize temperature above subduction # interface optimization_depth, **kwargs) self.log("Modeled upper-plate heat generation", royden.args[kwargs['vary']]) def on_step(solver, **kwargs): """ Function to change finite solver boundary conditions at each step """ self.step_function(solver, **kwargs) step = kwargs.pop("step") steps = kwargs.pop("steps") # How complete we will be at the # end of this step completion = (step + 1) / steps sz_depth = final_depth * completion # Decrease depth offset self.depth_offset = final_depth - sz_depth self._top_depth = sz_depth # Set temperature at the subduction # interface T = royden( (final_distance * completion).into("m"), sz_depth.into("m"), # Depth of interest sz_depth.into("m")) # Depth of subduction interface solver.set_constraints(upper=u(T, "degC")) # Set up finite solving for underplated slab kwargs['step_function'] = on_step i = self.section.profile[-1] solver = FiniteSolver(self.section, constraints=(u(0, 'degC'), i)) underplated = solver.final_section(duration=duration, **kwargs) # Construct the forearc geotherm forearc = Section(continental_crust.to_layer(final_depth)) temperatures = royden(final_distance.into("m"), forearc.cell_centers.into("m"), final_depth.into("m")) forearc.profile = u(temperatures, "degC") self.log( "Temperature at subduction interface " "at the time of underplating", forearc.profile[-1]) self.log("Subduction took", duration.to("Myr")) self.section = stack_sections(forearc, underplated) self.t -= duration self._top_depth = u(0, 'km') self.depth_offset = u(0, 'km') self.record("after-subduction")
class ModelRunner(object): trace_depths = u(40,'km'),u(75,'km') # Offset used for depth calculations for model tracers. # Reset when geotherms are stacked. depth_offset = None fields = ( "underplating_duration", "underplating_depth", "underplating_time", "start_time", "subduction_time") name_base = None def __init__(self, **info): self._top_depth = u(0,'km') for i in self.fields: setattr(self,i,None) def run(self): pass def set_state(self,time,section): self.t = time self.section = section def step_function(self, model=None, **kwargs): """ Step function for finite element solver that records model tracers """ t = self.t dt = kwargs.pop('simulation_time', None) if dt is not None: t -= dt if model is not None: v = model.value() self.section.profile[:len(v)] = v for depth in self.trace_depths: self.trace(depth, t=t) def finite_solve(self, end_time, **kwargs): defaults = dict( constraints = (u(0,"degC"), self.section.profile[-1]), step_function = self.step_function) for k,v in defaults.items(): if k not in kwargs: kwargs[k] = v self.log("Initializing finite solver") self.log("Constraints") labels = ('upper','lower') for l,c in zip(labels,kwargs['constraints']): self.log(" - "+l,c) duration = self.t - end_time solver = FiniteSolver(self.section, **kwargs) self.set_state(end_time,solver(duration)) def solve_to_present(self): self.finite_solve(present) # Record a final step self.step_function() self.record("final") def section_data(self, section): # Check if all cells are the same size z_ = section.cell_sizes.into('m') self.dz = z_[0] self.n_cells = int(record_max_depth.into('m')/self.dz) assert N.all(z_ == self.dz) T = list(section.profile[:self.n_cells].into("degC")) return T, self.dz def record(self, step_name, section=None, **kwargs): t = kwargs.pop("t",self.t).into("Myr") if section is None: section = self.section self.log("Saving profile "+style(step_name, fg='cyan')) T, dz = self.section_data(section) v = ModelProfile( name=step_name, time=t, run=self.__model, temperature=T, dz=dz) self.session.add(v) def setup_recorder(self): self.session = db.session() kw = dict(name=self.name) model = (self.session .query(ModelRun) .filter_by(**kw) .first()) if model is not None: secho("Deleting data from previous run", fg='red') self.session.delete(model) self.session.commit() vals = {k:getattr(self,k) for k in self.fields} kw.update({k: v.into( 'km' if k == 'underplating_depth' else 'Myr') for k,v in vals.items() if v is not None}) kw['type'] = self.name_base self.__model = ModelRun(**kw) self.session.add(self.__model) def trace(self, depth, t=None): if self.depth_offset is None: _depth = depth else: _depth = depth - self.depth_offset assert _depth >= u(0,'km') if t is None: t = self.t # Because we are dealing with # final section only cell = int((_depth-self._top_depth).into('m')/self.dz) T = self.section.profile[cell] v = ModelTracer( run=self.__model, time=t.into("Myr"), final_depth=depth.into("km"), depth=_depth.into("km"), temperature=T.into("degC")) self.session.add(v) def log(self, message, data=None): if data is None: echo(message) return width = 30 start = message+": " dt = width-len(start) start += " "*dt echo(start+style(str(data),fg='green')) def __call__(self, *args, **kwargs): self.setup_recorder() try: self.run(*args,**kwargs) finally: self.session.commit()
import numpy as N from click import echo, secho, style from geotherm.solvers import FiniteSolver from geotherm.plot import Plotter from geotherm.units import u from .config import ( record_max_depth, solver_constraints, present) from .database import db from .database.models import meta, ModelRun, ModelTracer, ModelProfile FiniteSolver.set_defaults( type="implicit", time_step=u(0.5,"Myr"), constraints=solver_constraints, plotter=Plotter(range=(0,1600))) class ModelRunner(object): trace_depths = u(40,'km'),u(75,'km') # Offset used for depth calculations for model tracers. # Reset when geotherms are stacked. depth_offset = None fields = ( "underplating_duration", "underplating_depth", "underplating_time", "start_time", "subduction_time") name_base = None
def __init__(self, **info): self._top_depth = u(0,'km') for i in self.fields: setattr(self,i,None)
def __init__(self, sub_age, oc_age): self.name = "{0}-{1}-{2}".format(self.name_base,sub_age,oc_age) args = (u(sub_age+oc_age,"Myr"), u(sub_age,"Myr")) SubductionCase.__init__(self, *args)
for dT in (0,2,4,6): case = FarallonReheated(dT) registry[case.name] = case case = Underplated(dT) registry[case.name] = case # Run scenarios requested if all: scenarios = registry.keys() if len(scenarios) == 0: click.echo("Specify scenario names or --all") click.echo("Possible scenarios:") for i in registry: click.echo(" "+i) return FiniteSolver.set_defaults(type= 'implicit' if implicit else 'crank-nicholson') if time_step is not None: dt = u(time_step,'Myr') click.echo("Target dt for finite solver: {}".format(dt)) FiniteSolver.set_defaults(time_step=dt) for s in scenarios: click.echo("Running scenario "+click.style(s,fg='green')) registry[s]() click.echo("")
#!/usr/bin/env python from geotherm.units import u from geotherm.plot import ComparisonPlotter, Plotter from geotherm.models.geometry import Section from geotherm.materials import oceanic_mantle from geotherm.solvers import HalfSpaceSolver, AdvancedFiniteSolver layer = oceanic_mantle.to_layer(u(100, "km")) section = Section([layer], uniform_temperature=u(1500, "degC")) finite = AdvancedFiniteSolver(section) half_space = HalfSpaceSolver(section) plotter = ComparisonPlotter(half_space.profile, title="Half-space test", range=(0, 2000)) #plotter = Plotter(range=(0,2000)) finite.solution(u(3, "Myr"), plotter=plotter)
Usage: crystal_knob.py <solution> """ from __future__ import division, print_function from docopt import docopt import json from geotherm.plot import Plotter from geotherm.units import u from geotherm.materials import oceanic_mantle, continental_crust from geotherm.models.geometry import Section, Layer, stack_sections from geotherm.solvers import HalfSpaceSolver from geotherm.solvers.finite import AdvancedFiniteSolver args = docopt(__doc__) solution = args["<solution>"] present = u(1.65,"Myr") # K-Ar age for Crystal Knob xenoliths solver_constraints = ( u(25,"degC"), # Surface temperature u(1400,"degC")) #u(48,"mW/m**2")) # Globally averaged mantle heat flux from Pollack, et al., 1977 oceanic_section = Section([oceanic_mantle.to_layer(u(200,"km"))]) oceanic_solver = HalfSpaceSolver(oceanic_section) forearc = Section([ continental_crust.to_layer(u(30,"km")) ], uniform_temperature=u(400,"degC")) # This is obviously over-simplified
def __init__(self): self.name = "monterey-plate" SubductionCase.__init__(self, u(27, "Myr"), u(22, "Myr"))
def monterey_plate(): return subduction_case("Monterey Plate" u(28,"Myr"), u(26,"Myr"))
def setup(self): self.pre_subduction() self.stepped_subduction( final_temperature=u(775,"degC"), optimization_depth=u(28,'km'), vary=self.variable_term)
def farallon_plate(): return subduction_case("Farallon Plate" u(140, "Myr"), u(70, "Myr"))
#!/usr/bin/env python from geotherm.units import u from geotherm.materials import oceanic_mantle, continental_crust from geotherm.models.geometry import Section, Layer, stack_sections from geotherm.solvers import HalfSpaceSolver, FiniteSolver, AdiabatSolver from geotherm.plot import Plotter Layer.defaults["grid_spacing"] = u(100, "m") mantle_section = Section([oceanic_mantle.to_layer(u(100, "km"))]) apply_adiabat = AdiabatSolver() starting_mantle = apply_adiabat(mantle_section) from IPython import embed embed() # Initialize oceanic crust (analytical) oceanic = HalfSpaceSolver(mantle_layer) evolved_oceanic = oceanic(u(30, "Myr")) # Will put royden solver here. # Initialize continental crust (analytical) evolved_forearc = Section([continental_crust.to_layer(u(30, "km"))], uniform_temperature=u(200, "degC")) # Stack the two of them final_section = stack_sections(
from geotherm.units import u from geotherm.materials import oceanic_mantle, continental_crust # Recorder constraints record_max_depth = u(100, 'km') surface_temperature = u(0, 'degC') # temperature of the base of the lithosphere # used in the Royden model finite solving asthenosphere_temperature = u(1450, "degC") # Consider mostly steady-state upper portion for # forearc cooling model forearc_base_temperature = u(800, "degC") # Distance backarc of subduction zone for our # final sections (influences Royden model evolution # and duration of subduction). underplating_distance = u(100, 'km') # Set conductivity to value from GDH model oceanic_mantle.conductivity = u(3.138, "W/m/K") # Could be higher? u(3300,"W/m/K") # Continental crust is mostl granitic and metapelitic here # This value is likely rather high continental_crust.heat_generation = u(2.5, "uW/m**3") continental_crust.conductivity = u(2.7, "W/m/K") # Continental crust conductivity is 2.7 W/m/K # but could be as low as 1.9 (citation needed) # not sure what we should do here # Depths