-
Notifications
You must be signed in to change notification settings - Fork 17
Add EnergyCalculatorCallback
#940
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
Draft
efaulhaber
wants to merge
17
commits into
trixi-framework:main
Choose a base branch
from
efaulhaber:clamped-motion-energy
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 5 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
9b01fee
Add `EnergyCalculatorCallback`
efaulhaber 79eb1f1
Improve comments
efaulhaber d152927
Add docstring
efaulhaber be1b585
Reformat
efaulhaber d54ea66
Add tests
efaulhaber 9ebead7
Add function to retrieve calculated energy
efaulhaber 7298243
Fix for split integration
efaulhaber b8594d5
Use `OffsetMatrix` instead of `CombinedMatrix` to avoid allocations
efaulhaber 6016e45
Fix return value
efaulhaber 93273d9
Fix tests
efaulhaber d45e845
Merge branch 'main' into clamped-motion-energy
efaulhaber 6ae7dc1
Only calculate energy for a single system
efaulhaber 8fd43f9
Implement `only_compute_force_on_fluid`
efaulhaber 4e87fca
Fix example test
efaulhaber 019be6f
Add second test with FSI
efaulhaber 96e3469
Undo changes in TLSPH system
efaulhaber c9df08f
Update docs
efaulhaber File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
|
|
||
| """ | ||
| EnergyCalculatorCallback{ELTYPE}(; interval=1) | ||
|
|
||
| Callback to calculate the energy contribution from clamped particles in | ||
| [`TotalLagrangianSPHSystem`](@ref)s. | ||
|
|
||
| This callback computes the work done by clamped particles that are moving according to a | ||
| [`PrescribedMotion`](@ref). | ||
| The energy is calculated by integrating the instantaneous power, which is the dot product | ||
| of the force exerted by the clamped particle and its prescribed velocity. | ||
| The callback uses a simple explicit Euler time integration for the energy calculation. | ||
|
|
||
| !!! info "TLSPH System Configuration" | ||
| The [`TotalLagrangianSPHSystem`](@ref) must be created with | ||
| `use_with_energy_calculator_callback=true` to prepare the system for energy | ||
| calculation by allocating the necessary cache for clamped particles. | ||
|
|
||
| !!! warning "Experimental implementation" | ||
| This is an experimental feature and may change in future releases. | ||
|
|
||
| # Arguments | ||
| - `ELTYPE`: The floating point type used for time and energy calculations. | ||
| This should match the floating point type used in the | ||
| [`TotalLagrangianSPHSystem`](@ref), which can be obtained via `eltype(system)`. | ||
|
|
||
| # Keywords | ||
| - `interval=1`: Interval (in number of time steps) at which to compute the energy. | ||
| It is recommended to set this either to `1` (every time step) or to a small | ||
| integer (e.g., `2` or `5`) to reduce time integration errors | ||
| in the energy calculation. | ||
|
|
||
| # Examples | ||
| ```jldoctest; output = false, setup = :(structure = RectangularShape(0.1, (3, 4), (0.1, 0.0), density=1.0); smoothing_kernel = WendlandC2Kernel{2}(); smoothing_length = 1.0; young_modulus = 1e6; poisson_ratio = 0.3; n_clamped_particles = 0; clamped_particles_motion = nothing) | ||
| # Create TLSPH system with `use_with_energy_calculator_callback=true` | ||
| system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothing_length, | ||
| young_modulus, poisson_ratio, | ||
| n_clamped_particles=n_clamped_particles, | ||
| clamped_particles_motion=clamped_particles_motion, | ||
| use_with_energy_calculator_callback=true) # Important! | ||
|
|
||
| # Create an energy calculator that runs every 2 time steps | ||
| energy_cb = EnergyCalculatorCallback{eltype(system)}(; interval=2) | ||
|
|
||
| # output | ||
| ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ | ||
| │ EnergyCalculatorCallback{Float64} │ | ||
| │ ═════════════════════════════════ │ | ||
| │ interval: ……………………………………………………… 2 │ | ||
| └──────────────────────────────────────────────────────────────────────────────────────────────────┘ | ||
| ``` | ||
| """ | ||
| struct EnergyCalculatorCallback{T} | ||
| interval :: Int | ||
| t :: T | ||
| energy :: T | ||
| end | ||
efaulhaber marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| function EnergyCalculatorCallback{ELTYPE}(; interval=1) where {ELTYPE <: Real} | ||
| cb = EnergyCalculatorCallback(interval, Ref(zero(ELTYPE)), Ref(zero(ELTYPE))) | ||
|
|
||
| # The first one is the `condition`, the second the `affect!` | ||
| return DiscreteCallback(cb, cb, save_positions=(false, false)) | ||
| end | ||
|
|
||
| # `condition` | ||
| function (callback::EnergyCalculatorCallback)(u, t, integrator) | ||
| (; interval) = callback | ||
|
|
||
| return condition_integrator_interval(integrator, interval) | ||
| end | ||
|
|
||
| # `affect!` | ||
| function (callback::EnergyCalculatorCallback)(integrator) | ||
| # Determine time step size as difference to last time this callback was called | ||
| t = integrator.t | ||
| dt = t - callback.t[] | ||
|
|
||
| # Update time of last call | ||
| callback.t[] = t | ||
|
|
||
| semi = integrator.p | ||
| v_ode, u_ode = integrator.u.x | ||
| energy = callback.energy | ||
|
|
||
| update_energy_calculator!(energy, v_ode, u_ode, semi.systems[1], semi, t, dt) | ||
efaulhaber marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # Tell OrdinaryDiffEq that `u` has not been modified | ||
| u_modified!(integrator, false) | ||
|
|
||
| return integrator | ||
| end | ||
|
|
||
| function update_energy_calculator!(energy, v_ode, u_ode, | ||
| system::AbstractStructureSystem, semi, t, dt) | ||
| @trixi_timeit timer() "calculate energy" begin | ||
| # Update quantities that are stored in the systems. These quantities (e.g. pressure) | ||
| # still have the values from the last stage of the previous step if not updated here. | ||
| @trixi_timeit timer() "update systems and nhs" begin | ||
| # Don't create sub-timers here to avoid cluttering the timer output | ||
| @notimeit timer() update_systems_and_nhs(v_ode, u_ode, semi, t) | ||
| end | ||
|
|
||
| dv_clamped = system.cache.dv_clamped | ||
| set_zero!(dv_clamped) | ||
|
|
||
| dv = zero(wrap_v(v_ode, system, semi)) | ||
| dv_combined = CombinedMatrix(dv, dv_clamped) | ||
| v = wrap_v(v_ode, system, semi) | ||
| u = wrap_u(u_ode, system, semi) | ||
| eachparticle = (n_integrated_particles(system) + 1):nparticles(system) | ||
|
|
||
| foreach_system(semi) do neighbor_system | ||
| v_neighbor = wrap_v(v_ode, neighbor_system, semi) | ||
| u_neighbor = wrap_u(u_ode, neighbor_system, semi) | ||
|
|
||
| interact!(dv_combined, v, u, v_neighbor, u_neighbor, | ||
| system, neighbor_system, semi, | ||
| eachparticle=eachparticle) | ||
| end | ||
|
|
||
| @threaded semi for particle in eachparticle | ||
| add_acceleration!(dv_combined, particle, system) | ||
| add_source_terms_inner!(dv_combined, v, u, particle, system, | ||
| source_terms(system), t) | ||
| end | ||
|
|
||
| n_clamped_particles = nparticles(system) - n_integrated_particles(system) | ||
| for clamped_particle in 1:n_clamped_particles | ||
| particle = clamped_particle + n_integrated_particles(system) | ||
| velocity = current_velocity(nothing, system, particle) | ||
| dv_particle = extract_svector(dv_clamped, system, clamped_particle) | ||
|
|
||
| # The force on the clamped particle is mass times acceleration | ||
| F_particle = system.mass[particle] * dv_particle | ||
|
|
||
| # To obtain energy, we need to integrate the instantaneous power. | ||
| # Instantaneous power is force done BY the particle times prescribed velocity. | ||
| # The work done BY the particle is the negative of the work done ON it. | ||
| energy[] -= dot(F_particle, velocity) * dt | ||
| end | ||
| end | ||
| end | ||
|
|
||
| function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:EnergyCalculatorCallback}) | ||
| @nospecialize cb # reduce precompilation time | ||
|
|
||
| ELTYPE = eltype(cb.affect!.energy) | ||
| print(io, "EnergyCalculatorCallback{$ELTYPE}(interval=", cb.affect!.interval, ")") | ||
| end | ||
|
|
||
| function Base.show(io::IO, ::MIME"text/plain", | ||
| cb::DiscreteCallback{<:Any, EnergyCalculatorCallback{T}}) where {T} | ||
| @nospecialize cb # reduce precompilation time | ||
|
|
||
| if get(io, :compact, false) | ||
| show(io, cb) | ||
| else | ||
| update_cb = cb.affect! | ||
| ELTYPE = eltype(update_cb.energy) | ||
| setup = [ | ||
| "interval" => update_cb.interval | ||
| ] | ||
| summary_box(io, "EnergyCalculatorCallback{$ELTYPE}", setup) | ||
| end | ||
| end | ||
|
|
||
| # Data type that combines two matrices to behave like a single matrix. | ||
| # This is used above to combine the `dv` for integrated particles and the `dv_clamped` | ||
| # for clamped particles into a single matrix that can be passed to `interact!`. | ||
| struct CombinedMatrix{T, M1, M2} <: AbstractMatrix{T} | ||
| matrix1::M1 | ||
| matrix2::M2 | ||
|
|
||
| function CombinedMatrix(matrix1, matrix2) | ||
| @assert size(matrix1, 1) == size(matrix2, 1) | ||
| @assert eltype(matrix1) == eltype(matrix2) | ||
|
|
||
| new{eltype(matrix1), typeof(matrix1), typeof(matrix2)}(matrix1, matrix2) | ||
| end | ||
| end | ||
|
|
||
| @inline function Base.size(cm::CombinedMatrix) | ||
| return (size(cm.matrix1, 1), size(cm.matrix1, 2) + size(cm.matrix2, 2)) | ||
| end | ||
|
|
||
| @inline function Base.getindex(cm::CombinedMatrix, i, j) | ||
| @boundscheck checkbounds(cm, i, j) | ||
|
|
||
| length1 = size(cm.matrix1, 2) | ||
| if j <= length1 | ||
| return @inbounds cm.matrix1[i, j] | ||
| else | ||
| return @inbounds cm.matrix2[i, j - length1] | ||
| end | ||
| end | ||
|
|
||
| @inline function Base.setindex!(cm::CombinedMatrix, value, i, j) | ||
| @boundscheck checkbounds(cm, i, j) | ||
|
|
||
| length1 = size(cm.matrix1, 2) | ||
| if j <= length1 | ||
| return @inbounds cm.matrix1[i, j] = value | ||
| else | ||
| return @inbounds cm.matrix2[i, j - length1] = value | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.