Closed ejmeitz closed 1 year ago
Patch coverage: 88.23
% and no project coverage change.
Comparison is base (
5cf4d29
) 72.82% compared to head (660a66c
) 72.82%.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.
@jgreener64 if you get the chance could you look at the crystal constructor I added to types.jl and help me fix the type instability? I think the instability is because of the Boundary if-else block but I'm not really sure how to fix that as the Boundary is something that is different depending on what crystal gets passed (e.g. cubic for FCC triclinic for others)
Probably can just dispatch on the type somehow but then Ill have like 4 crystal constructors
What do you mean by type instability in this context? Can you give example code that shows it?
Yeah for sure, below is an example of constructing a system from a 4x4x4 unit-cell FCC system. The construction of the System struct is type-unstable and when I call simulate!() with this struct the run-times are much slower than I expect. The results of the simulation are what I expect but the output from @code_warntype on the construction of the System indicates that the compiler cannot assign concrete types to everything. This will break auto-diff break and slow down execution.
using Molly
using SimpleCrystals
fcc_crystal = FCC(5.4u"Å", :Ar, SVector(4,4,4))
n_atoms = length(fcc_crystal)
atom_mass = atomic_mass(fcc_crystal,1)
velocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]
sys = @code_warntype System(
fcc_crystal,
velocities=velocities,
pairwise_inters=(LennardJones(),),
loggers=(ke=KineticEnergyLogger(10),
pe=PotentialEnergyLogger(10)),
energy_units = u"kJ * mol^-1",
force_units = u"kJ * mol^-1 * Å^-1")
If you actually want this code to run with LJ you have to use that extra convenience constructor and modify all the atoms in the system to have the proper sigma and epsilon values.
σ = 3.4u"Å"
ϵ = (4.184*0.24037)u"kJ * mol^-1"
updated_atoms = []
for i in range(1,length(sys.atoms))
push!(updated_atoms, Molly.Atom(index = sys.atoms[i].index, charge = sys.atoms[i].charge,
mass = sys.atoms[i].mass, σ = σ, ϵ = ϵ, solute = sys.atoms[i].solute))
end
sys = System(sys, atoms = updated_atoms)
The constructor itself can be type unstable since it is only called once at the start. The concern is what is returned, i.e. typeof(sys)
. In the first case this is concrete so all is well - here this can be checked by seeing that all the type parameters in typeof(sys)
are concrete.
The problem is that updated_atoms = []
creates a Vector{Any}
which is not concrete. A quick fix is sys = System(sys, atoms = [updated_atoms...])
.
Cool yeah
[updated_atoms...]
fixed the call to simulate! which is what matters. I guess I'm unhappy now that the user has to avoid that pitfall or will suffer 50% performance loss.
The interface works from the few systems I've ran but I still am not satisfied with the extra work/knowledge put onto the user with the current implementation. The code 2 comments above is basically what you will have to do every time. I'm open to suggestions of how I could change SimpleCrystals or the code I've added to Molly. For now my only idea requires breaking changes to Molly (moving $\sigma$ and $\epsilon$ and similar params to potential definitions).
σ and ϵ only need to be set in cases where they will be used, i.e. there is a Lennard Jones or similar interaction. I am less familiar with these systems - how often would a Lennard Jones-type interaction be used when simulating? Always, sometimes or never?
Moving the parameters to the interactions is probably the right way to go long term but requires a lot of changes and careful thought about how to not make setup harder in other contexts.
I use LJ to test that the system set up is correct. As far as giving accurate properties LJ is really only good for Argon, but I know lots of people use it to study phenomenological things because it is the cheapest potential to evaluate.
However useful LJ is, other potentials like Buckingham require atom properties (A_ij, B_ij) as well which are not even a part of the base Atom struct. I'd be willing to undertake this change as I think long term it is the correct thing, but it might require changing how interactions are defined (I have to look more into this). Right now mixing rules are automatically calculated I know we have InteractionList2Atoms
etc. which I've never used.
I think there should be limited breaking changes but lots of internal changes.
I'm not going to stop you giving it a go, but I would warn you that it is really a lot of work and probably not worth it just to make the above setup process easier. There are considerations about differentiable simulations and GPU performance, for example. It might be better to just document the setup procedure clearly.
The lack of properties for Buckingham potentials etc. is an issue but it is possible to use a custom atom type without a performance penalty, one is used in the tests for Buckingham:https://github.com/JuliaMolSim/Molly.jl/blob/2c0631401a2b51d5215308fc1d877456b8f60411/test/interactions.jl#L80-L85
Sounds good, I guess for now I'll just write documentation and add some tests.
Tests should pass, in conclusion this PR adds:
System
built from another System
objectWriting the tests for this made me realize that my NoseHoover implementation doesn't work for solids, made another PR to add that to the docs. Might need help to fix that.
Don't even know what happened in the last branch. Unbroke whatever merge issues I had from pulling the CUDA branch.
Things to remember:
Possible type conflict between SimpleCrystals.Atom & Molly.Atom
Class for replica system too?
Float promotion?
How to take extra Atom params like sigma and epsilon? (having things like this in the Atom object makes it impossible/annoying to actually use the AtomsBase interface to load a system from another package and simulate it).
Part of me feels that Atom data like this should be stored as a dictionary separate from the Atom struct and not per atom. Probably not a huge performance hit for small systems but I think it could be a lot of extra memory for really large systems. Would be a pretty big change as most interaction classes rely on each atom having its own data stored internally.