Active Learning: Single-Molecule Setup and Run
**********************************************

MLPotential Active Learning ParAMS M3GNet Molecular Dynamics

Set up a minimal Simple Active Learning workflow for a small organic
molecule, combining reference method, molecular dynamics, and M3GNet
retraining settings in Python.

**Requires:** AMS2026 or later

**Required AMS Packages:** m3gnet

-[ Related examples ]-

* Active Learning: Single-Molecule Results

* Active Learning: Single-Molecule Comparison to M3GNet-UP-2022

* Active Learning: Single-Molecule Production Simulation

* Active Learning: Continue with a New System

-[ Related tutorials ]-

* Simple Active Learning tutorial

* Getting Started: Lennard-Jones Potential for Argon

-[ Related documentation ]-

* Simple Active Learning Python API

* MLPotential engine

* Molecular Dynamics task

[image]


Initial imports
===============

   from scm.simple_active_learning import SimpleActiveLearningJob
   import scm.plams as plams


Initialize PLAMS
================

   plams.init()

   PLAMS working folder: /path/to/plams_workdir


Input system
============

   mol = plams.from_smiles("OCC=O")
   for at in mol:
       at.properties = {}
   plams.plot_molecule(mol)

   [image: image generated from notebook][image]


Reference engine settings
=========================

For time reasons we use the UFF force field as the reference method.
Typically you would instead train to DFT using ADF, BAND, or Quantum
ESPRESSO.

   ref_s = plams.Settings()
   ref_s.input.ForceField.Type = "UFF"
   ref_s.runscript.nproc = 1

   print(plams.AMSJob(settings=ref_s).get_input())

   Engine ForceField
     Type UFF
   EndEngine


Molecular dynamics settings
===========================

Here, we use the convenient "AMSNVTJob" recipe to easily initialize
sone MD settings.

   md_s = plams.AMSNVTJob(temperature=300, timestep=0.5, nsteps=10000).settings

   print(plams.AMSJob(settings=md_s).get_input())

   MolecularDynamics
     BinLog
       DipoleMoment False
       PressureTensor False
       Time False
   ... output trimmed ....
   End

   Task MolecularDynamics


ParAMS ML Training settings
===========================

(Technical note: When using "SimpleActiveLearningJob" the ParAMS
settings go under "input.ams". When using "ParAMSJob" the settings
instead simply go under "input". See the ParAMS Python tutorials.)

   ml_s = plams.Settings()
   ml_s.input.ams.MachineLearning.Backend = "M3GNet"
   ml_s.input.ams.MachineLearning.CommitteeSize = 1
   ml_s.input.ams.MachineLearning.M3GNet.Model = "UniversalPotential"
   ml_s.input.ams.MachineLearning.MaxEpochs = 200
   print(SimpleActiveLearningJob(settings=ml_s).get_input())

   MachineLearning
     Backend M3GNet
     CommitteeSize 1
     M3GNet
       Model UniversalPotential
     End
     MaxEpochs 200
   End


Active learning settings
========================

   al_s = plams.Settings()
   al_s.input.ams.ActiveLearning.Steps.Type = "Geometric"
   al_s.input.ams.ActiveLearning.Steps.Geometric.Start = 10  # 10 MD frames
   al_s.input.ams.ActiveLearning.Steps.Geometric.NumSteps = 5  # 10 AL steps
   print(SimpleActiveLearningJob(settings=al_s).get_input())

   ActiveLearning
     Steps
       Geometric
         NumSteps 5
         Start 10
       End
       Type Geometric
     End
   End


Simple Active Learning Job
==========================

   settings = ref_s + md_s + ml_s + al_s
   job = SimpleActiveLearningJob(settings=settings, molecule=mol, name="sal")
   print(job.get_input())

   ActiveLearning
     Steps
       Geometric
         NumSteps 5
         Start 10
   ... output trimmed ....
        4 8 1.0
     End
     Charge 0
   End


Run the job
===========

   job.run(watch=True)

   [31.01|17:43:33] JOB sal STARTED
   [31.01|17:43:33] JOB sal RUNNING
   [31.01|17:43:34] Simple Active Learning 2023.205,  Nodes: 1, Procs: 1
   [31.01|17:43:36] Composition of main system: C2H4O2
   [31.01|17:43:36] All REFERENCE calculations will be performed with the following ForceField engine:
   ... output trimmed ....
   [31.01|17:55:16] Active learning finished!
   [31.01|17:55:16] Rerunning the simulation with the final parameters...
   [31.01|17:56:52] Goodbye!
   [31.01|17:56:52] JOB sal FINISHED
   [31.01|17:56:52] JOB sal SUCCESSFUL





   <scm.params.plams.simple_active_learning_job.SimpleActiveLearningResults at 0x7fd4176eaa90>


See also
========


Related examples
----------------

* Active Learning: Single-Molecule Results

* Active Learning: Single-Molecule Comparison to M3GNet-UP-2022

* Active Learning: Single-Molecule Production Simulation

* Active Learning: Continue with a New System


Related tutorials
-----------------

* Simple Active Learning tutorial

* Getting Started: Lennard-Jones Potential for Argon


Related documentation
---------------------

* Simple Active Learning Python API

* MLPotential engine

* Molecular Dynamics task


Python Script
=============

   #!/usr/bin/env python
   # coding: utf-8

   # ## Initial imports

   from scm.simple_active_learning import SimpleActiveLearningJob
   import scm.plams as plams


   # ## Initialize PLAMS

   plams.init()


   # ## Input system

   mol = plams.from_smiles("OCC=O")
   for at in mol:
       at.properties = {}
   plams.plot_molecule(mol)


   # ## Reference engine settings
   # For time reasons we use the UFF force field as the reference method. Typically you would instead train to DFT using ADF, BAND, or Quantum ESPRESSO.

   ref_s = plams.Settings()
   ref_s.input.ForceField.Type = "UFF"
   ref_s.runscript.nproc = 1


   print(plams.AMSJob(settings=ref_s).get_input())


   # ## Molecular dynamics settings
   # Here, we use the convenient ``AMSNVTJob`` recipe to easily initialize sone MD settings.

   md_s = plams.AMSNVTJob(temperature=300, timestep=0.5, nsteps=10000).settings


   print(plams.AMSJob(settings=md_s).get_input())


   # ## ParAMS ML Training settings
   # 
   # (Technical note: When using ``SimpleActiveLearningJob`` the ParAMS settings go under ``input.ams``. When using ``ParAMSJob`` the settings instead simply go under ``input``. See the ParAMS Python tutorials.)

   ml_s = plams.Settings()
   ml_s.input.ams.MachineLearning.Backend = "M3GNet"
   ml_s.input.ams.MachineLearning.CommitteeSize = 1
   ml_s.input.ams.MachineLearning.M3GNet.Model = "UniversalPotential"
   ml_s.input.ams.MachineLearning.MaxEpochs = 200
   print(SimpleActiveLearningJob(settings=ml_s).get_input())


   # ## Active learning settings

   al_s = plams.Settings()
   al_s.input.ams.ActiveLearning.Steps.Type = "Geometric"
   al_s.input.ams.ActiveLearning.Steps.Geometric.Start = 10  # 10 MD frames
   al_s.input.ams.ActiveLearning.Steps.Geometric.NumSteps = 5  # 10 AL steps
   print(SimpleActiveLearningJob(settings=al_s).get_input())


   # ## Simple Active Learning Job

   settings = ref_s + md_s + ml_s + al_s
   job = SimpleActiveLearningJob(settings=settings, molecule=mol, name="sal")
   print(job.get_input())


   # ## Run the job

   job.run(watch=True)

