From 81a61375a441ffbdf29ae89d7542137322b6c79a Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 00:02:12 +0200 Subject: [PATCH 01/47] TypeEnum --- src/LAMMPS.jl | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/api.jl | 9 +++++---- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index d9d3661..021f2fe 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -1,12 +1,64 @@ module LAMMPS import MPI include("api.jl") +import .API: LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, - gather, scatter!, group_to_atom_ids, get_category_ids + gather, scatter!, group_to_atom_ids, get_category_ids, + + LAMMPS_NONE, + LAMMPS_INT, + LAMMPS_INT_2D, + LAMMPS_DOUBLE, + LAMMPS_DOUBLE_2D, + LAMMPS_INT64, + LAMMPS_INT64_2D, + LAMMPS_STRING, + + TYPE_SCALAR, + TYPE_VECTOR, + TYPE_ARRAY, + SIZE_VECTOR, + SIZE_ROWS, + SIZE_COLS, + + VAR_EQUAL, + VAR_ATOM, + VAR_VECTOR, + VAR_STRING using Preferences +abstract type TypeEnum{N} end +get_enum(::TypeEnum{N}) where N = N + +struct _LMP_DATATYPE{N} <: TypeEnum{N} end + +const LAMMPS_NONE = _LMP_DATATYPE{API.LAMMPS_NONE}() +const LAMMPS_INT = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() +const LAMMPS_INT_2D = _LMP_DATATYPE{API.LAMMPS_INT_2D}() +const LAMMPS_DOUBLE = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() +const LAMMPS_DOUBLE_2D = _LMP_DATATYPE{API.LAMMPS_DOUBLE_2D}() +const LAMMPS_INT64 = _LMP_DATATYPE{API.LAMMPS_INT64}() +const LAMMPS_INT64_2D = _LMP_DATATYPE{API.LAMMPS_INT64_2D}() +const LAMMPS_STRING = _LMP_DATATYPE{API.LAMMPS_STRING}() + +struct _LMP_TYPE{N} <: TypeEnum{N} end + +const TYPE_SCALAR = _LMP_TYPE{API.LMP_TYPE_SCALAR}() +const TYPE_VECTOR = _LMP_TYPE{API.LMP_TYPE_VECTOR}() +const TYPE_ARRAY = _LMP_TYPE{API.LMP_TYPE_ARRAY}() +const SIZE_VECTOR = _LMP_TYPE{API.LMP_SIZE_VECTOR}() +const SIZE_ROWS = _LMP_TYPE{API.LMP_SIZE_ROWS}() +const SIZE_COLS = _LMP_TYPE{API.LMP_SIZE_COLS}() + +struct LMP_VARIABLE{N} <: TypeEnum{N} end + +const VAR_EQUAL = LMP_VARIABLE{API.LMP_VAR_EQUAL}() +const VAR_ATOM = LMP_VARIABLE{API.LMP_VAR_ATOM}() +const VAR_VECTOR = LMP_VARIABLE{API.LMP_VAR_VECTOR}() +const VAR_STRING = LMP_VARIABLE{API.LMP_VAR_STRING}() + """ locate() diff --git a/src/api.jl b/src/api.jl index d40d62c..c61eb4d 100644 --- a/src/api.jl +++ b/src/api.jl @@ -6,7 +6,8 @@ import LAMMPS_jll import LAMMPS_jll: liblammps import MPI: MPI_Comm -@cenum _LMP_DATATYPE_CONST::UInt32 begin +@cenum _LMP_DATATYPE_CONST::Int32 begin + LAMMPS_NONE = -1 LAMMPS_INT = 0 LAMMPS_INT_2D = 1 LAMMPS_DOUBLE = 2 @@ -16,13 +17,13 @@ import MPI: MPI_Comm LAMMPS_STRING = 6 end -@cenum _LMP_STYLE_CONST::UInt32 begin +@cenum _LMP_STYLE_CONST::Int32 begin LMP_STYLE_GLOBAL = 0 LMP_STYLE_ATOM = 1 LMP_STYLE_LOCAL = 2 end -@cenum _LMP_TYPE_CONST::UInt32 begin +@cenum _LMP_TYPE_CONST::Int32 begin LMP_TYPE_SCALAR = 0 LMP_TYPE_VECTOR = 1 LMP_TYPE_ARRAY = 2 @@ -31,7 +32,7 @@ end LMP_SIZE_COLS = 5 end -@cenum _LMP_ERROR_CONST::UInt32 begin +@cenum _LMP_ERROR_CONST::Int32 begin LMP_ERROR_WARNING = 0 LMP_ERROR_ONE = 1 LMP_ERROR_ALL = 2 From eb32e191d9b6e75842b308ad2c4e6d5c8b69c1db Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 00:26:39 +0200 Subject: [PATCH 02/47] fixup! LAMMPS_INT --- src/LAMMPS.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 021f2fe..1aaf3ac 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -35,7 +35,7 @@ get_enum(::TypeEnum{N}) where N = N struct _LMP_DATATYPE{N} <: TypeEnum{N} end const LAMMPS_NONE = _LMP_DATATYPE{API.LAMMPS_NONE}() -const LAMMPS_INT = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() +const LAMMPS_INT = _LMP_DATATYPE{API.LAMMPS_INT}() const LAMMPS_INT_2D = _LMP_DATATYPE{API.LAMMPS_INT_2D}() const LAMMPS_DOUBLE = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() const LAMMPS_DOUBLE_2D = _LMP_DATATYPE{API.LAMMPS_DOUBLE_2D}() From f7739d0babffd8d3d8c94f1c7a039423b700ad28 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 00:31:59 +0200 Subject: [PATCH 03/47] extract_global --- src/LAMMPS.jl | 92 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 1aaf3ac..e5b13b4 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -233,47 +233,73 @@ function get_natoms(lmp::LMP) Int64(API.lammps_get_natoms(lmp)) end -function dtype2type(dtype::API._LMP_DATATYPE_CONST) - if dtype == API.LAMMPS_INT - type = Ptr{Int32} - elseif dtype == API.LAMMPS_INT_2D - type = Ptr{Ptr{Int32}} - elseif dtype == API.LAMMPS_INT64 - type = Ptr{Int64} - elseif dtype == API.LAMMPS_INT64_2D - type = Ptr{Ptr{Int64}} - elseif dtype == API.LAMMPS_DOUBLE - type = Ptr{Float64} - elseif dtype == API.LAMMPS_DOUBLE_2D - type = Ptr{Ptr{Float64}} - elseif dtype == API.LAMMPS_STRING - type = Ptr{Cchar} - else - @assert false "Unknown dtype: $dtype" - end - return type +function lammps_string(ptr::Ptr, copy=true) + ptr == C_NULL && error("Wrapping NULL-pointer!") + + result = Base.unsafe_string(ptr) + return copy ? deepcopy(result) : result +end + +function lammps_wrap(ptr::Ptr{<:Real}, shape::Integer, copy=true) + ptr == C_NULL && error("Wrapping NULL-pointer!") + + result = Base.unsafe_wrap(Array, ptr, shape, own=false) + return copy ? Base.copy(result) : result +end + +function lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T + ptr == C_NULL && error("Wrapping NULL-pointer!") + + (count, ndata) = shape + + ndata == 0 && return Matrix{T}(undef, count, ndata) + + pointers = Base.unsafe_wrap(Array, ptr, ndata) + + @assert all(diff(pointers) .== count*sizeof(T)) + result = Base.unsafe_wrap(Array, pointers[1], shape, own=false) + + return copy ? Base.copy(result) : result +end + +function lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) + T === LAMMPS_INT && return Base.reinterpret(Ptr{Int32}, ptr) + T === LAMMPS_INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) + T === LAMMPS_DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) + T === LAMMPS_DOUBLE_2D && return Base.reinterpret(Ptr{Ptr{Float64}}, ptr) + T === LAMMPS_INT64 && return Base.reinterpret(Ptr{Int64}, ptr) + T === LAMMPS_INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) + T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end """ extract_global(lmp, name, dtype=nothing) """ -function extract_global(lmp::LMP, name, dtype=nothing) - if dtype === nothing - dtype = API.lammps_extract_global_datatype(lmp, name) - end - dtype = API._LMP_DATATYPE_CONST(dtype) - type = dtype2type(dtype) +function extract_global(lmp::LMP, name, lmp_type::_LMP_DATATYPE; copy=true) + void_ptr = API.lammps_extract_global(lmp, name) + void_ptr == C_NULL && error("Unknown global variable $name") - ptr = API.lammps_extract_global(lmp, name) - ptr = reinterpret(type, ptr) + expect = extract_global_datatype(lmp, name) + recieve = get_enum(lmp_type) + expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") - if ptr !== C_NULL - if dtype == API.LAMMPS_STRING - return Base.unsafe_string(ptr) - end - # TODO: deal with non-scalar data - return Base.unsafe_load(ptr) + ptr = lammps_reinterpret(lmp_type, void_ptr) + + lmp_type == LAMMPS_STRING && return lammps_string(ptr, copy) + + if name in ("boxlo", "boxhi", "sublo", "subhi", "sublo_lambda", "subhi_lambda", "periodicity") + length = 3 + elseif name in ("special_lj", "special_coul") + length = 4 + else + length = 1 end + + return lammps_wrap(ptr, length, copy) +end + +function extract_global_datatype(lmp::LMP, name) + return API._LMP_DATATYPE_CONST(API.lammps_extract_global_datatype(lmp, name)) end function unsafe_wrap(ptr, shape) From 4dafaf1e09f0ee444306074018da10817725acfb Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 00:36:23 +0200 Subject: [PATCH 04/47] extract_setting --- src/LAMMPS.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index e5b13b4..b7cf86e 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -4,7 +4,7 @@ include("api.jl") import .API: LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, - gather, scatter!, group_to_atom_ids, get_category_ids, + extract_setting, gather, scatter!, group_to_atom_ids, get_category_ids, LAMMPS_NONE, LAMMPS_INT, @@ -272,6 +272,13 @@ function lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end +""" + extract_setting(lmp::LMP, name::String) +""" +function extract_setting(lmp::LMP, name::String) + return API.lammps_extract_setting(lmp, name) +end + """ extract_global(lmp, name, dtype=nothing) """ From 78b221bf3b45ac8b53e49869cf9c61f16ae22f25 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 00:50:24 +0200 Subject: [PATCH 05/47] extract_atom --- src/LAMMPS.jl | 51 ++++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index b7cf86e..7eb4df8 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -271,6 +271,7 @@ function lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) T === LAMMPS_INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end +is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) """ extract_setting(lmp::LMP, name::String) @@ -328,48 +329,36 @@ function unsafe_wrap(ptr, shape) end """ - extract_atom(lmp, name, dtype=nothing, axes1, axes2) + extract_atom(lmp::LMP, name::String, dtype::_LMP_DATATYPE; copy=true) """ -function extract_atom(lmp::LMP, name, - dtype::Union{Nothing, API._LMP_DATATYPE_CONST} = nothing, - axes1=nothing, axes2=nothing) +function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) + void_ptr = API.lammps_extract_atom(lmp, name) + void_ptr == C_NULL && error("Unknown per-atom variable $name") + expect = extract_atom_datatype(lmp, name) + recieve = get_enum(lmp_type) + expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") - if dtype === nothing - dtype = API.lammps_extract_atom_datatype(lmp, name) - dtype = API._LMP_DATATYPE_CONST(dtype) - end + ptr = lammps_reinterpret(lmp_type, void_ptr) - if axes1 === nothing if name == "mass" - axes1 = extract_global(lmp, "ntypes") + 1 - else - axes1 = extract_global(lmp, "nlocal") % Int - end + length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] + ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused + return lammps_wrap(ptr, length, copy) end - if axes2 === nothing - if dtype in (API.LAMMPS_INT_2D, API.LAMMPS_INT64_2D, API.LAMMPS_DOUBLE_2D) - # TODO: Other fields? - if name in ("x", "v", "f", "angmom", "torque", "csforce", "vforce") - axes2 = 3 - else - axes2 = 2 - end - end - end + length = extract_setting(lmp, "nlocal") - if axes2 !== nothing - shape = (axes1, axes2) - else - shape = (axes1, ) + if is_2D_datatype(lmp_type) + count = name == "quat" ? Int32(4) : Int32(3) # only Quaternions have 4 entries + return lammps_wrap(ptr, (count, length), copy) end - type = dtype2type(dtype) - ptr = API.lammps_extract_atom(lmp, name) - ptr = reinterpret(type, ptr) + return lammps_wrap(ptr, length, copy) +end - unsafe_wrap(ptr, shape) +function extract_atom_datatype(lmp::LMP, name) + return API._LMP_DATATYPE_CONST(API.lammps_extract_atom_datatype(lmp, name)) end function unsafe_extract_compute(lmp::LMP, name, style, type) From d9ae598da7a1cf47338ad63fcafc4ed1e904fbaa Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 01:07:03 +0200 Subject: [PATCH 06/47] exports --- src/LAMMPS.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 7eb4df8..53d1633 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -1,7 +1,7 @@ module LAMMPS import MPI include("api.jl") -import .API: LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL +import .API: _LMP_STYLE_CONST, LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, extract_setting, gather, scatter!, group_to_atom_ids, get_category_ids, @@ -25,7 +25,11 @@ export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, VAR_EQUAL, VAR_ATOM, VAR_VECTOR, - VAR_STRING + VAR_STRING, + + LMP_STYLE_GLOBAL, + LMP_STYLE_ATOM, + LMP_STYLE_LOCAL using Preferences From e99180de7fb893ab4d67fdbfac775cfed21fa7a9 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 01:07:31 +0200 Subject: [PATCH 07/47] extract_compute --- src/LAMMPS.jl | 95 ++++++++++++++------------------------------------- 1 file changed, 26 insertions(+), 69 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 53d1633..60aaa24 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -275,6 +275,7 @@ function lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) T === LAMMPS_INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end + is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) """ @@ -365,82 +366,38 @@ function extract_atom_datatype(lmp::LMP, name) return API._LMP_DATATYPE_CONST(API.lammps_extract_atom_datatype(lmp, name)) end -function unsafe_extract_compute(lmp::LMP, name, style, type) - if type == API.LMP_TYPE_SCALAR - if style == API.LMP_STYLE_GLOBAL - dtype = Ptr{Float64} - elseif style == API.LMP_STYLE_LOCAL - dtype = Ptr{Cint} - elseif style == API.LMP_STYLE_ATOM - return nothing - end - extract = true - elseif type == API.LMP_TYPE_VECTOR - dtype = Ptr{Float64} - extract = false - elseif type == API.LMP_TYPE_ARRAY - dtype = Ptr{Ptr{Float64}} - extract = false - elseif type == API.LMP_SIZE_COLS - dtype = Ptr{Cint} - extract = true - elseif type == API.LMP_SIZE_ROWS || - type == API.LMP_SIZE_VECTOR - if style == API.LMP_STYLE_ATOM - return nothing - end - dtype = Ptr{Cint} - extract = true - else - @assert false "Unknown type: $type" - end +""" + extract_compute(lmp::LMP, name::String, style::_LMP_STYLE, type::_LMP_TYPE; copy=true) +""" +function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy=true) + API.lammps_has_id(lmp, "compute", name) != 1 && error("Unknown compute $name") - ptr = API.lammps_extract_compute(lmp, name, style, type) - ptr == C_NULL && check(lmp) + void_ptr = API.lammps_extract_compute(lmp, name, style, get_enum(lmp_type)) + void_ptr == C_NULL && error("Compute $name doesn't have data matching $style, $(get_enum(lmp_type))") - if ptr == C_NULL - error("Could not extract_compute $name with $style and $type") + if lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR) + ptr = lammps_reinterpret(LAMMPS_INT, void_ptr) + return lammps_wrap(ptr, 1, copy) end - ptr = reinterpret(dtype, ptr) - if extract - return Base.unsafe_load(ptr) + if lmp_type == TYPE_SCALAR + ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + return lammps_wrap(ptr, 1, copy) end - return ptr -end -""" - extract_compute(lmp, name, style, type) -""" -function extract_compute(lmp::LMP, name, style, type) - ptr_or_value = unsafe_extract_compute(lmp, name, style, type) - if style == API.LMP_TYPE_SCALAR - return ptr_or_value - end - if ptr_or_value === nothing - return nothing - end - ptr = ptr_or_value::Ptr - - if style in (API.LMP_STYLE_GLOBAL, API.LMP_STYLE_LOCAL) - if type == API.LMP_TYPE_VECTOR - nrows = unsafe_extract_compute(lmp, name, style, API.LMP_SIZE_VECTOR) - return unsafe_wrap(ptr, (nrows,)) - elseif type == API.LMP_TYPE_ARRAY - nrows = unsafe_extract_compute(lmp, name, style, API.LMP_SIZE_ROWS) - ncols = unsafe_extract_compute(lmp, name, style, API.LMP_SIZE_COLS) - return unsafe_wrap(ptr, (nrows, ncols)) - end - else style = API.LMP_STYLE_ATOM - nlocal = extract_global(lmp, "nlocal") - if type == API.LMP_TYPE_VECTOR - return unsafe_wrap(ptr, (nlocal,)) - elseif type == API.LMP_TYPE_ARRAY - ncols = unsafe_extract_compute(lmp, name, style, API.LMP_SIZE_COLS) - return unsafe_wrap(ptr, (nlocal, ncols)) - end + ndata = style == LMP_STYLE_ATOM ? + extract_setting(lmp, "nlocal") : + extract_compute(lmp, name, style, TYPE_SCALAR, copy=false)[] + + if lmp_type == TYPE_VECTOR + ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + return lammps_wrap(ptr, ndata, copy) end - return nothing + + count = extract_compute(lmp, name, style, SIZE_COLS)[] + ptr = lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) + + return lammps_wrap(ptr, (count, ndata), copy) end """ From 7dbcf7230a5a2119f730e63e436c3720c614ec2b Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 01:29:59 +0200 Subject: [PATCH 08/47] fixup! --- src/LAMMPS.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 60aaa24..45a12a6 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -153,13 +153,10 @@ end LMP(f::Function, args=String[], comm=nothing) Create a new LAMMPS instance and call `f` on that instance while returning the result from `f`. -This constructor closes the LAMMPS instance immediately after `f` has executed. """ function LMP(f::Function, args=String[], comm=nothing) lmp = LMP(args, comm) - result = f(lmp) - close!(lmp) - return result + return f(lmp) end function version(lmp::LMP) From 2776caa75f3229261f000a43fcd9045312a953d7 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 01:30:44 +0200 Subject: [PATCH 09/47] extract_variable --- src/LAMMPS.jl | 79 +++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 45a12a6..36dae30 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -343,7 +343,7 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true ptr = lammps_reinterpret(lmp_type, void_ptr) - if name == "mass" + if name == "mass" length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused return lammps_wrap(ptr, length, copy) @@ -398,55 +398,60 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty end """ - extract_variable(lmp::LMP, name, group) - + extract_variable(lmp::LMP, name::String, variable::LMP_VARIABLE, group=C_NULL; copy=true) Extracts the data from a LAMMPS variable. When the variable is either an `equal`-style compatible variable, a `vector`-style variable, or an `atom`-style variable, the variable is evaluated and the corresponding value(s) returned. Variables of style `internal` are compatible with `equal`-style variables, if they return a numeric value. For other variable styles, their string value is returned. """ -function extract_variable(lmp::LMP, name::String, group=nothing) - var = API.lammps_extract_variable_datatype(lmp, name) - if var == -1 - throw(KeyError(name)) - end - if group === nothing +function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group=nothing; copy=true) + lmp_variable != VAR_ATOM && !isnothing(group) && error("the group parameter is only supported for per atom variables!") + + if isnothing(group) group = C_NULL end - if var == API.LMP_VAR_EQUAL - ptr = API.lammps_extract_variable(lmp, name, C_NULL) - val = Base.unsafe_load(Base.unsafe_convert(Ptr{Float64}, ptr)) - API.lammps_free(ptr) - return val - elseif var == API.LMP_VAR_ATOM - nlocal = extract_global(lmp, "nlocal") - ptr = API.lammps_extract_variable(lmp, name, group) - if ptr == C_NULL - error("Group $group for variable $name with style atom not available.") - end - # LAMMPS uses malloc, so and we are taking ownership of this buffer - val = copy(Base.unsafe_wrap(Array, Base.unsafe_convert(Ptr{Float64}, ptr), nlocal; own=false)) + void_ptr = API.lammps_extract_variable(lmp, name, group) + void_ptr == C_NULL && error("Unknown variable $name") + + expect = extract_variable_datatype(lmp, name) + recieve = get_enum(lmp_variable) + expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") + + if lmp_variable == VAR_EQUAL + ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + result = unsafe_load(ptr) API.lammps_free(ptr) - return val - elseif var == API.LMP_VAR_VECTOR - # TODO Fix lammps docs `GET_VECTOR_SIZE` - ptr = API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR") - if ptr == C_NULL - error("$name is a vector style variable but has no size.") - end - sz = Base.unsafe_load(Base.unsafe_convert(Ptr{Cint}, ptr)) + return result + end + + if lmp_variable == VAR_VECTOR + ndata_ptr = lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "GET_VECTOR_SIZE")) + ndata = unsafe_load(ndata_ptr) + API.lammps_free(ndata_ptr) + + ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + return lammps_wrap(ptr, ndata, copy) + end + + if lmp_variable == VAR_ATOM + ndata = extract_setting(lmp, "nlocal") + + ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + result = lammps_wrap(ptr, ndata, true) API.lammps_free(ptr) - ptr = API.lammps_extract_variable(lmp, name, C_NULL) - return Base.unsafe_wrap(Array, Base.unsafe_convert(Ptr{Float64}, ptr), sz, own=false) - elseif var == API.LMP_VAR_STRING - ptr = API.lammps_extract_variable(lmp, name, C_NULL) - return Base.unsafe_string(Base.unsafe_convert(Ptr{Cchar}, ptr)) - else - error("Unkown variable style $var") + return result end + + ptr = lammps_reinterpret(LAMMPS_STRING, void_ptr) + return lammps_string(ptr, copy) end +function extract_variable_datatype(lmp::LMP, name) + return API._LMP_VAR_CONST(API.lammps_extract_variable_datatype(lmp, name)) +end + + @deprecate gather_atoms(lmp::LMP, name, T, count) gather(lmp, name, T) From 3dcc6f1dbcd1357302631ddcd895ccadaa913797 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 01:31:04 +0200 Subject: [PATCH 10/47] adjust tests --- test/runtests.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 8cd826b..f8365ff 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -30,13 +30,13 @@ end # TODO: x is 3d, how do we access more than the first dims command(lmp, "variable var4 vector f_press") - @test LAMMPS.extract_variable(lmp, "var1") == 1.0 - @test LAMMPS.extract_variable(lmp, "var2") == "hello" - x = LAMMPS.extract_atom(lmp, "x") - x_var = LAMMPS.extract_variable(lmp, "var3") + @test LAMMPS.extract_variable(lmp, "var1", VAR_EQUAL) == 1.0 + @test LAMMPS.extract_variable(lmp, "var2", VAR_STRING) == "hello" + x = LAMMPS.extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + x_var = LAMMPS.extract_variable(lmp, "var3", VAR_ATOM) @test length(x_var) == 10 @test x_var == x[1, :] - press = LAMMPS.extract_variable(lmp, "var4") + press = LAMMPS.extract_variable(lmp, "var4", VAR_VECTOR) @test press isa Vector{Float64} end end From 4aef5464fa0a21b2aef8443ff46a078a8a0ecba7 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 01:32:31 +0200 Subject: [PATCH 11/47] fix example --- examples/lj_forces.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lj_forces.jl b/examples/lj_forces.jl index aaf62eb..6087aca 100644 --- a/examples/lj_forces.jl +++ b/examples/lj_forces.jl @@ -43,4 +43,4 @@ command(lmp, "run 0") # extract output forces = gather(lmp, "f", Float64) -energies = extract_compute(lmp, "pot_e", LAMMPS.API.LMP_STYLE_GLOBAL, LAMMPS.API.LMP_TYPE_SCALAR) \ No newline at end of file +energies = extract_compute(lmp, "pot_e", LMP_STYLE_GLOBAL, TYPE_SCALAR) \ No newline at end of file From bb979a3ba04e5d92d8bd1da8de577b251bf34d8f Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 02:15:42 +0200 Subject: [PATCH 12/47] fixup! extract_compute --- src/LAMMPS.jl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 36dae30..bfed2aa 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -5,6 +5,7 @@ import .API: _LMP_STYLE_CONST, LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, extract_setting, gather, scatter!, group_to_atom_ids, get_category_ids, + extract_variable, LAMMPS_NONE, LAMMPS_INT, @@ -382,16 +383,20 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty return lammps_wrap(ptr, 1, copy) end - ndata = style == LMP_STYLE_ATOM ? - extract_setting(lmp, "nlocal") : - extract_compute(lmp, name, style, TYPE_SCALAR, copy=false)[] - if lmp_type == TYPE_VECTOR + ndata = (style == LMP_STYLE_ATOM) ? + extract_setting(lmp, "nlocal") : + extract_compute(lmp, name, style, SIZE_VECTOR, copy=false)[] + ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) return lammps_wrap(ptr, ndata, copy) end - count = extract_compute(lmp, name, style, SIZE_COLS)[] + ndata = (style == LMP_STYLE_ATOM) ? + extract_setting(lmp, "nlocal") : + extract_compute(lmp, name, style, SIZE_ROWS, copy=false)[] + + count = extract_compute(lmp, name, style, SIZE_COLS, copy=false)[] ptr = lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) return lammps_wrap(ptr, (count, ndata), copy) From a6135732ed49da1123a9e8695120e025c5c1ceaa Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 02:16:23 +0200 Subject: [PATCH 13/47] add tests --- test/runtests.jl | 74 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index f8365ff..bdcf866 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,6 +15,30 @@ LMP(["-screen", "none"]) do lmp @test_throws ErrorException command(lmp, "nonsense") end +@testset "Ectract Setting/Global" begin + LMP(["-screen", "none"]) do lmp + command(lmp, """ + atom_modify map yes + region cell block 0 1 0 2 0 3 + create_box 1 cell + """) + + @test extract_global(lmp, "dt", LAMMPS_DOUBLE)[] isa Float64 + @test extract_global(lmp, "boxhi", LAMMPS_DOUBLE) == [1, 2, 3] + @test extract_global(lmp, "nlocal", LAMMPS_INT)[] == extract_setting(lmp, "nlocal") == 0 + + with_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT) + with_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT) + + @test pointer(with_copy1) != pointer(with_copy2) + + without_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) + without_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) + + @test pointer(with_copy1) != pointer(with_copy2) + end +end + @testset "Variables" begin LMP(["-screen", "none"]) do lmp command(lmp, "box tilt large") @@ -30,13 +54,13 @@ end # TODO: x is 3d, how do we access more than the first dims command(lmp, "variable var4 vector f_press") - @test LAMMPS.extract_variable(lmp, "var1", VAR_EQUAL) == 1.0 - @test LAMMPS.extract_variable(lmp, "var2", VAR_STRING) == "hello" - x = LAMMPS.extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) - x_var = LAMMPS.extract_variable(lmp, "var3", VAR_ATOM) + @test extract_variable(lmp, "var1", VAR_EQUAL) == 1.0 + @test extract_variable(lmp, "var2", VAR_STRING) == "hello" + x = extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + x_var = extract_variable(lmp, "var3", VAR_ATOM) @test length(x_var) == 10 @test x_var == x[1, :] - press = LAMMPS.extract_variable(lmp, "var4", VAR_VECTOR) + press = extract_variable(lmp, "var4", VAR_VECTOR) @test press isa Vector{Float64} end end @@ -44,17 +68,20 @@ end @testset "gather/scatter" begin LMP(["-screen", "none"]) do lmp # setting up example data - command(lmp, "atom_modify map yes") - command(lmp, "region cell block 0 3 0 3 0 3") - command(lmp, "create_box 1 cell") - command(lmp, "lattice sc 1") - command(lmp, "create_atoms 1 region cell") - command(lmp, "mass 1 1") + command(lmp, """ + atom_modify map yes + region cell block 0 3 0 3 0 3 + create_box 1 cell + lattice sc 1 + create_atoms 1 region cell + mass 1 1 + + compute pos all property/atom x y z + fix pos all ave/atom 10 1 10 c_pos[1] c_pos[2] c_pos[3] - command(lmp, "compute pos all property/atom x y z") - command(lmp, "fix pos all ave/atom 10 1 10 c_pos[1] c_pos[2] c_pos[3]") + run 10 + """) - command(lmp, "run 10") data = zeros(Float64, 3, 27) subset = Int32.([2,5,10, 5]) data_subset = ones(Float64, 3, 4) @@ -101,6 +128,25 @@ end end end +@testset "Extract Compute" begin + LMP(["-screen", "none"]) do lmp + command(lmp, """ + atom_modify map yes + region cell block 0 3 0 3 0 3 + create_box 1 cell + lattice sc 1 + create_atoms 1 region cell + mass 1 1 + + compute pos all property/atom x y z + """) + + @test extract_compute(lmp, "pos", LMP_STYLE_ATOM, TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) == [0] + @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) == [0, 0, 0, 0, 0, 0] + end +end + @testset "Utilities" begin LMP(["-screen", "none"]) do lmp # setting up example data From a88504749f83495842699858067aecd7b0eeca9f Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 02:34:15 +0200 Subject: [PATCH 14/47] extract_compute docstring --- src/LAMMPS.jl | 24 ++++++++++++++++++++++++ test/runtests.jl | 8 ++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index bfed2aa..99b9f00 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -366,6 +366,29 @@ end """ extract_compute(lmp::LMP, name::String, style::_LMP_STYLE, type::_LMP_TYPE; copy=true) + +Extract data provided by a compute command identified by the compute-ID. +Computes may provide global, per-atom, or local data, and those may be a scalar, a vector or an array. +Since computes may provide multiple kinds of data, it is required to set style and type flags representing what specific data is desired. + +the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. +As the pointer to the underlying data is not persistent, it's highly recommended to only disable this, +if you wish to modify the internal state of the LAMMPS instance. + +Scalar values get returned as a vector with a single element. This way it's possible to +modify the internal state of the LAMMPS instance even if the data is scalar. + +# Examples + +```julia + LMP(["-screen", "none"]) do lmp + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR)[2] = 2 + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR, copy=false)[3] = 3 + + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) |> println # [0.0] + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) |> println # [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] + end +``` """ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy=true) API.lammps_has_id(lmp, "compute", name) != 1 && error("Unknown compute $name") @@ -404,6 +427,7 @@ end """ extract_variable(lmp::LMP, name::String, variable::LMP_VARIABLE, group=C_NULL; copy=true) + Extracts the data from a LAMMPS variable. When the variable is either an `equal`-style compatible variable, a `vector`-style variable, or an `atom`-style variable, the variable is evaluated and the corresponding value(s) returned. Variables of style `internal` are compatible with `equal`-style variables, if they return a numeric value. diff --git a/test/runtests.jl b/test/runtests.jl index bdcf866..db32a7d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -142,8 +142,12 @@ end """) @test extract_compute(lmp, "pos", LMP_STYLE_ATOM, TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) - @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) == [0] - @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) == [0, 0, 0, 0, 0, 0] + + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR)[2] = 2 + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR, copy=false)[3] = 3 + + @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) == [0.0] + @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] end end From 21da2c1b68d385d493832f50accb1c1b7966ac00 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 02:42:42 +0200 Subject: [PATCH 15/47] docstring extract_setting --- src/LAMMPS.jl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 99b9f00..beab554 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -278,6 +278,25 @@ is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_D """ extract_setting(lmp::LMP, name::String) + +Query LAMMPS about global settings. + +A full list of settings can be found here: + +# Examples +```julia + LMP(["-screen", "none"]) do lmp + command(lmp, \""" + region cell block 0 3 0 3 0 3 + create_box 1 cell + lattice sc 1 + create_atoms 1 region cell + \""") + + extract_setting(lmp, "dimension") |> println # 3 + extract_setting(lmp, "nlocal") |> println # 27 + end +``` """ function extract_setting(lmp::LMP, name::String) return API.lammps_extract_setting(lmp, name) From e50e048e4f471fb5ebfc60d88bc42d259876b09d Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 11:52:02 +0200 Subject: [PATCH 16/47] remove unsafe_wrap --- src/LAMMPS.jl | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index beab554..3adc753 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -332,24 +332,6 @@ function extract_global_datatype(lmp::LMP, name) return API._LMP_DATATYPE_CONST(API.lammps_extract_global_datatype(lmp, name)) end -function unsafe_wrap(ptr, shape) - if length(shape) > 1 - # We got a list of ptrs, - # but the first pointer points to the whole data - ptr = Base.unsafe_load(ptr) - - @assert length(shape) == 2 - - # Note: Julia like Fortran is column-major - # so the data is transposed from Julia's perspective - shape = reverse(shape) - end - - # TODO: Who is responsible for freeing this data - array = Base.unsafe_wrap(Array, ptr, shape, own=false) - return array -end - """ extract_atom(lmp::LMP, name::String, dtype::_LMP_DATATYPE; copy=true) """ From 102f21c9adc2932d4a3a740bc16aaa216749b506 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:27:48 +0200 Subject: [PATCH 17/47] rename internal functions --- src/LAMMPS.jl | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 3adc753..9ab2037 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -235,21 +235,21 @@ function get_natoms(lmp::LMP) Int64(API.lammps_get_natoms(lmp)) end -function lammps_string(ptr::Ptr, copy=true) +function _lammps_string(ptr::Ptr, copy=true) ptr == C_NULL && error("Wrapping NULL-pointer!") result = Base.unsafe_string(ptr) return copy ? deepcopy(result) : result end -function lammps_wrap(ptr::Ptr{<:Real}, shape::Integer, copy=true) +function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer, copy=true) ptr == C_NULL && error("Wrapping NULL-pointer!") result = Base.unsafe_wrap(Array, ptr, shape, own=false) return copy ? Base.copy(result) : result end -function lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T +function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T ptr == C_NULL && error("Wrapping NULL-pointer!") (count, ndata) = shape @@ -264,7 +264,7 @@ function lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T return copy ? Base.copy(result) : result end -function lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) +function _lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) T === LAMMPS_INT && return Base.reinterpret(Ptr{Int32}, ptr) T === LAMMPS_INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) T === LAMMPS_DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) @@ -313,9 +313,9 @@ function extract_global(lmp::LMP, name, lmp_type::_LMP_DATATYPE; copy=true) recieve = get_enum(lmp_type) expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") - ptr = lammps_reinterpret(lmp_type, void_ptr) + ptr = _lammps_reinterpret(lmp_type, void_ptr) - lmp_type == LAMMPS_STRING && return lammps_string(ptr, copy) + lmp_type == LAMMPS_STRING && return _lammps_string(ptr, copy) if name in ("boxlo", "boxhi", "sublo", "subhi", "sublo_lambda", "subhi_lambda", "periodicity") length = 3 @@ -325,7 +325,7 @@ function extract_global(lmp::LMP, name, lmp_type::_LMP_DATATYPE; copy=true) length = 1 end - return lammps_wrap(ptr, length, copy) + return _lammps_wrap(ptr, length, copy) end function extract_global_datatype(lmp::LMP, name) @@ -343,22 +343,21 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true recieve = get_enum(lmp_type) expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") - ptr = lammps_reinterpret(lmp_type, void_ptr) + ptr = _lammps_reinterpret(lmp_type, void_ptr) if name == "mass" length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused - return lammps_wrap(ptr, length, copy) + return _lammps_wrap(ptr, length, copy) end length = extract_setting(lmp, "nlocal") if is_2D_datatype(lmp_type) - count = name == "quat" ? Int32(4) : Int32(3) # only Quaternions have 4 entries - return lammps_wrap(ptr, (count, length), copy) + return _lammps_wrap(ptr, (count, length), copy) end - return lammps_wrap(ptr, length, copy) + return _lammps_wrap(ptr, length, copy) end function extract_atom_datatype(lmp::LMP, name) @@ -397,9 +396,8 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty void_ptr = API.lammps_extract_compute(lmp, name, style, get_enum(lmp_type)) void_ptr == C_NULL && error("Compute $name doesn't have data matching $style, $(get_enum(lmp_type))") - if lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR) - ptr = lammps_reinterpret(LAMMPS_INT, void_ptr) - return lammps_wrap(ptr, 1, copy) + ptr = _lammps_reinterpret(LAMMPS_INT, void_ptr) + return _lammps_wrap(ptr, 1, copy) end if lmp_type == TYPE_SCALAR @@ -449,32 +447,32 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") if lmp_variable == VAR_EQUAL - ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) result = unsafe_load(ptr) API.lammps_free(ptr) return result end if lmp_variable == VAR_VECTOR - ndata_ptr = lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "GET_VECTOR_SIZE")) + ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "GET_VECTOR_SIZE")) ndata = unsafe_load(ndata_ptr) API.lammps_free(ndata_ptr) - ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return lammps_wrap(ptr, ndata, copy) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + return _lammps_wrap(ptr, ndata, copy) end if lmp_variable == VAR_ATOM ndata = extract_setting(lmp, "nlocal") - ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - result = lammps_wrap(ptr, ndata, true) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + result = _lammps_wrap(ptr, ndata, true) API.lammps_free(ptr) return result end - ptr = lammps_reinterpret(LAMMPS_STRING, void_ptr) - return lammps_string(ptr, copy) + ptr = _lammps_reinterpret(LAMMPS_STRING, void_ptr) + return _lammps_string(ptr) end function extract_variable_datatype(lmp::LMP, name) From c55a6b416e0d0dccae3cd2cf5c13450a74f0bbb5 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:29:40 +0200 Subject: [PATCH 18/47] minor changes and fixup! --- src/LAMMPS.jl | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 9ab2037..7bad11c 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -235,17 +235,16 @@ function get_natoms(lmp::LMP) Int64(API.lammps_get_natoms(lmp)) end -function _lammps_string(ptr::Ptr, copy=true) +function _lammps_string(ptr::Ptr) ptr == C_NULL && error("Wrapping NULL-pointer!") - - result = Base.unsafe_string(ptr) - return copy ? deepcopy(result) : result + return Base.unsafe_string(ptr) end function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer, copy=true) ptr == C_NULL && error("Wrapping NULL-pointer!") result = Base.unsafe_wrap(Array, ptr, shape, own=false) + return copy ? Base.copy(result) : result end @@ -254,11 +253,16 @@ function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T (count, ndata) = shape - ndata == 0 && return Matrix{T}(undef, count, ndata) + ndata == 0 && return Matrix{T}(undef, count, ndata) # There is no data that can be wrapped pointers = Base.unsafe_wrap(Array, ptr, ndata) + # This assert verifies that all the pointers are evenly spaced according to count. + # While it seems like this is allways the case, it's not explicitly stated in the + # API documentation. It also helps to verify, that the count is correct. @assert all(diff(pointers) .== count*sizeof(T)) + + # It the pointers are evenly spaced, we can simply use the first pointer to wrap our matrix. result = Base.unsafe_wrap(Array, pointers[1], shape, own=false) return copy ? Base.copy(result) : result @@ -354,6 +358,10 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true length = extract_setting(lmp, "nlocal") if is_2D_datatype(lmp_type) + # only Quaternions have 4 entries + # length is a Int32 and lammps_wrap expects a NTuple, so it's + # neccecary to use Int32 for count as well + count = name == "quat" ? Int32(4) : Int32(3) return _lammps_wrap(ptr, (count, length), copy) end @@ -396,13 +404,15 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty void_ptr = API.lammps_extract_compute(lmp, name, style, get_enum(lmp_type)) void_ptr == C_NULL && error("Compute $name doesn't have data matching $style, $(get_enum(lmp_type))") + # `lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR)` causes type instability for some reason + if lmp_type == SIZE_COLS || lmp_type == SIZE_ROWS || lmp_type == SIZE_VECTOR ptr = _lammps_reinterpret(LAMMPS_INT, void_ptr) return _lammps_wrap(ptr, 1, copy) end if lmp_type == TYPE_SCALAR - ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return lammps_wrap(ptr, 1, copy) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + return _lammps_wrap(ptr, 1, copy) end if lmp_type == TYPE_VECTOR @@ -410,8 +420,8 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_setting(lmp, "nlocal") : extract_compute(lmp, name, style, SIZE_VECTOR, copy=false)[] - ptr = lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return lammps_wrap(ptr, ndata, copy) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + return _lammps_wrap(ptr, ndata, copy) end ndata = (style == LMP_STYLE_ATOM) ? @@ -419,9 +429,9 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_compute(lmp, name, style, SIZE_ROWS, copy=false)[] count = extract_compute(lmp, name, style, SIZE_COLS, copy=false)[] - ptr = lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) - return lammps_wrap(ptr, (count, ndata), copy) + return _lammps_wrap(ptr, (count, ndata), copy) end """ From 583d72d1308027e5e8b6b1867c90af53043b0047 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:29:58 +0200 Subject: [PATCH 19/47] documentation --- src/LAMMPS.jl | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 7bad11c..8a97b6e 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -281,7 +281,7 @@ end is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) """ - extract_setting(lmp::LMP, name::String) + extract_setting(lmp::LMP, name::String)::Int32 Query LAMMPS about global settings. @@ -302,14 +302,23 @@ A full list of settings can be found here: Date: Thu, 27 Jun 2024 14:30:25 +0200 Subject: [PATCH 20/47] add tests --- test/runtests.jl | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index db32a7d..49a7258 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,7 +15,7 @@ LMP(["-screen", "none"]) do lmp @test_throws ErrorException command(lmp, "nonsense") end -@testset "Ectract Setting/Global" begin +@testset "Extract Setting/Global" begin LMP(["-screen", "none"]) do lmp command(lmp, """ atom_modify map yes @@ -41,18 +41,21 @@ end @testset "Variables" begin LMP(["-screen", "none"]) do lmp - command(lmp, "box tilt large") - command(lmp, "region cell block 0 1.0 0 1.0 0 1.0 units box") - command(lmp, "create_box 1 cell") - command(lmp, "create_atoms 1 random 10 1 NULL") - command(lmp, "compute press all pressure NULL pair"); - command(lmp, "fix press all ave/time 1 1 1 c_press mode vector"); - - command(lmp, "variable var1 equal 1.0") - command(lmp, "variable var2 string \"hello\"") - command(lmp, "variable var3 atom x") - # TODO: x is 3d, how do we access more than the first dims - command(lmp, "variable var4 vector f_press") + command(lmp, """ + box tilt large + region cell block 0 1.0 0 1.0 0 1.0 units box + create_box 1 cell + create_atoms 1 random 10 1 NULL + compute press all pressure NULL pair + fix press all ave/time 1 1 1 c_press mode vector + + variable var1 equal 1.0 + variable var2 string \"hello\" + variable var3 atom x + # TODO: x is 3d, how do we access more than the first dims + variable var4 vector f_press + group odd id 1 3 5 7 + """) @test extract_variable(lmp, "var1", VAR_EQUAL) == 1.0 @test extract_variable(lmp, "var2", VAR_STRING) == "hello" @@ -62,6 +65,12 @@ end @test x_var == x[1, :] press = extract_variable(lmp, "var4", VAR_VECTOR) @test press isa Vector{Float64} + + x_var_group = extract_variable(lmp, "var3", VAR_ATOM, "odd") + in_group = BitVector((1, 0, 1, 0, 1, 0, 1, 0, 0, 0)) + + @test x_var_group[in_group] == x[1, in_group] + @test all(x_var_group[.!in_group] .== 0) end end From b8c677571b9adba050439ed0c7eeafb4d9fcd9c9 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:37:00 +0200 Subject: [PATCH 21/47] fixup! --- src/LAMMPS.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 8a97b6e..341cd6e 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -328,7 +328,7 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=tr ptr = _lammps_reinterpret(lmp_type, void_ptr) - lmp_type == LAMMPS_STRING && return _lammps_string(ptr, copy) + lmp_type == LAMMPS_STRING && return _lammps_string(ptr) if name in ("boxlo", "boxhi", "sublo", "subhi", "sublo_lambda", "subhi_lambda", "periodicity") length = 3 From 45a2763d4b83384b057197b24d8f1eb405ad294e Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:42:51 +0200 Subject: [PATCH 22/47] add comment --- src/LAMMPS.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 341cd6e..7e4e884 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -158,6 +158,7 @@ Create a new LAMMPS instance and call `f` on that instance while returning the r function LMP(f::Function, args=String[], comm=nothing) lmp = LMP(args, comm) return f(lmp) + # `close!` is registered as a finalizer for LMP, no need to close it here. end function version(lmp::LMP) From d71420ad810f21e3213151523c03b3ef90c58be9 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:43:21 +0200 Subject: [PATCH 23/47] docstrings --- src/LAMMPS.jl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 7e4e884..93b6ef7 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -318,6 +318,8 @@ However it's still recommended to only disable this, if you wish to modify the i Scalar values get returned as a vector with a single element. This way it's possible to modify the internal state of the LAMMPS instance even if the data is scalar. + +A full list of global variables can be found here: """ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) void_ptr = API.lammps_extract_global(lmp, name) @@ -347,7 +349,24 @@ function extract_global_datatype(lmp::LMP, name) end """ - extract_atom(lmp::LMP, name::String, dtype::_LMP_DATATYPE; copy=true) + extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) + +Extract per-atom data from the lammps instance. + +| valid values for `lmp_type`: | resulting return type: | +| :--------------------------- | :--------------------- | +| `LAMMPS_INT` | `Vector{Int32}` | +| `LAMMPS_INT_2D` | `Matrix{Int32}` | +| `LAMMPS_DOUBLE` | `Vector{Float64}` | +| `LAMMPS_DOUBLE_2D` | `Matrix{Float64}` | +| `LAMMPS_INT64` | `Vector{Int64}` | +| `LAMMPS_INT64_2D` | `Matrix{Int64}` | + +the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. +As the pointer to the underlying data is not persistent, it's highly recommended to only disable this, +if you wish to modify the internal state of the LAMMPS instance. + +A table with suported name keywords can be found here: """ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) void_ptr = API.lammps_extract_atom(lmp, name) From aa8834d833fa69773fb5a0fad5626fde35b769d9 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:47:34 +0200 Subject: [PATCH 24/47] test for missed errors --- test/runtests.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 49a7258..bf2e81c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -36,6 +36,9 @@ end without_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) @test pointer(with_copy1) != pointer(with_copy2) + + # verify that no errors were missed + @test LAMMPS.API.lammps_has_error(lmp) == 0 end end @@ -71,6 +74,9 @@ end @test x_var_group[in_group] == x[1, in_group] @test all(x_var_group[.!in_group] .== 0) + + # verify that no errors were missed + @test LAMMPS.API.lammps_has_error(lmp) == 0 end end @@ -134,6 +140,8 @@ end @test gather(lmp, "x", Float64, subset) == gather(lmp, "c_pos", Float64, subset) == gather(lmp, "f_pos", Float64, subset) == data_subset + # verify that no errors were missed + @test LAMMPS.API.lammps_has_error(lmp) == 0 end end @@ -157,6 +165,9 @@ end @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) == [0.0] @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] + + # verify that no errors were missed + @test LAMMPS.API.lammps_has_error(lmp) == 0 end end @@ -192,6 +203,9 @@ end @test get_category_ids(lmp, "compute") == ["thermo_temp", "thermo_press", "thermo_pe", "pos"] # some of these computes are there by default it seems @test get_category_ids(lmp, "fix") == ["1"] @test_throws ErrorException get_category_ids(lmp, "nonesense") + + # verify that no errors were missed + @test LAMMPS.API.lammps_has_error(lmp) == 0 end end From 9fbf239093e508daf811fdc2546a5813fd7b00cb Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:56:38 +0200 Subject: [PATCH 25/47] fixup! naming of internal functions --- src/LAMMPS.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 93b6ef7..67d96d8 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -279,7 +279,7 @@ function _lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end -is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) +_is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) """ extract_setting(lmp::LMP, name::String)::Int32 @@ -386,7 +386,7 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true length = extract_setting(lmp, "nlocal") - if is_2D_datatype(lmp_type) + if _is_2D_datatype(lmp_type) # only Quaternions have 4 entries # length is a Int32 and lammps_wrap expects a NTuple, so it's # neccecary to use Int32 for count as well From a6b46588a250f23c1c1e68f38a71ca2a81ee7c17 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 14:56:59 +0200 Subject: [PATCH 26/47] improving docstrings --- src/LAMMPS.jl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 67d96d8..fa218d9 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -308,10 +308,20 @@ function extract_setting(lmp::LMP, name::String)::Int32 end """ - extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) + extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::Bool=true) Extract a global property from a LAMMPS instance. +| valid values for `lmp_type`: | resulting return type: | +| :--------------------------- | :--------------------- | +| `LAMMPS_INT` | `Vector{Int32}` | +| `LAMMPS_INT_2D` | `Matrix{Int32}` | +| `LAMMPS_DOUBLE` | `Vector{Float64}` | +| `LAMMPS_DOUBLE_2D` | `Matrix{Float64}` | +| `LAMMPS_INT64` | `Vector{Int64}` | +| `LAMMPS_INT64_2D` | `Matrix{Int64}` | +| `LAMMPS_STRING` | `String` | + the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. the pointer to the underlying data is generally persistent, unless a clear command is issued. However it's still recommended to only disable this, if you wish to modify the internal state of the LAMMPS instance. @@ -321,7 +331,7 @@ modify the internal state of the LAMMPS instance even if the data is scalar. A full list of global variables can be found here: """ -function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) +function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::Bool=true) void_ptr = API.lammps_extract_global(lmp, name) void_ptr == C_NULL && error("Unknown global variable $name") @@ -402,7 +412,7 @@ function extract_atom_datatype(lmp::LMP, name) end """ - function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy=true) + extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy::Bool=true) Extract data provided by a compute command identified by the compute-ID. Computes may provide global, per-atom, or local data, and those may be a scalar, a vector or an array. @@ -442,7 +452,7 @@ modify the internal state of the LAMMPS instance even if the data is scalar. end ``` """ -function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy=true) +function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy::Bool=true) API.lammps_has_id(lmp, "compute", name) != 1 && error("Unknown compute $name") void_ptr = API.lammps_extract_compute(lmp, name, style, get_enum(lmp_type)) @@ -479,7 +489,7 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty end """ - extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group=nothing; copy=true) + extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=true) Extracts the data from a LAMMPS variable. When the variable is either an `equal`-style compatible variable, a `vector`-style variable, or an `atom`-style variable, the variable is evaluated and the corresponding value(s) returned. @@ -500,7 +510,7 @@ the kwarg `group` determines for which atoms the variable will be extracted. It' `VAR_ATOM` and will cause an error if used for other variable types. The entires for all atoms not in the group will be zeroed out. By default, all atoms will be extracted. """ -function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group=nothing; copy=true) +function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=true) lmp_variable != VAR_ATOM && !isnothing(group) && error("the group parameter is only supported for per atom variables!") if isnothing(group) From 05f7143f60d288bc8110411d00c4a2592402df4a Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 15:07:31 +0200 Subject: [PATCH 27/47] test extract_atom --- test/runtests.jl | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index bf2e81c..24c36d0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -42,6 +42,32 @@ end end end +@testset "Extract Atom" begin + LMP(["-screen", "none"]) do lmp + command(lmp, """ + atom_modify map yes + region cell block 0 3 0 3 0 3 + create_box 1 cell + lattice sc 1 + create_atoms 1 region cell + mass 1 1 + """) + + @test extract_atom(lmp, "mass", LAMMPS_DOUBLE) isa Vector{Float64} + @test extract_atom(lmp, "mass", LAMMPS_DOUBLE) == [1] + + x = extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + @test size(x) == (3, 27) + + @test extract_atom(lmp, "image", LAMMPS_INT) isa Vector{Int32} + + @test_throws ErrorException extract_atom(lmp, "v", LAMMPS_DOUBLE) + + # verify that no errors were missed + @test LAMMPS.API.lammps_has_error(lmp) == 0 + end +end + @testset "Variables" begin LMP(["-screen", "none"]) do lmp command(lmp, """ @@ -166,6 +192,9 @@ end @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) == [0.0] @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] + @test_throws ErrorException extract_compute(lmp, "thermo_temp", LMP_STYLE_ATOM, TYPE_SCALAR) + @test_throws ErrorException extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_ARRAY) + # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 end From b862a137f2218ae4f11fc8ceea655908e6bd1d28 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 15:15:34 +0200 Subject: [PATCH 28/47] fix potential memory leak --- src/LAMMPS.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index fa218d9..9d5f242 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -522,7 +522,13 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr expect = extract_variable_datatype(lmp, name) recieve = get_enum(lmp_variable) - expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") + if expect != recieve + if lmp_variable == VAR_EQUAL || lmp_variable == VAR_ATOM + API.lammps_Free(void_ptr) + end + + error("TypeMismatch: Expected $expect got $recieve instead!") + end if lmp_variable == VAR_EQUAL ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) From b2ff74e4835d38e7c95860766a10b740376601f0 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 15:22:26 +0200 Subject: [PATCH 29/47] fixup! fix potential memory leak --- src/LAMMPS.jl | 4 ++-- test/runtests.jl | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 9d5f242..e44eec2 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -523,8 +523,8 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr expect = extract_variable_datatype(lmp, name) recieve = get_enum(lmp_variable) if expect != recieve - if lmp_variable == VAR_EQUAL || lmp_variable == VAR_ATOM - API.lammps_Free(void_ptr) + if expect in (API.LMP_VAR_ATOM, API.LMP_VAR_VECTOR) + API.lammps_free(void_ptr) end error("TypeMismatch: Expected $expect got $recieve instead!") diff --git a/test/runtests.jl b/test/runtests.jl index 24c36d0..6888015 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -101,6 +101,8 @@ end @test x_var_group[in_group] == x[1, in_group] @test all(x_var_group[.!in_group] .== 0) + @test_throws ErrorException extract_variable(lmp, "var3", VAR_EQUAL) + # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 end From c407391ef8dbccb84247ba3ed1d3c31bafb30de1 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 15:40:20 +0200 Subject: [PATCH 30/47] add warning --- src/LAMMPS.jl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index e44eec2..fb80c96 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -312,15 +312,15 @@ end Extract a global property from a LAMMPS instance. -| valid values for `lmp_type`: | resulting return type: | -| :--------------------------- | :--------------------- | -| `LAMMPS_INT` | `Vector{Int32}` | -| `LAMMPS_INT_2D` | `Matrix{Int32}` | -| `LAMMPS_DOUBLE` | `Vector{Float64}` | -| `LAMMPS_DOUBLE_2D` | `Matrix{Float64}` | -| `LAMMPS_INT64` | `Vector{Int64}` | -| `LAMMPS_INT64_2D` | `Matrix{Int64}` | -| `LAMMPS_STRING` | `String` | +| valid values for `lmp_type`: | resulting return type: | +| :--------------------------- | :------------------------ | +| `LAMMPS_INT` | `Vector{Int32}` | +| `LAMMPS_INT_2D` | `Matrix{Int32}` | +| `LAMMPS_DOUBLE` | `Vector{Float64}` | +| `LAMMPS_DOUBLE_2D` | `Matrix{Float64}` | +| `LAMMPS_INT64` | `Vector{Int64}` | +| `LAMMPS_INT64_2D` | `Matrix{Int64}` | +| `LAMMPS_STRING` | `String` (allways a copy) | the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. the pointer to the underlying data is generally persistent, unless a clear command is issued. @@ -328,6 +328,10 @@ However it's still recommended to only disable this, if you wish to modify the i Scalar values get returned as a vector with a single element. This way it's possible to modify the internal state of the LAMMPS instance even if the data is scalar. +!!! warning + Modifying the data through `extract_global` may lead to inconsistent internal data and thus may cause failures or crashes or bogus simulations. + In general it is thus usually better to use a LAMMPS input command that sets or changes these parameters. + Those will take care of all side effects and necessary updates of settings derived from such settings. A full list of global variables can be found here: """ From 028e52a0e6d86ed97478efceeaabfe34ecf55529 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 16:06:24 +0200 Subject: [PATCH 31/47] fixup! extract_variable --- src/LAMMPS.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index fb80c96..cf579be 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -527,7 +527,8 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr expect = extract_variable_datatype(lmp, name) recieve = get_enum(lmp_variable) if expect != recieve - if expect in (API.LMP_VAR_ATOM, API.LMP_VAR_VECTOR) + # the documentation instructs us to free the pointers for these styles specifically + if expect in (API.LMP_VAR_ATOM, API.LMP_VAR_EQUAL) API.lammps_free(void_ptr) end @@ -542,6 +543,10 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr end if lmp_variable == VAR_VECTOR + # Calling lammps_extract_variable directly through the API instead of the higher level wrapper, as + # "GET_VECTOR_SIZE" is the only group name that won't be ignored for Vector Style Variables. + # This isn't exposed to the high level API as it causes type instability for something that probably won't + # ever be used outside of this implementation ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "GET_VECTOR_SIZE")) ndata = unsafe_load(ndata_ptr) API.lammps_free(ndata_ptr) From 2be1d406c2d5dcc1bce612b5b9b4e8e3a0c97585 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 27 Jun 2024 18:01:46 +0200 Subject: [PATCH 32/47] fix memory leak --- src/LAMMPS.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index cf579be..4d89305 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -544,10 +544,10 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr if lmp_variable == VAR_VECTOR # Calling lammps_extract_variable directly through the API instead of the higher level wrapper, as - # "GET_VECTOR_SIZE" is the only group name that won't be ignored for Vector Style Variables. + # "LMP_SIZE_VECTOR" is the only group name that won't be ignored for Vector Style Variables. # This isn't exposed to the high level API as it causes type instability for something that probably won't # ever be used outside of this implementation - ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "GET_VECTOR_SIZE")) + ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) ndata = unsafe_load(ndata_ptr) API.lammps_free(ndata_ptr) From 0e9d9820c8a455ed80eea96534496d8db007fb17 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 21:37:14 +0200 Subject: [PATCH 33/47] fix spelling receive --- src/LAMMPS.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 4d89305..7e5ac70 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -340,8 +340,8 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::B void_ptr == C_NULL && error("Unknown global variable $name") expect = extract_global_datatype(lmp, name) - recieve = get_enum(lmp_type) - expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") + receive = get_enum(lmp_type) + expect != receive && error("TypeMismatch: Expected $expect got $receive instead!") ptr = _lammps_reinterpret(lmp_type, void_ptr) @@ -387,8 +387,8 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true void_ptr == C_NULL && error("Unknown per-atom variable $name") expect = extract_atom_datatype(lmp, name) - recieve = get_enum(lmp_type) - expect != recieve && error("TypeMismatch: Expected $expect got $recieve instead!") + receive = get_enum(lmp_type) + expect != receive && error("TypeMismatch: Expected $expect got $receive instead!") ptr = _lammps_reinterpret(lmp_type, void_ptr) @@ -525,14 +525,14 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr void_ptr == C_NULL && error("Unknown variable $name") expect = extract_variable_datatype(lmp, name) - recieve = get_enum(lmp_variable) - if expect != recieve + receive = get_enum(lmp_variable) + if expect != receive # the documentation instructs us to free the pointers for these styles specifically if expect in (API.LMP_VAR_ATOM, API.LMP_VAR_EQUAL) API.lammps_free(void_ptr) end - error("TypeMismatch: Expected $expect got $recieve instead!") + error("TypeMismatch: Expected $expect got $receive instead!") end if lmp_variable == VAR_EQUAL From 9217085608fd0bec179299be242a3069e73246f7 Mon Sep 17 00:00:00 2001 From: Joroks Date: Thu, 27 Jun 2024 23:57:42 +0200 Subject: [PATCH 34/47] revert manual changes to API.jl --- src/api.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api.jl b/src/api.jl index c6f5254..d9571e6 100644 --- a/src/api.jl +++ b/src/api.jl @@ -17,13 +17,13 @@ import MPI: MPI_Comm LAMMPS_STRING = 6 end -@cenum _LMP_STYLE_CONST::Int32 begin +@cenum _LMP_STYLE_CONST::UInt32 begin LMP_STYLE_GLOBAL = 0 LMP_STYLE_ATOM = 1 LMP_STYLE_LOCAL = 2 end -@cenum _LMP_TYPE_CONST::Int32 begin +@cenum _LMP_TYPE_CONST::UInt32 begin LMP_TYPE_SCALAR = 0 LMP_TYPE_VECTOR = 1 LMP_TYPE_ARRAY = 2 @@ -32,7 +32,7 @@ end LMP_SIZE_COLS = 5 end -@cenum _LMP_ERROR_CONST::Int32 begin +@cenum _LMP_ERROR_CONST::UInt32 begin LMP_ERROR_WARNING = 0 LMP_ERROR_ONE = 1 LMP_ERROR_ALL = 2 From b3214248ebe63156d9c1a40e445e922866614de6 Mon Sep 17 00:00:00 2001 From: Joroks Date: Fri, 28 Jun 2024 14:57:35 +0200 Subject: [PATCH 35/47] rename constants --- src/LAMMPS.jl | 105 +++++++++++++++++++---------------------------- test/runtests.jl | 52 +++++++++++------------ 2 files changed, 69 insertions(+), 88 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index e991203..8a2d5f0 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -1,36 +1,10 @@ module LAMMPS import MPI include("api.jl") -import .API: _LMP_STYLE_CONST, LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, extract_setting, gather, scatter!, group_to_atom_ids, get_category_ids, - extract_variable, - - LAMMPS_NONE, - LAMMPS_INT, - LAMMPS_INT_2D, - LAMMPS_DOUBLE, - LAMMPS_DOUBLE_2D, - LAMMPS_INT64, - LAMMPS_INT64_2D, - LAMMPS_STRING, - - TYPE_SCALAR, - TYPE_VECTOR, - TYPE_ARRAY, - SIZE_VECTOR, - SIZE_ROWS, - SIZE_COLS, - - VAR_EQUAL, - VAR_ATOM, - VAR_VECTOR, - VAR_STRING, - - LMP_STYLE_GLOBAL, - LMP_STYLE_ATOM, - LMP_STYLE_LOCAL + extract_variable using Preferences @@ -39,14 +13,14 @@ get_enum(::TypeEnum{N}) where N = N struct _LMP_DATATYPE{N} <: TypeEnum{N} end -const LAMMPS_NONE = _LMP_DATATYPE{API.LAMMPS_NONE}() -const LAMMPS_INT = _LMP_DATATYPE{API.LAMMPS_INT}() -const LAMMPS_INT_2D = _LMP_DATATYPE{API.LAMMPS_INT_2D}() -const LAMMPS_DOUBLE = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() -const LAMMPS_DOUBLE_2D = _LMP_DATATYPE{API.LAMMPS_DOUBLE_2D}() -const LAMMPS_INT64 = _LMP_DATATYPE{API.LAMMPS_INT64}() -const LAMMPS_INT64_2D = _LMP_DATATYPE{API.LAMMPS_INT64_2D}() -const LAMMPS_STRING = _LMP_DATATYPE{API.LAMMPS_STRING}() +const NONE = _LMP_DATATYPE{API.LAMMPS_NONE}() +const INT = _LMP_DATATYPE{API.LAMMPS_INT}() +const INT_2D = _LMP_DATATYPE{API.LAMMPS_INT_2D}() +const DOUBLE = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() +const DOUBLE_2D = _LMP_DATATYPE{API.LAMMPS_DOUBLE_2D}() +const INT64 = _LMP_DATATYPE{API.LAMMPS_INT64}() +const INT64_2D = _LMP_DATATYPE{API.LAMMPS_INT64_2D}() +const STRING = _LMP_DATATYPE{API.LAMMPS_STRING}() struct _LMP_TYPE{N} <: TypeEnum{N} end @@ -57,12 +31,19 @@ const SIZE_VECTOR = _LMP_TYPE{API.LMP_SIZE_VECTOR}() const SIZE_ROWS = _LMP_TYPE{API.LMP_SIZE_ROWS}() const SIZE_COLS = _LMP_TYPE{API.LMP_SIZE_COLS}() -struct LMP_VARIABLE{N} <: TypeEnum{N} end +struct _LMP_VARIABLE{N} <: TypeEnum{N} end -const VAR_EQUAL = LMP_VARIABLE{API.LMP_VAR_EQUAL}() -const VAR_ATOM = LMP_VARIABLE{API.LMP_VAR_ATOM}() -const VAR_VECTOR = LMP_VARIABLE{API.LMP_VAR_VECTOR}() -const VAR_STRING = LMP_VARIABLE{API.LMP_VAR_STRING}() +const VAR_EQUAL = _LMP_VARIABLE{API.LMP_VAR_EQUAL}() +const VAR_ATOM = _LMP_VARIABLE{API.LMP_VAR_ATOM}() +const VAR_VECTOR = _LMP_VARIABLE{API.LMP_VAR_VECTOR}() +const VAR_STRING = _LMP_VARIABLE{API.LMP_VAR_STRING}() + +# these are not defined as TypeEnum as they don't carry type information +const _LMP_STYLE_CONST = API._LMP_STYLE_CONST + +const STYLE_GLOBAL = API.LMP_STYLE_GLOBAL +const STYLE_ATOM = API.LMP_STYLE_ATOM +const STYLE_LOCAL = API.LMP_STYLE_LOCAL """ locate() @@ -268,16 +249,16 @@ function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T end function _lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) - T === LAMMPS_INT && return Base.reinterpret(Ptr{Int32}, ptr) - T === LAMMPS_INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) - T === LAMMPS_DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) - T === LAMMPS_DOUBLE_2D && return Base.reinterpret(Ptr{Ptr{Float64}}, ptr) - T === LAMMPS_INT64 && return Base.reinterpret(Ptr{Int64}, ptr) - T === LAMMPS_INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) - T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) + T === INT && return Base.reinterpret(Ptr{Int32}, ptr) + T === INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) + T === DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) + T === DOUBLE_2D && return Base.reinterpret(Ptr{Ptr{Float64}}, ptr) + T === INT64 && return Base.reinterpret(Ptr{Int64}, ptr) + T === INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) + T === STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end -_is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) +_is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (INT_2D, DOUBLE_2D, INT64_2D) """ extract_setting(lmp::LMP, name::String)::Int32 @@ -343,7 +324,7 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::B ptr = _lammps_reinterpret(lmp_type, void_ptr) - lmp_type == LAMMPS_STRING && return _lammps_string(ptr) + lmp_type == STRING && return _lammps_string(ptr) if name in ("boxlo", "boxhi", "sublo", "subhi", "sublo_lambda", "subhi_lambda", "periodicity") length = 3 @@ -391,7 +372,7 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true ptr = _lammps_reinterpret(lmp_type, void_ptr) if name == "mass" - length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] + length = extract_global(lmp, "ntypes", INT, copy=false)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused return _lammps_wrap(ptr, length, copy) end @@ -462,30 +443,30 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty # `lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR)` causes type instability for some reason if lmp_type == SIZE_COLS || lmp_type == SIZE_ROWS || lmp_type == SIZE_VECTOR - ptr = _lammps_reinterpret(LAMMPS_INT, void_ptr) + ptr = _lammps_reinterpret(INT, void_ptr) return _lammps_wrap(ptr, 1, copy) end if lmp_type == TYPE_SCALAR - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _lammps_reinterpret(DOUBLE, void_ptr) return _lammps_wrap(ptr, 1, copy) end if lmp_type == TYPE_VECTOR - ndata = (style == LMP_STYLE_ATOM) ? + ndata = (style == STYLE_ATOM) ? extract_setting(lmp, "nlocal") : extract_compute(lmp, name, style, SIZE_VECTOR, copy=false)[] - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _lammps_reinterpret(DOUBLE, void_ptr) return _lammps_wrap(ptr, ndata, copy) end - ndata = (style == LMP_STYLE_ATOM) ? + ndata = (style == STYLE_ATOM) ? extract_setting(lmp, "nlocal") : extract_compute(lmp, name, style, SIZE_ROWS, copy=false)[] count = extract_compute(lmp, name, style, SIZE_COLS, copy=false)[] - ptr = _lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) + ptr = _lammps_reinterpret(DOUBLE_2D, void_ptr) return _lammps_wrap(ptr, (count, ndata), copy) end @@ -512,7 +493,7 @@ the kwarg `group` determines for which atoms the variable will be extracted. It' `VAR_ATOM` and will cause an error if used for other variable types. The entires for all atoms not in the group will be zeroed out. By default, all atoms will be extracted. """ -function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=true) +function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=true) lmp_variable != VAR_ATOM && !isnothing(group) && error("the group parameter is only supported for per atom variables!") if isnothing(group) @@ -534,7 +515,7 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr end if lmp_variable == VAR_EQUAL - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _lammps_reinterpret(DOUBLE, void_ptr) result = unsafe_load(ptr) API.lammps_free(ptr) return result @@ -545,24 +526,24 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, gr # "LMP_SIZE_VECTOR" is the only group name that won't be ignored for Vector Style Variables. # This isn't exposed to the high level API as it causes type instability for something that probably won't # ever be used outside of this implementation - ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) + ndata_ptr = _lammps_reinterpret(INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) ndata = unsafe_load(ndata_ptr) API.lammps_free(ndata_ptr) - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _lammps_reinterpret(DOUBLE, void_ptr) return _lammps_wrap(ptr, ndata, copy) end if lmp_variable == VAR_ATOM ndata = extract_setting(lmp, "nlocal") - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _lammps_reinterpret(DOUBLE, void_ptr) result = _lammps_wrap(ptr, ndata, true) API.lammps_free(ptr) return result end - ptr = _lammps_reinterpret(LAMMPS_STRING, void_ptr) + ptr = _lammps_reinterpret(STRING, void_ptr) return _lammps_string(ptr) end diff --git a/test/runtests.jl b/test/runtests.jl index 6888015..73bdb06 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,17 +23,17 @@ end create_box 1 cell """) - @test extract_global(lmp, "dt", LAMMPS_DOUBLE)[] isa Float64 - @test extract_global(lmp, "boxhi", LAMMPS_DOUBLE) == [1, 2, 3] - @test extract_global(lmp, "nlocal", LAMMPS_INT)[] == extract_setting(lmp, "nlocal") == 0 + @test extract_global(lmp, "dt", LAMMPS.DOUBLE)[] isa Float64 + @test extract_global(lmp, "boxhi", LAMMPS.DOUBLE) == [1, 2, 3] + @test extract_global(lmp, "nlocal", LAMMPS.INT)[] == extract_setting(lmp, "nlocal") == 0 - with_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT) - with_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT) + with_copy1 = extract_global(lmp, "periodicity", LAMMPS.INT) + with_copy2 = extract_global(lmp, "periodicity", LAMMPS.INT) @test pointer(with_copy1) != pointer(with_copy2) - without_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) - without_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) + without_copy1 = extract_global(lmp, "periodicity", LAMMPS.INT, copy=false) + without_copy2 = extract_global(lmp, "periodicity", LAMMPS.INT, copy=false) @test pointer(with_copy1) != pointer(with_copy2) @@ -53,15 +53,15 @@ end mass 1 1 """) - @test extract_atom(lmp, "mass", LAMMPS_DOUBLE) isa Vector{Float64} - @test extract_atom(lmp, "mass", LAMMPS_DOUBLE) == [1] + @test extract_atom(lmp, "mass", LAMMPS.DOUBLE) isa Vector{Float64} + @test extract_atom(lmp, "mass", LAMMPS.DOUBLE) == [1] - x = extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + x = extract_atom(lmp, "x", LAMMPS.DOUBLE_2D) @test size(x) == (3, 27) - @test extract_atom(lmp, "image", LAMMPS_INT) isa Vector{Int32} + @test extract_atom(lmp, "image", LAMMPS.INT) isa Vector{Int32} - @test_throws ErrorException extract_atom(lmp, "v", LAMMPS_DOUBLE) + @test_throws ErrorException extract_atom(lmp, "v", LAMMPS.DOUBLE) # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 @@ -86,22 +86,22 @@ end group odd id 1 3 5 7 """) - @test extract_variable(lmp, "var1", VAR_EQUAL) == 1.0 - @test extract_variable(lmp, "var2", VAR_STRING) == "hello" - x = extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) - x_var = extract_variable(lmp, "var3", VAR_ATOM) + @test extract_variable(lmp, "var1", LAMMPS.VAR_EQUAL) == 1.0 + @test extract_variable(lmp, "var2", LAMMPS.VAR_STRING) == "hello" + x = extract_atom(lmp, "x", LAMMPS.DOUBLE_2D) + x_var = extract_variable(lmp, "var3", LAMMPS.VAR_ATOM) @test length(x_var) == 10 @test x_var == x[1, :] - press = extract_variable(lmp, "var4", VAR_VECTOR) + press = extract_variable(lmp, "var4", LAMMPS.VAR_VECTOR) @test press isa Vector{Float64} - x_var_group = extract_variable(lmp, "var3", VAR_ATOM, "odd") + x_var_group = extract_variable(lmp, "var3", LAMMPS.VAR_ATOM, "odd") in_group = BitVector((1, 0, 1, 0, 1, 0, 1, 0, 0, 0)) @test x_var_group[in_group] == x[1, in_group] @test all(x_var_group[.!in_group] .== 0) - @test_throws ErrorException extract_variable(lmp, "var3", VAR_EQUAL) + @test_throws ErrorException extract_variable(lmp, "var3", LAMMPS.VAR_EQUAL) # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 @@ -186,16 +186,16 @@ end compute pos all property/atom x y z """) - @test extract_compute(lmp, "pos", LMP_STYLE_ATOM, TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + @test extract_compute(lmp, "pos", LAMMPS.STYLE_ATOM, LAMMPS.TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS.DOUBLE_2D) - extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR)[2] = 2 - extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR, copy=false)[3] = 3 + extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_VECTOR)[2] = 2 + extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_VECTOR, copy=false)[3] = 3 - @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) == [0.0] - @test extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] + @test extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_SCALAR) == [0.0] + @test extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] - @test_throws ErrorException extract_compute(lmp, "thermo_temp", LMP_STYLE_ATOM, TYPE_SCALAR) - @test_throws ErrorException extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_ARRAY) + @test_throws ErrorException extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_ATOM, LAMMPS.TYPE_SCALAR) + @test_throws ErrorException extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_ARRAY) # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 From 07fca32e15ff2a5be252a8d912c65ba0aaf4ad09 Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 12:26:35 +0200 Subject: [PATCH 36/47] rename constants --- src/LAMMPS.jl | 92 ++++++++++++++++++++++++++++++++---------------- test/runtests.jl | 52 +++++++++++++-------------- 2 files changed, 87 insertions(+), 57 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 8a2d5f0..018a8e0 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -4,7 +4,37 @@ include("api.jl") export LMP, command, get_natoms, extract_atom, extract_compute, extract_global, extract_setting, gather, scatter!, group_to_atom_ids, get_category_ids, - extract_variable + extract_variable, + + # _LMP_DATATYPE + LAMMPS_NONE, + LAMMPS_INT, + LAMMPS_INT_2D, + LAMMPS_DOUBLE, + LAMMPS_DOUBLE_2D, + LAMMPS_INT64, + LAMMPS_INT64_2D, + LAMMPS_STRING, + + # _LMP_TYPE + TYPE_SCALAR, + TYPE_VECTOR, + TYPE_ARRAY, + SIZE_VECTOR, + SIZE_ROWS, + SIZE_COLS, + + # _LMP_VARIABLE + VAR_EQUAL, + VAR_ATOM, + VAR_VECTOR, + VAR_STRING, + + # _LMP_STYLE_CONST + STYLE_GLOBAL, + STYLE_ATOM, + STYLE_LOCAL + using Preferences @@ -13,14 +43,14 @@ get_enum(::TypeEnum{N}) where N = N struct _LMP_DATATYPE{N} <: TypeEnum{N} end -const NONE = _LMP_DATATYPE{API.LAMMPS_NONE}() -const INT = _LMP_DATATYPE{API.LAMMPS_INT}() -const INT_2D = _LMP_DATATYPE{API.LAMMPS_INT_2D}() -const DOUBLE = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() -const DOUBLE_2D = _LMP_DATATYPE{API.LAMMPS_DOUBLE_2D}() -const INT64 = _LMP_DATATYPE{API.LAMMPS_INT64}() -const INT64_2D = _LMP_DATATYPE{API.LAMMPS_INT64_2D}() -const STRING = _LMP_DATATYPE{API.LAMMPS_STRING}() +const LAMMPS_NONE = _LMP_DATATYPE{API.LAMMPS_NONE}() +const LAMMPS_INT = _LMP_DATATYPE{API.LAMMPS_INT}() +const LAMMPS_INT_2D = _LMP_DATATYPE{API.LAMMPS_INT_2D}() +const LAMMPS_DOUBLE = _LMP_DATATYPE{API.LAMMPS_DOUBLE}() +const LAMMPS_DOUBLE_2D = _LMP_DATATYPE{API.LAMMPS_DOUBLE_2D}() +const LAMMPS_INT64 = _LMP_DATATYPE{API.LAMMPS_INT64}() +const LAMMPS_INT64_2D = _LMP_DATATYPE{API.LAMMPS_INT64_2D}() +const LAMMPS_STRING = _LMP_DATATYPE{API.LAMMPS_STRING}() struct _LMP_TYPE{N} <: TypeEnum{N} end @@ -249,16 +279,16 @@ function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T end function _lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) - T === INT && return Base.reinterpret(Ptr{Int32}, ptr) - T === INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) - T === DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) - T === DOUBLE_2D && return Base.reinterpret(Ptr{Ptr{Float64}}, ptr) - T === INT64 && return Base.reinterpret(Ptr{Int64}, ptr) - T === INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) - T === STRING && return Base.reinterpret(Ptr{UInt8}, ptr) + T === LAMMPS_INT && return Base.reinterpret(Ptr{Int32}, ptr) + T === LAMMPS_INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) + T === LAMMPS_DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) + T === LAMMPS_DOUBLE_2D && return Base.reinterpret(Ptr{Ptr{Float64}}, ptr) + T === LAMMPS_INT64 && return Base.reinterpret(Ptr{Int64}, ptr) + T === LAMMPS_INT64_2D && return Base.reinterpret(Ptr{Ptr{Int64}}, ptr) + T === LAMMPS_STRING && return Base.reinterpret(Ptr{UInt8}, ptr) end -_is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (INT_2D, DOUBLE_2D, INT64_2D) +_is_2D_datatype(lmp_dtype::_LMP_DATATYPE) = lmp_dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D) """ extract_setting(lmp::LMP, name::String)::Int32 @@ -324,7 +354,7 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::B ptr = _lammps_reinterpret(lmp_type, void_ptr) - lmp_type == STRING && return _lammps_string(ptr) + lmp_type == LAMMPS_STRING && return _lammps_string(ptr) if name in ("boxlo", "boxhi", "sublo", "subhi", "sublo_lambda", "subhi_lambda", "periodicity") length = 3 @@ -372,7 +402,7 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true ptr = _lammps_reinterpret(lmp_type, void_ptr) if name == "mass" - length = extract_global(lmp, "ntypes", INT, copy=false)[] + length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused return _lammps_wrap(ptr, length, copy) end @@ -403,9 +433,9 @@ Since computes may provide multiple kinds of data, it is required to set style a | valid values for `style`: | | :------------------------ | -| `LMP_STYLE_GLOBAL` | -| `LMP_STYLE_ATOM` | -| `LMP_STYLE_LOCAL` | +| `STYLE_GLOBAL` | +| `STYLE_ATOM` | +| `STYLE_LOCAL` | | valid values for `lmp_type`: | resulting return type: | | :--------------------------- | :--------------------- | @@ -443,12 +473,12 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty # `lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR)` causes type instability for some reason if lmp_type == SIZE_COLS || lmp_type == SIZE_ROWS || lmp_type == SIZE_VECTOR - ptr = _lammps_reinterpret(INT, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_INT, void_ptr) return _lammps_wrap(ptr, 1, copy) end if lmp_type == TYPE_SCALAR - ptr = _lammps_reinterpret(DOUBLE, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) return _lammps_wrap(ptr, 1, copy) end @@ -457,7 +487,7 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_setting(lmp, "nlocal") : extract_compute(lmp, name, style, SIZE_VECTOR, copy=false)[] - ptr = _lammps_reinterpret(DOUBLE, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) return _lammps_wrap(ptr, ndata, copy) end @@ -466,7 +496,7 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_compute(lmp, name, style, SIZE_ROWS, copy=false)[] count = extract_compute(lmp, name, style, SIZE_COLS, copy=false)[] - ptr = _lammps_reinterpret(DOUBLE_2D, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) return _lammps_wrap(ptr, (count, ndata), copy) end @@ -515,7 +545,7 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g end if lmp_variable == VAR_EQUAL - ptr = _lammps_reinterpret(DOUBLE, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) result = unsafe_load(ptr) API.lammps_free(ptr) return result @@ -526,24 +556,24 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g # "LMP_SIZE_VECTOR" is the only group name that won't be ignored for Vector Style Variables. # This isn't exposed to the high level API as it causes type instability for something that probably won't # ever be used outside of this implementation - ndata_ptr = _lammps_reinterpret(INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) + ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) ndata = unsafe_load(ndata_ptr) API.lammps_free(ndata_ptr) - ptr = _lammps_reinterpret(DOUBLE, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) return _lammps_wrap(ptr, ndata, copy) end if lmp_variable == VAR_ATOM ndata = extract_setting(lmp, "nlocal") - ptr = _lammps_reinterpret(DOUBLE, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) result = _lammps_wrap(ptr, ndata, true) API.lammps_free(ptr) return result end - ptr = _lammps_reinterpret(STRING, void_ptr) + ptr = _lammps_reinterpret(LAMMPS_STRING, void_ptr) return _lammps_string(ptr) end diff --git a/test/runtests.jl b/test/runtests.jl index 73bdb06..8d95b75 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,17 +23,17 @@ end create_box 1 cell """) - @test extract_global(lmp, "dt", LAMMPS.DOUBLE)[] isa Float64 - @test extract_global(lmp, "boxhi", LAMMPS.DOUBLE) == [1, 2, 3] - @test extract_global(lmp, "nlocal", LAMMPS.INT)[] == extract_setting(lmp, "nlocal") == 0 + @test extract_global(lmp, "dt", LAMMPS_DOUBLE)[] isa Float64 + @test extract_global(lmp, "boxhi", LAMMPS_DOUBLE) == [1, 2, 3] + @test extract_global(lmp, "nlocal", LAMMPS_INT)[] == extract_setting(lmp, "nlocal") == 0 - with_copy1 = extract_global(lmp, "periodicity", LAMMPS.INT) - with_copy2 = extract_global(lmp, "periodicity", LAMMPS.INT) + with_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT) + with_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT) @test pointer(with_copy1) != pointer(with_copy2) - without_copy1 = extract_global(lmp, "periodicity", LAMMPS.INT, copy=false) - without_copy2 = extract_global(lmp, "periodicity", LAMMPS.INT, copy=false) + without_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) + without_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=false) @test pointer(with_copy1) != pointer(with_copy2) @@ -53,15 +53,15 @@ end mass 1 1 """) - @test extract_atom(lmp, "mass", LAMMPS.DOUBLE) isa Vector{Float64} - @test extract_atom(lmp, "mass", LAMMPS.DOUBLE) == [1] + @test extract_atom(lmp, "mass", LAMMPS_DOUBLE) isa Vector{Float64} + @test extract_atom(lmp, "mass", LAMMPS_DOUBLE) == [1] - x = extract_atom(lmp, "x", LAMMPS.DOUBLE_2D) + x = extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) @test size(x) == (3, 27) - @test extract_atom(lmp, "image", LAMMPS.INT) isa Vector{Int32} + @test extract_atom(lmp, "image", LAMMPS_INT) isa Vector{Int32} - @test_throws ErrorException extract_atom(lmp, "v", LAMMPS.DOUBLE) + @test_throws ErrorException extract_atom(lmp, "v", LAMMPS_DOUBLE) # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 @@ -86,22 +86,22 @@ end group odd id 1 3 5 7 """) - @test extract_variable(lmp, "var1", LAMMPS.VAR_EQUAL) == 1.0 - @test extract_variable(lmp, "var2", LAMMPS.VAR_STRING) == "hello" - x = extract_atom(lmp, "x", LAMMPS.DOUBLE_2D) - x_var = extract_variable(lmp, "var3", LAMMPS.VAR_ATOM) + @test extract_variable(lmp, "var1", VAR_EQUAL) == 1.0 + @test extract_variable(lmp, "var2", VAR_STRING) == "hello" + x = extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) + x_var = extract_variable(lmp, "var3", VAR_ATOM) @test length(x_var) == 10 @test x_var == x[1, :] - press = extract_variable(lmp, "var4", LAMMPS.VAR_VECTOR) + press = extract_variable(lmp, "var4", VAR_VECTOR) @test press isa Vector{Float64} - x_var_group = extract_variable(lmp, "var3", LAMMPS.VAR_ATOM, "odd") + x_var_group = extract_variable(lmp, "var3", VAR_ATOM, "odd") in_group = BitVector((1, 0, 1, 0, 1, 0, 1, 0, 0, 0)) @test x_var_group[in_group] == x[1, in_group] @test all(x_var_group[.!in_group] .== 0) - @test_throws ErrorException extract_variable(lmp, "var3", LAMMPS.VAR_EQUAL) + @test_throws ErrorException extract_variable(lmp, "var3", VAR_EQUAL) # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 @@ -186,16 +186,16 @@ end compute pos all property/atom x y z """) - @test extract_compute(lmp, "pos", LAMMPS.STYLE_ATOM, LAMMPS.TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS.DOUBLE_2D) + @test extract_compute(lmp, "pos", STYLE_ATOM, TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) - extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_VECTOR)[2] = 2 - extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_VECTOR, copy=false)[3] = 3 + extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_VECTOR)[2] = 2 + extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_VECTOR, copy=false)[3] = 3 - @test extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_SCALAR) == [0.0] - @test extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] + @test extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_SCALAR) == [0.0] + @test extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_VECTOR) == [0.0, 0.0, 3.0, 0.0, 0.0, 0.0] - @test_throws ErrorException extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_ATOM, LAMMPS.TYPE_SCALAR) - @test_throws ErrorException extract_compute(lmp, "thermo_temp", LAMMPS.STYLE_GLOBAL, LAMMPS.TYPE_ARRAY) + @test_throws ErrorException extract_compute(lmp, "thermo_temp", STYLE_ATOM, TYPE_SCALAR) + @test_throws ErrorException extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_ARRAY) # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 From 7edc82a7616728cd37aa268dfb836652d435eb06 Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 12:42:05 +0200 Subject: [PATCH 37/47] seperate _lammps_wrap and _lammps_copy --- src/LAMMPS.jl | 65 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 018a8e0..0fdb616 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -250,32 +250,45 @@ function _lammps_string(ptr::Ptr) return Base.unsafe_string(ptr) end -function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer, copy=true) +function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer) ptr == C_NULL && error("Wrapping NULL-pointer!") - - result = Base.unsafe_wrap(Array, ptr, shape, own=false) - - return copy ? Base.copy(result) : result + return Base.unsafe_wrap(Array, ptr, shape, own=false) end -function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}, copy=true) where T +function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}) where T ptr == C_NULL && error("Wrapping NULL-pointer!") - (count, ndata) = shape + shape[2] == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped - ndata == 0 && return Matrix{T}(undef, count, ndata) # There is no data that can be wrapped + # TODO: implement a debug mode to do this assert + # pointers = Base.unsafe_wrap(Array, ptr, ndata) + # @assert all(diff(pointers) .== count*sizeof(T)) - pointers = Base.unsafe_wrap(Array, ptr, ndata) + # If the pointers are evenly spaced, we can simply use the first pointer to wrap our matrix. + first_pointer = unsafe_load(ptr) + return Base.unsafe_wrap(Array, first_pointer, shape, own=false) - # This assert verifies that all the pointers are evenly spaced according to count. - # While it seems like this is allways the case, it's not explicitly stated in the - # API documentation. It also helps to verify, that the count is correct. - @assert all(diff(pointers) .== count*sizeof(T)) +end + +function _lammps_copy(ptr::Ptr{T}, shape::Integer) where T <: Real + ptr == C_NULL && error("Copying NULL-pointer!") + return [unsafe_load(ptr, i) for i in 1:shape] +end - # It the pointers are evenly spaced, we can simply use the first pointer to wrap our matrix. - result = Base.unsafe_wrap(Array, pointers[1], shape, own=false) +function _lammps_copy(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}) where T + ptr == C_NULL && error("Copying NULL-pointer!") + + shape[2] == 0 && return Matrix{T}(undef, shape) + + # If the pointers are evenly spaced, we can simply use the first pointer to read our matrix. + first_ptr = unsafe_load(ptr) + result = Matrix{T}(undef, shape) + + for i in 1:prod(shape) + @inbounds result[i] = unsafe_load(first_ptr, i) + end - return copy ? Base.copy(result) : result + return result end function _lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) @@ -364,7 +377,7 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::B length = 1 end - return _lammps_wrap(ptr, length, copy) + return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) end function extract_global_datatype(lmp::LMP, name) @@ -404,7 +417,7 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true if name == "mass" length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused - return _lammps_wrap(ptr, length, copy) + return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) end length = extract_setting(lmp, "nlocal") @@ -414,10 +427,10 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true # length is a Int32 and lammps_wrap expects a NTuple, so it's # neccecary to use Int32 for count as well count = name == "quat" ? Int32(4) : Int32(3) - return _lammps_wrap(ptr, (count, length), copy) + return copy ? _lammps_copy(ptr, (count, length)) : _lammps_wrap(ptr, (count, length)) end - return _lammps_wrap(ptr, length, copy) + return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) end function extract_atom_datatype(lmp::LMP, name) @@ -474,12 +487,12 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty # `lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR)` causes type instability for some reason if lmp_type == SIZE_COLS || lmp_type == SIZE_ROWS || lmp_type == SIZE_VECTOR ptr = _lammps_reinterpret(LAMMPS_INT, void_ptr) - return _lammps_wrap(ptr, 1, copy) + return copy ? _lammps_copy(ptr, 1) : _lammps_wrap(ptr, 1) end if lmp_type == TYPE_SCALAR ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return _lammps_wrap(ptr, 1, copy) + return copy ? _lammps_copy(ptr, 1) : _lammps_wrap(ptr, 1) end if lmp_type == TYPE_VECTOR @@ -488,7 +501,7 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_compute(lmp, name, style, SIZE_VECTOR, copy=false)[] ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return _lammps_wrap(ptr, ndata, copy) + return copy ? _lammps_copy(ptr, ndata) : _lammps_wrap(ptr, ndata) end ndata = (style == STYLE_ATOM) ? @@ -498,7 +511,7 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty count = extract_compute(lmp, name, style, SIZE_COLS, copy=false)[] ptr = _lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) - return _lammps_wrap(ptr, (count, ndata), copy) + return copy ? _lammps_copy(ptr, (count, ndata)) : _lammps_wrap(ptr, (count, ndata)) end """ @@ -561,14 +574,14 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g API.lammps_free(ndata_ptr) ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return _lammps_wrap(ptr, ndata, copy) + return copy ? _lammps_copy(ptr, ndata) : _lammps_wrap(ptr, ndata) end if lmp_variable == VAR_ATOM ndata = extract_setting(lmp, "nlocal") ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - result = _lammps_wrap(ptr, ndata, true) + result = _lammps_copy(ptr, ndata) API.lammps_free(ptr) return result end From a2b99fa5145d57cb4588d0743b41435d6ad6620e Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 12:59:15 +0200 Subject: [PATCH 38/47] take ownership of copied data instead of copying twice --- src/LAMMPS.jl | 14 ++++++-------- test/runtests.jl | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 0fdb616..e3fb06b 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -250,12 +250,12 @@ function _lammps_string(ptr::Ptr) return Base.unsafe_string(ptr) end -function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer) +function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer; own=false) ptr == C_NULL && error("Wrapping NULL-pointer!") - return Base.unsafe_wrap(Array, ptr, shape, own=false) + return Base.unsafe_wrap(Array, ptr, shape, own=own) end -function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}) where T +function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; own=false) where T ptr == C_NULL && error("Wrapping NULL-pointer!") shape[2] == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped @@ -266,7 +266,7 @@ function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}) where T # If the pointers are evenly spaced, we can simply use the first pointer to wrap our matrix. first_pointer = unsafe_load(ptr) - return Base.unsafe_wrap(Array, first_pointer, shape, own=false) + return Base.unsafe_wrap(Array, first_pointer, shape, own=own) end @@ -579,11 +579,9 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g if lmp_variable == VAR_ATOM ndata = extract_setting(lmp, "nlocal") - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - result = _lammps_copy(ptr, ndata) - API.lammps_free(ptr) - return result + # lammps expects us to take ownership of the data + return _lammps_wrap(ptr, ndata; own=true) end ptr = _lammps_reinterpret(LAMMPS_STRING, void_ptr) diff --git a/test/runtests.jl b/test/runtests.jl index 8d95b75..257c5e7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -106,6 +106,26 @@ end # verify that no errors were missed @test LAMMPS.API.lammps_has_error(lmp) == 0 end + + # check if the memory allocated by LAMMPS persists after closing the instance + lmp = LMP(["-screen", "none"]) + command(lmp, """ + atom_modify map yes + region cell block 0 3 0 3 0 3 + create_box 1 cell + lattice sc 1 + create_atoms 1 region cell + mass 1 1 + + variable var atom id + """) + + var = extract_variable(lmp, "var", VAR_ATOM) + var_copy = copy(var) + LAMMPS.close!(lmp) + + @test var == var_copy + end @testset "gather/scatter" begin From 7fee776b87b0513594cf4daf25052f184af5ecbc Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 13:30:59 +0200 Subject: [PATCH 39/47] don't copy by default for extract methods --- src/LAMMPS.jl | 50 ++++++++++++++++++++++++++++-------------------- test/runtests.jl | 6 +++--- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index e3fb06b..9b903d5 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -330,7 +330,7 @@ function extract_setting(lmp::LMP, name::String)::Int32 end """ - extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::Bool=true) + extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::Bool=false) Extract a global property from a LAMMPS instance. @@ -344,12 +344,13 @@ Extract a global property from a LAMMPS instance. | `LAMMPS_INT64_2D` | `Matrix{Int64}` | | `LAMMPS_STRING` | `String` (allways a copy) | -the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. -the pointer to the underlying data is generally persistent, unless a clear command is issued. -However it's still recommended to only disable this, if you wish to modify the internal state of the LAMMPS instance. - Scalar values get returned as a vector with a single element. This way it's possible to modify the internal state of the LAMMPS instance even if the data is scalar. + +!!! info + Closing the LAMMPS instance or issuing a clear command after calling this method + will result in the returned data becoming invalid. To prevent this, set `copy=true`. + !!! warning Modifying the data through `extract_global` may lead to inconsistent internal data and thus may cause failures or crashes or bogus simulations. In general it is thus usually better to use a LAMMPS input command that sets or changes these parameters. @@ -357,7 +358,7 @@ modify the internal state of the LAMMPS instance even if the data is scalar. A full list of global variables can be found here: """ -function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::Bool=true) +function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::Bool=false) void_ptr = API.lammps_extract_global(lmp, name) void_ptr == C_NULL && error("Unknown global variable $name") @@ -385,7 +386,7 @@ function extract_global_datatype(lmp::LMP, name) end """ - extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) + extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=false) Extract per-atom data from the lammps instance. @@ -402,9 +403,15 @@ the kwarg `copy`, which defaults to true, determies wheter a copy of the underly As the pointer to the underlying data is not persistent, it's highly recommended to only disable this, if you wish to modify the internal state of the LAMMPS instance. +!!! info + The returned data may become invalid if a re-neighboring operation + is triggered at any point after calling this method. If this has happened, + trying to read from this data will likely cause julia to crash. + To prevent this, set `copy=true`. + A table with suported name keywords can be found here: """ -function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true) +function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=false) void_ptr = API.lammps_extract_atom(lmp, name) void_ptr == C_NULL && error("Unknown per-atom variable $name") @@ -415,7 +422,7 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=true ptr = _lammps_reinterpret(lmp_type, void_ptr) if name == "mass" - length = extract_global(lmp, "ntypes", LAMMPS_INT, copy=false)[] + length = extract_global(lmp, "ntypes", LAMMPS_INT)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) end @@ -459,18 +466,19 @@ Since computes may provide multiple kinds of data, it is required to set style a | `SIZE_COLS` | `Vector{Int32}` | | `SIZE_ROWS` | `Vector{Int32}` | -the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. -As the pointer to the underlying data is not persistent, it's highly recommended to only disable this, -if you wish to modify the internal state of the LAMMPS instance. - Scalar values get returned as a vector with a single element. This way it's possible to modify the internal state of the LAMMPS instance even if the data is scalar. +!!! info + The returned data may become invalid as soon as another LAMMPS command has been issued at any point after calling this method. + If this has happened, trying to read from this data will likely cause julia to crash. + To prevent this, set `copy=true`. + # Examples ```julia LMP(["-screen", "none"]) do lmp - extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR)[2] = 2 + extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR, copy=true)[2] = 2 extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_VECTOR, copy=false)[3] = 3 extract_compute(lmp, "thermo_temp", LMP_STYLE_GLOBAL, TYPE_SCALAR) |> println # [0.0] @@ -478,7 +486,7 @@ modify the internal state of the LAMMPS instance even if the data is scalar. end ``` """ -function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy::Bool=true) +function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_type::_LMP_TYPE; copy::Bool=false) API.lammps_has_id(lmp, "compute", name) != 1 && error("Unknown compute $name") void_ptr = API.lammps_extract_compute(lmp, name, style, get_enum(lmp_type)) @@ -498,7 +506,7 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty if lmp_type == TYPE_VECTOR ndata = (style == STYLE_ATOM) ? extract_setting(lmp, "nlocal") : - extract_compute(lmp, name, style, SIZE_VECTOR, copy=false)[] + extract_compute(lmp, name, style, SIZE_VECTOR)[] ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) return copy ? _lammps_copy(ptr, ndata) : _lammps_wrap(ptr, ndata) @@ -506,16 +514,16 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty ndata = (style == STYLE_ATOM) ? extract_setting(lmp, "nlocal") : - extract_compute(lmp, name, style, SIZE_ROWS, copy=false)[] + extract_compute(lmp, name, style, SIZE_ROWS)[] - count = extract_compute(lmp, name, style, SIZE_COLS, copy=false)[] + count = extract_compute(lmp, name, style, SIZE_COLS)[] ptr = _lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) return copy ? _lammps_copy(ptr, (count, ndata)) : _lammps_wrap(ptr, (count, ndata)) end """ - extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=true) + extract_variable(lmp::LMP, name::String, lmp_variable::LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=false) Extracts the data from a LAMMPS variable. When the variable is either an `equal`-style compatible variable, a `vector`-style variable, or an `atom`-style variable, the variable is evaluated and the corresponding value(s) returned. @@ -529,14 +537,14 @@ For other variable styles, their string value is returned. | `VAR_STRING` | `String` | | `VAR_VECTOR` | `Vector{Float64}` | -the kwarg `copy`, which defaults to true, determies wheter a copy of the underlying data is made. +the kwarg `copy` determies wheter a copy of the underlying data is made. `copy` is only aplicable for `VAR_VECTOR`. For all other variable types, a copy will be made regardless. the kwarg `group` determines for which atoms the variable will be extracted. It's only aplicable for `VAR_ATOM` and will cause an error if used for other variable types. The entires for all atoms not in the group will be zeroed out. By default, all atoms will be extracted. """ -function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=true) +function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, group::Union{String, Nothing}=nothing; copy::Bool=false) lmp_variable != VAR_ATOM && !isnothing(group) && error("the group parameter is only supported for per atom variables!") if isnothing(group) diff --git a/test/runtests.jl b/test/runtests.jl index 257c5e7..267cfcc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -27,8 +27,8 @@ end @test extract_global(lmp, "boxhi", LAMMPS_DOUBLE) == [1, 2, 3] @test extract_global(lmp, "nlocal", LAMMPS_INT)[] == extract_setting(lmp, "nlocal") == 0 - with_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT) - with_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT) + with_copy1 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=true) + with_copy2 = extract_global(lmp, "periodicity", LAMMPS_INT, copy=true) @test pointer(with_copy1) != pointer(with_copy2) @@ -208,7 +208,7 @@ end @test extract_compute(lmp, "pos", STYLE_ATOM, TYPE_ARRAY) == extract_atom(lmp, "x", LAMMPS_DOUBLE_2D) - extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_VECTOR)[2] = 2 + extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_VECTOR, copy=true)[2] = 2 extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_VECTOR, copy=false)[3] = 3 @test extract_compute(lmp, "thermo_temp", STYLE_GLOBAL, TYPE_SCALAR) == [0.0] From 6cca56b8a000e42f61d5a6bdf4a3f038b3f21f73 Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 13:43:06 +0200 Subject: [PATCH 40/47] use KeyError / ArgumentError over generic error --- src/LAMMPS.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 9b903d5..29663fe 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -360,7 +360,7 @@ A full list of global variables can be found here: Date: Sun, 30 Jun 2024 13:46:48 +0200 Subject: [PATCH 41/47] version bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 31ab158..305ad92 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "LAMMPS" uuid = "ee2e13b9-eee9-4449-aafa-cfa6a2dbe14d" authors = ["Valentin Churavy "] -version = "0.4.2" +version = "0.5.0" [deps] CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" From 0d0b6649476764a09b05394b275e689909d8e554 Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 14:28:33 +0200 Subject: [PATCH 42/47] fixup! example --- examples/lj_forces.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lj_forces.jl b/examples/lj_forces.jl index 6087aca..1374552 100644 --- a/examples/lj_forces.jl +++ b/examples/lj_forces.jl @@ -43,4 +43,4 @@ command(lmp, "run 0") # extract output forces = gather(lmp, "f", Float64) -energies = extract_compute(lmp, "pot_e", LMP_STYLE_GLOBAL, TYPE_SCALAR) \ No newline at end of file +energies = extract_compute(lmp, "pot_e", STYLE_GLOBAL, TYPE_SCALAR) \ No newline at end of file From 238eedf03aa35cce877a8c140549ef2fb1a554d6 Mon Sep 17 00:00:00 2001 From: Joroks Date: Sun, 30 Jun 2024 17:16:57 +0200 Subject: [PATCH 43/47] naming and combine copy and wrap into extract --- src/LAMMPS.jl | 82 ++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 29663fe..b50ca50 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -245,17 +245,19 @@ function get_natoms(lmp::LMP) Int64(API.lammps_get_natoms(lmp)) end -function _lammps_string(ptr::Ptr) +function _string(ptr::Ptr) ptr == C_NULL && error("Wrapping NULL-pointer!") return Base.unsafe_string(ptr) end -function _lammps_wrap(ptr::Ptr{<:Real}, shape::Integer; own=false) +function _extract(ptr::Ptr{<:Real}, shape::Integer; copy=false, own=false) ptr == C_NULL && error("Wrapping NULL-pointer!") - return Base.unsafe_wrap(Array, ptr, shape, own=own) + result = Base.unsafe_wrap(Array, ptr, shape, own=own) + + return copy ? Base.copy(result) : result end -function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; own=false) where T +function _extract(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; copy=false, own=false) where T ptr == C_NULL && error("Wrapping NULL-pointer!") shape[2] == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped @@ -266,32 +268,12 @@ function _lammps_wrap(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; own=false) where T # If the pointers are evenly spaced, we can simply use the first pointer to wrap our matrix. first_pointer = unsafe_load(ptr) - return Base.unsafe_wrap(Array, first_pointer, shape, own=own) - -end - -function _lammps_copy(ptr::Ptr{T}, shape::Integer) where T <: Real - ptr == C_NULL && error("Copying NULL-pointer!") - return [unsafe_load(ptr, i) for i in 1:shape] -end - -function _lammps_copy(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}) where T - ptr == C_NULL && error("Copying NULL-pointer!") - - shape[2] == 0 && return Matrix{T}(undef, shape) - - # If the pointers are evenly spaced, we can simply use the first pointer to read our matrix. - first_ptr = unsafe_load(ptr) - result = Matrix{T}(undef, shape) - - for i in 1:prod(shape) - @inbounds result[i] = unsafe_load(first_ptr, i) - end + result = Base.unsafe_wrap(Array, first_pointer, shape, own=own) - return result + return copy ? Base.copy(result) : result end -function _lammps_reinterpret(T::_LMP_DATATYPE, ptr::Ptr) +function _reinterpret(T::_LMP_DATATYPE, ptr::Ptr) T === LAMMPS_INT && return Base.reinterpret(Ptr{Int32}, ptr) T === LAMMPS_INT_2D && return Base.reinterpret(Ptr{Ptr{Int32}}, ptr) T === LAMMPS_DOUBLE && return Base.reinterpret(Ptr{Float64}, ptr) @@ -366,9 +348,9 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::B receive = get_enum(lmp_type) expect != receive && error("TypeMismatch: Expected $expect got $receive instead!") - ptr = _lammps_reinterpret(lmp_type, void_ptr) + ptr = _reinterpret(lmp_type, void_ptr) - lmp_type == LAMMPS_STRING && return _lammps_string(ptr) + lmp_type == LAMMPS_STRING && return _string(ptr) if name in ("boxlo", "boxhi", "sublo", "subhi", "sublo_lambda", "subhi_lambda", "periodicity") length = 3 @@ -378,7 +360,7 @@ function extract_global(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy::B length = 1 end - return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) + return _extract(ptr, length; copy=copy) end function extract_global_datatype(lmp::LMP, name) @@ -419,12 +401,12 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=fals receive = get_enum(lmp_type) expect != receive && error("TypeMismatch: Expected $expect got $receive instead!") - ptr = _lammps_reinterpret(lmp_type, void_ptr) + ptr = _reinterpret(lmp_type, void_ptr) if name == "mass" length = extract_global(lmp, "ntypes", LAMMPS_INT)[] ptr += sizeof(eltype(ptr)) # Scarry pointer arithemtic; The first entry in the array is unused - return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) + return _extract(ptr, length; copy=copy) end length = extract_setting(lmp, "nlocal") @@ -434,10 +416,10 @@ function extract_atom(lmp::LMP, name::String, lmp_type::_LMP_DATATYPE; copy=fals # length is a Int32 and lammps_wrap expects a NTuple, so it's # neccecary to use Int32 for count as well count = name == "quat" ? Int32(4) : Int32(3) - return copy ? _lammps_copy(ptr, (count, length)) : _lammps_wrap(ptr, (count, length)) + return _extract(ptr, (count, length); copy=copy) end - return copy ? _lammps_copy(ptr, length) : _lammps_wrap(ptr, length) + return _extract(ptr, length; copy=copy) end function extract_atom_datatype(lmp::LMP, name) @@ -494,13 +476,13 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty # `lmp_type in (SIZE_COLS, SIZE_ROWS, SIZE_VECTOR)` causes type instability for some reason if lmp_type == SIZE_COLS || lmp_type == SIZE_ROWS || lmp_type == SIZE_VECTOR - ptr = _lammps_reinterpret(LAMMPS_INT, void_ptr) - return copy ? _lammps_copy(ptr, 1) : _lammps_wrap(ptr, 1) + ptr = _reinterpret(LAMMPS_INT, void_ptr) + return _extract(ptr, 1; copy=copy) end if lmp_type == TYPE_SCALAR - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return copy ? _lammps_copy(ptr, 1) : _lammps_wrap(ptr, 1) + ptr = _reinterpret(LAMMPS_DOUBLE, void_ptr) + return _extract(ptr, 1; copy=copy) end if lmp_type == TYPE_VECTOR @@ -508,8 +490,8 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_setting(lmp, "nlocal") : extract_compute(lmp, name, style, SIZE_VECTOR)[] - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return copy ? _lammps_copy(ptr, ndata) : _lammps_wrap(ptr, ndata) + ptr = _reinterpret(LAMMPS_DOUBLE, void_ptr) + return _extract(ptr, ndata; copy=copy) end ndata = (style == STYLE_ATOM) ? @@ -517,9 +499,9 @@ function extract_compute(lmp::LMP, name::String, style::_LMP_STYLE_CONST, lmp_ty extract_compute(lmp, name, style, SIZE_ROWS)[] count = extract_compute(lmp, name, style, SIZE_COLS)[] - ptr = _lammps_reinterpret(LAMMPS_DOUBLE_2D, void_ptr) + ptr = _reinterpret(LAMMPS_DOUBLE_2D, void_ptr) - return copy ? _lammps_copy(ptr, (count, ndata)) : _lammps_wrap(ptr, (count, ndata)) + return _extract(ptr, (count, ndata); copy=copy) end """ @@ -566,7 +548,7 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g end if lmp_variable == VAR_EQUAL - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _reinterpret(LAMMPS_DOUBLE, void_ptr) result = unsafe_load(ptr) API.lammps_free(ptr) return result @@ -577,23 +559,23 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g # "LMP_SIZE_VECTOR" is the only group name that won't be ignored for Vector Style Variables. # This isn't exposed to the high level API as it causes type instability for something that probably won't # ever be used outside of this implementation - ndata_ptr = _lammps_reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) + ndata_ptr = _reinterpret(LAMMPS_INT, API.lammps_extract_variable(lmp, name, "LMP_SIZE_VECTOR")) ndata = unsafe_load(ndata_ptr) API.lammps_free(ndata_ptr) - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) - return copy ? _lammps_copy(ptr, ndata) : _lammps_wrap(ptr, ndata) + ptr = _reinterpret(LAMMPS_DOUBLE, void_ptr) + return _extract(ptr, ndata; copy=copy) end if lmp_variable == VAR_ATOM ndata = extract_setting(lmp, "nlocal") - ptr = _lammps_reinterpret(LAMMPS_DOUBLE, void_ptr) + ptr = _reinterpret(LAMMPS_DOUBLE, void_ptr) # lammps expects us to take ownership of the data - return _lammps_wrap(ptr, ndata; own=true) + return _extract(ptr, ndata; own=true) end - ptr = _lammps_reinterpret(LAMMPS_STRING, void_ptr) - return _lammps_string(ptr) + ptr = _reinterpret(LAMMPS_STRING, void_ptr) + return _string(ptr) end function extract_variable_datatype(lmp::LMP, name) From 4ea5f761c78c815003c928bf2ecbb50acb4b56d7 Mon Sep 17 00:00:00 2001 From: Joroks <32484985+Joroks@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:33:15 +0200 Subject: [PATCH 44/47] apply suggestion from review Co-authored-by: Valentin Churavy --- src/LAMMPS.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index b50ca50..95be205 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -260,7 +260,7 @@ end function _extract(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; copy=false, own=false) where T ptr == C_NULL && error("Wrapping NULL-pointer!") - shape[2] == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped + prod(shape) == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped # TODO: implement a debug mode to do this assert # pointers = Base.unsafe_wrap(Array, ptr, ndata) From 540dffb8c5805d9ab59a6d3d30902d022e94b438 Mon Sep 17 00:00:00 2001 From: Joroks Date: Mon, 1 Jul 2024 00:28:09 +0200 Subject: [PATCH 45/47] register lammps_free as finalizer --- src/LAMMPS.jl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 95be205..c7f1616 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -252,12 +252,16 @@ end function _extract(ptr::Ptr{<:Real}, shape::Integer; copy=false, own=false) ptr == C_NULL && error("Wrapping NULL-pointer!") - result = Base.unsafe_wrap(Array, ptr, shape, own=own) + result = Base.unsafe_wrap(Array, ptr, shape; own=false) + + if own + finalizer(API.lammps_free, result) + end return copy ? Base.copy(result) : result end -function _extract(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; copy=false, own=false) where T +function _extract(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; copy=false) where T ptr == C_NULL && error("Wrapping NULL-pointer!") prod(shape) == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped @@ -268,8 +272,7 @@ function _extract(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; copy=false, own=false) w # If the pointers are evenly spaced, we can simply use the first pointer to wrap our matrix. first_pointer = unsafe_load(ptr) - result = Base.unsafe_wrap(Array, first_pointer, shape, own=own) - + result = Base.unsafe_wrap(Array, first_pointer, shape; own=false) return copy ? Base.copy(result) : result end From 933f8ff3b9a2ec280ae62fe4a86c89b460b7deef Mon Sep 17 00:00:00 2001 From: Joroks Date: Mon, 1 Jul 2024 09:11:15 +0200 Subject: [PATCH 46/47] future-proof for julia v1.11 --- src/LAMMPS.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index c7f1616..74adbc3 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -255,13 +255,19 @@ function _extract(ptr::Ptr{<:Real}, shape::Integer; copy=false, own=false) result = Base.unsafe_wrap(Array, ptr, shape; own=false) if own - finalizer(API.lammps_free, result) + @static if VERSION >= v"1.11-dev" + finalizer(API.lammps_free, result.ref.mem) + else + finalizer(API.lammps_free, result) + end end return copy ? Base.copy(result) : result end function _extract(ptr::Ptr{<:Ptr{T}}, shape::NTuple{2}; copy=false) where T + # The `own` kwarg is not implemented for 2D data, as this is currently not used anywhere + ptr == C_NULL && error("Wrapping NULL-pointer!") prod(shape) == 0 && return Matrix{T}(undef, shape) # There is no data that can be wrapped From b7921716f2ed05fb82e4dc1e020c08954db5754f Mon Sep 17 00:00:00 2001 From: Joroks Date: Mon, 1 Jul 2024 14:42:45 +0200 Subject: [PATCH 47/47] Apply suggestions from code review --- src/LAMMPS.jl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/LAMMPS.jl b/src/LAMMPS.jl index 74adbc3..2cb2502 100644 --- a/src/LAMMPS.jl +++ b/src/LAMMPS.jl @@ -254,6 +254,12 @@ function _extract(ptr::Ptr{<:Real}, shape::Integer; copy=false, own=false) ptr == C_NULL && error("Wrapping NULL-pointer!") result = Base.unsafe_wrap(Array, ptr, shape; own=false) + if own && copy + result_copy = Base.copy(result) + API.lammps_free(result) + return result_copy + end + if own @static if VERSION >= v"1.11-dev" finalizer(API.lammps_free, result.ref.mem) @@ -529,7 +535,11 @@ For other variable styles, their string value is returned. | `VAR_VECTOR` | `Vector{Float64}` | the kwarg `copy` determies wheter a copy of the underlying data is made. -`copy` is only aplicable for `VAR_VECTOR`. For all other variable types, a copy will be made regardless. +`copy` is only aplicable for `VAR_VECTOR` and `VAR_ATOM`. For all other variable types, a copy will be made regardless. +The underlying LAMMPS API call for `VAR_ATOM` internally allways creates a copy of the data. As the memory for this gets allocated by LAMMPS instead of julia, +it needs to be dereferenced using `LAMMPS.API.lammps_free` instead of through the garbage collector. +If `copy=false` this gets acieved by registering `LAMMPS.API.lammps_free` as a finalizer for the returned data. +Alternatively, setting `copy=true` will instead create a new copy of the data. The lammps allocated block of memory will then be freed immediately. the kwarg `group` determines for which atoms the variable will be extracted. It's only aplicable for `VAR_ATOM` and will cause an error if used for other variable types. The entires for all atoms not in the group @@ -580,7 +590,7 @@ function extract_variable(lmp::LMP, name::String, lmp_variable::_LMP_VARIABLE, g ndata = extract_setting(lmp, "nlocal") ptr = _reinterpret(LAMMPS_DOUBLE, void_ptr) # lammps expects us to take ownership of the data - return _extract(ptr, ndata; own=true) + return _extract(ptr, ndata; copy=copy, own=true) end ptr = _reinterpret(LAMMPS_STRING, void_ptr)