Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CI and rename Temperature to TemperatureSetting #363

Merged
merged 2 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/NQCModels/combining_models.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For example, in order to run [Molecular Dynamics with Electronic friction](@ref
However, most models for potential energy surfaces only provide `potential` and `derivative`, whereas `friction!` is provided by an `ElectronicFrictionProvider`.

To combine different models together for a system, we can use [`Subsystem`](@ref)s to define which atoms in the system a given model should be applied to, and a [`CompositeModel`](@ref) to combine the different models into one.
If different [`Subsystem`](@ref)s should experience different effective temperatures, a [`Thermostat`](@ref) can be provided when initialising a [`Simulation`](@ref).
If different [`Subsystem`](@ref)s should experience different effective temperatures, a [`TemperatureSetting`](@ref) can be provided when initialising a [`Simulation`](@ref).

![An overview of the different components used to combine models in NQCModels.jl](../assets/compositemodels/struct-explainer.svg)

Expand Down
2 changes: 1 addition & 1 deletion docs/src/dynamicssimulations/dynamicssimulations.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ For time-dependent temperatures, the temperature can be set to a function. This

**Different temperatures per atom**

If different parts of the simulated system should be affected by different effective temperatures, a `Vector` of [`Temperature`](@ref)s can be passed as the temperature argument.
If different parts of the simulated system should be affected by different effective temperatures, a `Vector` of [`TemperatureSetting`](@ref)s can be passed as the temperature argument.
However, only one temperature should be applied to each atom in the system.

### DynamicsVariables
Expand Down
6 changes: 3 additions & 3 deletions docs/src/examples/mdef_multithermostat.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ combined_model = CompositeModel(
phononic_friction
)

# Create Temperature objects to apply T_el to adsorbates, T_ph to surface
# Create TemperatureSetting objects to apply T_el to adsorbates, T_ph to surface
# Manually specified indices
electron_thermostat = Temperature(T_el_function, indices = [55,56])
electron_thermostat = TemperatureSetting(T_el_function, indices = [55,56])
# Inherit indices from a Subsystem
phonon_thermostat = Temperature(T_ph_function, phononic_friction)
phonon_thermostat = TemperatureSetting(T_ph_function, phononic_friction)

sim_T_el_only = Simulation{MDEF}(
atoms,
Expand Down
2 changes: 1 addition & 1 deletion src/NQCDynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export Simulation,
RingPolymerSimulation,
natoms,
masses,
Thermostat,
TemperatureSetting,
get_temperature

@reexport using NQCDistributions
Expand Down
38 changes: 19 additions & 19 deletions src/simulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ function Simulation(atoms::Atoms{T}, model::Model, method::M;
temperature=0u"K", cell::AbstractCell=InfiniteCell()) where {M,T}
calc = Calculator(model, length(atoms), T)
# If a thermostat is provided, check it covers the whole system.
isa(temperature, Thermostat{Vector{Int}}) ? throw(DomainError(temperature, "Thermostat must apply to all atoms.")) : nothing
# If multiple Thermostats are provided, check that each atom only has one thermostat applied to it.
if isa(temperature, Vector{<:Thermostat})
isa(temperature, TemperatureSetting{Vector{Int}}) ? throw(DomainError(temperature, "TemperatureSetting must apply to all atoms.")) : nothing
# If multiple TemperatureSettings are provided, check that each atom only has one thermostat applied to it.
if isa(temperature, Vector{<:TemperatureSetting})
indices = vcat([thermostat.indices for thermostat in temperature]...)
if length(unique(indices)) != length(atoms.masses)
throw(DomainError(temperature, "Every atom must have a Thermostat applied to it."))
throw(DomainError(temperature, "Every atom must have a TemperatureSetting applied to it."))
end
if length(indices) != length(unique(indices))
throw(DomainError(temperature, "Atoms can only have one thermostat applied to them."))
Expand Down Expand Up @@ -113,7 +113,7 @@ function Base.show(io::IO, sim::RingPolymerSimulation{M}) where {M}
end

"""
A Temperature contains both temperature information and the atom indices within a Simulation that it is applied to.
A TemperatureSetting contains both temperature information and the atom indices within a Simulation that it is applied to.

**If you don't need to apply different temperatures to different parts of your Simulation, assign the `temperature` keyword argument of your simulation to a number or function.**

Expand All @@ -124,46 +124,46 @@ A Temperature contains both temperature information and the atom indices within
`indices`: Indices of the atoms to which this thermostat is applied. Can be a range of indices, a single `Int`, or a `Vector{Int}`.

"""
struct Temperature{I}
struct TemperatureSetting{I}
value::Function
indices::I
end

function Base.show(io::IO, temperature::Temperature)
print(io, "Temperature:\n\tT(t=0) = $(get_temperature(temperature, 0u"fs"))\n\tApplies to atoms: $(temperature.indices)\n")
function Base.show(io::IO, temperature::TemperatureSetting)
print(io, "TemperatureSetting:\n\tT(t=0) = $(get_temperature(temperature, 0u"fs"))\n\tApplies to atoms: $(temperature.indices)\n")
end

function Temperature(value, indices=:)
function TemperatureSetting(value, indices=:)
if isa(indices, UnitRange)
indices=collect(indices)
elseif isa(indices, Int)
indices=[indices]
end
if !isa(value, Function)
temperature_function(t)=value
return Temperature(temperature_function, indices)
return TemperatureSetting(temperature_function, indices)
else
return Temperature(value, indices)
return TemperatureSetting(value, indices)
end
end

"""
Temperature(temperature, subsystem::NQCModels.Subsystem)
TemperatureSetting(temperature, subsystem::NQCModels.Subsystem)

Apply a `Temperature` to all atoms in a `Subsystem`.
Apply a `TemperatureSetting` to all atoms in a `Subsystem`.
"""
function Temperature(temperature, subsystem::NQCModels.Subsystem)
Temperature(temperature, subsystem.indices)
function TemperatureSetting(temperature, subsystem::NQCModels.Subsystem)
TemperatureSetting(temperature, subsystem.indices)
end

get_temperature(thermostat::Temperature{<:Any}, t=0u"fs") = austrip(thermostat.value(t))
get_temperature(thermostat::TemperatureSetting{<:Any}, t=0u"fs") = austrip(thermostat.value(t))

"""
get_temperature(thermostats::Vector{<:Temperature}, t=0u"fs")
get_temperature(thermostats::Vector{<:TemperatureSetting}, t=0u"fs")

Gets the temperature from multiple `Temperature`s and returns a vector of the temperature applied to each atom.
Gets the temperature from multiple `TemperatureSetting`s and returns a vector of the temperature applied to each atom.
"""
function get_temperature(thermostats::Vector{<:Temperature}, t=0u"fs")
function get_temperature(thermostats::Vector{<:TemperatureSetting}, t=0u"fs")
indices=vcat([thermostat.indices for thermostat in thermostats]...)
temperature_vector=zeros(Number, length(indices))
for thermostat in thermostats
Expand Down
6 changes: 3 additions & 3 deletions test/Core/simulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ model = NQCModels.Free()

@testset "Thermostats" begin
# Init thermostat with Unitful quantity
thermostat1=Thermostat(10u"K", [1,2])
thermostat1=TemperatureSetting(10u"K", [1,2])
@test NQCDynamics.get_temperature(thermostat1, 0) == NQCDynamics.get_temperature(thermostat1, 100)
# Init thermostat with function
thermostat2=Thermostat(x->(10+x*u"fs^-1")*u"K", [2,3])
thermostat2=TemperatureSetting(x->(10+x*u"fs^-1")*u"K", [2,3])
@test NQCDynamics.get_temperature(thermostat2) == austrip(10u"K")
@test NQCDynamics.get_temperature(thermostat2, 10u"fs") == austrip(20u"K")
# Test that thermostats with overlapping indices throw an error
@test_throws DomainError Simulation(atoms, model; temperature=[thermostat1, thermostat2])
# Test system size / total thermostat size mismatch
@test_throws DomainError Simulation(Atoms([:N, :H, :H, :H,]), model; temperature=thermostat1)
thermostat3=thermostat2=Thermostat(x->(10+x*u"fs^-1")*u"K", [3,4])
thermostat3=thermostat2=TemperatureSetting(x->(10+x*u"fs^-1")*u"K", [3,4])
# Combined temperature evaluation
sim = Simulation(Atoms([:N, :H, :H, :H,]), model; temperature=[thermostat1, thermostat3])
@test NQCDynamics.get_temperature(sim, austrip(1u"fs")) == austrip.([10u"K", 10u"K", 11u"K", 11u"K"])
Expand Down
Loading