From e0fa0eddd13e5d7f96d0de0e3db3075aa286cfb2 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Sun, 3 Nov 2024 23:26:23 +0000 Subject: [PATCH] build based on 67859fc --- dev/.documenter-siteinfo.json | 2 +- dev/advection/09a42f74.gif | Bin 10248 -> 0 bytes dev/advection/e2bfbca2.gif | Bin 0 -> 34501 bytes dev/advection/index.html | 4 +- dev/api/index.html | 66 +++++++++++++------------- dev/composition/index.html | 4 +- dev/coord_transforms/index.html | 2 +- dev/example_all_together/76a8d88f.svg | 50 +++++++++++++++++++ dev/example_all_together/efbbc156.svg | 50 ------------------- dev/example_all_together/index.html | 18 +++---- dev/icbc/index.html | 2 +- dev/index.html | 20 ++++---- dev/operator/index.html | 2 +- dev/operator_compose/index.html | 4 +- dev/param_to_var/index.html | 6 +-- dev/search_index.js | 2 +- 16 files changed, 117 insertions(+), 115 deletions(-) delete mode 100644 dev/advection/09a42f74.gif create mode 100644 dev/advection/e2bfbca2.gif create mode 100644 dev/example_all_together/76a8d88f.svg delete mode 100644 dev/example_all_together/efbbc156.svg diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 34799649..f7dee889 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-10-31T21:37:22","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-03T23:26:16","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/dev/advection/09a42f74.gif b/dev/advection/09a42f74.gif deleted file mode 100644 index 2057b5e52f6b5e2eee3e91fd76fbd1f58e895e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10248 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 Z0YE^2e>J%O-vavY7XF9r_-~J^`Cl4gpfLad diff --git a/dev/advection/e2bfbca2.gif b/dev/advection/e2bfbca2.gif new file mode 100644 index 0000000000000000000000000000000000000000..c10e3e034f67ccef3628abd8a0cd7cd93da55d5b GIT binary patch literal 34501 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
sys_advection = couple(ExampleSys(), domain, ConstantWind(t, 1.0), Advection())
 sys_mtk = convert(PDESystem, sys_advection)

\[ \begin{align} -\frac{\mathrm{d}}{\mathrm{d}t} \mathtt{ExampleSys.y}\left( t, x \right) &= \mathtt{ExampleSys.p} - \mathtt{MeanWind.v\_x}\left( t, x \right) \frac{\mathrm{d}}{\mathrm{d}x} \mathtt{ExampleSys.y}\left( t, x \right) \\ \mathtt{MeanWind.v\_x}\left( t, x \right) &= \mathtt{ConstantWind.v\_1}\left( t, x \right) \\ +\frac{\mathrm{d}}{\mathrm{d}t} \mathtt{ExampleSys.y}\left( t, x \right) &= \mathtt{ExampleSys.p} - \mathtt{MeanWind.v\_x}\left( t, x \right) \frac{\mathrm{d}}{\mathrm{d}x} \mathtt{ExampleSys.y}\left( t, x \right) \\ \mathtt{ConstantWind.v\_1}\left( t, x \right) &= \mathtt{ConstantWind.c\_v1} \end{align} \]

Finally, we can discretize the system and solve it:

using MethodOfLines, DifferentialEquations, Plots
@@ -34,4 +34,4 @@
 anim = @animate for k in 1:length(discrete_t)
     plot(discrete_x, soly[k, 1:end], title="t=\$(discrete_t[k])", ylim=(0,2.5), lab=:none)
 end
-gif(anim, fps = 8)
Example block output +gif(anim, fps = 8)Example block output diff --git a/dev/api/index.html b/dev/api/index.html index dfd873d5..8120871d 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,15 +1,15 @@ -API Reference · EarthSciMLBase.jl

API Index

API Documentation

EarthSciMLBase.CoupledSystemType

A system for composing together other systems using the couple function.

  • systems: Model components to be composed together

  • domaininfo: Initial and boundary conditions and other domain information

  • pdefunctions: A vector of functions where each function takes as an argument the resulting PDESystem after DomainInfo is added to this system, and returns a transformed PDESystem.

  • ops: A vector of operators to run during simulations.
  • callbacks: A vector of callbacks to run during simulations.

  • init_callbacks: Objects x with an init_callback(x, Simulator)::DECallback method.

Things that can be added to a CoupledSystem: * ModelingToolkit.ODESystems. If the ODESystem has a field in the metadata called :coupletype (e.g. ModelingToolkit.get_metadata(sys)[:coupletype] returns a struct type with a single field called sys) then that type will be used to check for methods of EarthSciMLBase.couple that use that type. * Operators * DomainInfos * Callbacks * Types X that implement a EarthSciMLBase.init_callback(::X, sys::CoupledSystem, sys_mtk, obs_eqs, domain::DomainInfo)::DECallback method * Other CoupledSystems * Types X that implement a EarthSciMLBase.couple2(::X, ::CoupledSystem) or EarthSciMLBase.couple2(::CoupledSystem, ::X) method. * Tuples or AbstractVectors of any of the things above.

source
EarthSciMLBase.DomainInfoType

Domain information for a ModelingToolkit.jl PDESystem. It can be used with the + operator to add initial and boundary conditions and coordinate transforms to a ModelingToolkit.jl ODESystem or Catalyst.jl ReactionSystem.

NOTE: The independent variable (usually time) must be first in the list of initial and boundary conditions.

  • partial_derivative_funcs: Function that returns spatial derivatives of the partially-independent variables, optionally performing a coordinate transformation first.

    Current function options in this package are:

    • partialderivatives_δxyδlonlat: Returns partial derivatives after transforming any variables named lat and lon

    from degrees to cartesian meters, assuming a spherical Earth.

    Other packages may implement additional functions. They are encouraged to use function names starting with partialderivatives_.

  • grid_spacing: The discretization intervals for the partial independent variables.

  • icbc: The sets of initial and/or boundary conditions.

  • spatial_ref: The spatial reference system for the domain.

  • time_offset: The time offset for the domain.

source
EarthSciMLBase.OperatorType

Operators are objects that modify the current state of a Simulator system. Each operator should be define a function with the signature:

`EarthSciMLBase.get_scimlop(::Operator, csys::CoupledSystem, mtk_sys, domain::DomainInfo, obs_functions, coordinate_transform_functions, u0, p)::AbstractSciMLOperator`

which should return a SciMLOperators.AbstractSciMLOperator. Refer to the SciMLOperators.jl documentation for more information on how to define operators.

source
EarthSciMLBase.SolverIMEXType

A solver strategy based on implicit-explicit (IMEX) time integration. See here for additional information.

kwargs for ODEProblem constructor:

  • stiff_scimlop: Whether the stiff ODE function should be implemented as a SciMLOperator.
  • stiff_sparse: Whether the stiff ODE function should use a sparse Jacobian.
  • stiff_jac: Whether the stiff ODE function should use an analytical Jacobian.
  • stiffjacscimlop: Whether the stiff ODE function Jacobian should be implemented as a SciMLOperator. (Ignored if stiff_jac==false.)
  • stiff_tgrad: Whether the stiff ODE function should use an analytical time gradient.
  • u0: initial condtions; if "nothing", default values will be used.
  • p: parameters; if "nothing", default values will be used.
source
EarthSciMLBase.SolverStrangType

A simulator strategy based on Strang splitting. Choose either SimulatorStrangThreads or SimulatorStrangSerial to run the simulation.

kwargs for ODEProblem constructor:

  • u0: initial condtions; if "nothing", default values will be used.
  • p: parameters; if "nothing", default values will be used.
  • nonstiff_params: parameters for the non-stiff ODE system.
  • name: name of the system.
source
EarthSciMLBase.SolverStrangSerialType
# Specify the stiff ODE solver algorithm.
+API Reference · EarthSciMLBase.jl

API Index

API Documentation

EarthSciMLBase.CoupledSystemType

A system for composing together other systems using the couple function.

  • systems: Model components to be composed together

  • domaininfo: Initial and boundary conditions and other domain information

  • pdefunctions: A vector of functions where each function takes as an argument the resulting PDESystem after DomainInfo is added to this system, and returns a transformed PDESystem.

  • ops: A vector of operators to run during simulations.
  • callbacks: A vector of callbacks to run during simulations.

  • init_callbacks: Objects x with an init_callback(x, Simulator)::DECallback method.

Things that can be added to a CoupledSystem: * ModelingToolkit.ODESystems. If the ODESystem has a field in the metadata called :coupletype (e.g. ModelingToolkit.get_metadata(sys)[:coupletype] returns a struct type with a single field called sys) then that type will be used to check for methods of EarthSciMLBase.couple that use that type. * Operators * DomainInfos * Callbacks * Types X that implement a EarthSciMLBase.init_callback(::X, sys::CoupledSystem, sys_mtk, obs_eqs, domain::DomainInfo)::DECallback method * Other CoupledSystems * Types X that implement a EarthSciMLBase.couple2(::X, ::CoupledSystem) or EarthSciMLBase.couple2(::CoupledSystem, ::X) method. * Tuples or AbstractVectors of any of the things above.

source
EarthSciMLBase.DomainInfoType

Domain information for a ModelingToolkit.jl PDESystem. It can be used with the + operator to add initial and boundary conditions and coordinate transforms to a ModelingToolkit.jl ODESystem or Catalyst.jl ReactionSystem.

NOTE: The independent variable (usually time) must be first in the list of initial and boundary conditions.

  • partial_derivative_funcs: Function that returns spatial derivatives of the partially-independent variables, optionally performing a coordinate transformation first.

    Current function options in this package are:

    • partialderivatives_δxyδlonlat: Returns partial derivatives after transforming any variables named lat and lon

    from degrees to cartesian meters, assuming a spherical Earth.

    Other packages may implement additional functions. They are encouraged to use function names starting with partialderivatives_.

  • grid_spacing: The discretization intervals for the partial independent variables.

  • icbc: The sets of initial and/or boundary conditions.

  • spatial_ref: The spatial reference system for the domain.

  • time_offset: The time offset for the domain.

source
EarthSciMLBase.OperatorType

Operators are objects that modify the current state of a Simulator system. Each operator should be define a function with the signature:

`EarthSciMLBase.get_scimlop(::Operator, csys::CoupledSystem, mtk_sys, domain::DomainInfo, obs_functions, coordinate_transform_functions, u0, p)::AbstractSciMLOperator`

which should return a SciMLOperators.AbstractSciMLOperator. Refer to the SciMLOperators.jl documentation for more information on how to define operators.

source
EarthSciMLBase.SolverIMEXType

A solver strategy based on implicit-explicit (IMEX) time integration. See here for additional information.

kwargs:

  • stiff_scimlop: Whether the stiff ODE function should be implemented as a SciMLOperator.
  • stiff_sparse: Whether the stiff ODE function should use a sparse Jacobian.
  • stiff_jac: Whether the stiff ODE function should use an analytical Jacobian.
  • stiffjacscimlop: Whether the stiff ODE function Jacobian should be implemented as a SciMLOperator. (Ignored if stiff_jac==false.)
  • stiff_tgrad: Whether the stiff ODE function should use an analytical time gradient.

Additional kwargs for ODEProblem constructor:

  • u0: initial condtions; if "nothing", default values will be used.
  • p: parameters; if "nothing", default values will be used.
  • name: name of the model.
source
EarthSciMLBase.SolverStrangType

A simulator strategy based on Strang splitting. Choose either SimulatorStrangThreads or SimulatorStrangSerial to run the simulation.

kwargs for ODEProblem constructor:

  • u0: initial condtions; if "nothing", default values will be used.
  • p: parameters; if "nothing", default values will be used.
  • nonstiff_params: parameters for the non-stiff ODE system.
  • name: name of the system.
source
EarthSciMLBase.SolverStrangSerialType
# Specify the stiff ODE solver algorithm.
 # `timestep` is the length of time for each splitting step.
-SimulatorStrangSerial(stiffalg, timestep; kwargs...)

Perform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution will be calculated in serial.

  • stiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)

  • timestep: Length of each splitting time step

  • stiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.

source
EarthSciMLBase.SolverStrangThreadsType
# Specify the number of threads and the stiff ODE solver algorithm.
+SimulatorStrangSerial(stiffalg, timestep; kwargs...)

Perform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution will be calculated in serial.

Additional kwargs for ODEProblem constructor:

  • u0: initial condtions; if "nothing", default values will be used.
  • p: parameters; if "nothing", default values will be used.
  • nonstiff_params: parameters for the Operators.
  • stiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)

  • timestep: Length of each splitting time step

  • stiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.

source
EarthSciMLBase.SolverStrangThreadsType
# Specify the number of threads and the stiff ODE solver algorithm.
 # `timestep` is the length of time for each splitting step.
 SimulatorStrangThreads(threads, stiffalg, timestep; kwargs...)
 # Use the default number of threads.
-SimulatorStrangThreads(stiffalg, timestep; kwargs...)

Perform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution of the stiff ODE system is parallelized across grid cells using the specified number of threads.

  • threads: Number of threads to use

  • stiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)

  • timestep: Length of each splitting time step

  • stiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.

source
EarthSciMLBase.constBCType

Construct constant boundary conditions equal to the value specified by val.

  • val: The value of the constant boundary conditions.

  • partialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].

source
EarthSciMLBase.constICType

Construct constant initial conditions equal to the value specified by val.

  • val: The value of the constant initial conditions.

  • indepdomain: The independent domain, e.g. t ∈ Interval(t_min, t_max).

source
EarthSciMLBase.periodicBCType

Construct periodic boundary conditions for the given partialdomains. Periodic boundary conditions are defined as when the value at one side of the domain is set equal to the value at the other side, so that the domain "wraps around" from one side to the other.

  • partialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].
source
EarthSciMLBase.zerogradBCType

Construct zero-gradient boundary conditions for the given partialdomains.

  • partialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].
source
Base.convertMethod
convert(, sys; name, simplify, kwargs...)
-

Get the ODE ModelingToolkit ODESystem representation of a CoupledSystem.

kwargs:

  • name: The desired name for the resulting ODESystem
  • simplify: if true, the observed variables that are not needed to specify the state variables will be pruned and returned as a second return value after the ODESystem, which will be structurally simplified.
source
EarthSciMLBase.ConstantWindMethod
ConstantWind(t, vals; name)
-

Construct a constant wind velocity model component with the given wind speed(s), which should include units. For example, ConstantWind(t, 1u"m/s", 2u"m/s").

source
EarthSciMLBase.MeanWindMethod
MeanWind(t, domain)
-

A model component that represents the mean wind velocity, where pvars is the partial dependent variables for the domain.

source
EarthSciMLBase.add_dimsMethod
add_dims(expression, vars, dims)
+SimulatorStrangThreads(stiffalg, timestep; kwargs...)

Perform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution of the stiff ODE system is parallelized across grid cells using the specified number of threads.

  • threads: Number of threads to use

  • stiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)

  • timestep: Length of each splitting time step

  • stiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.

source
EarthSciMLBase.constBCType

Construct constant boundary conditions equal to the value specified by val.

  • val: The value of the constant boundary conditions.

  • partialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].

source
EarthSciMLBase.constICType

Construct constant initial conditions equal to the value specified by val.

  • val: The value of the constant initial conditions.

  • indepdomain: The independent domain, e.g. t ∈ Interval(t_min, t_max).

source
EarthSciMLBase.periodicBCType

Construct periodic boundary conditions for the given partialdomains. Periodic boundary conditions are defined as when the value at one side of the domain is set equal to the value at the other side, so that the domain "wraps around" from one side to the other.

  • partialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].
source
EarthSciMLBase.zerogradBCType

Construct zero-gradient boundary conditions for the given partialdomains.

  • partialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].
source
Base.convertMethod
convert(, sys; name, simplify, kwargs...)
+

Get the ODE ModelingToolkit ODESystem representation of a CoupledSystem.

kwargs:

  • name: The desired name for the resulting ODESystem
  • simplify: if true, the observed variables that are not needed to specify the state variables will be pruned and returned as a second return value after the ODESystem, which will be structurally simplified.
source
EarthSciMLBase.ConstantWindMethod
ConstantWind(t, vals; name)
+

Construct a constant wind velocity model component with the given wind speed(s), which should include units. For example, ConstantWind(t, 1u"m/s", 2u"m/s").

source
EarthSciMLBase.MeanWindMethod
MeanWind(t, domain)
+

A model component that represents the mean wind velocity, where pvars is the partial dependent variables for the domain.

source
EarthSciMLBase.add_dimsMethod
add_dims(expression, vars, dims)
 add_dims(equation, vars, dims)

Add the given dimensions to each variable in vars in the given expression or equation. Each variable in vars must be unidimensional, i.e. defined like @variables u(t) rather than @variables u(..).

Example:

using EarthSciMLBase, ModelingToolkit
 
 @parameters x y k t
@@ -18,39 +18,41 @@
 EarthSciMLBase.add_dims(exp, [u, q], [x, y, t])
 
 # output
-1 + 2u(x, y, t) + 3k*q(x, y, t)
source
EarthSciMLBase.add_scopeMethod
add_scope(sys, v, iv)
-

Add a system scope to a variable name, for example so that x in system sys1 becomes sys1₊x. iv is the independent variable.

source
EarthSciMLBase.add_scopeMethod
add_scope(sys, v, iv)
+

Add a system scope to a variable name, for example so that x in system sys1 becomes sys1₊x. iv is the independent variable.

source
EarthSciMLBase.copy_with_changeMethod
copy_with_change(
     sys;
     eqs,
     name,
+    unknowns,
+    parameters,
     metadata,
     continuous_events,
     discrete_events
 )
-

Create a copy of an ODESystem with the given changes.

source
EarthSciMLBase.coupleMethod
couple(systems...) -> CoupledSystem
-

Couple multiple ModelingToolkit systems together.

The systems that are arguments to this system can be of type ModelingToolkit.AbstractSystem, CoupledSystem, DomainInfo, or any type T that has a method couple(::CoupledSystem, ::T)::CoupledSystem or a method couple(::T, ::CoupledSystem)::CoupledSystem defined for it.

source
EarthSciMLBase.coupleMethod
couple(systems...) -> CoupledSystem
+

Couple multiple ModelingToolkit systems together.

The systems that are arguments to this system can be of type ModelingToolkit.AbstractSystem, CoupledSystem, DomainInfo, or any type T that has a method couple(::CoupledSystem, ::T)::CoupledSystem or a method couple(::T, ::CoupledSystem)::CoupledSystem defined for it.

source
EarthSciMLBase.couple2Method
couple2()
 

Perform bi-directional coupling for two equation systems.

To specify couplings for system pairs, create methods for this function with the signature:

EarthSciMLBase.couple2(a::ACoupler, b::BCoupler)::ConnectorSystem

where ACoupler and BCoupler are :coupletypes defined like this:

struct ACoupler sys end
-@named asys = ODESystem([], t, metadata=Dict(:coupletype=>ACoupler))
source
EarthSciMLBase.dimsMethod
dims(
     icbc::EarthSciMLBase.ICcomponent
 ) -> Vector{Symbolics.Num}
-

Returns the dimensions of the independent and partial domains associated with these initial or boundary conditions.

source
EarthSciMLBase.domainsMethod
domains(icbc::EarthSciMLBase.ICcomponent) -> Vector
-

Returns the domains associated with these initial or boundary conditions.

source
EarthSciMLBase.dtypeMethod
dtype(_)
-

Return the data type of the state variables for this domain, based on the data types of the boundary conditions domain intervals.

source
EarthSciMLBase.get_dvMethod

Return the dependent variable, which is the first argument of the term, unless the term is a time derivative, in which case the dependent variable is the argument of the time derivative.

source
EarthSciMLBase.get_needed_varsMethod
get_needed_vars(sys)
-

Return the indexes of the system variables that the state variables of the final simplified system depend on. This should be done before running structural_simplify on the system.

source
EarthSciMLBase.gridMethod
grid(d)
-

Return the ranges representing the discretization of the partial independent variables for this domain, based on the discretization intervals given in Δs.

source
EarthSciMLBase.icbcMethod
icbc(di, states)
-

Return a vector of equations that define the initial and boundary conditions for the given state variables.

source
EarthSciMLBase.init_callbackMethod

Types that implement an:

init_callback(x, sys::CoupledSystem, obs_eqs, domain::DomainInfo)::DECallback

method can also be coupled into a CoupledSystem. The init_callback function will be run before the simulator is run to get the callback.

source
EarthSciMLBase.ivarMethod
ivar(di::DomainInfo) -> Any
-

Return the independent variable associated with these initial and boundary conditions.

source
EarthSciMLBase.observed_expressionMethod
observed_expression(eqs, x)
-

Return an expression for the observed value of a variable x after substituting in the constants observed values of other variables. extra_eqs is a list of additional equations to use in the substitution.

source
EarthSciMLBase.observed_functionMethod
observed_function(eqs, x, coords)
-

Return a function to for the observed value of a variable x based on the input arguments in coords. extra_eqs is a list of additional equations to use to determine the value of x.

source
EarthSciMLBase.operator_composeFunction
operator_compose(a, b)
+

Returns the dimensions of the independent and partial domains associated with these initial or boundary conditions.

source
EarthSciMLBase.domainsMethod
domains(icbc::EarthSciMLBase.ICcomponent) -> Vector
+

Returns the domains associated with these initial or boundary conditions.

source
EarthSciMLBase.dtypeMethod
dtype(_)
+

Return the data type of the state variables for this domain, based on the data types of the boundary conditions domain intervals.

source
EarthSciMLBase.get_dvMethod

Return the dependent variable, which is the first argument of the term, unless the term is a time derivative, in which case the dependent variable is the argument of the time derivative.

source
EarthSciMLBase.get_needed_varsMethod
get_needed_vars(sys)
+

Return the indexes of the system variables that the state variables of the final simplified system depend on. This should be done before running structural_simplify on the system.

source
EarthSciMLBase.gridMethod
grid(d)
+

Return the ranges representing the discretization of the partial independent variables for this domain, based on the discretization intervals given in Δs.

source
EarthSciMLBase.icbcMethod
icbc(di, states)
+

Return a vector of equations that define the initial and boundary conditions for the given state variables.

source
EarthSciMLBase.init_callbackMethod

Types that implement an:

init_callback(x, sys::CoupledSystem, obs_eqs, domain::DomainInfo)::DECallback

method can also be coupled into a CoupledSystem. The init_callback function will be run before the simulator is run to get the callback.

source
EarthSciMLBase.ivarMethod
ivar(di::DomainInfo) -> Any
+

Return the independent variable associated with these initial and boundary conditions.

source
EarthSciMLBase.observed_expressionMethod
observed_expression(eqs, x)
+

Return an expression for the observed value of a variable x after substituting in the constants observed values of other variables. extra_eqs is a list of additional equations to use in the substitution.

source
EarthSciMLBase.observed_functionMethod
observed_function(eqs, x, coords)
+

Return a function to for the observed value of a variable x based on the input arguments in coords. extra_eqs is a list of additional equations to use to determine the value of x.

source
EarthSciMLBase.operator_composeFunction
operator_compose(a, b)
 operator_compose(a, b, translate)
-

Compose to systems of equations together by adding the right-hand side terms together of equations that have matching left-hand sides. The left hand sides of two equations will be considered matching if:

  1. They are both time derivatives of the same variable.
  2. The first one is a time derivative of a variable and the second one is the variable itself.
  3. There is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, e.g. Dict(sys1.sys.x => sys2.sys.y).
  4. There is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, with a conversion factor, e.g. Dict(sys1.sys.x => sys2.sys.y => 6).
source
EarthSciMLBase.param_to_varMethod

Replace the parameter p in the system sys with a new variable that has the same name, units, and description as p.

param_to_var(sys, ps)
-

This can be useful to replace a parameter that does not change in time in a model component with one specified by another system that does change in time (or space). For example, the code below specifies a first-order loss equation, and then changes the temperature (which determines the loss rate) with a temperature value that varies in time. ```

source
EarthSciMLBase.partialderivatives_δxyδlonlatMethod
partialderivatives_δxyδlonlat(pvars; default_lat)
-

Return partial derivative operator transform factors corresponding for the given partial-independent variables after converting variables named lon and lat from degrees to x and y meters, assuming they represent longitude and latitude on a spherical Earth.

source
EarthSciMLBase.prune_observedMethod
prune_observed(sys)
-

Remove equations from an ODESystem where the variable in the LHS is not present in any of the equations for the state variables. This can be used to remove computationally intensive equations that are not used in the final model.

source
EarthSciMLBase.pvarsMethod
pvars(di::DomainInfo) -> Any
-

Return the partial independent variables associated with these initial and boundary conditions.

source
EarthSciMLBase.steplengthMethod
steplength(timesteps)
-

Return the time step length common to all of the given timesteps. Throw an error if not all timesteps are the same length.

source
+

Compose to systems of equations together by adding the right-hand side terms together of equations that have matching left-hand sides. The left hand sides of two equations will be considered matching if:

  1. They are both time derivatives of the same variable.
  2. The first one is a time derivative of a variable and the second one is the variable itself.
  3. There is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, e.g. Dict(sys1.sys.x => sys2.sys.y).
  4. There is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, with a conversion factor, e.g. Dict(sys1.sys.x => sys2.sys.y => 6).
source
EarthSciMLBase.param_to_varMethod

Replace the parameter p in the system sys with a new variable that has the same name, units, and description as p.

param_to_var(sys, ps)
+

This can be useful to replace a parameter that does not change in time in a model component with one specified by another system that does change in time (or space). For example, the code below specifies a first-order loss equation, and then changes the temperature (which determines the loss rate) with a temperature value that varies in time. ```

source
EarthSciMLBase.partialderivatives_δxyδlonlatMethod
partialderivatives_δxyδlonlat(pvars; default_lat)
+

Return partial derivative operator transform factors corresponding for the given partial-independent variables after converting variables named lon and lat from degrees to x and y meters, assuming they represent longitude and latitude on a spherical Earth.

source
EarthSciMLBase.prune_observedMethod
prune_observed(sys)
+

Remove equations from an ODESystem where the variable in the LHS is not present in any of the equations for the state variables. This can be used to remove computationally intensive equations that are not used in the final model.

source
EarthSciMLBase.pvarsMethod
pvars(di::DomainInfo) -> Any
+

Return the partial independent variables associated with these initial and boundary conditions.

source
EarthSciMLBase.steplengthMethod
steplength(timesteps)
+

Return the time step length common to all of the given timesteps. Throw an error if not all timesteps are the same length.

source
diff --git a/dev/composition/index.html b/dev/composition/index.html index aabf2581..874c4426 100644 --- a/dev/composition/index.html +++ b/dev/composition/index.html @@ -54,10 +54,10 @@ )) end

Finally, we can compose the model components together to create our complete model. To do so, we just initialize each model component and add the components together using the couple function. We can use the convert function to convert the composed model to a ModelingToolkit ODESystem so we can see what the combined equations look like.

model = couple(Photolysis(), Chemistry(), Emissions())
 convert(ODESystem, model)

\[ \begin{align} -\frac{\mathrm{d} \mathtt{Chemistry.NO2}\left( t \right)}{\mathrm{d}t} &= \mathtt{Chemistry.Emissions\_NO2}\left( t \right) - \mathtt{Chemistry.jNO2}\left( t \right) \mathtt{Chemistry.NO2}\left( t \right) \\ \mathtt{Chemistry.jNO2}\left( t \right) &= \mathtt{Photolysis.j\_NO2}\left( t \right) \\ \mathtt{Chemistry.Emissions\_NO2}\left( t \right) &= \mathtt{Emissions.NO2}\left( t \right) \\ \mathtt{Photolysis.j\_NO2}\left( t \right) &= max\left( \sin\left( \frac{1}{86400} t \right), 0 \right) \\ +\frac{\mathrm{d} \mathtt{Chemistry.NO2}\left( t \right)}{\mathrm{d}t} &= \mathtt{Chemistry.Emissions\_NO2}\left( t \right) - \mathtt{Chemistry.jNO2}\left( t \right) \mathtt{Chemistry.NO2}\left( t \right) \\ \mathtt{Emissions.NO2}\left( t \right) &= \mathtt{Emissions.emis} \end{align} \]

Finally, we can use the graph function to visualize the model components and their connections.

using MetaGraphsNext
@@ -68,4 +68,4 @@
 f, ax, p = graphplot(g; ilabels=labels(g))
 hidedecorations!(ax); hidespines!(ax); ax.aspect = DataAspect()
 
-f
Example block output +fExample block output diff --git a/dev/coord_transforms/index.html b/dev/coord_transforms/index.html index d672aae6..c8d9c1fb 100644 --- a/dev/coord_transforms/index.html +++ b/dev/coord_transforms/index.html @@ -36,4 +36,4 @@ \end{array} \right] \end{equation} - \]

You can see an example of how this is implemented in the source code for the Advection model component.

Additional transformation functions may be defined in other packages. We recommend that the names of these functions start with partialderivatives_ to make it clear that they are intended to be used in this context.

+ \]

You can see an example of how this is implemented in the source code for the Advection model component.

Additional transformation functions may be defined in other packages. We recommend that the names of these functions start with partialderivatives_ to make it clear that they are intended to be used in this context.

diff --git a/dev/example_all_together/76a8d88f.svg b/dev/example_all_together/76a8d88f.svg new file mode 100644 index 00000000..a874ff4a --- /dev/null +++ b/dev/example_all_together/76a8d88f.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/example_all_together/efbbc156.svg b/dev/example_all_together/efbbc156.svg deleted file mode 100644 index d53b199e..00000000 --- a/dev/example_all_together/efbbc156.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dev/example_all_together/index.html b/dev/example_all_together/index.html index 7ba19bef..0c16d048 100644 --- a/dev/example_all_together/index.html +++ b/dev/example_all_together/index.html @@ -44,12 +44,12 @@ sys = couple(sys1, sys2) convert(ODESystem, sys)

\[ \begin{align} -\frac{\mathrm{d} \mathtt{Sys1.c_1}\left( t \right)}{\mathrm{d}t} &= \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t \right) - 2 \mathtt{Sys1.c_1}\left( t \right) \\ -\frac{\mathrm{d} \mathtt{Sys1.c_2}\left( t \right)}{\mathrm{d}t} &= \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t \right) + 2 \mathtt{Sys1.c_1}\left( t \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t \right) &= \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t \right) \\ \mathtt{Sys1.c_1}\left( t \right) &= \mathtt{Sys2.c_1}\left( t \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t \right) &= \mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t \right) \\ \mathtt{Sys1.c_2}\left( t \right) &= \mathtt{Sys2.c_2}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{Sys1.c_1}\left( t \right)}{\mathrm{d}t} &= \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t \right) - 2 \mathtt{Sys1.c_1}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{Sys1.c_2}\left( t \right)}{\mathrm{d}t} &= \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t \right) + 2 \mathtt{Sys1.c_1}\left( t \right) \\ \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t \right) &= - \mathtt{Sys2.p{_1}} \\ \mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t \right) &= \mathtt{Sys2.p{_2}} \end{align} @@ -59,15 +59,15 @@ \end{align} \]

observed(simplified_sys)

\[ \begin{align} \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t \right) &= - \mathtt{Sys2.p{_1}} \\ -\mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t \right) &= \mathtt{Sys2.p{_2}} \\ \mathtt{Sys2.c_1}\left( t \right) &= \mathtt{Sys1.c_1}\left( t \right) \\ +\mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t \right) &= \mathtt{Sys2.p{_2}} \\ \mathtt{Sys2.c_2}\left( t \right) &= \mathtt{Sys1.c_2}\left( t \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t \right) &= \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t \right) &= \mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t \right) \end{align} \]

We can also run simulations using this system:

odeprob = ODEProblem(simplified_sys, [], (0.0,10.0), [])
 odesol = solve(odeprob)
-plot(odesol)
Example block output

Once we've confirmed that our model works in a 0D "box model" setting, we can expand it to 1, 2, or 3 dimensions using by adding in initial and boundary conditions. We will also add in advection using constant-velocity wind fields add the same time.

x_min = y_min = t_min = 0.0
+plot(odesol)
Example block output

Once we've confirmed that our model works in a 0D "box model" setting, we can expand it to 1, 2, or 3 dimensions using by adding in initial and boundary conditions. We will also add in advection using constant-velocity wind fields add the same time.

x_min = y_min = t_min = 0.0
 x_max = y_max = t_max = 1.0
 domain = DomainInfo(
     constIC(4.0, t ∈ Interval(t_min, t_max)),
@@ -78,14 +78,14 @@
 sys_pde = couple(sys, domain, ConstantWind(t, 1.0, 1.0), Advection())
 
 sys_pde_mtk = convert(PDESystem, sys_pde)

\[ \begin{align} -\frac{\mathrm{d}}{\mathrm{d}t} \mathtt{Sys1.c_1}\left( t, x, y \right) &= - 2 \mathtt{Sys1.c_1}\left( t, x, y \right) + \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}y} \mathtt{Sys1.c_1}\left( t, x, y \right) \mathtt{MeanWind.v\_y}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}x} \mathtt{Sys1.c_1}\left( t, x, y \right) \mathtt{MeanWind.v\_x}\left( t, x, y \right) \\ -\frac{\mathrm{d}}{\mathrm{d}t} \mathtt{Sys1.c_2}\left( t, x, y \right) &= 2 \mathtt{Sys1.c_1}\left( t, x, y \right) + \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) - \mathtt{MeanWind.v\_y}\left( t, x, y \right) \frac{\mathrm{d}}{\mathrm{d}y} \mathtt{Sys1.c_2}\left( t, x, y \right) - \mathtt{MeanWind.v\_x}\left( t, x, y \right) \frac{\mathrm{d}}{\mathrm{d}x} \mathtt{Sys1.c_2}\left( t, x, y \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) &= \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) \\ \mathtt{Sys1.c_1}\left( t, x, y \right) &= \mathtt{Sys2.c_1}\left( t, x, y \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) &= \mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) \\ \mathtt{Sys1.c_2}\left( t, x, y \right) &= \mathtt{Sys2.c_2}\left( t, x, y \right) \\ \mathtt{MeanWind.v\_x}\left( t, x, y \right) &= \mathtt{ConstantWind.v\_1}\left( t, x, y \right) \\ \mathtt{MeanWind.v\_y}\left( t, x, y \right) &= \mathtt{ConstantWind.v\_2}\left( t, x, y \right) \\ +\frac{\mathrm{d}}{\mathrm{d}t} \mathtt{Sys1.c_1}\left( t, x, y \right) &= - 2 \mathtt{Sys1.c_1}\left( t, x, y \right) + \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}y} \mathtt{Sys1.c_1}\left( t, x, y \right) \mathtt{MeanWind.v\_y}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}x} \mathtt{Sys1.c_1}\left( t, x, y \right) \mathtt{MeanWind.v\_x}\left( t, x, y \right) \\ +\frac{\mathrm{d}}{\mathrm{d}t} \mathtt{Sys1.c_2}\left( t, x, y \right) &= 2 \mathtt{Sys1.c_1}\left( t, x, y \right) + \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) - \mathtt{MeanWind.v\_y}\left( t, x, y \right) \frac{\mathrm{d}}{\mathrm{d}y} \mathtt{Sys1.c_2}\left( t, x, y \right) - \mathtt{MeanWind.v\_x}\left( t, x, y \right) \frac{\mathrm{d}}{\mathrm{d}x} \mathtt{Sys1.c_2}\left( t, x, y \right) \\ \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) &= - \mathtt{Sys2.p{_1}} \\ \mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) &= \mathtt{Sys2.p{_2}} \\ \mathtt{ConstantWind.v\_1}\left( t, x, y \right) &= \mathtt{ConstantWind.c\_v1} \\ @@ -94,13 +94,13 @@ \]

Now we can inspect this new system that we've created:

sys_pde_mtk.dvs

\[ \begin{equation} \left[ \begin{array}{c} -\mathtt{Sys1.c_1}\left( t, x, y \right) \\ -\mathtt{Sys1.c_2}\left( t, x, y \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) \\ \mathtt{Sys2.Sys2\_ddt\_c_{1}ˍt}\left( t, x, y \right) \\ +\mathtt{Sys1.c_1}\left( t, x, y \right) \\ \mathtt{Sys2.c_1}\left( t, x, y \right) \\ \mathtt{Sys1.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) \\ \mathtt{Sys2.Sys2\_ddt\_c_{2}ˍt}\left( t, x, y \right) \\ +\mathtt{Sys1.c_2}\left( t, x, y \right) \\ \mathtt{Sys2.c_2}\left( t, x, y \right) \\ \mathtt{MeanWind.v\_x}\left( t, x, y \right) \\ \mathtt{ConstantWind.v\_1}\left( t, x, y \right) \\ @@ -132,4 +132,4 @@ p2 = heatmap(solc2[k, 1:end-1, 1:end-1], title="c₂ t=\$(discrete_t[k])", clim=(0,7.0), lab=:none) plot(p1, p2, layout=(1,2), size=(800,400)) end -gif(anim, fps = 8)Example block output

Because our system is a system of ordinary differential equations rather than partial differential equations, all of the grid cells in the animation above have the same value. Refer to the advection example for an example of a system of partial differential equations.

+gif(anim, fps = 8)Example block output

Because our system is a system of ordinary differential equations rather than partial differential equations, all of the grid cells in the animation above have the same value. Refer to the advection example for an example of a system of partial differential equations.

diff --git a/dev/icbc/index.html b/dev/icbc/index.html index 829b9627..587128dc 100644 --- a/dev/icbc/index.html +++ b/dev/icbc/index.html @@ -50,4 +50,4 @@ \mathtt{Docs.Example.v}\left( t, x, 0 \right) &= 16 \\ \mathtt{Docs.Example.v}\left( t, x, 1 \right) &= 16 \end{align} - \]

+ \]

diff --git a/dev/index.html b/dev/index.html index 49befd5b..c2fe3d42 100644 --- a/dev/index.html +++ b/dev/index.html @@ -6,14 +6,14 @@ [0c46a032] DifferentialEquations v7.14.0 [e30172f5] Documenter v1.7.0 [5b8099bc] DomainSets v0.7.14 - [06fc5a27] DynamicQuantities v1.1.0 + [06fc5a27] DynamicQuantities v1.2.0 [e53f1632] EarthSciMLBase v0.19.2 `~/work/EarthSciMLBase.jl/EarthSciMLBase.jl` [1ecd5474] GraphMakie v0.5.12 [fa8bd995] MetaGraphsNext v0.7.1 [94925ecb] MethodOfLines v0.11.6 [961ee093] ModelingToolkit v9.49.0 [91a5bcdd] Plots v1.40.8 - [c0aeaf25] SciMLOperators v0.3.11 + [c0aeaf25] SciMLOperators v0.3.12 [0c5d862f] Symbolics v6.17.0
and using this machine and Julia version.
Julia Version 1.11.1
 Commit 8f5b7ca12ad (2024-10-16 10:53 UTC)
 Build Info:
@@ -100,7 +100,7 @@
   [e30172f5] Documenter v1.7.0
   [5b8099bc] DomainSets v0.7.14
   [7c1d4256] DynamicPolynomials v0.6.0
-  [06fc5a27] DynamicQuantities v1.1.0
+  [06fc5a27] DynamicQuantities v1.2.0
   [e53f1632] EarthSciMLBase v0.19.2 `~/work/EarthSciMLBase.jl/EarthSciMLBase.jl`
   [4e289a0a] EnumX v1.0.4
   [f151be2c] EnzymeCore v0.8.5
@@ -125,7 +125,7 @@
   [6a86dc24] FiniteDiff v2.26.0
   [53c48c17] FixedPointNumbers v0.8.5
   [1fa38f19] Format v1.3.7
-  [f6369f11] ForwardDiff v0.10.36
+  [f6369f11] ForwardDiff v0.10.37
   [b38be410] FreeType v4.1.1
   [663a7486] FreeTypeAbstraction v0.10.4
   [069b7b12] FunctionWrappers v1.1.3
@@ -179,7 +179,7 @@
   [b964fa9f] LaTeXStrings v1.4.0
   [23fbe1c1] Latexify v0.16.5
   [10f19ff3] LayoutPointers v0.1.17
-  [0e77f7df] LazilyInitializedFields v1.2.2
+  [0e77f7df] LazilyInitializedFields v1.3.0
   [5078a376] LazyArrays v2.2.1
   [8cdb02fc] LazyModules v0.3.1
   [2d8b4e74] LevyArea v1.0.0
@@ -214,7 +214,7 @@
   [77ba4419] NaNMath v1.0.2
   [f09324ee] Netpbm v1.1.1
   [46757867] NetworkLayout v0.4.7
-  [8913a72c] NonlinearSolve v3.15.1
+ [8913a72c] NonlinearSolve v3.15.1
   [510215fc] Observables v0.5.5
   [6fe1bfb0] OffsetArrays v1.14.1
   [52e1d378] OpenEXR v0.3.2
@@ -263,7 +263,7 @@
   [b98c9c47] Pipe v1.3.0
   [eebad327] PkgVersion v0.3.3
   [ccf2f8ad] PlotThemes v3.3.0
-  [995b91a9] PlotUtils v1.4.2
+  [995b91a9] PlotUtils v1.4.3
   [91a5bcdd] Plots v1.40.8
   [e409e4f3] PoissonRandom v0.4.4
   [f517fe37] Polyester v0.7.16
@@ -302,7 +302,7 @@
   [476501e8] SLEEFPirates v0.6.43
   [0bca4576] SciMLBase v2.58.0
   [19f34311] SciMLJacobianOperators v0.1.1
-  [c0aeaf25] SciMLOperators v0.3.11
+  [c0aeaf25] SciMLOperators v0.3.12
   [53ae85a6] SciMLStructures v1.5.0
   [6c6a2e73] Scratch v1.2.1
   [efcf1570] Setfield v1.1.1
@@ -310,7 +310,7 @@
   [992d4aef] Showoff v1.0.3
   [73760f76] SignedDistanceFields v0.4.0
   [777ac1f9] SimpleBufferStream v1.2.0
-  [727e6d20] SimpleNonlinearSolve v1.12.3
+ [727e6d20] SimpleNonlinearSolve v1.12.3
   [699a6c99] SimpleTraits v0.9.4
   [ce78b400] SimpleUnPack v1.1.0
   [45858cf5] Sixel v0.1.3
@@ -514,4 +514,4 @@
   [3f19e933] p7zip_jll v17.4.0+2
 Info Packages marked with  have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`
You can also download the manifest file and the -project file. +project file. diff --git a/dev/operator/index.html b/dev/operator/index.html index 710cad41..b136dfa2 100644 --- a/dev/operator/index.html +++ b/dev/operator/index.html @@ -76,4 +76,4 @@ heatmap(u[1, :, :, 1]), ) end -gif(anim, fps = 15)Example block output +gif(anim, fps = 15)Example block output diff --git a/dev/operator_compose/index.html b/dev/operator_compose/index.html index d474774a..417def88 100644 --- a/dev/operator_compose/index.html +++ b/dev/operator_compose/index.html @@ -39,9 +39,9 @@ combined = couple(sys1, sys2) combined_mtk = convert(ODESystem, combined)

\[ \begin{align} -\frac{\mathrm{d} \mathtt{ExampleSys.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{ExampleSys.p} + \mathtt{ExampleSys.ExampleSys2\_ddt\_xˍt}\left( t \right) \\ \mathtt{ExampleSys.ExampleSys2\_ddt\_xˍt}\left( t \right) &= \mathtt{ExampleSys2.ExampleSys2\_ddt\_xˍt}\left( t \right) \\ \mathtt{ExampleSys.x}\left( t \right) &= \mathtt{ExampleSys2.x}\left( t \right) \\ +\frac{\mathrm{d} \mathtt{ExampleSys.x}\left( t \right)}{\mathrm{d}t} &= \mathtt{ExampleSys.p} + \mathtt{ExampleSys.ExampleSys2\_ddt\_xˍt}\left( t \right) \\ \mathtt{ExampleSys2.ExampleSys2\_ddt\_xˍt}\left( t \right) &= 2 \mathtt{ExampleSys2.p} \end{align} \]

The simplified equation should be D(x) = p + sys2_xˍt:

combined_simplified = structural_simplify(combined_mtk)

\[ \begin{align} @@ -125,4 +125,4 @@ \mathtt{Docs.ExampleSysNonODE2.y}\left( t \right) &= \mathtt{Docs.ExampleSysNonODE2.p} \\ \mathtt{ExampleSys.Docs.ExampleSysNonODE2\_y}\left( t \right) &= 6 \mathtt{Docs.ExampleSysNonODE2.y}\left( t \right) \end{align} - \]

+ \]

diff --git a/dev/param_to_var/index.html b/dev/param_to_var/index.html index ec1d81d5..b1510d55 100644 --- a/dev/param_to_var/index.html +++ b/dev/param_to_var/index.html @@ -36,8 +36,8 @@ variable_loss = couple(l, temp) convert(ODESystem, variable_loss)

\[ \begin{align} +\mathtt{Loss.T}\left( t \right) &= \mathtt{Temperature.T}\left( t \right) \\ \frac{\mathrm{d} \mathtt{Loss.A}\left( t \right)}{\mathrm{d}t} &= - \mathtt{Loss.k} e^{\frac{\mathtt{Loss.T}\left( t \right)}{\mathtt{Loss.T{_0}}}} \mathtt{Loss.A}\left( t \right) \\ -\frac{\mathrm{d} \mathtt{Temperature.T}\left( t \right)}{\mathrm{d}t} &= \mathtt{Temperature.Tc} \sin\left( \frac{t}{\mathtt{Temperature.tc}} \right) \\ -\mathtt{Loss.T}\left( t \right) &= \mathtt{Temperature.T}\left( t \right) +\frac{\mathrm{d} \mathtt{Temperature.T}\left( t \right)}{\mathrm{d}t} &= \mathtt{Temperature.Tc} \sin\left( \frac{t}{\mathtt{Temperature.tc}} \right) \end{align} - \]

If we wanted to, we could then run a simulation with the composed system.

+ \]

If we wanted to, we could then run a simulation with the composed system.

diff --git a/dev/search_index.js b/dev/search_index.js index f3d8d60a..bc3fb321 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"api/#API-Index","page":"API Reference","title":"API Index","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"","category":"page"},{"location":"api/#API-Documentation","page":"API Reference","title":"API Documentation","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"Modules = [EarthSciMLBase]","category":"page"},{"location":"api/#EarthSciMLBase.Advection","page":"API Reference","title":"EarthSciMLBase.Advection","text":"Apply advection to a model.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.ConnectorSystem","page":"API Reference","title":"EarthSciMLBase.ConnectorSystem","text":"A connector for two systems.\n\neqs\nfrom\nto\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.CoupledSystem","page":"API Reference","title":"EarthSciMLBase.CoupledSystem","text":"A system for composing together other systems using the couple function.\n\nsystems: Model components to be composed together\ndomaininfo: Initial and boundary conditions and other domain information\npdefunctions: A vector of functions where each function takes as an argument the resulting PDESystem after DomainInfo is added to this system, and returns a transformed PDESystem.\n\nops: A vector of operators to run during simulations.\n\ncallbacks: A vector of callbacks to run during simulations.\ninit_callbacks: Objects x with an init_callback(x, Simulator)::DECallback method.\n\nThings that can be added to a CoupledSystem: * ModelingToolkit.ODESystems. If the ODESystem has a field in the metadata called :coupletype (e.g. ModelingToolkit.get_metadata(sys)[:coupletype] returns a struct type with a single field called sys) then that type will be used to check for methods of EarthSciMLBase.couple that use that type. * Operators * DomainInfos * Callbacks * Types X that implement a EarthSciMLBase.init_callback(::X, sys::CoupledSystem, sys_mtk, obs_eqs, domain::DomainInfo)::DECallback method * Other CoupledSystems * Types X that implement a EarthSciMLBase.couple2(::X, ::CoupledSystem) or EarthSciMLBase.couple2(::CoupledSystem, ::X) method. * Tuples or AbstractVectors of any of the things above.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.DomainInfo","page":"API Reference","title":"EarthSciMLBase.DomainInfo","text":"Domain information for a ModelingToolkit.jl PDESystem. It can be used with the + operator to add initial and boundary conditions and coordinate transforms to a ModelingToolkit.jl ODESystem or Catalyst.jl ReactionSystem.\n\nNOTE: The independent variable (usually time) must be first in the list of initial and boundary conditions.\n\npartial_derivative_funcs: Function that returns spatial derivatives of the partially-independent variables, optionally performing a coordinate transformation first.\nCurrent function options in this package are:\npartialderivatives_δxyδlonlat: Returns partial derivatives after transforming any variables named lat and lon\nfrom degrees to cartesian meters, assuming a spherical Earth.\nOther packages may implement additional functions. They are encouraged to use function names starting with partialderivatives_.\n\ngrid_spacing: The discretization intervals for the partial independent variables.\nicbc: The sets of initial and/or boundary conditions.\nspatial_ref: The spatial reference system for the domain.\ntime_offset: The time offset for the domain.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.ICBCcomponent","page":"API Reference","title":"EarthSciMLBase.ICBCcomponent","text":"Initial and boundary condition components that can be combined to create an DomainInfo object.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.Operator","page":"API Reference","title":"EarthSciMLBase.Operator","text":"Operators are objects that modify the current state of a Simulator system. Each operator should be define a function with the signature:\n\n`EarthSciMLBase.get_scimlop(::Operator, csys::CoupledSystem, mtk_sys, domain::DomainInfo, obs_functions, coordinate_transform_functions, u0, p)::AbstractSciMLOperator`\n\nwhich should return a SciMLOperators.AbstractSciMLOperator. Refer to the SciMLOperators.jl documentation for more information on how to define operators.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverIMEX","page":"API Reference","title":"EarthSciMLBase.SolverIMEX","text":"A solver strategy based on implicit-explicit (IMEX) time integration. See here for additional information.\n\nkwargs for ODEProblem constructor:\n\nstiff_scimlop: Whether the stiff ODE function should be implemented as a SciMLOperator.\nstiff_sparse: Whether the stiff ODE function should use a sparse Jacobian.\nstiff_jac: Whether the stiff ODE function should use an analytical Jacobian.\nstiffjacscimlop: Whether the stiff ODE function Jacobian should be implemented as a SciMLOperator. (Ignored if stiff_jac==false.)\nstiff_tgrad: Whether the stiff ODE function should use an analytical time gradient.\nu0: initial condtions; if \"nothing\", default values will be used.\np: parameters; if \"nothing\", default values will be used.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrang","page":"API Reference","title":"EarthSciMLBase.SolverStrang","text":"A simulator strategy based on Strang splitting. Choose either SimulatorStrangThreads or SimulatorStrangSerial to run the simulation.\n\nkwargs for ODEProblem constructor:\n\nu0: initial condtions; if \"nothing\", default values will be used.\np: parameters; if \"nothing\", default values will be used.\nnonstiff_params: parameters for the non-stiff ODE system.\nname: name of the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrangSerial","page":"API Reference","title":"EarthSciMLBase.SolverStrangSerial","text":"# Specify the stiff ODE solver algorithm.\n# `timestep` is the length of time for each splitting step.\nSimulatorStrangSerial(stiffalg, timestep; kwargs...)\n\nPerform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution will be calculated in serial.\n\nstiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)\ntimestep: Length of each splitting time step\nstiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrangThreads","page":"API Reference","title":"EarthSciMLBase.SolverStrangThreads","text":"# Specify the number of threads and the stiff ODE solver algorithm.\n# `timestep` is the length of time for each splitting step.\nSimulatorStrangThreads(threads, stiffalg, timestep; kwargs...)\n# Use the default number of threads.\nSimulatorStrangThreads(stiffalg, timestep; kwargs...)\n\nPerform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution of the stiff ODE system is parallelized across grid cells using the specified number of threads.\n\nthreads: Number of threads to use\nstiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)\ntimestep: Length of each splitting time step\nstiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrategy","page":"API Reference","title":"EarthSciMLBase.SolverStrategy","text":"SolverStrategy is an abstract type that defines the strategy for running a simulation.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.constBC","page":"API Reference","title":"EarthSciMLBase.constBC","text":"Construct constant boundary conditions equal to the value specified by val.\n\nval: The value of the constant boundary conditions.\npartialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.constIC","page":"API Reference","title":"EarthSciMLBase.constIC","text":"Construct constant initial conditions equal to the value specified by val.\n\nval: The value of the constant initial conditions.\nindepdomain: The independent domain, e.g. t ∈ Interval(t_min, t_max).\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.periodicBC","page":"API Reference","title":"EarthSciMLBase.periodicBC","text":"Construct periodic boundary conditions for the given partialdomains. Periodic boundary conditions are defined as when the value at one side of the domain is set equal to the value at the other side, so that the domain \"wraps around\" from one side to the other.\n\npartialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.zerogradBC","page":"API Reference","title":"EarthSciMLBase.zerogradBC","text":"Construct zero-gradient boundary conditions for the given partialdomains.\n\npartialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].\n\n\n\n\n\n","category":"type"},{"location":"api/#Base.convert-Tuple{Type{<:ModelingToolkit.ODESystem}, CoupledSystem}","page":"API Reference","title":"Base.convert","text":"convert(, sys; name, simplify, kwargs...)\n\n\nGet the ODE ModelingToolkit ODESystem representation of a CoupledSystem.\n\nkwargs:\n\nname: The desired name for the resulting ODESystem\nsimplify: if true, the observed variables that are not needed to specify the state variables will be pruned and returned as a second return value after the ODESystem, which will be structurally simplified.\n\n\n\n\n\n","category":"method"},{"location":"api/#Base.convert-Tuple{Type{<:ModelingToolkit.PDESystem}, CoupledSystem}","page":"API Reference","title":"Base.convert","text":"convert(, sys; name, kwargs...)\n\n\nGet the ModelingToolkit PDESystem representation of a CoupledSystem.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.ConstantWind-Tuple{Any, Vararg{Any}}","page":"API Reference","title":"EarthSciMLBase.ConstantWind","text":"ConstantWind(t, vals; name)\n\n\nConstruct a constant wind velocity model component with the given wind speed(s), which should include units. For example, ConstantWind(t, 1u\"m/s\", 2u\"m/s\").\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.MeanWind-Tuple{Any, DomainInfo}","page":"API Reference","title":"EarthSciMLBase.MeanWind","text":"MeanWind(t, domain)\n\n\nA model component that represents the mean wind velocity, where pvars is the partial dependent variables for the domain.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.add_dims-Tuple{Any, AbstractVector, AbstractVector}","page":"API Reference","title":"EarthSciMLBase.add_dims","text":"add_dims(expression, vars, dims)\nadd_dims(equation, vars, dims)\n\nAdd the given dimensions to each variable in vars in the given expression or equation. Each variable in vars must be unidimensional, i.e. defined like @variables u(t) rather than @variables u(..).\n\nExample:\n\nusing EarthSciMLBase, ModelingToolkit\n\n@parameters x y k t\n@variables u(t) q(t)\nexp = 2u + 3k*q + 1\nEarthSciMLBase.add_dims(exp, [u, q], [x, y, t])\n\n# output\n1 + 2u(x, y, t) + 3k*q(x, y, t)\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.add_metadata-Tuple{Any, Any}","page":"API Reference","title":"EarthSciMLBase.add_metadata","text":"Add the units and description in the variable from to the variable to.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.add_scope-Tuple{Any, Any, Any}","page":"API Reference","title":"EarthSciMLBase.add_scope","text":"add_scope(sys, v, iv)\n\n\nAdd a system scope to a variable name, for example so that x in system sys1 becomes sys1₊x. iv is the independent variable.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.copy_with_change-Tuple{ModelingToolkit.ODESystem}","page":"API Reference","title":"EarthSciMLBase.copy_with_change","text":"copy_with_change(\n sys;\n eqs,\n name,\n metadata,\n continuous_events,\n discrete_events\n)\n\n\nCreate a copy of an ODESystem with the given changes.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.couple-Tuple","page":"API Reference","title":"EarthSciMLBase.couple","text":"couple(systems...) -> CoupledSystem\n\n\nCouple multiple ModelingToolkit systems together.\n\nThe systems that are arguments to this system can be of type ModelingToolkit.AbstractSystem, CoupledSystem, DomainInfo, or any type T that has a method couple(::CoupledSystem, ::T)::CoupledSystem or a method couple(::T, ::CoupledSystem)::CoupledSystem defined for it.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.couple2-Tuple{}","page":"API Reference","title":"EarthSciMLBase.couple2","text":"couple2()\n\n\nPerform bi-directional coupling for two equation systems.\n\nTo specify couplings for system pairs, create methods for this function with the signature:\n\nEarthSciMLBase.couple2(a::ACoupler, b::BCoupler)::ConnectorSystem\n\nwhere ACoupler and BCoupler are :coupletypes defined like this:\n\nstruct ACoupler sys end\n@named asys = ODESystem([], t, metadata=Dict(:coupletype=>ACoupler))\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.dims-Tuple{EarthSciMLBase.ICcomponent}","page":"API Reference","title":"EarthSciMLBase.dims","text":"dims(\n icbc::EarthSciMLBase.ICcomponent\n) -> Vector{Symbolics.Num}\n\n\nReturns the dimensions of the independent and partial domains associated with these initial or boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.domains-Tuple{EarthSciMLBase.ICcomponent}","page":"API Reference","title":"EarthSciMLBase.domains","text":"domains(icbc::EarthSciMLBase.ICcomponent) -> Vector\n\n\nReturns the domains associated with these initial or boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.dtype-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T","page":"API Reference","title":"EarthSciMLBase.dtype","text":"dtype(_)\n\n\nReturn the data type of the state variables for this domain, based on the data types of the boundary conditions domain intervals.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.endpoints-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T","page":"API Reference","title":"EarthSciMLBase.endpoints","text":"endpoints(d)\n\n\nReturn the endpoints of the partial independent variables for this domain.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.get_coupletype-Tuple{ModelingToolkit.AbstractSystem}","page":"API Reference","title":"EarthSciMLBase.get_coupletype","text":"Return the coupling type associated with the given system.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.get_dv-Tuple{Any, Any}","page":"API Reference","title":"EarthSciMLBase.get_dv","text":"Return the dependent variable, which is the first argument of the term, unless the term is a time derivative, in which case the dependent variable is the argument of the time derivative.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.get_needed_vars-Tuple{ModelingToolkit.ODESystem}","page":"API Reference","title":"EarthSciMLBase.get_needed_vars","text":"get_needed_vars(sys)\n\n\nReturn the indexes of the system variables that the state variables of the final simplified system depend on. This should be done before running structural_simplify on the system.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.graph-Tuple{CoupledSystem}","page":"API Reference","title":"EarthSciMLBase.graph","text":"Create a graph from a CoupledSystem using the MetaGraphsNext package.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.grid-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T","page":"API Reference","title":"EarthSciMLBase.grid","text":"grid(d)\n\n\nReturn the ranges representing the discretization of the partial independent variables for this domain, based on the discretization intervals given in Δs.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.icbc-Tuple{DomainInfo, AbstractVector}","page":"API Reference","title":"EarthSciMLBase.icbc","text":"icbc(di, states)\n\n\nReturn a vector of equations that define the initial and boundary conditions for the given state variables.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.init_callback-Tuple{}","page":"API Reference","title":"EarthSciMLBase.init_callback","text":"Types that implement an:\n\ninit_callback(x, sys::CoupledSystem, obs_eqs, domain::DomainInfo)::DECallback\n\nmethod can also be coupled into a CoupledSystem. The init_callback function will be run before the simulator is run to get the callback.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.init_u-Tuple{ModelingToolkit.ODESystem, DomainInfo}","page":"API Reference","title":"EarthSciMLBase.init_u","text":"Initialize the state variables.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.ivar-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.ivar","text":"ivar(di::DomainInfo) -> Any\n\n\nReturn the independent variable associated with these initial and boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.observed_expression-Tuple{Any, Any}","page":"API Reference","title":"EarthSciMLBase.observed_expression","text":"observed_expression(eqs, x)\n\n\nReturn an expression for the observed value of a variable x after substituting in the constants observed values of other variables. extra_eqs is a list of additional equations to use in the substitution.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.observed_function-Tuple{Any, Any, Any}","page":"API Reference","title":"EarthSciMLBase.observed_function","text":"observed_function(eqs, x, coords)\n\n\nReturn a function to for the observed value of a variable x based on the input arguments in coords. extra_eqs is a list of additional equations to use to determine the value of x.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.ode_step!-Tuple{Any, SolverStrangThreads, Vararg{Any, 5}}","page":"API Reference","title":"EarthSciMLBase.ode_step!","text":"Take a step using the ODE solver.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.operator_compose","page":"API Reference","title":"EarthSciMLBase.operator_compose","text":"operator_compose(a, b)\noperator_compose(a, b, translate)\n\n\nCompose to systems of equations together by adding the right-hand side terms together of equations that have matching left-hand sides. The left hand sides of two equations will be considered matching if:\n\nThey are both time derivatives of the same variable.\nThe first one is a time derivative of a variable and the second one is the variable itself.\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, e.g. Dict(sys1.sys.x => sys2.sys.y).\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, with a conversion factor, e.g. Dict(sys1.sys.x => sys2.sys.y => 6).\n\n\n\n\n\n","category":"function"},{"location":"api/#EarthSciMLBase.param_to_var-Tuple{ModelingToolkit.AbstractSystem, Vararg{Symbol}}","page":"API Reference","title":"EarthSciMLBase.param_to_var","text":"Replace the parameter p in the system sys with a new variable that has the same name, units, and description as p.\n\nparam_to_var(sys, ps)\n\n\nThis can be useful to replace a parameter that does not change in time in a model component with one specified by another system that does change in time (or space). For example, the code below specifies a first-order loss equation, and then changes the temperature (which determines the loss rate) with a temperature value that varies in time. ```\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.partialderivative_transforms-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.partialderivative_transforms","text":"partialderivative_transforms(di::DomainInfo) -> Vector{Any}\n\n\nReturn transform factor to multiply each partial derivative operator by, for example to convert from degrees to meters.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.partialderivatives-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.partialderivatives","text":"partialderivatives(di::DomainInfo) -> Any\n\n\nReturn the partial derivative operators for the given domain.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.partialderivatives_δxyδlonlat-Tuple{AbstractVector}","page":"API Reference","title":"EarthSciMLBase.partialderivatives_δxyδlonlat","text":"partialderivatives_δxyδlonlat(pvars; default_lat)\n\n\nReturn partial derivative operator transform factors corresponding for the given partial-independent variables after converting variables named lon and lat from degrees to x and y meters, assuming they represent longitude and latitude on a spherical Earth.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.prune_observed-Tuple{ModelingToolkit.ODESystem}","page":"API Reference","title":"EarthSciMLBase.prune_observed","text":"prune_observed(sys)\n\n\nRemove equations from an ODESystem where the variable in the LHS is not present in any of the equations for the state variables. This can be used to remove computationally intensive equations that are not used in the final model.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.pvars-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.pvars","text":"pvars(di::DomainInfo) -> Any\n\n\nReturn the partial independent variables associated with these initial and boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.single_ode_step!-NTuple{6, Any}","page":"API Reference","title":"EarthSciMLBase.single_ode_step!","text":"Take a step using the ODE solver with the given IIchunk (grid cell interator) and integrator.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.steplength-Tuple{Any}","page":"API Reference","title":"EarthSciMLBase.steplength","text":"steplength(timesteps)\n\n\nReturn the time step length common to all of the given timesteps. Throw an error if not all timesteps are the same length.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.stiff_callback-Union{Tuple{T}, Tuple{Any, AbstractArray{T}, EarthSciMLBase.SolverStrang, Any, Any}} where T","page":"API Reference","title":"EarthSciMLBase.stiff_callback","text":"A callback to periodically run the stiff solver.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.threaded_ode_step!-NTuple{6, Any}","page":"API Reference","title":"EarthSciMLBase.threaded_ode_step!","text":"Take a step using the ODE solver.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.timesteps-Union{Tuple{Vararg{AbstractVector{T}}}, Tuple{T}} where T<:AbstractFloat","page":"API Reference","title":"EarthSciMLBase.timesteps","text":"timesteps(tsteps)\n\n\nReturn the time points during which integration should be stopped to run the operators.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.tspan-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T<:AbstractFloat","page":"API Reference","title":"EarthSciMLBase.tspan","text":"tspan(d)\n\n\nReturn the time range associated with this domain.\n\n\n\n\n\n","category":"method"},{"location":"icbc/#Initial-and-Boundary-conditions","page":"Initial and Boundary Conditions","title":"Initial and Boundary conditions","text":"","category":"section"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"Oftentimes we will want to do a 1, 2, or 3-dimensional simulation, rather than the 0-dimensional simulation we get by default with a system of ordinary differential equations. In these cases, we will need to specify initial and boundary conditions for the system.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"To demonstrate how to do this, we will use the following simple system of ordinary differential equations:","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"using EarthSciMLBase\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\n@parameters x y\n\nfunction ExampleSys()\n @variables u(t) v(t)\n eqs = [\n D(u) ~ √abs(v),\n D(v) ~ √abs(u),\n ]\n ODESystem(eqs, t; name=:Docs₊Example)\nend\n\nExampleSys()","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"Next, we specify our initial and boundary conditions using the DomainInfo type. We initialize DomainInfo with sets of initial and boundary conditions. In the example below, we set constant initial conditions using constIC and constant boundary conditions using constBC.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"using DomainSets\n\nx_min = y_min = t_min = 0.0\nx_max = y_max = t_max = 1.0\n\n# Create constant initial conditions = 16.0 and boundary conditions = 4.0.\nicbc = DomainInfo(\n constIC(4.0, t ∈ Interval(t_min, t_max)),\n constBC(16.0,\n x ∈ Interval(x_min, x_max),\n y ∈ Interval(y_min, y_max),\n ),\n)\nnothing # hide","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"It is also possible to use periodic boundary conditions with periodicBC and zero-gradient boundary conditions with zerogradBC.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"Finally, we combine our initial and boundary conditions with our system of equations using the couple function.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"model = couple(ExampleSys(), icbc)\n\neq_sys = convert(PDESystem, model)","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"We can also look at the expanded boundary conditions of the resulting equation system:","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"eq_sys.bcs","category":"page"},{"location":"operator_compose/#Operator-Composition","page":"Operator Composition","title":"Operator Composition","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"There are a lot of cases where there are two different \"processes\" or \"operators\" that change the same variable. For example, CO2 in the atmosphere can be emitted by human activity, and it can also be absorbed by the ocean. In models, typically the emission and removal are considered separate processes which are represented by separate model components. However, when we want to combine these two components into a single model, we need to be able to compose them together.","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"We can use the operator_compose function for this. It composes to systems of equations together by adding the right-hand side terms together of equations that have matching left-hand sides. The left hand sides of two equations will be considered matching if:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"They are both time derivatives of the same variable.\nThe first one is a time derivative of a variable and the second one is the variable itself.\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, e.g. Dict(sys1.sys.x => sys2.sys.y).\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, with a conversion factor, e.g. Dict(sys1.sys.x => sys2.sys.y => 6).","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"Perhaps we can make this somewhat clearer with some examples.","category":"page"},{"location":"operator_compose/#Examples","page":"Operator Composition","title":"Examples","text":"","category":"section"},{"location":"operator_compose/#Example-with-matching-variable-time-derivatives","page":"Operator Composition","title":"Example with matching variable time derivatives","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"The example below shows that when we operator_compose two systems together that are both equal to D(x) = p, the resulting system is equal to D(x) = 2p.","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"using EarthSciMLBase\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\n\nstruct ExampleSysCoupler sys end\nfunction ExampleSys()\n @variables x(t)\n @parameters p\n ODESystem([D(x) ~ p], t; name=:ExampleSys,\n metadata=Dict(:coupletype=>ExampleSysCoupler))\nend\n\nExampleSys()","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSys2Coupler sys end\nfunction ExampleSys2()\n @variables x(t)\n @parameters p\n ODESystem([D(x) ~ 2p], t; name=:ExampleSys2,\n metadata=Dict(:coupletype=>ExampleSys2Coupler))\nend\n\nExampleSys2()","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"sys1 = ExampleSys()\nsys2 = ExampleSys2()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSys2Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2)\nend\n\ncombined = couple(sys1, sys2)\n\ncombined_mtk = convert(ODESystem, combined)","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"The simplified equation should be D(x) = p + sys2_xˍt:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"combined_simplified = structural_simplify(combined_mtk)","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"where sys2_xˍt is also equal to p:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(combined_simplified)","category":"page"},{"location":"operator_compose/#Example-with-non-matching-variables","page":"Operator Composition","title":"Example with non-matching variables","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"This example demonstrates a case where one variable in the first system is equal to another variable in the second system:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSys3Coupler sys end\nfunction ExampleSys3()\n @variables y(t)\n @parameters p\n ODESystem([D(y) ~ p], t; name=:ExampleSys3,\n metadata=Dict(:coupletype=>ExampleSys3Coupler))\nend\n\nsys1 = ExampleSys()\nsys2 = ExampleSys3()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSys3Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2, Dict(sys1.x => sys2.y))\nend\n\ncombined = couple(sys1, sys2)\ncombined_simplified = structural_simplify(convert(ODESystem, combined))","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(combined_simplified)","category":"page"},{"location":"operator_compose/#Example-with-a-non-ODE-system","page":"Operator Composition","title":"Example with a non-ODE system","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"In the second case above, we might have a variable in the second system that is equal to a rate, but it is not a time derivative. This could happen if we are extracting emissions from a file, and those emissions are already in units of kg/s, or something similar. The example below demonstrates this case. (Note that this case can also be used with the conversion factors shown in the last example.)","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSysNonODECoupler sys end\nfunction ExampleSysNonODE()\n @variables y(t)\n @parameters p\n ODESystem([y ~ p], t; name=:ExampleSysNonODE,\n metadata=Dict(:coupletype=>ExampleSysNonODECoupler))\nend\n\nsys1 = ExampleSys()\nsys2 = ExampleSysNonODE()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSysNonODECoupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2, Dict(sys1.x => sys2.y))\nend\n\ncombined = couple(sys1, sys2)\nsys_combined = structural_simplify(convert(ODESystem, combined))","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(sys_combined)","category":"page"},{"location":"operator_compose/#Example-with-non-matching-variables-and-a-conversion-factor","page":"Operator Composition","title":"Example with non-matching variables and a conversion factor","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"Finally, this last example shows the fourth case, where a conversion factor is included in the translation dictionary.","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSysNonODE2Coupler sys end\nfunction ExampleSysNonODE2()\n @variables y(t)\n @parameters p\n ODESystem([y ~ p], t; name=:Docs₊ExampleSysNonODE2,\n metadata=Dict(:coupletype=>ExampleSysNonODE2Coupler))\nend\n\nsys1 = ExampleSys()\nsys2 = ExampleSysNonODE2()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSysNonODE2Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2, Dict(sys1.x => sys2.y => 6.0))\nend\n\ncombined = couple(sys1, sys2)\ncombined_simplified = structural_simplify(convert(ODESystem, combined))","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(combined_simplified)","category":"page"},{"location":"param_to_var/#Converting-parameters-to-variables","page":"Parameter Replacement","title":"Converting parameters to variables","text":"","category":"section"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"This can be useful to replace a parameter that does not change in time in a model component with one specified by another system that does change in time (or space). For example, the code below specifies a first-order loss equation, and then changes the temperature (which determines the loss rate) with a temperature value that varies in time.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"As an example, we will create a loss equation that depends on the temperature, starting with a constant temperature. We will then create a temperature equation that varies in time, and use the param_to_var function to replace the constant temperature in the loss equation with the time-varying temperature.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"So first, let's specify the original system with constant temperature.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"using ModelingToolkit, EarthSciMLBase, DynamicQuantities\nusing ModelingToolkit: t, D\n\nstruct LossCoupler sys end\nfunction Loss()\n @variables A(t)=1 [unit=u\"kg\"]\n @parameters k=1 [unit=u\"s^-1\"]\n @parameters T=300 [unit=u\"K\"]\n @constants T₀=300 [unit=u\"K\"]\n eq = D(A) ~ -k*exp(T/T₀) * A\n ODESystem([eq], t; name=:Loss, metadata=Dict(:coupletype=>LossCoupler))\nend\n\nLoss()","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"Next, we specify the temperature that varies in time.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"struct TemperatureCoupler sys end\nfunction Temperature()\n @variables T(t)=300 [unit=u\"K\"]\n @constants Tc=1.0 [unit=u\"K/s\"]\n @constants tc=1.0 [unit=u\"s\"]\n eq = D(T) ~ sin(t/tc)*Tc\n ODESystem([eq], t; name=:Temperature, metadata=Dict(:coupletype=>TemperatureCoupler))\nend\n\nTemperature()","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"Now, we specify how to compose the two systems using param_to_var.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"function EarthSciMLBase.couple2(loss::LossCoupler, temp::TemperatureCoupler)\n loss, temp = loss.sys, temp.sys\n loss = param_to_var(loss, :T)\n ConnectorSystem([loss.T ~ temp.T], loss, temp)\nend","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"Finally, we create the system components and the composed system.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"l = Loss()\ntemp = Temperature()\nvariable_loss = couple(l, temp)\n\nconvert(ODESystem, variable_loss)","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"If we wanted to, we could then run a simulation with the composed system.","category":"page"},{"location":"operator/#Operators-for-Large-scale-3D-simulations","page":"Operators","title":"Operators for Large-scale 3D simulations","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"In this documentation so far, we have talked about creating systems of ordinary differential equations in ModelingToolkit and then converting them to systems of partial differential equations to perform 1-, 2-, or 3-dimensional simulations. However, currently this does not work for large scale simulations.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"While this ModelingToolkit functionality is being built, we have a different solution based on the Operator type in this package. Using this system, we still define systems of ODEs to define behavior in a single grid cell, and we also have Operator processes that define behavior between grid cells.","category":"page"},{"location":"operator/#ODE-System","page":"Operators","title":"ODE System","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"As an example, let's first define a system of ODEs:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"using EarthSciMLBase\nusing ModelingToolkit, DifferentialEquations\nusing SciMLOperators, Plots\nusing DomainSets\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\n@parameters y lon = 0.0 lat = 0.0 lev = 1.0 α = 10.0\n@constants p = 1.0\n@variables(\n u(t) = 1.0, v(t) = 1.0, x(t) = 1.0, y(t) = 1.0, windspeed(t) = 1.0\n)\n\neqs = [D(u) ~ -α * √abs(v) + lon,\n D(v) ~ -α * √abs(u) + lat + 1e-14 * lev,\n windspeed ~ lat + lon + lev,\n]\nsys = ODESystem(eqs, t; name=:sys)","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"The equations above don't really have any physical meaning, but they include two state variables, some parameters, and a constant. There is also a variable windspeed which is \"observed\" based on the parameters, rather than being a state variable, which will be important later.","category":"page"},{"location":"operator/#Operator","page":"Operators","title":"Operator","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"Next, we define an operator. To do so, first we create a new type that is a subtype of Operator:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"mutable struct ExampleOp <: Operator\n α::Num # Multiplier from ODESystem\nend","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"In the case above, we're setting up our operator so that it can hold a parameter from our ODE system.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"Next, we need to define a method of EarthSciMLBase.get_scimlop for our operator. This method will be called to get a SciMLOperators.AbstractSciMLOperator that will be used conjunction with the ModelingToolkit system above to integrate the simulation forward in time.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"function EarthSciMLBase.get_scimlop(op::ExampleOp, csys::CoupledSystem, mtk_sys, domain::DomainInfo, obs_functions, coordinate_transform_functions, u0, p)\n obs_f = obs_functions(op.α)\n grd = EarthSciMLBase.grid(domain)\n function run(du, u, p, t)\n u = reshape(u, size(u0)...)\n du = reshape(du, size(u0)...)\n for ix ∈ 1:size(u, 1)\n for (i, c1) ∈ enumerate(grd[1])\n for (j, c2) ∈ enumerate(grd[2])\n for (k, c3) ∈ enumerate(grd[3])\n # Demonstrate coordinate transforms\n t1 = coordinate_transform_functions[1](t, c1, c2, c3)\n t2 = coordinate_transform_functions[2](t, c1, c2, c3)\n t3 = coordinate_transform_functions[3](t, c1, c2, c3)\n # Demonstrate calculating observed value.\n fv = obs_f(t, c1, c2, c3)\n # Set derivative value.\n du[ix, i, j, k] = (t1 + t2 + t3) * fv\n end\n end\n end\n end\n nothing\n end\n FunctionOperator(run, u0[:], p=p)\nend","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"The function above also doesn't have any physical meaning, but it demonstrates some functionality of the Operator \"s\". First, it retrieves a function to get the current value of an observed variable in our ODE system using the obs_functions argement, and it demonstrates how to call the resulting function to get that value. It also demonstrates how to get coordinate transforms using the coordinate_transform_functions argument. Coordinate transforms are discussed in more detail in the documentation for the DomainInfo type.","category":"page"},{"location":"operator/#Domain","page":"Operators","title":"Domain","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"Once we have an ODE system and an operator, the final component we need is a domain to run the simulation on. Defining a domain is covered in more depth in the documentation for the DomainInfo type, but for now we'll just define a simple domain:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"t_min = 0.0\nlon_min, lon_max = -π, π\nlat_min, lat_max = -0.45π, 0.45π\nt_max = 11.5\n\nindepdomain = t ∈ Interval(t_min, t_max)\n\npartialdomains = [lon ∈ Interval(lon_min, lon_max),\n lat ∈ Interval(lat_min, lat_max),\n lev ∈ Interval(1, 3)]\n\ndomain = DomainInfo(\n partialderivatives_δxyδlonlat,\n constIC(16.0, indepdomain), constBC(16.0, partialdomains...);\n grid_spacing = [0.1π, 0.1π, 1])\nnothing #hide","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"Note that our domain includes a coordinate transform to convert from degrees latitude and longitude to meters. Our domain specification also includes grid spacing the the lon, lat, and lev coordinates, which we set as 0.1π, 0.1π, and 1, respectively.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"warning: Warning\nInitial and boundary conditions are not fully implemented for this case, so regardless of the conditions you specify, the initial conditions will be the default values of the variables in the ODE system, and the boundary conditions will be zero.","category":"page"},{"location":"operator/#Coupling-and-Running-the-Simulation","page":"Operators","title":"Coupling and Running the Simulation","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"Next, initialize our operator, giving the the windspeed observed variable, and we can couple our ODESystem, Operator, and Domain together into a single model:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"op = ExampleOp(sys.windspeed)\n\ncsys = couple(sys, op, domain)","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"Finally, we can choose a EarthSciMLBase.SolverStrategy and run the simulation. We choose the SolverStrangThreads strategy, which needs us to specify an ODE solver from the options available in DifferentialEquations.jl for both the MTK system. We choose the Tsit5 solver. Then we create an ODEProblem which can be used to run the simulation. Finally, we solve the problem using the solve function. At this point we need to choose a solver for the Operator part of the system, and we choose the Euler solver. We also choose a splitting time step of 1.0 seconds, which we pass both to our SolverStrangThreads strategy and to the solve function.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"dt = 1.0 # Splitting time step\nst = SolverStrangThreads(Tsit5(), 1.0)\n\nprob = ODEProblem(csys, st)\nsol = solve(prob, Euler(); dt=1.0)\nnothing #hide","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"After the simulation finishes, we can plot the result:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"anim = @animate for i ∈ 1:length(sol.u)\n u = reshape(sol.u[i], :, size(domain)...)\n plot(\n heatmap(u[1, :, :, 1]),\n heatmap(u[1, :, :, 1]),\n )\nend\ngif(anim, fps = 15)","category":"page"},{"location":"coord_transforms/#Coordinate-Transforms-for-Partial-Derivatives","page":"Coordinate Transforms","title":"Coordinate Transforms for Partial Derivatives","text":"","category":"section"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"Often, the coordinates of a grid may be defined in a different coordinate system than the one in which the partial derivatives are desired. For example, grids are often defined in latitude and longitude, but partial derivatives may be required in units of meters to correspond with wind speeds in meters per second.","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"To handle this, the DomainInfo type can be used to define coordinate system transformations. To use it, a coordinate transform function first needs to be defined, for example partialderivatives_δxyδlonlat which transforms partial derivatives from longitude and latitude to meters:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"using EarthSciMLBase\nusing ModelingToolkit\nusing ModelingToolkit: t, D\nusing DomainSets\nusing DynamicQuantities\n\n@parameters lon [unit = u\"rad\"]\n@parameters lat [unit = u\"rad\"]\n@parameters lev\n\npartialderivatives_δxyδlonlat([lev, lon, lat])","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"As you can see in the output of the code above, the function should take as arguments a list of the coordinates describing the grid (in the case above we have a 3-dimensional grid with vertical level, latitude, and longitude), and return a Dictionary relating the index of each coordinate with a factor to multiply the partial derivative by to convert it to the desired units. The function only needs to return factors for the coordinates that are being transformed.","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"To include a coordinate transform in our domain, we include the function in the DomainInfo constructor:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"deg2rad(x) = x * π / 180.0\ndomain = DomainInfo(\n partialderivatives_δxyδlonlat,\n constIC(0.0, t ∈ Interval(0.0f0, 3600.0f0)),\n periodicBC(lat ∈ Interval(deg2rad(-90.0f0), deg2rad(90.0f0))),\n periodicBC(lon ∈ Interval(deg2rad(-180.0f0), deg2rad(180.0f0))),\n zerogradBC(lev ∈ Interval(1.0f0, 10.0f0)),\n);","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"Multiple functions can be included in the DomainInfo constructor, just by including them as a vector, e.g.:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"domain = DomainInfo(\n [transform1, transform2, ...],\n constIC(0.0, t ∈ Interval(0.0f0, 3600.0f0)),\n ...\n)","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"After we include the coordinate transforms in our domain, in general everything should be handled automatically. The coordinate transforms may also be automatically added when different model components are coupled together, so you may not need to worry about them at all in many cases. However, if you would like to use the transformed partial derivatives, for example to create a new PDE equation system, you can get them using the EarthSciMLBase.partialderivatives function:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"δs = partialderivatives(domain)","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"This returns a list of functions, one corresponding to each coordinate in our domain. Then we can calculate the symbolic partial derivative of a variable by just calling each function:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"@variables u(t)\n\n[δs[i](u) for i ∈ eachindex(δs)]","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"You can see an example of how this is implemented in the source code for the Advection model component.","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"Additional transformation functions may be defined in other packages. We recommend that the names of these functions start with partialderivatives_ to make it clear that they are intended to be used in this context.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"CurrentModule = EarthSciMLBase","category":"page"},{"location":"example_all_together/#Example-using-different-components-of-EarthSciMLBase-together","page":"All Together","title":"Example using different components of EarthSciMLBase together","text":"","category":"section"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"This example shows how to define and couple different components of EarthSciMLBase together to create a more complex model. First, we create several model components.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Our first example system is a simple reaction system:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"using EarthSciMLBase\nusing ModelingToolkit, Catalyst, DomainSets, MethodOfLines, DifferentialEquations\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\nusing Plots\n\n# Create our independent variable `t` and our partially-independent variables `x` and `y`.\n@parameters x y\n\nstruct ExampleSys1Coupler sys end\nfunction ExampleSys1()\n @species c₁(t)=5.0 c₂(t)=5.0\n rs = ReactionSystem(\n [Reaction(2.0, [c₁], [c₂])],\n t; name=:Sys1, combinatoric_ratelaws=false)\n convert(ODESystem, complete(rs), metadata=Dict(:coupletype=>ExampleSys1Coupler))\nend\n\nExampleSys1()","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Our second example system is a simple ODE system, with the same two variables.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"struct ExampleSys2Coupler sys end\nfunction ExampleSys2()\n @variables c₁(t)=5.0 c₂(t)=5.0\n @parameters p₁=1.0 p₂=0.5\n ODESystem(\n [D(c₁) ~ -p₁, D(c₂) ~ p₂],\n t; name=:Sys2, metadata=Dict(:coupletype=>ExampleSys2Coupler))\nend\n\nExampleSys2()","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Now, we specify what should happen when we couple the two systems together. In this case, we want the the derivative of the composed system to be equal to the sum of the derivatives of the two systems. We can do that using the operator_compose function from this package.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"function EarthSciMLBase.couple2(sys1::ExampleSys1Coupler, sys2::ExampleSys2Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n sys1 = convert(ODESystem, sys1)\n operator_compose(sys1, sys2)\nend\nnothing # hide","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Once we specify all of the above, it is simple to create our two individual systems and then couple them together. ","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"sys1 = ExampleSys1()\nsys2 = ExampleSys2()\nsys = couple(sys1, sys2)\n\nconvert(ODESystem, sys)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"At this point we have an ODE system that is composed of two other ODE systems. We can inspect its observed variables using the observed function.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"simplified_sys = structural_simplify(convert(ODESystem, sys))","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"observed(simplified_sys)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"We can also run simulations using this system:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"odeprob = ODEProblem(simplified_sys, [], (0.0,10.0), [])\nodesol = solve(odeprob)\nplot(odesol)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Once we've confirmed that our model works in a 0D \"box model\" setting, we can expand it to 1, 2, or 3 dimensions using by adding in initial and boundary conditions. We will also add in advection using constant-velocity wind fields add the same time.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"x_min = y_min = t_min = 0.0\nx_max = y_max = t_max = 1.0\ndomain = DomainInfo(\n constIC(4.0, t ∈ Interval(t_min, t_max)),\n periodicBC(x ∈ Interval(x_min, x_max)),\n zerogradBC(y ∈ Interval(y_min, y_max)),\n)\n\nsys_pde = couple(sys, domain, ConstantWind(t, 1.0, 1.0), Advection())\n\nsys_pde_mtk = convert(PDESystem, sys_pde)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Now we can inspect this new system that we've created:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"sys_pde_mtk.dvs","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"sys_pde_mtk.bcs","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Finally, we can run a simulation using this system:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"discretization = MOLFiniteDifference([x=>10, y=>10], t, approx_order=2)\n@time pdeprob = discretize(sys_pde_mtk, discretization)\n@time pdesol = solve(pdeprob, Tsit5(), saveat=0.1)\n\n# Plot the solution.\ndiscrete_x, discrete_y, discrete_t = pdesol[x], pdesol[y], pdesol[t]\n@variables Sys1₊c₁(..) Sys1₊c₂(..)\nsolc1, solc2 = pdesol[Sys1₊c₁(t, x, y)], pdesol[Sys1₊c₂(t, x, y)]\nanim = @animate for k in 1:length(discrete_t)\n p1 = heatmap(solc1[k, 1:end-1, 1:end-1], title=\"c₁ t=\\$(discrete_t[k])\", clim=(0,4.0), lab=:none)\n p2 = heatmap(solc2[k, 1:end-1, 1:end-1], title=\"c₂ t=\\$(discrete_t[k])\", clim=(0,7.0), lab=:none)\n plot(p1, p2, layout=(1,2), size=(800,400))\nend\ngif(anim, fps = 8)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Because our system is a system of ordinary differential equations rather than partial differential equations, all of the grid cells in the animation above have the same value. Refer to the advection example for an example of a system of partial differential equations.","category":"page"},{"location":"composition/#Model-Composition","page":"Composition","title":"Model Composition","text":"","category":"section"},{"location":"composition/","page":"Composition","title":"Composition","text":"A main goal of the EarthSciMLBase package is to allow model components to be created independently and composed together. We achieve this by creating coupletypes that allow us to use multiple dispatch on the EarthSciMLBase.couple2 function to specify how particular systems should be coupled together.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"To demonstrate how this works, below we define three model components, Photolysis, Chemistry, and Emissions, which represent different processes in the atmosphere.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"using ModelingToolkit, Catalyst, EarthSciMLBase\nusing ModelingToolkit: t_nounits\nt = t_nounits\n\nstruct PhotolysisCoupler sys end\nfunction Photolysis(; name=:Photolysis)\n @variables j_NO2(t)\n eqs = [\n j_NO2 ~ max(sin(t/86400),0)\n ]\n ODESystem(eqs, t, [j_NO2], [], name=name,\n metadata=Dict(:coupletype=>PhotolysisCoupler))\nend\n\nPhotolysis()","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"You can see that the system defined above is mostly a standard ModelingToolkit ODE system, except for two things:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"The first unique part is that we define a PhotolysisCoupler type:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"struct PhotolysisCoupler sys end","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"It is important that this type is a struct, and that the struct has a single field named sys. When defining your own coupled systems, you can just copy the line of code above but change the name of the type (i.e., change it to something besides PhotolysisCoupler).","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"The second unique part is that we define some metadata for our ODESystem to tell it what coupling type to use:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"metadata=Dict(:coupletype=>PhotolysisCoupler)","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Again, when making your own components, just copy the code above but change PhotolysisCoupler to something else.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Let's follow the same process for some additional components:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"struct ChemistryCoupler sys end\nfunction Chemistry(; name=:Chemistry)\n @parameters jNO2\n @species NO2(t)\n rxs = [\n Reaction(jNO2, [NO2], [], [1], [])\n ]\n rsys = ReactionSystem(rxs, t, [NO2], [jNO2]; \n combinatoric_ratelaws=false, name=name)\n convert(ODESystem, complete(rsys), metadata=Dict(:coupletype=>ChemistryCoupler))\nend\n\nChemistry()","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"For our chemistry component above, because it's is originally a ReactionSystem instead of an ODESystem, we convert it to an ODESystem before adding the metadata.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"struct EmissionsCoupler sys end\nfunction Emissions(; name=:Emissions)\n @parameters emis = 1\n @variables NO2(t)\n eqs = [NO2 ~ emis]\n ODESystem(eqs, t; name=name,\n metadata=Dict(:coupletype=>EmissionsCoupler))\nend\n\nEmissions()","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Now, we need to define ways to couple the model components together. We can do this by defining a coupling function (as a method of EarthSciMLBase.couple2) for each pair of model components that we want to couple. Each coupling function should have the signature EarthSciMLBase.couple2(a::ACoupler, b::BCoupler)::ConnectorSystem, and should assume that the two ODESystems are inside their respective couplers in the sys field. It should make any edits to the components as needed and return a ConnectorSystem which defines the relationship between the two components.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"The code below defines a method for coupling the Chemistry and Photolysis components. First, it uses the param_to_var function to convert the photolysis rate parameter jNO2 from the Chemistry component to a variable, then it creates a new Chemistry component with the updated photolysis rate, and finally, it creates a ConnectorSystem object that sets the j_NO2 variable from the Photolysis component equal to the jNO2 variable from the Chemistry component. The next effect is that the photolysis rate in the Chemistry component is now controlled by the Photolysis component.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"function EarthSciMLBase.couple2(c::ChemistryCoupler, p::PhotolysisCoupler)\n c, p = c.sys, p.sys\n c = param_to_var(convert(ODESystem, c), :jNO2)\n ConnectorSystem([c.jNO2 ~ p.j_NO2], c, p)\nend\nnothing # hide","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Next, we define a method for coupling the Chemistry and Emissions components. To do this we use the operator_compose function to add the NO2 species from the Emissions component to the time derivative of NO2 in the Chemistry component.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"function EarthSciMLBase.couple2(c::ChemistryCoupler, emis::EmissionsCoupler)\n c, emis = c.sys, emis.sys\n operator_compose(convert(ODESystem, c), emis, Dict(\n c.NO2 => emis.NO2,\n ))\nend\nnothing # hide","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Finally, we can compose the model components together to create our complete model. To do so, we just initialize each model component and add the components together using the couple function. We can use the convert function to convert the composed model to a ModelingToolkit ODESystem so we can see what the combined equations look like.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"model = couple(Photolysis(), Chemistry(), Emissions())\nconvert(ODESystem, model)","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Finally, we can use the graph function to visualize the model components and their connections.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"using MetaGraphsNext\nusing CairoMakie, GraphMakie\n\ng = graph(model)\n\nf, ax, p = graphplot(g; ilabels=labels(g))\nhidedecorations!(ax); hidespines!(ax); ax.aspect = DataAspect()\n\nf","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = EarthSciMLBase","category":"page"},{"location":"#EarthSciMLBase:-Utilities-for-Symbolic-Earth-Science-Modeling-and-Machine-Learning","page":"Home","title":"EarthSciMLBase: Utilities for Symbolic Earth Science Modeling and Machine Learning","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for EarthSciMLBase.","category":"page"},{"location":"","page":"Home","title":"Home","text":"This package contains utilities for constructing Earth Science models in Julia using ModelingToolkit.jl.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"EarthSciMLBase\")","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"This package contains types and functions designed to simplify the process of constructing and composing symbolically-defined Earth Science model components together.","category":"page"},{"location":"#Feature-List","page":"Home","title":"Feature List","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Operations to compose ModelingToolkit.jl equation systems together.\nOperations to add intitial and boundary conditions to systems and to turn ODE systems into PDE systems, and to provide coordinate transformations.\nOperations to add Advection terms to systems.\nA Simulator type for running large-scale simulations.","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Please refer to the SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages for guidance on PRs, issues, and other matters relating to contributing.","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this EarthSciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(;mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"You can also download the \nmanifest file and the\nproject file.","category":"page"},{"location":"advection/#Advection","page":"Advection","title":"Advection","text":"","category":"section"},{"location":"advection/","page":"Advection","title":"Advection","text":"The Advection function adds advection to a system of equations. This is useful for modeling the transport of a substance by a fluid. Advection is implemented with the Advection type.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"!warning Fully symbolic partial differential equations like those shown here don't currently work on domains that have a large number of grid cells. See here for additional information.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"To demonstrate how this can work, we will start with a simple system of equations:","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"using EarthSciMLBase, ModelingToolkit\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\nfunction ExampleSys()\n @variables y(t)\n @parameters p=2.0\n ODESystem([D(y) ~ p], t; name=:ExampleSys)\nend\n\nExampleSys()","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"We also need to create our initial and boundary conditions.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"using DomainSets\n@parameters x\ndomain = DomainInfo(constIC(0.0, t ∈ Interval(0, 1.0)), constBC(1.0, x ∈ Interval(0, 1.0)))\nnothing # hide","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"Now we convert add advection to each of the state variables. We're also adding a constant wind (ConstantWind) in the x-direction, with a speed of 1.0.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"sys_advection = couple(ExampleSys(), domain, ConstantWind(t, 1.0), Advection())\nsys_mtk = convert(PDESystem, sys_advection)","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"Finally, we can discretize the system and solve it:","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"using MethodOfLines, DifferentialEquations, Plots\ndiscretization = MOLFiniteDifference([x=>10], t, approx_order=2)\n@time prob = discretize(sys_mtk, discretization)\n@time sol = solve(prob, Tsit5(), saveat=0.1)\n\n\n# Plot the solution.\ndiscrete_x = sol[x]\ndiscrete_t = sol[t]\nsoly = sol[sys_mtk.dvs[3]]\nanim = @animate for k in 1:length(discrete_t)\n plot(discrete_x, soly[k, 1:end], title=\"t=\\$(discrete_t[k])\", ylim=(0,2.5), lab=:none)\nend\ngif(anim, fps = 8)","category":"page"}] +[{"location":"api/#API-Index","page":"API Reference","title":"API Index","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"","category":"page"},{"location":"api/#API-Documentation","page":"API Reference","title":"API Documentation","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"Modules = [EarthSciMLBase]","category":"page"},{"location":"api/#EarthSciMLBase.Advection","page":"API Reference","title":"EarthSciMLBase.Advection","text":"Apply advection to a model.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.ConnectorSystem","page":"API Reference","title":"EarthSciMLBase.ConnectorSystem","text":"A connector for two systems.\n\neqs\nfrom\nto\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.CoupledSystem","page":"API Reference","title":"EarthSciMLBase.CoupledSystem","text":"A system for composing together other systems using the couple function.\n\nsystems: Model components to be composed together\ndomaininfo: Initial and boundary conditions and other domain information\npdefunctions: A vector of functions where each function takes as an argument the resulting PDESystem after DomainInfo is added to this system, and returns a transformed PDESystem.\n\nops: A vector of operators to run during simulations.\n\ncallbacks: A vector of callbacks to run during simulations.\ninit_callbacks: Objects x with an init_callback(x, Simulator)::DECallback method.\n\nThings that can be added to a CoupledSystem: * ModelingToolkit.ODESystems. If the ODESystem has a field in the metadata called :coupletype (e.g. ModelingToolkit.get_metadata(sys)[:coupletype] returns a struct type with a single field called sys) then that type will be used to check for methods of EarthSciMLBase.couple that use that type. * Operators * DomainInfos * Callbacks * Types X that implement a EarthSciMLBase.init_callback(::X, sys::CoupledSystem, sys_mtk, obs_eqs, domain::DomainInfo)::DECallback method * Other CoupledSystems * Types X that implement a EarthSciMLBase.couple2(::X, ::CoupledSystem) or EarthSciMLBase.couple2(::CoupledSystem, ::X) method. * Tuples or AbstractVectors of any of the things above.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.DomainInfo","page":"API Reference","title":"EarthSciMLBase.DomainInfo","text":"Domain information for a ModelingToolkit.jl PDESystem. It can be used with the + operator to add initial and boundary conditions and coordinate transforms to a ModelingToolkit.jl ODESystem or Catalyst.jl ReactionSystem.\n\nNOTE: The independent variable (usually time) must be first in the list of initial and boundary conditions.\n\npartial_derivative_funcs: Function that returns spatial derivatives of the partially-independent variables, optionally performing a coordinate transformation first.\nCurrent function options in this package are:\npartialderivatives_δxyδlonlat: Returns partial derivatives after transforming any variables named lat and lon\nfrom degrees to cartesian meters, assuming a spherical Earth.\nOther packages may implement additional functions. They are encouraged to use function names starting with partialderivatives_.\n\ngrid_spacing: The discretization intervals for the partial independent variables.\nicbc: The sets of initial and/or boundary conditions.\nspatial_ref: The spatial reference system for the domain.\ntime_offset: The time offset for the domain.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.ICBCcomponent","page":"API Reference","title":"EarthSciMLBase.ICBCcomponent","text":"Initial and boundary condition components that can be combined to create an DomainInfo object.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.Operator","page":"API Reference","title":"EarthSciMLBase.Operator","text":"Operators are objects that modify the current state of a Simulator system. Each operator should be define a function with the signature:\n\n`EarthSciMLBase.get_scimlop(::Operator, csys::CoupledSystem, mtk_sys, domain::DomainInfo, obs_functions, coordinate_transform_functions, u0, p)::AbstractSciMLOperator`\n\nwhich should return a SciMLOperators.AbstractSciMLOperator. Refer to the SciMLOperators.jl documentation for more information on how to define operators.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverIMEX","page":"API Reference","title":"EarthSciMLBase.SolverIMEX","text":"A solver strategy based on implicit-explicit (IMEX) time integration. See here for additional information.\n\nkwargs:\n\nstiff_scimlop: Whether the stiff ODE function should be implemented as a SciMLOperator.\nstiff_sparse: Whether the stiff ODE function should use a sparse Jacobian.\nstiff_jac: Whether the stiff ODE function should use an analytical Jacobian.\nstiffjacscimlop: Whether the stiff ODE function Jacobian should be implemented as a SciMLOperator. (Ignored if stiff_jac==false.)\nstiff_tgrad: Whether the stiff ODE function should use an analytical time gradient.\n\nAdditional kwargs for ODEProblem constructor:\n\nu0: initial condtions; if \"nothing\", default values will be used.\np: parameters; if \"nothing\", default values will be used.\nname: name of the model.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrang","page":"API Reference","title":"EarthSciMLBase.SolverStrang","text":"A simulator strategy based on Strang splitting. Choose either SimulatorStrangThreads or SimulatorStrangSerial to run the simulation.\n\nkwargs for ODEProblem constructor:\n\nu0: initial condtions; if \"nothing\", default values will be used.\np: parameters; if \"nothing\", default values will be used.\nnonstiff_params: parameters for the non-stiff ODE system.\nname: name of the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrangSerial","page":"API Reference","title":"EarthSciMLBase.SolverStrangSerial","text":"# Specify the stiff ODE solver algorithm.\n# `timestep` is the length of time for each splitting step.\nSimulatorStrangSerial(stiffalg, timestep; kwargs...)\n\nPerform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution will be calculated in serial.\n\nAdditional kwargs for ODEProblem constructor:\n\nu0: initial condtions; if \"nothing\", default values will be used.\np: parameters; if \"nothing\", default values will be used.\nnonstiff_params: parameters for the Operators.\n\nstiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)\ntimestep: Length of each splitting time step\nstiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrangThreads","page":"API Reference","title":"EarthSciMLBase.SolverStrangThreads","text":"# Specify the number of threads and the stiff ODE solver algorithm.\n# `timestep` is the length of time for each splitting step.\nSimulatorStrangThreads(threads, stiffalg, timestep; kwargs...)\n# Use the default number of threads.\nSimulatorStrangThreads(stiffalg, timestep; kwargs...)\n\nPerform a simulation using Strang splitting, where the MTK system is assumed to be stiff and the operators are assumed to be non-stiff. The solution of the stiff ODE system is parallelized across grid cells using the specified number of threads.\n\nthreads: Number of threads to use\nstiffalg: Stiff solver algorithm to use (see https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/)\ntimestep: Length of each splitting time step\nstiff_kwargs: Keyword arguments for the stiff ODEProblem constructor and solver.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.SolverStrategy","page":"API Reference","title":"EarthSciMLBase.SolverStrategy","text":"SolverStrategy is an abstract type that defines the strategy for running a simulation.\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.constBC","page":"API Reference","title":"EarthSciMLBase.constBC","text":"Construct constant boundary conditions equal to the value specified by val.\n\nval: The value of the constant boundary conditions.\npartialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.constIC","page":"API Reference","title":"EarthSciMLBase.constIC","text":"Construct constant initial conditions equal to the value specified by val.\n\nval: The value of the constant initial conditions.\nindepdomain: The independent domain, e.g. t ∈ Interval(t_min, t_max).\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.periodicBC","page":"API Reference","title":"EarthSciMLBase.periodicBC","text":"Construct periodic boundary conditions for the given partialdomains. Periodic boundary conditions are defined as when the value at one side of the domain is set equal to the value at the other side, so that the domain \"wraps around\" from one side to the other.\n\npartialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].\n\n\n\n\n\n","category":"type"},{"location":"api/#EarthSciMLBase.zerogradBC","page":"API Reference","title":"EarthSciMLBase.zerogradBC","text":"Construct zero-gradient boundary conditions for the given partialdomains.\n\npartialdomains: The partial domains, e.g. [x ∈ Interval(x_min, x_max), y ∈ Interval(y_min, y_max)].\n\n\n\n\n\n","category":"type"},{"location":"api/#Base.convert-Tuple{Type{<:ModelingToolkit.ODESystem}, CoupledSystem}","page":"API Reference","title":"Base.convert","text":"convert(, sys; name, simplify, kwargs...)\n\n\nGet the ODE ModelingToolkit ODESystem representation of a CoupledSystem.\n\nkwargs:\n\nname: The desired name for the resulting ODESystem\nsimplify: if true, the observed variables that are not needed to specify the state variables will be pruned and returned as a second return value after the ODESystem, which will be structurally simplified.\n\n\n\n\n\n","category":"method"},{"location":"api/#Base.convert-Tuple{Type{<:ModelingToolkit.PDESystem}, CoupledSystem}","page":"API Reference","title":"Base.convert","text":"convert(, sys; name, kwargs...)\n\n\nGet the ModelingToolkit PDESystem representation of a CoupledSystem.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.ConstantWind-Tuple{Any, Vararg{Any}}","page":"API Reference","title":"EarthSciMLBase.ConstantWind","text":"ConstantWind(t, vals; name)\n\n\nConstruct a constant wind velocity model component with the given wind speed(s), which should include units. For example, ConstantWind(t, 1u\"m/s\", 2u\"m/s\").\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.MeanWind-Tuple{Any, DomainInfo}","page":"API Reference","title":"EarthSciMLBase.MeanWind","text":"MeanWind(t, domain)\n\n\nA model component that represents the mean wind velocity, where pvars is the partial dependent variables for the domain.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.add_dims-Tuple{Any, AbstractVector, AbstractVector}","page":"API Reference","title":"EarthSciMLBase.add_dims","text":"add_dims(expression, vars, dims)\nadd_dims(equation, vars, dims)\n\nAdd the given dimensions to each variable in vars in the given expression or equation. Each variable in vars must be unidimensional, i.e. defined like @variables u(t) rather than @variables u(..).\n\nExample:\n\nusing EarthSciMLBase, ModelingToolkit\n\n@parameters x y k t\n@variables u(t) q(t)\nexp = 2u + 3k*q + 1\nEarthSciMLBase.add_dims(exp, [u, q], [x, y, t])\n\n# output\n1 + 2u(x, y, t) + 3k*q(x, y, t)\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.add_metadata-Tuple{Any, Any}","page":"API Reference","title":"EarthSciMLBase.add_metadata","text":"Add the units and description in the variable from to the variable to.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.add_scope-Tuple{Any, Any, Any}","page":"API Reference","title":"EarthSciMLBase.add_scope","text":"add_scope(sys, v, iv)\n\n\nAdd a system scope to a variable name, for example so that x in system sys1 becomes sys1₊x. iv is the independent variable.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.copy_with_change-Tuple{ModelingToolkit.ODESystem}","page":"API Reference","title":"EarthSciMLBase.copy_with_change","text":"copy_with_change(\n sys;\n eqs,\n name,\n unknowns,\n parameters,\n metadata,\n continuous_events,\n discrete_events\n)\n\n\nCreate a copy of an ODESystem with the given changes.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.couple-Tuple","page":"API Reference","title":"EarthSciMLBase.couple","text":"couple(systems...) -> CoupledSystem\n\n\nCouple multiple ModelingToolkit systems together.\n\nThe systems that are arguments to this system can be of type ModelingToolkit.AbstractSystem, CoupledSystem, DomainInfo, or any type T that has a method couple(::CoupledSystem, ::T)::CoupledSystem or a method couple(::T, ::CoupledSystem)::CoupledSystem defined for it.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.couple2-Tuple{}","page":"API Reference","title":"EarthSciMLBase.couple2","text":"couple2()\n\n\nPerform bi-directional coupling for two equation systems.\n\nTo specify couplings for system pairs, create methods for this function with the signature:\n\nEarthSciMLBase.couple2(a::ACoupler, b::BCoupler)::ConnectorSystem\n\nwhere ACoupler and BCoupler are :coupletypes defined like this:\n\nstruct ACoupler sys end\n@named asys = ODESystem([], t, metadata=Dict(:coupletype=>ACoupler))\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.dims-Tuple{EarthSciMLBase.ICcomponent}","page":"API Reference","title":"EarthSciMLBase.dims","text":"dims(\n icbc::EarthSciMLBase.ICcomponent\n) -> Vector{Symbolics.Num}\n\n\nReturns the dimensions of the independent and partial domains associated with these initial or boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.domains-Tuple{EarthSciMLBase.ICcomponent}","page":"API Reference","title":"EarthSciMLBase.domains","text":"domains(icbc::EarthSciMLBase.ICcomponent) -> Vector\n\n\nReturns the domains associated with these initial or boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.dtype-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T","page":"API Reference","title":"EarthSciMLBase.dtype","text":"dtype(_)\n\n\nReturn the data type of the state variables for this domain, based on the data types of the boundary conditions domain intervals.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.endpoints-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T","page":"API Reference","title":"EarthSciMLBase.endpoints","text":"endpoints(d)\n\n\nReturn the endpoints of the partial independent variables for this domain.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.get_coupletype-Tuple{ModelingToolkit.AbstractSystem}","page":"API Reference","title":"EarthSciMLBase.get_coupletype","text":"Return the coupling type associated with the given system.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.get_dv-Tuple{Any, Any}","page":"API Reference","title":"EarthSciMLBase.get_dv","text":"Return the dependent variable, which is the first argument of the term, unless the term is a time derivative, in which case the dependent variable is the argument of the time derivative.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.get_needed_vars-Tuple{ModelingToolkit.ODESystem}","page":"API Reference","title":"EarthSciMLBase.get_needed_vars","text":"get_needed_vars(sys)\n\n\nReturn the indexes of the system variables that the state variables of the final simplified system depend on. This should be done before running structural_simplify on the system.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.graph-Tuple{CoupledSystem}","page":"API Reference","title":"EarthSciMLBase.graph","text":"Create a graph from a CoupledSystem using the MetaGraphsNext package.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.grid-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T","page":"API Reference","title":"EarthSciMLBase.grid","text":"grid(d)\n\n\nReturn the ranges representing the discretization of the partial independent variables for this domain, based on the discretization intervals given in Δs.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.icbc-Tuple{DomainInfo, AbstractVector}","page":"API Reference","title":"EarthSciMLBase.icbc","text":"icbc(di, states)\n\n\nReturn a vector of equations that define the initial and boundary conditions for the given state variables.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.init_callback-Tuple{}","page":"API Reference","title":"EarthSciMLBase.init_callback","text":"Types that implement an:\n\ninit_callback(x, sys::CoupledSystem, obs_eqs, domain::DomainInfo)::DECallback\n\nmethod can also be coupled into a CoupledSystem. The init_callback function will be run before the simulator is run to get the callback.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.init_u-Tuple{ModelingToolkit.ODESystem, DomainInfo}","page":"API Reference","title":"EarthSciMLBase.init_u","text":"Initialize the state variables.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.ivar-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.ivar","text":"ivar(di::DomainInfo) -> Any\n\n\nReturn the independent variable associated with these initial and boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.observed_expression-Tuple{Any, Any}","page":"API Reference","title":"EarthSciMLBase.observed_expression","text":"observed_expression(eqs, x)\n\n\nReturn an expression for the observed value of a variable x after substituting in the constants observed values of other variables. extra_eqs is a list of additional equations to use in the substitution.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.observed_function-Tuple{Any, Any, Any}","page":"API Reference","title":"EarthSciMLBase.observed_function","text":"observed_function(eqs, x, coords)\n\n\nReturn a function to for the observed value of a variable x based on the input arguments in coords. extra_eqs is a list of additional equations to use to determine the value of x.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.ode_step!-Tuple{Any, SolverStrangThreads, Vararg{Any, 5}}","page":"API Reference","title":"EarthSciMLBase.ode_step!","text":"Take a step using the ODE solver.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.operator_compose","page":"API Reference","title":"EarthSciMLBase.operator_compose","text":"operator_compose(a, b)\noperator_compose(a, b, translate)\n\n\nCompose to systems of equations together by adding the right-hand side terms together of equations that have matching left-hand sides. The left hand sides of two equations will be considered matching if:\n\nThey are both time derivatives of the same variable.\nThe first one is a time derivative of a variable and the second one is the variable itself.\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, e.g. Dict(sys1.sys.x => sys2.sys.y).\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, with a conversion factor, e.g. Dict(sys1.sys.x => sys2.sys.y => 6).\n\n\n\n\n\n","category":"function"},{"location":"api/#EarthSciMLBase.param_to_var-Tuple{ModelingToolkit.AbstractSystem, Vararg{Symbol}}","page":"API Reference","title":"EarthSciMLBase.param_to_var","text":"Replace the parameter p in the system sys with a new variable that has the same name, units, and description as p.\n\nparam_to_var(sys, ps)\n\n\nThis can be useful to replace a parameter that does not change in time in a model component with one specified by another system that does change in time (or space). For example, the code below specifies a first-order loss equation, and then changes the temperature (which determines the loss rate) with a temperature value that varies in time. ```\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.partialderivative_transforms-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.partialderivative_transforms","text":"partialderivative_transforms(di::DomainInfo) -> Vector{Any}\n\n\nReturn transform factor to multiply each partial derivative operator by, for example to convert from degrees to meters.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.partialderivatives-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.partialderivatives","text":"partialderivatives(di::DomainInfo) -> Any\n\n\nReturn the partial derivative operators for the given domain.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.partialderivatives_δxyδlonlat-Tuple{AbstractVector}","page":"API Reference","title":"EarthSciMLBase.partialderivatives_δxyδlonlat","text":"partialderivatives_δxyδlonlat(pvars; default_lat)\n\n\nReturn partial derivative operator transform factors corresponding for the given partial-independent variables after converting variables named lon and lat from degrees to x and y meters, assuming they represent longitude and latitude on a spherical Earth.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.prune_observed-Tuple{ModelingToolkit.ODESystem}","page":"API Reference","title":"EarthSciMLBase.prune_observed","text":"prune_observed(sys)\n\n\nRemove equations from an ODESystem where the variable in the LHS is not present in any of the equations for the state variables. This can be used to remove computationally intensive equations that are not used in the final model.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.pvars-Tuple{DomainInfo}","page":"API Reference","title":"EarthSciMLBase.pvars","text":"pvars(di::DomainInfo) -> Any\n\n\nReturn the partial independent variables associated with these initial and boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.single_ode_step!-NTuple{6, Any}","page":"API Reference","title":"EarthSciMLBase.single_ode_step!","text":"Take a step using the ODE solver with the given IIchunk (grid cell interator) and integrator.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.steplength-Tuple{Any}","page":"API Reference","title":"EarthSciMLBase.steplength","text":"steplength(timesteps)\n\n\nReturn the time step length common to all of the given timesteps. Throw an error if not all timesteps are the same length.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.stiff_callback-Union{Tuple{T}, Tuple{Any, AbstractArray{T}, EarthSciMLBase.SolverStrang, Any, Any}} where T","page":"API Reference","title":"EarthSciMLBase.stiff_callback","text":"A callback to periodically run the stiff solver.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.threaded_ode_step!-NTuple{6, Any}","page":"API Reference","title":"EarthSciMLBase.threaded_ode_step!","text":"Take a step using the ODE solver.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.timesteps-Union{Tuple{Vararg{AbstractVector{T}}}, Tuple{T}} where T<:AbstractFloat","page":"API Reference","title":"EarthSciMLBase.timesteps","text":"timesteps(tsteps)\n\n\nReturn the time points during which integration should be stopped to run the operators.\n\n\n\n\n\n","category":"method"},{"location":"api/#EarthSciMLBase.tspan-Union{Tuple{DomainInfo{T}}, Tuple{T}} where T<:AbstractFloat","page":"API Reference","title":"EarthSciMLBase.tspan","text":"tspan(d)\n\n\nReturn the time range associated with this domain.\n\n\n\n\n\n","category":"method"},{"location":"icbc/#Initial-and-Boundary-conditions","page":"Initial and Boundary Conditions","title":"Initial and Boundary conditions","text":"","category":"section"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"Oftentimes we will want to do a 1, 2, or 3-dimensional simulation, rather than the 0-dimensional simulation we get by default with a system of ordinary differential equations. In these cases, we will need to specify initial and boundary conditions for the system.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"To demonstrate how to do this, we will use the following simple system of ordinary differential equations:","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"using EarthSciMLBase\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\n@parameters x y\n\nfunction ExampleSys()\n @variables u(t) v(t)\n eqs = [\n D(u) ~ √abs(v),\n D(v) ~ √abs(u),\n ]\n ODESystem(eqs, t; name=:Docs₊Example)\nend\n\nExampleSys()","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"Next, we specify our initial and boundary conditions using the DomainInfo type. We initialize DomainInfo with sets of initial and boundary conditions. In the example below, we set constant initial conditions using constIC and constant boundary conditions using constBC.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"using DomainSets\n\nx_min = y_min = t_min = 0.0\nx_max = y_max = t_max = 1.0\n\n# Create constant initial conditions = 16.0 and boundary conditions = 4.0.\nicbc = DomainInfo(\n constIC(4.0, t ∈ Interval(t_min, t_max)),\n constBC(16.0,\n x ∈ Interval(x_min, x_max),\n y ∈ Interval(y_min, y_max),\n ),\n)\nnothing # hide","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"It is also possible to use periodic boundary conditions with periodicBC and zero-gradient boundary conditions with zerogradBC.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"Finally, we combine our initial and boundary conditions with our system of equations using the couple function.","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"model = couple(ExampleSys(), icbc)\n\neq_sys = convert(PDESystem, model)","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"We can also look at the expanded boundary conditions of the resulting equation system:","category":"page"},{"location":"icbc/","page":"Initial and Boundary Conditions","title":"Initial and Boundary Conditions","text":"eq_sys.bcs","category":"page"},{"location":"operator_compose/#Operator-Composition","page":"Operator Composition","title":"Operator Composition","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"There are a lot of cases where there are two different \"processes\" or \"operators\" that change the same variable. For example, CO2 in the atmosphere can be emitted by human activity, and it can also be absorbed by the ocean. In models, typically the emission and removal are considered separate processes which are represented by separate model components. However, when we want to combine these two components into a single model, we need to be able to compose them together.","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"We can use the operator_compose function for this. It composes to systems of equations together by adding the right-hand side terms together of equations that have matching left-hand sides. The left hand sides of two equations will be considered matching if:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"They are both time derivatives of the same variable.\nThe first one is a time derivative of a variable and the second one is the variable itself.\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, e.g. Dict(sys1.sys.x => sys2.sys.y).\nThere is an entry in the optional translate dictionary that maps the dependent variable in the first system to the dependent variable in the second system, with a conversion factor, e.g. Dict(sys1.sys.x => sys2.sys.y => 6).","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"Perhaps we can make this somewhat clearer with some examples.","category":"page"},{"location":"operator_compose/#Examples","page":"Operator Composition","title":"Examples","text":"","category":"section"},{"location":"operator_compose/#Example-with-matching-variable-time-derivatives","page":"Operator Composition","title":"Example with matching variable time derivatives","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"The example below shows that when we operator_compose two systems together that are both equal to D(x) = p, the resulting system is equal to D(x) = 2p.","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"using EarthSciMLBase\nusing ModelingToolkit\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\n\nstruct ExampleSysCoupler sys end\nfunction ExampleSys()\n @variables x(t)\n @parameters p\n ODESystem([D(x) ~ p], t; name=:ExampleSys,\n metadata=Dict(:coupletype=>ExampleSysCoupler))\nend\n\nExampleSys()","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSys2Coupler sys end\nfunction ExampleSys2()\n @variables x(t)\n @parameters p\n ODESystem([D(x) ~ 2p], t; name=:ExampleSys2,\n metadata=Dict(:coupletype=>ExampleSys2Coupler))\nend\n\nExampleSys2()","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"sys1 = ExampleSys()\nsys2 = ExampleSys2()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSys2Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2)\nend\n\ncombined = couple(sys1, sys2)\n\ncombined_mtk = convert(ODESystem, combined)","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"The simplified equation should be D(x) = p + sys2_xˍt:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"combined_simplified = structural_simplify(combined_mtk)","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"where sys2_xˍt is also equal to p:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(combined_simplified)","category":"page"},{"location":"operator_compose/#Example-with-non-matching-variables","page":"Operator Composition","title":"Example with non-matching variables","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"This example demonstrates a case where one variable in the first system is equal to another variable in the second system:","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSys3Coupler sys end\nfunction ExampleSys3()\n @variables y(t)\n @parameters p\n ODESystem([D(y) ~ p], t; name=:ExampleSys3,\n metadata=Dict(:coupletype=>ExampleSys3Coupler))\nend\n\nsys1 = ExampleSys()\nsys2 = ExampleSys3()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSys3Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2, Dict(sys1.x => sys2.y))\nend\n\ncombined = couple(sys1, sys2)\ncombined_simplified = structural_simplify(convert(ODESystem, combined))","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(combined_simplified)","category":"page"},{"location":"operator_compose/#Example-with-a-non-ODE-system","page":"Operator Composition","title":"Example with a non-ODE system","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"In the second case above, we might have a variable in the second system that is equal to a rate, but it is not a time derivative. This could happen if we are extracting emissions from a file, and those emissions are already in units of kg/s, or something similar. The example below demonstrates this case. (Note that this case can also be used with the conversion factors shown in the last example.)","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSysNonODECoupler sys end\nfunction ExampleSysNonODE()\n @variables y(t)\n @parameters p\n ODESystem([y ~ p], t; name=:ExampleSysNonODE,\n metadata=Dict(:coupletype=>ExampleSysNonODECoupler))\nend\n\nsys1 = ExampleSys()\nsys2 = ExampleSysNonODE()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSysNonODECoupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2, Dict(sys1.x => sys2.y))\nend\n\ncombined = couple(sys1, sys2)\nsys_combined = structural_simplify(convert(ODESystem, combined))","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(sys_combined)","category":"page"},{"location":"operator_compose/#Example-with-non-matching-variables-and-a-conversion-factor","page":"Operator Composition","title":"Example with non-matching variables and a conversion factor","text":"","category":"section"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"Finally, this last example shows the fourth case, where a conversion factor is included in the translation dictionary.","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"struct ExampleSysNonODE2Coupler sys end\nfunction ExampleSysNonODE2()\n @variables y(t)\n @parameters p\n ODESystem([y ~ p], t; name=:Docs₊ExampleSysNonODE2,\n metadata=Dict(:coupletype=>ExampleSysNonODE2Coupler))\nend\n\nsys1 = ExampleSys()\nsys2 = ExampleSysNonODE2()\n\nfunction EarthSciMLBase.couple2(sys1::ExampleSysCoupler, sys2::ExampleSysNonODE2Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n operator_compose(sys1, sys2, Dict(sys1.x => sys2.y => 6.0))\nend\n\ncombined = couple(sys1, sys2)\ncombined_simplified = structural_simplify(convert(ODESystem, combined))","category":"page"},{"location":"operator_compose/","page":"Operator Composition","title":"Operator Composition","text":"observed(combined_simplified)","category":"page"},{"location":"param_to_var/#Converting-parameters-to-variables","page":"Parameter Replacement","title":"Converting parameters to variables","text":"","category":"section"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"This can be useful to replace a parameter that does not change in time in a model component with one specified by another system that does change in time (or space). For example, the code below specifies a first-order loss equation, and then changes the temperature (which determines the loss rate) with a temperature value that varies in time.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"As an example, we will create a loss equation that depends on the temperature, starting with a constant temperature. We will then create a temperature equation that varies in time, and use the param_to_var function to replace the constant temperature in the loss equation with the time-varying temperature.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"So first, let's specify the original system with constant temperature.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"using ModelingToolkit, EarthSciMLBase, DynamicQuantities\nusing ModelingToolkit: t, D\n\nstruct LossCoupler sys end\nfunction Loss()\n @variables A(t)=1 [unit=u\"kg\"]\n @parameters k=1 [unit=u\"s^-1\"]\n @parameters T=300 [unit=u\"K\"]\n @constants T₀=300 [unit=u\"K\"]\n eq = D(A) ~ -k*exp(T/T₀) * A\n ODESystem([eq], t; name=:Loss, metadata=Dict(:coupletype=>LossCoupler))\nend\n\nLoss()","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"Next, we specify the temperature that varies in time.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"struct TemperatureCoupler sys end\nfunction Temperature()\n @variables T(t)=300 [unit=u\"K\"]\n @constants Tc=1.0 [unit=u\"K/s\"]\n @constants tc=1.0 [unit=u\"s\"]\n eq = D(T) ~ sin(t/tc)*Tc\n ODESystem([eq], t; name=:Temperature, metadata=Dict(:coupletype=>TemperatureCoupler))\nend\n\nTemperature()","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"Now, we specify how to compose the two systems using param_to_var.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"function EarthSciMLBase.couple2(loss::LossCoupler, temp::TemperatureCoupler)\n loss, temp = loss.sys, temp.sys\n loss = param_to_var(loss, :T)\n ConnectorSystem([loss.T ~ temp.T], loss, temp)\nend","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"Finally, we create the system components and the composed system.","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"l = Loss()\ntemp = Temperature()\nvariable_loss = couple(l, temp)\n\nconvert(ODESystem, variable_loss)","category":"page"},{"location":"param_to_var/","page":"Parameter Replacement","title":"Parameter Replacement","text":"If we wanted to, we could then run a simulation with the composed system.","category":"page"},{"location":"operator/#Operators-for-Large-scale-3D-simulations","page":"Operators","title":"Operators for Large-scale 3D simulations","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"In this documentation so far, we have talked about creating systems of ordinary differential equations in ModelingToolkit and then converting them to systems of partial differential equations to perform 1-, 2-, or 3-dimensional simulations. However, currently this does not work for large scale simulations.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"While this ModelingToolkit functionality is being built, we have a different solution based on the Operator type in this package. Using this system, we still define systems of ODEs to define behavior in a single grid cell, and we also have Operator processes that define behavior between grid cells.","category":"page"},{"location":"operator/#ODE-System","page":"Operators","title":"ODE System","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"As an example, let's first define a system of ODEs:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"using EarthSciMLBase\nusing ModelingToolkit, DifferentialEquations\nusing SciMLOperators, Plots\nusing DomainSets\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\n@parameters y lon = 0.0 lat = 0.0 lev = 1.0 α = 10.0\n@constants p = 1.0\n@variables(\n u(t) = 1.0, v(t) = 1.0, x(t) = 1.0, y(t) = 1.0, windspeed(t) = 1.0\n)\n\neqs = [D(u) ~ -α * √abs(v) + lon,\n D(v) ~ -α * √abs(u) + lat + 1e-14 * lev,\n windspeed ~ lat + lon + lev,\n]\nsys = ODESystem(eqs, t; name=:sys)","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"The equations above don't really have any physical meaning, but they include two state variables, some parameters, and a constant. There is also a variable windspeed which is \"observed\" based on the parameters, rather than being a state variable, which will be important later.","category":"page"},{"location":"operator/#Operator","page":"Operators","title":"Operator","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"Next, we define an operator. To do so, first we create a new type that is a subtype of Operator:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"mutable struct ExampleOp <: Operator\n α::Num # Multiplier from ODESystem\nend","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"In the case above, we're setting up our operator so that it can hold a parameter from our ODE system.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"Next, we need to define a method of EarthSciMLBase.get_scimlop for our operator. This method will be called to get a SciMLOperators.AbstractSciMLOperator that will be used conjunction with the ModelingToolkit system above to integrate the simulation forward in time.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"function EarthSciMLBase.get_scimlop(op::ExampleOp, csys::CoupledSystem, mtk_sys, domain::DomainInfo, obs_functions, coordinate_transform_functions, u0, p)\n obs_f = obs_functions(op.α)\n grd = EarthSciMLBase.grid(domain)\n function run(du, u, p, t)\n u = reshape(u, size(u0)...)\n du = reshape(du, size(u0)...)\n for ix ∈ 1:size(u, 1)\n for (i, c1) ∈ enumerate(grd[1])\n for (j, c2) ∈ enumerate(grd[2])\n for (k, c3) ∈ enumerate(grd[3])\n # Demonstrate coordinate transforms\n t1 = coordinate_transform_functions[1](t, c1, c2, c3)\n t2 = coordinate_transform_functions[2](t, c1, c2, c3)\n t3 = coordinate_transform_functions[3](t, c1, c2, c3)\n # Demonstrate calculating observed value.\n fv = obs_f(t, c1, c2, c3)\n # Set derivative value.\n du[ix, i, j, k] = (t1 + t2 + t3) * fv\n end\n end\n end\n end\n nothing\n end\n FunctionOperator(run, u0[:], p=p)\nend","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"The function above also doesn't have any physical meaning, but it demonstrates some functionality of the Operator \"s\". First, it retrieves a function to get the current value of an observed variable in our ODE system using the obs_functions argement, and it demonstrates how to call the resulting function to get that value. It also demonstrates how to get coordinate transforms using the coordinate_transform_functions argument. Coordinate transforms are discussed in more detail in the documentation for the DomainInfo type.","category":"page"},{"location":"operator/#Domain","page":"Operators","title":"Domain","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"Once we have an ODE system and an operator, the final component we need is a domain to run the simulation on. Defining a domain is covered in more depth in the documentation for the DomainInfo type, but for now we'll just define a simple domain:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"t_min = 0.0\nlon_min, lon_max = -π, π\nlat_min, lat_max = -0.45π, 0.45π\nt_max = 11.5\n\nindepdomain = t ∈ Interval(t_min, t_max)\n\npartialdomains = [lon ∈ Interval(lon_min, lon_max),\n lat ∈ Interval(lat_min, lat_max),\n lev ∈ Interval(1, 3)]\n\ndomain = DomainInfo(\n partialderivatives_δxyδlonlat,\n constIC(16.0, indepdomain), constBC(16.0, partialdomains...);\n grid_spacing = [0.1π, 0.1π, 1])\nnothing #hide","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"Note that our domain includes a coordinate transform to convert from degrees latitude and longitude to meters. Our domain specification also includes grid spacing the the lon, lat, and lev coordinates, which we set as 0.1π, 0.1π, and 1, respectively.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"warning: Warning\nInitial and boundary conditions are not fully implemented for this case, so regardless of the conditions you specify, the initial conditions will be the default values of the variables in the ODE system, and the boundary conditions will be zero.","category":"page"},{"location":"operator/#Coupling-and-Running-the-Simulation","page":"Operators","title":"Coupling and Running the Simulation","text":"","category":"section"},{"location":"operator/","page":"Operators","title":"Operators","text":"Next, initialize our operator, giving the the windspeed observed variable, and we can couple our ODESystem, Operator, and Domain together into a single model:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"op = ExampleOp(sys.windspeed)\n\ncsys = couple(sys, op, domain)","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"Finally, we can choose a EarthSciMLBase.SolverStrategy and run the simulation. We choose the SolverStrangThreads strategy, which needs us to specify an ODE solver from the options available in DifferentialEquations.jl for both the MTK system. We choose the Tsit5 solver. Then we create an ODEProblem which can be used to run the simulation. Finally, we solve the problem using the solve function. At this point we need to choose a solver for the Operator part of the system, and we choose the Euler solver. We also choose a splitting time step of 1.0 seconds, which we pass both to our SolverStrangThreads strategy and to the solve function.","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"dt = 1.0 # Splitting time step\nst = SolverStrangThreads(Tsit5(), 1.0)\n\nprob = ODEProblem(csys, st)\nsol = solve(prob, Euler(); dt=1.0)\nnothing #hide","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"After the simulation finishes, we can plot the result:","category":"page"},{"location":"operator/","page":"Operators","title":"Operators","text":"anim = @animate for i ∈ 1:length(sol.u)\n u = reshape(sol.u[i], :, size(domain)...)\n plot(\n heatmap(u[1, :, :, 1]),\n heatmap(u[1, :, :, 1]),\n )\nend\ngif(anim, fps = 15)","category":"page"},{"location":"coord_transforms/#Coordinate-Transforms-for-Partial-Derivatives","page":"Coordinate Transforms","title":"Coordinate Transforms for Partial Derivatives","text":"","category":"section"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"Often, the coordinates of a grid may be defined in a different coordinate system than the one in which the partial derivatives are desired. For example, grids are often defined in latitude and longitude, but partial derivatives may be required in units of meters to correspond with wind speeds in meters per second.","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"To handle this, the DomainInfo type can be used to define coordinate system transformations. To use it, a coordinate transform function first needs to be defined, for example partialderivatives_δxyδlonlat which transforms partial derivatives from longitude and latitude to meters:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"using EarthSciMLBase\nusing ModelingToolkit\nusing ModelingToolkit: t, D\nusing DomainSets\nusing DynamicQuantities\n\n@parameters lon [unit = u\"rad\"]\n@parameters lat [unit = u\"rad\"]\n@parameters lev\n\npartialderivatives_δxyδlonlat([lev, lon, lat])","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"As you can see in the output of the code above, the function should take as arguments a list of the coordinates describing the grid (in the case above we have a 3-dimensional grid with vertical level, latitude, and longitude), and return a Dictionary relating the index of each coordinate with a factor to multiply the partial derivative by to convert it to the desired units. The function only needs to return factors for the coordinates that are being transformed.","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"To include a coordinate transform in our domain, we include the function in the DomainInfo constructor:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"deg2rad(x) = x * π / 180.0\ndomain = DomainInfo(\n partialderivatives_δxyδlonlat,\n constIC(0.0, t ∈ Interval(0.0f0, 3600.0f0)),\n periodicBC(lat ∈ Interval(deg2rad(-90.0f0), deg2rad(90.0f0))),\n periodicBC(lon ∈ Interval(deg2rad(-180.0f0), deg2rad(180.0f0))),\n zerogradBC(lev ∈ Interval(1.0f0, 10.0f0)),\n);","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"Multiple functions can be included in the DomainInfo constructor, just by including them as a vector, e.g.:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"domain = DomainInfo(\n [transform1, transform2, ...],\n constIC(0.0, t ∈ Interval(0.0f0, 3600.0f0)),\n ...\n)","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"After we include the coordinate transforms in our domain, in general everything should be handled automatically. The coordinate transforms may also be automatically added when different model components are coupled together, so you may not need to worry about them at all in many cases. However, if you would like to use the transformed partial derivatives, for example to create a new PDE equation system, you can get them using the EarthSciMLBase.partialderivatives function:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"δs = partialderivatives(domain)","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"This returns a list of functions, one corresponding to each coordinate in our domain. Then we can calculate the symbolic partial derivative of a variable by just calling each function:","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"@variables u(t)\n\n[δs[i](u) for i ∈ eachindex(δs)]","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"You can see an example of how this is implemented in the source code for the Advection model component.","category":"page"},{"location":"coord_transforms/","page":"Coordinate Transforms","title":"Coordinate Transforms","text":"Additional transformation functions may be defined in other packages. We recommend that the names of these functions start with partialderivatives_ to make it clear that they are intended to be used in this context.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"CurrentModule = EarthSciMLBase","category":"page"},{"location":"example_all_together/#Example-using-different-components-of-EarthSciMLBase-together","page":"All Together","title":"Example using different components of EarthSciMLBase together","text":"","category":"section"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"This example shows how to define and couple different components of EarthSciMLBase together to create a more complex model. First, we create several model components.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Our first example system is a simple reaction system:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"using EarthSciMLBase\nusing ModelingToolkit, Catalyst, DomainSets, MethodOfLines, DifferentialEquations\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\nusing Plots\n\n# Create our independent variable `t` and our partially-independent variables `x` and `y`.\n@parameters x y\n\nstruct ExampleSys1Coupler sys end\nfunction ExampleSys1()\n @species c₁(t)=5.0 c₂(t)=5.0\n rs = ReactionSystem(\n [Reaction(2.0, [c₁], [c₂])],\n t; name=:Sys1, combinatoric_ratelaws=false)\n convert(ODESystem, complete(rs), metadata=Dict(:coupletype=>ExampleSys1Coupler))\nend\n\nExampleSys1()","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Our second example system is a simple ODE system, with the same two variables.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"struct ExampleSys2Coupler sys end\nfunction ExampleSys2()\n @variables c₁(t)=5.0 c₂(t)=5.0\n @parameters p₁=1.0 p₂=0.5\n ODESystem(\n [D(c₁) ~ -p₁, D(c₂) ~ p₂],\n t; name=:Sys2, metadata=Dict(:coupletype=>ExampleSys2Coupler))\nend\n\nExampleSys2()","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Now, we specify what should happen when we couple the two systems together. In this case, we want the the derivative of the composed system to be equal to the sum of the derivatives of the two systems. We can do that using the operator_compose function from this package.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"function EarthSciMLBase.couple2(sys1::ExampleSys1Coupler, sys2::ExampleSys2Coupler)\n sys1, sys2 = sys1.sys, sys2.sys\n sys1 = convert(ODESystem, sys1)\n operator_compose(sys1, sys2)\nend\nnothing # hide","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Once we specify all of the above, it is simple to create our two individual systems and then couple them together. ","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"sys1 = ExampleSys1()\nsys2 = ExampleSys2()\nsys = couple(sys1, sys2)\n\nconvert(ODESystem, sys)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"At this point we have an ODE system that is composed of two other ODE systems. We can inspect its observed variables using the observed function.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"simplified_sys = structural_simplify(convert(ODESystem, sys))","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"observed(simplified_sys)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"We can also run simulations using this system:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"odeprob = ODEProblem(simplified_sys, [], (0.0,10.0), [])\nodesol = solve(odeprob)\nplot(odesol)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Once we've confirmed that our model works in a 0D \"box model\" setting, we can expand it to 1, 2, or 3 dimensions using by adding in initial and boundary conditions. We will also add in advection using constant-velocity wind fields add the same time.","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"x_min = y_min = t_min = 0.0\nx_max = y_max = t_max = 1.0\ndomain = DomainInfo(\n constIC(4.0, t ∈ Interval(t_min, t_max)),\n periodicBC(x ∈ Interval(x_min, x_max)),\n zerogradBC(y ∈ Interval(y_min, y_max)),\n)\n\nsys_pde = couple(sys, domain, ConstantWind(t, 1.0, 1.0), Advection())\n\nsys_pde_mtk = convert(PDESystem, sys_pde)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Now we can inspect this new system that we've created:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"sys_pde_mtk.dvs","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"sys_pde_mtk.bcs","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Finally, we can run a simulation using this system:","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"discretization = MOLFiniteDifference([x=>10, y=>10], t, approx_order=2)\n@time pdeprob = discretize(sys_pde_mtk, discretization)\n@time pdesol = solve(pdeprob, Tsit5(), saveat=0.1)\n\n# Plot the solution.\ndiscrete_x, discrete_y, discrete_t = pdesol[x], pdesol[y], pdesol[t]\n@variables Sys1₊c₁(..) Sys1₊c₂(..)\nsolc1, solc2 = pdesol[Sys1₊c₁(t, x, y)], pdesol[Sys1₊c₂(t, x, y)]\nanim = @animate for k in 1:length(discrete_t)\n p1 = heatmap(solc1[k, 1:end-1, 1:end-1], title=\"c₁ t=\\$(discrete_t[k])\", clim=(0,4.0), lab=:none)\n p2 = heatmap(solc2[k, 1:end-1, 1:end-1], title=\"c₂ t=\\$(discrete_t[k])\", clim=(0,7.0), lab=:none)\n plot(p1, p2, layout=(1,2), size=(800,400))\nend\ngif(anim, fps = 8)","category":"page"},{"location":"example_all_together/","page":"All Together","title":"All Together","text":"Because our system is a system of ordinary differential equations rather than partial differential equations, all of the grid cells in the animation above have the same value. Refer to the advection example for an example of a system of partial differential equations.","category":"page"},{"location":"composition/#Model-Composition","page":"Composition","title":"Model Composition","text":"","category":"section"},{"location":"composition/","page":"Composition","title":"Composition","text":"A main goal of the EarthSciMLBase package is to allow model components to be created independently and composed together. We achieve this by creating coupletypes that allow us to use multiple dispatch on the EarthSciMLBase.couple2 function to specify how particular systems should be coupled together.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"To demonstrate how this works, below we define three model components, Photolysis, Chemistry, and Emissions, which represent different processes in the atmosphere.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"using ModelingToolkit, Catalyst, EarthSciMLBase\nusing ModelingToolkit: t_nounits\nt = t_nounits\n\nstruct PhotolysisCoupler sys end\nfunction Photolysis(; name=:Photolysis)\n @variables j_NO2(t)\n eqs = [\n j_NO2 ~ max(sin(t/86400),0)\n ]\n ODESystem(eqs, t, [j_NO2], [], name=name,\n metadata=Dict(:coupletype=>PhotolysisCoupler))\nend\n\nPhotolysis()","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"You can see that the system defined above is mostly a standard ModelingToolkit ODE system, except for two things:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"The first unique part is that we define a PhotolysisCoupler type:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"struct PhotolysisCoupler sys end","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"It is important that this type is a struct, and that the struct has a single field named sys. When defining your own coupled systems, you can just copy the line of code above but change the name of the type (i.e., change it to something besides PhotolysisCoupler).","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"The second unique part is that we define some metadata for our ODESystem to tell it what coupling type to use:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"metadata=Dict(:coupletype=>PhotolysisCoupler)","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Again, when making your own components, just copy the code above but change PhotolysisCoupler to something else.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Let's follow the same process for some additional components:","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"struct ChemistryCoupler sys end\nfunction Chemistry(; name=:Chemistry)\n @parameters jNO2\n @species NO2(t)\n rxs = [\n Reaction(jNO2, [NO2], [], [1], [])\n ]\n rsys = ReactionSystem(rxs, t, [NO2], [jNO2]; \n combinatoric_ratelaws=false, name=name)\n convert(ODESystem, complete(rsys), metadata=Dict(:coupletype=>ChemistryCoupler))\nend\n\nChemistry()","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"For our chemistry component above, because it's is originally a ReactionSystem instead of an ODESystem, we convert it to an ODESystem before adding the metadata.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"struct EmissionsCoupler sys end\nfunction Emissions(; name=:Emissions)\n @parameters emis = 1\n @variables NO2(t)\n eqs = [NO2 ~ emis]\n ODESystem(eqs, t; name=name,\n metadata=Dict(:coupletype=>EmissionsCoupler))\nend\n\nEmissions()","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Now, we need to define ways to couple the model components together. We can do this by defining a coupling function (as a method of EarthSciMLBase.couple2) for each pair of model components that we want to couple. Each coupling function should have the signature EarthSciMLBase.couple2(a::ACoupler, b::BCoupler)::ConnectorSystem, and should assume that the two ODESystems are inside their respective couplers in the sys field. It should make any edits to the components as needed and return a ConnectorSystem which defines the relationship between the two components.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"The code below defines a method for coupling the Chemistry and Photolysis components. First, it uses the param_to_var function to convert the photolysis rate parameter jNO2 from the Chemistry component to a variable, then it creates a new Chemistry component with the updated photolysis rate, and finally, it creates a ConnectorSystem object that sets the j_NO2 variable from the Photolysis component equal to the jNO2 variable from the Chemistry component. The next effect is that the photolysis rate in the Chemistry component is now controlled by the Photolysis component.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"function EarthSciMLBase.couple2(c::ChemistryCoupler, p::PhotolysisCoupler)\n c, p = c.sys, p.sys\n c = param_to_var(convert(ODESystem, c), :jNO2)\n ConnectorSystem([c.jNO2 ~ p.j_NO2], c, p)\nend\nnothing # hide","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Next, we define a method for coupling the Chemistry and Emissions components. To do this we use the operator_compose function to add the NO2 species from the Emissions component to the time derivative of NO2 in the Chemistry component.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"function EarthSciMLBase.couple2(c::ChemistryCoupler, emis::EmissionsCoupler)\n c, emis = c.sys, emis.sys\n operator_compose(convert(ODESystem, c), emis, Dict(\n c.NO2 => emis.NO2,\n ))\nend\nnothing # hide","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Finally, we can compose the model components together to create our complete model. To do so, we just initialize each model component and add the components together using the couple function. We can use the convert function to convert the composed model to a ModelingToolkit ODESystem so we can see what the combined equations look like.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"model = couple(Photolysis(), Chemistry(), Emissions())\nconvert(ODESystem, model)","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"Finally, we can use the graph function to visualize the model components and their connections.","category":"page"},{"location":"composition/","page":"Composition","title":"Composition","text":"using MetaGraphsNext\nusing CairoMakie, GraphMakie\n\ng = graph(model)\n\nf, ax, p = graphplot(g; ilabels=labels(g))\nhidedecorations!(ax); hidespines!(ax); ax.aspect = DataAspect()\n\nf","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = EarthSciMLBase","category":"page"},{"location":"#EarthSciMLBase:-Utilities-for-Symbolic-Earth-Science-Modeling-and-Machine-Learning","page":"Home","title":"EarthSciMLBase: Utilities for Symbolic Earth Science Modeling and Machine Learning","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for EarthSciMLBase.","category":"page"},{"location":"","page":"Home","title":"Home","text":"This package contains utilities for constructing Earth Science models in Julia using ModelingToolkit.jl.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"EarthSciMLBase\")","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"This package contains types and functions designed to simplify the process of constructing and composing symbolically-defined Earth Science model components together.","category":"page"},{"location":"#Feature-List","page":"Home","title":"Feature List","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Operations to compose ModelingToolkit.jl equation systems together.\nOperations to add intitial and boundary conditions to systems and to turn ODE systems into PDE systems, and to provide coordinate transformations.\nOperations to add Advection terms to systems.\nA Simulator type for running large-scale simulations.","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Please refer to the SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages for guidance on PRs, issues, and other matters relating to contributing.","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this EarthSciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(;mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"You can also download the \nmanifest file and the\nproject file.","category":"page"},{"location":"advection/#Advection","page":"Advection","title":"Advection","text":"","category":"section"},{"location":"advection/","page":"Advection","title":"Advection","text":"The Advection function adds advection to a system of equations. This is useful for modeling the transport of a substance by a fluid. Advection is implemented with the Advection type.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"!warning Fully symbolic partial differential equations like those shown here don't currently work on domains that have a large number of grid cells. See here for additional information.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"To demonstrate how this can work, we will start with a simple system of equations:","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"using EarthSciMLBase, ModelingToolkit\nusing ModelingToolkit: t_nounits, D_nounits\nt = t_nounits\nD = D_nounits\n\nfunction ExampleSys()\n @variables y(t)\n @parameters p=2.0\n ODESystem([D(y) ~ p], t; name=:ExampleSys)\nend\n\nExampleSys()","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"We also need to create our initial and boundary conditions.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"using DomainSets\n@parameters x\ndomain = DomainInfo(constIC(0.0, t ∈ Interval(0, 1.0)), constBC(1.0, x ∈ Interval(0, 1.0)))\nnothing # hide","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"Now we convert add advection to each of the state variables. We're also adding a constant wind (ConstantWind) in the x-direction, with a speed of 1.0.","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"sys_advection = couple(ExampleSys(), domain, ConstantWind(t, 1.0), Advection())\nsys_mtk = convert(PDESystem, sys_advection)","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"Finally, we can discretize the system and solve it:","category":"page"},{"location":"advection/","page":"Advection","title":"Advection","text":"using MethodOfLines, DifferentialEquations, Plots\ndiscretization = MOLFiniteDifference([x=>10], t, approx_order=2)\n@time prob = discretize(sys_mtk, discretization)\n@time sol = solve(prob, Tsit5(), saveat=0.1)\n\n\n# Plot the solution.\ndiscrete_x = sol[x]\ndiscrete_t = sol[t]\nsoly = sol[sys_mtk.dvs[3]]\nanim = @animate for k in 1:length(discrete_t)\n plot(discrete_x, soly[k, 1:end], title=\"t=\\$(discrete_t[k])\", ylim=(0,2.5), lab=:none)\nend\ngif(anim, fps = 8)","category":"page"}] }

Now we convert add advection to each of the state variables. We're also adding a constant wind (ConstantWind) in the x-direction, with a speed of 1.0.