Skip to content

Commit

Permalink
Merge branch 'v0.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
Amphiluke committed Jul 16, 2018
2 parents dfefb55 + b5fb9d4 commit 7cfa52f
Show file tree
Hide file tree
Showing 25 changed files with 3,769 additions and 3,037 deletions.
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# potprox

Approximation of computed data with empirical pair potentials.

## Synopsis

It is a quite common case when there is a need to describe computed numerical *ab initio* data with some analytical form of pair potential (e.g. the [Lennard-Jones function](https://en.wikipedia.org/wiki/Lennard-Jones_potential) or the [Morse function](https://en.wikipedia.org/wiki/Morse_potential)).
Expand All @@ -16,7 +18,7 @@ Potprox uses the [method of least squares](https://en.wikipedia.org/wiki/Least_s

Use the module in [environments with ES6 support](https://kangax.github.io/compat-table/es6/).

## Install
## Install and load potprox

**As a NodeJS module:**

Expand All @@ -32,6 +34,12 @@ The version for browsers (and web workers) is also available: check out the [dis
<script src="dist/potprox.min.js"></script>
```

If you use ES modules, you may import the potprox module from the [potprox.min.mjs](dist/potprox.min.mjs) file.

```javascript
import potprox from "./dist/potprox.min.mjs";
```

**Web workers:**

```javascript
Expand Down Expand Up @@ -235,6 +243,40 @@ let rSqr = potprox.utils.rSqr(data, morse);
console.log(`Coefficient of determination = ${rSqr}`);
```

#### `potprox.utils.points(potential [, options])`

The method `potprox.utils.points()` can be used to generate points of a potential function in the given distance range. The method can take one or two arguments and returns a [Generator object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) which you may iterate over. The first parameter of the method is the approximating potential instance, and the second one (optional) is the configuration object. The following configuration options are available (each of them is optional):

* `start` — starting interatomic distance to generate points from (by default it’s set to a half of the equilibrium distance);
* `end` — end interatomic distance where to stop (by default it’s double of the equilibrium distance);
* `step` — step for point generation (default step is configured to generate 50 points).

```javascript
let morse = new potprox.Morse({d0: 0.0368, r0: 5.316, a: 0.867});

// Generate 50 points starting from r = r0/2 and finishing at r = 2*r0
for (let {r, e, index} of potprox.utils.points(morse)) {
console.log(`${index + 1}. r = ${r.toFixed(4)} nm; E = ${e.toFixed(3)} eV`);
}

// Generate 30 points in the user-defined distance range
let start = 5.0;
let end = 8.5;
let pointCount = 30;
let step = (end - start) / (pointCount - 1);
for (let {r, e, index} of potprox.utils.points(morse, {start, end, step})) {
console.log(`${index + 1}. r = ${r.toFixed(4)} nm; E = ${e.toFixed(3)} eV`);
}

// Generate points infinitely until the given energy threshold is reached
for (let {r, e, index} of potprox.utils.points(morse, {start: 5.0, end: Infinity, step: 0.1})) {
console.log(`${index + 1}. r = ${r.toFixed(4)} nm; E = ${e.toFixed(5)} eV`);
if (e > -0.001) {
break;
}
}
```

## Tips

The overridden method `toJSON()` allows the instances of the potprox potential classes to be easily serialized to a JSON string, and restored from the JSON string later on.
Expand Down
9 changes: 9 additions & 0 deletions ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default {
files: ["test/**/*-spec.mjs"],
sources: ["dist/potprox.mjs"],
babel: {
extensions: ["mjs"]
},
require: ["esm"],
verbose: true
};
25 changes: 23 additions & 2 deletions dist/potprox.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
potprox v0.5.2
potprox v0.6.0
https://amphiluke.github.io/potprox/
*/
(function (global, factory) {
Expand Down Expand Up @@ -853,7 +853,7 @@ https://amphiluke.github.io/potprox/
/**
* Calculate the coefficient of determination to measure the goodness of fit
* @param {Array.<{r: Number, e: Number}>} data - Experimental/ab initio data
* @param {Object} potential - Approximating potential
* @param {Object} potential - Approximating potential instance
* @returns {Number}
* @see https://en.wikipedia.org/wiki/Coefficient_of_determination
*/
Expand All @@ -872,6 +872,27 @@ https://amphiluke.github.io/potprox/
ssTot += diff * diff;
}
return 1 - ssRes / ssTot;
},

/**
* Generate points of the potential curve
* @param {Object} potential - Approximating potential instance
* @param {Object} [options] - Configuration options
* @param {Number} [options.start=potential.r0/2] - Starting interatomic distance
* @param {Number} [options.end=potential.r0*2] - End interatomic distance
* @param {Number} [options.step=(end-start)/49] - Step for point generation (defaults make 50 points)
* @returns {Generator<{r: Number, e: Number}>}
*/
* points(potential, {start = potential.r0 / 2, end = potential.r0 * 2, step = (end - start) / 49} = {}) {
let i = 0;
let r = start;
let direction = Math.sign(end - start); // when end < start, iteration is backward
step = Math.abs(step) * direction; // the user may specify step as signed or not
while ((end - r) * direction >= 0) {
yield {r, e: potential.at(r), index: i};
r = start + step * ++i;
}
return {r: end, e: potential.at(end)};
}
};

Expand Down
4 changes: 2 additions & 2 deletions dist/potprox.min.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions dist/potprox.min.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*!
potprox v0.6.0
https://amphiluke.github.io/potprox/
*/
let e=new WeakMap;class r{constructor({epsilon:r=1,sigma:t=1}={}){e.set(this,{}),this.epsilon=r,this.sigma=t}static get type(){return"LennardJones"}static from(e){if(!Array.isArray(e))throw new TypeError("Approximated data should be an array of points");if(e.length<3)throw new Error("Too little points. Approximation is impossible");let t=0,a=0,o=0,i=0,s=0;for(let{r:r,e:n}of e)t+=Math.pow(r,-24),a+=Math.pow(r,-18),o+=n*Math.pow(r,-12),i+=Math.pow(r,-12),s+=n*Math.pow(r,-6);let n=(s-a*o/t)/(i-a*a/t),h=(o-a*n)/t,l=Math.pow(-h/n,1/6),p=h/(4*Math.pow(l,12));return new r({epsilon:p,sigma:l})}get epsilon(){return e.get(this).epsilon}set epsilon(r){if(!Number.isFinite(r))throw new TypeError("The 'epsilon' parameter should be a finite number");if(r<=0)throw new RangeError("The 'epsilon' parameter should be greater than zero");e.get(this).epsilon=r}get sigma(){return e.get(this).sigma}set sigma(r){if(!Number.isFinite(r))throw new TypeError("The 'sigma' parameter should be a finite number");if(r<=0)throw new RangeError("The 'sigma' parameter should be greater than zero");e.get(this).sigma=r}get r0(){return 1.122462048309373*this.sigma}set r0(e){this.sigma=e/1.122462048309373}at(e){if("number"!=typeof e)throw new TypeError("Distance should be a number");if(e<0)throw new RangeError("Distance shouldn't be less than zero");let{epsilon:r,sigma:t}=this;return 4*r*(Math.pow(t/e,12)-Math.pow(t/e,6))}toJSON(){return{type:r.type,epsilon:this.epsilon,sigma:this.sigma}}}let t=new WeakMap;class a{constructor({d0:e=1,r0:r=1,a:a=2}={}){t.set(this,{}),this.d0=e,this.r0=r,this.a=a}static get type(){return"Buckingham"}static fastFrom(e){if(!Array.isArray(e))throw new TypeError("Approximated data should be an array of points");if(e.length<3)throw new Error("Too little points. Approximation is impossible");e=e.slice().sort((e,r)=>e.r-r.r);let r,t,o,i=Number.POSITIVE_INFINITY,s=1;for(let{r:r,e:t}of e)t<i&&(i=t,s=r);i=Math.abs(i);for(let a=1;a<e.length&&(r=e[a-1],!((t=e[a]).r>=s||r.e<0||t.e<0));a++);if(r&&t&&r.r<s&&t.r<=s){let e=r.e*(r.r-t.r)/(t.e-r.e)+r.r;if(e>0){let r=1-e/s,t=Math.pow(s/e,6)/6;o=(t-r-Math.sqrt(t*t-2*r*t-r*r))/(r*r),Number.isFinite(o)||(o=void 0)}}return new a({d0:i,r0:s,a:o})}static from(e,{d0Conv:r=.001,r0Conv:t=.001,aConv:a=.001}={}){let o=this.fastFrom(e),{d0:i,r0:s,a:n}=o;const h=i*r,l=s*t,p=n*a;let b,u,d;do{let r=0,t=0,a=0,o=0,h=0,l=0,p=0,w=0,f=0;for(let{r:b,e:u}of e){let e=n*Math.pow(s/b,6),d=Math.exp(n*(1-b/s)),m=i/(n-6)*(6*d-e),g=m/i,y=i/(n-6)*(6*d*n*b/(s*s)-6*e/s),T=-i/((n-6)*(n-6))*(6*d-e)+i/(n-6)*(6*(1-b/s)*d-e/n);r+=g*g,t+=y*g,a+=T*g,o+=(m-u)*g,h+=y*y,l+=T*y,p+=(m-u)*y,w+=T*T,f+=(m-u)*T}i+=b=(-t*(u=((a-r*l/t)*(d=-(o-r*p/t-(t-r*h/t)/(t-r*l/a)*(o-r*f/a))/(a-r*l/t-(a-r*w/a)*(t-r*h/t)/(t-r*l/a)))+(o-r*p/t))/(r*h/t-t))-a*d-o)/r,s+=u,n+=d}while(Math.abs(b)>h&&Math.abs(u)>l&&Math.abs(d)>p);return o.d0=i,o.r0=s,o.a=n,o}get d0(){return t.get(this).d0}set d0(e){if(!Number.isFinite(e))throw new TypeError("The 'd0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'd0' parameter should be greater than zero");t.get(this).d0=e}get r0(){return t.get(this).r0}set r0(e){if(!Number.isFinite(e))throw new TypeError("The 'r0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'r0' parameter should be greater than zero");t.get(this).r0=e}get a(){return t.get(this).a}set a(e){if(!Number.isFinite(e))throw new TypeError("The 'a' parameter should be a finite number");if(e<=0)throw new RangeError("The 'a' parameter should be greater than zero");t.get(this).a=e}at(e){if("number"!=typeof e)throw new TypeError("Distance should be a number");if(e<0)throw new RangeError("Distance shouldn't be less than zero");let{d0:r,r0:t,a:a}=this;return r/(a-6)*(6*Math.exp(a*(1-e/t))-a*Math.pow(t/e,6))}toJSON(){return{type:a.type,d0:this.d0,r0:this.r0,a:this.a}}}let o=new WeakMap;class i{constructor({d0:e=1,r0:r=1,a:t=1}={}){o.set(this,{}),this.d0=e,this.r0=r,this.a=t}static get type(){return"Morse"}static fastFrom(e){if(!Array.isArray(e))throw new TypeError("Approximated data should be an array of points");if(e.length<3)throw new Error("Too little points. Approximation is impossible");let r=Number.POSITIVE_INFINITY,t=1;for(let{r:a,e:o}of e)o<r&&(r=o,t=a);r=Math.abs(r);let a=0,o=0;for(let{r:i,e:s}of e){let e=Math.sqrt(1+s/r),n=Number.NaN;i>t?n=Math.log(1-e)/(t-i):i<t&&(n=Math.log(1+e)/(t-i)),Number.isFinite(n)&&(a+=n,o++)}return new i({d0:r,r0:t,a:a/=o})}static from(e,{d0Conv:r=.001,r0Conv:t=.001,aConv:a=.001}={}){let o=this.fastFrom(e),{d0:i,r0:s,a:n}=o;const h=i*r,l=s*t,p=n*a;let b,u,d;do{let r=0,t=0,a=0,o=0,h=0,l=0,p=0,w=0,f=0;for(let{r:b,e:u}of e){let e=Math.exp(n*(s-b)),d=i*(1-e)*(1-e)-i,m=d/i,g=-2*i*(1-e)*n*e,y=2*i*(1-e)*(b-s)*e;r+=m*m,t+=g*m,a+=y*m,o+=(d-u)*m,h+=g*g,l+=y*g,p+=(d-u)*g,w+=y*y,f+=(d-u)*y}i+=b=(-t*(u=((a-r*l/t)*(d=-(o-r*p/t-(t-r*h/t)/(t-r*l/a)*(o-r*f/a))/(a-r*l/t-(a-r*w/a)*(t-r*h/t)/(t-r*l/a)))+(o-r*p/t))/(r*h/t-t))-a*d-o)/r,s+=u,n+=d}while(Math.abs(b)>h&&Math.abs(u)>l&&Math.abs(d)>p);return o.d0=i,o.r0=s,o.a=n,o}get d0(){return o.get(this).d0}set d0(e){if(!Number.isFinite(e))throw new TypeError("The 'd0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'd0' parameter should be greater than zero");o.get(this).d0=e}get r0(){return o.get(this).r0}set r0(e){if(!Number.isFinite(e))throw new TypeError("The 'r0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'r0' parameter should be greater than zero");o.get(this).r0=e}get a(){return o.get(this).a}set a(e){if(!Number.isFinite(e))throw new TypeError("The 'a' parameter should be a finite number");if(e<=0)throw new RangeError("The 'a' parameter should be greater than zero");o.get(this).a=e}at(e){if("number"!=typeof e)throw new TypeError("Distance should be a number");if(e<0)throw new RangeError("Distance shouldn't be less than zero");let{d0:r,r0:t,a:a}=this,o=1-Math.exp(a*(t-e));return r*o*o-r}toJSON(){return{type:i.type,d0:this.d0,r0:this.r0,a:this.a}}}let s=new WeakMap;class n{constructor({d0:e=1,r0:r=1,b:t=2}={}){s.set(this,{}),this.d0=e,this.r0=r,this.b=t}static get type(){return"Rydberg"}static fastFrom(e){if(!Array.isArray(e))throw new TypeError("Approximated data should be an array of points");if(e.length<3)throw new Error("Too little points. Approximation is impossible");e=e.slice().sort((e,r)=>e.r-r.r);let r,t,a,o=Number.POSITIVE_INFINITY,i=1;for(let{r:r,e:t}of e)t<o&&(o=t,i=r);o=Math.abs(o);for(let a=1;a<e.length&&(r=e[a-1],!((t=e[a]).r>=i||r.e<0||t.e<0));a++);if(r&&t&&r.r<i&&t.r<=i){let e=r.e*(r.r-t.r)/(t.e-r.e)+r.r;e>0&&(a=i/(i-e))}return new n({d0:o,r0:i,b:a})}static from(e,{d0Conv:r=.001,r0Conv:t=.001,bConv:a=.001}={}){let o=this.fastFrom(e),{d0:i,r0:s,b:n}=o;const h=i*r,l=s*t,p=n*a;let b,u,d;do{let r=0,t=0,a=0,o=0,h=0,l=0,p=0,w=0,f=0;for(let{r:b,e:u}of e){let e=n*(b/s-1),d=Math.exp(-e),m=-i*(1+e)*d,g=m/i,y=-i*n*b/(s*s)*d*e,T=i*e/n*d*e;r+=g*g,t+=y*g,a+=T*g,o+=(m-u)*g,h+=y*y,l+=T*y,p+=(m-u)*y,w+=T*T,f+=(m-u)*T}i+=b=(-t*(u=((a-r*l/t)*(d=-(o-r*p/t-(t-r*h/t)/(t-r*l/a)*(o-r*f/a))/(a-r*l/t-(a-r*w/a)*(t-r*h/t)/(t-r*l/a)))+(o-r*p/t))/(r*h/t-t))-a*d-o)/r,s+=u,n+=d}while(Math.abs(b)>h&&Math.abs(u)>l&&Math.abs(d)>p);return o.d0=i,o.r0=s,o.b=n,o}get d0(){return s.get(this).d0}set d0(e){if(!Number.isFinite(e))throw new TypeError("The 'd0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'd0' parameter should be greater than zero");s.get(this).d0=e}get r0(){return s.get(this).r0}set r0(e){if(!Number.isFinite(e))throw new TypeError("The 'r0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'r0' parameter should be greater than zero");s.get(this).r0=e}get b(){return s.get(this).b}set b(e){if(!Number.isFinite(e))throw new TypeError("The 'b' parameter should be a finite number");if(e<=1)throw new RangeError("The 'b' parameter should be greater than 1");s.get(this).b=e}at(e){if("number"!=typeof e)throw new TypeError("Distance should be a number");if(e<0)throw new RangeError("Distance shouldn't be less than zero");let{d0:r,r0:t,b:a}=this,o=a*(e-t)/t;return-r*(1+o)*Math.exp(-o)}toJSON(){return{type:n.type,d0:this.d0,r0:this.r0,b:this.b}}}let h=new WeakMap;class l{constructor({d0:e=1,r0:r=1,b:t=1}={}){h.set(this,{}),this.d0=e,this.r0=r,this.b=t}static get type(){return"Varshni3"}static fastFrom(e){if(!Array.isArray(e))throw new TypeError("Approximated data should be an array of points");if(e.length<3)throw new Error("Too little points. Approximation is impossible");let r=Number.POSITIVE_INFINITY,t=1;for(let{r:a,e:o}of e)o<r&&(r=o,t=a);r=Math.abs(r);let a=0,o=0;for(let{r:i,e:s}of e){let e=Math.sqrt(1+s/r),n=Number.NaN;i>t?n=Math.log(i/t*(1-e))/(t*t-i*i):i<t&&(n=Math.log(i/t*(1+e))/(t*t-i*i)),Number.isFinite(n)&&(a+=n,o++)}return new l({d0:r,r0:t,b:a/=o})}static from(e,{d0Conv:r=.001,r0Conv:t=.001,bConv:a=.001}={}){let o=this.fastFrom(e),{d0:i,r0:s,b:n}=o;const h=i*r,l=s*t,p=n*a;let b,u,d;do{let r=0,t=0,a=0,o=0,h=0,l=0,p=0,w=0,f=0;for(let{r:b,e:u}of e){let e=s/b*Math.exp(n*(s*s-b*b)),d=i*(1-e)*(1-e)-i,m=d/i,g=2*i*(1-e)*(-e/s-2*e*n*s),y=2*i*(1-e)*e*(b*b-s*s);r+=m*m,t+=g*m,a+=y*m,o+=(d-u)*m,h+=g*g,l+=y*g,p+=(d-u)*g,w+=y*y,f+=(d-u)*y}i+=b=(-t*(u=((a-r*l/t)*(d=-(o-r*p/t-(t-r*h/t)/(t-r*l/a)*(o-r*f/a))/(a-r*l/t-(a-r*w/a)*(t-r*h/t)/(t-r*l/a)))+(o-r*p/t))/(r*h/t-t))-a*d-o)/r,s+=u,n+=d}while(Math.abs(b)>h&&Math.abs(u)>l&&Math.abs(d)>p);return o.d0=i,o.r0=s,o.b=n,o}get d0(){return h.get(this).d0}set d0(e){if(!Number.isFinite(e))throw new TypeError("The 'd0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'd0' parameter should be greater than zero");h.get(this).d0=e}get r0(){return h.get(this).r0}set r0(e){if(!Number.isFinite(e))throw new TypeError("The 'r0' parameter should be a finite number");if(e<=0)throw new RangeError("The 'r0' parameter should be greater than zero");h.get(this).r0=e}get b(){return h.get(this).b}set b(e){if(!Number.isFinite(e))throw new TypeError("The 'b' parameter should be a finite number");if(e<=0)throw new RangeError("The 'b' parameter should be greater than zero");h.get(this).b=e}at(e){if("number"!=typeof e)throw new TypeError("Distance should be a number");if(e<0)throw new RangeError("Distance shouldn't be less than zero");let{d0:r,r0:t,b:a}=this,o=1-t/e*Math.exp(a*(t*t-e*e));return r*o*o-r}toJSON(){return{type:l.type,d0:this.d0,r0:this.r0,b:this.b}}}let p={rSqr(e,r){let t=0,a=0;for(let{r:o,e:i}of e){t+=i;let e=i-r.at(o);a+=e*e}t/=e.length;let o=0;for(let{e:r}of e){let e=r-t;o+=e*e}return 1-a/o},*points(e,{start:r=e.r0/2,end:t=2*e.r0,step:a=(t-r)/49}={}){let o=0,i=r,s=Math.sign(t-r);for(a=Math.abs(a)*s;(t-i)*s>=0;)yield{r:i,e:e.at(i),index:o},i=r+a*++o;return{r:t,e:e.at(t)}}},b=Object.create(null);b[r.type]=r,b[a.type]=a,b[i.type]=i,b[n.type]=n,b[l.type]=l,Object.defineProperty(b,"utils",{configurable:!0,value:p});export default b;
Loading

0 comments on commit 7cfa52f

Please sign in to comment.