github.com/gopherd/gonum@v0.0.4/unit/constant/generate_constants.go (about)

     1  // Copyright ©2014 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build ignore
     6  // +build ignore
     7  
     8  package main
     9  
    10  import (
    11  	"bytes"
    12  	"go/format"
    13  	"log"
    14  	"math"
    15  	"os"
    16  	"strings"
    17  	"text/template"
    18  
    19  	"github.com/gopherd/gonum/unit"
    20  )
    21  
    22  const (
    23  	elementaryCharge = 1.602176634e-19
    24  	fineStructure    = 7.2973525693e-3
    25  	lightSpeed       = 2.99792458e8
    26  	planck           = 6.62607015e-34
    27  )
    28  
    29  var constants = []Constant{
    30  	{
    31  		Name: "AtomicMass", Value: 1.66053906660e-27,
    32  		Dimensions:  []Dimension{{massName, 1}},
    33  		Comment:     "AtomicMass is the atomic mass constant (mᵤ), one twelfth of the mass of an unbound atom of carbon-12 at rest and in its ground state.",
    34  		Uncertainty: 0.00000000050e-27,
    35  	},
    36  	{
    37  		Name: "Avogadro", Value: 6.02214076e23,
    38  		Dimensions: []Dimension{{moleName, -1}},
    39  		Comment:    "Avogadro is the Avogadro constant (A), the number of constituent particles contained in one mole of a substance.",
    40  	},
    41  	{
    42  		Name: "Boltzmann", Value: 1.380649e-23,
    43  		Dimensions: []Dimension{{massName, 1}, {lengthName, 2}, {timeName, -2}, {temperatureName, -1}},
    44  		Comment:    "Boltzmann is the Boltzmann constant (k), it relates the average relative kinetic energy of particles in a gas with the temperature of the gas.",
    45  	},
    46  	{
    47  		Name: "ElectricConstant", Value: 1 / (4 * math.Pi * 1e-7 * lightSpeed * lightSpeed),
    48  		Dimensions:  []Dimension{{currentName, 2}, {timeName, 4}, {massName, -1}, {lengthName, -3}},
    49  		Comment:     "ElectricConstant is the electric constant (ε₀), the value of the absolute dielectric permittivity of classical vacuum.",
    50  		Uncertainty: 0.0000000013e-12,
    51  	},
    52  	{
    53  		Name: "ElementaryCharge", Value: elementaryCharge,
    54  		Dimensions: []Dimension{{currentName, 1}, {timeName, 1}},
    55  		Comment:    "ElementaryCharge, is the elementary charge constant (e), the magnitude of electric charge carried by a single proton or electron.",
    56  	},
    57  	{
    58  		Name: "Faraday", Value: 96485.33212,
    59  		Dimensions: []Dimension{{currentName, 1}, {timeName, 1}, {moleName, -1}},
    60  		Comment:    "Faraday is the Faraday constant, the magnitude of electric charge per mole of electrons.",
    61  	},
    62  	{
    63  		Name: "FineStructure", Value: fineStructure,
    64  		Comment:     "FineStructure is the fine structure constant (α), it describes the strength of the electromagnetic interaction between elementary charged particles.",
    65  		Uncertainty: 0.0000000011e-3,
    66  	},
    67  	{
    68  		Name: "Gravitational", Value: 6.67430e-11,
    69  		Dimensions:  []Dimension{{massName, -1}, {lengthName, 3}, {timeName, -2}},
    70  		Comment:     "Gravitational is the universal gravitational constant (G), the proportionality constant connecting the gravitational force between two bodies.",
    71  		Uncertainty: 0.00015e-11,
    72  	},
    73  	{
    74  		Name: "LightSpeedInVacuum", Value: lightSpeed,
    75  		Dimensions: []Dimension{{lengthName, 1}, {timeName, -1}},
    76  		Comment:    "LightSpeedInVacuum is the c constant, the speed of light in a vacuum.",
    77  	},
    78  	{
    79  		Name: "MagneticConstant", Value: 2 * fineStructure * planck / (elementaryCharge * elementaryCharge * lightSpeed),
    80  		Dimensions:  []Dimension{{currentName, 2}, {timeName, 4}, {massName, -1}, {lengthName, -3}},
    81  		Comment:     "MagneticConstant is the magnetic constant (μ₀), the magnetic permeability in a classical vacuum.",
    82  		Uncertainty: 0.00000000019e-6,
    83  	},
    84  	{
    85  		Name: "Planck", Value: planck,
    86  		Dimensions: []Dimension{{massName, 1}, {lengthName, 2}, {timeName, -1}},
    87  		Comment:    "Planck is the Planck constant (h), it relates the energy carried by a photon to its frequency.",
    88  	},
    89  	{
    90  		Name: "StandardGravity", Value: 9.80665,
    91  		Dimensions: []Dimension{{lengthName, 1}, {timeName, -2}},
    92  		Comment:    "StandardGravity is the standard gravity constant (g₀), the nominal gravitational acceleration of an object in a vacuum near the surface of the Earth",
    93  	},
    94  }
    95  
    96  const (
    97  	angleName             = "AngleDim"
    98  	currentName           = "CurrentDim"
    99  	lengthName            = "LengthDim"
   100  	luminousIntensityName = "LuminousIntensityDim"
   101  	massName              = "MassDim"
   102  	moleName              = "MoleDim"
   103  	temperatureName       = "TemperatureDim"
   104  	timeName              = "TimeDim"
   105  )
   106  
   107  var dimOf = map[string]unit.Dimension{
   108  	"AngleDim":             unit.AngleDim,
   109  	"CurrentDim":           unit.CurrentDim,
   110  	"LengthDim":            unit.LengthDim,
   111  	"LuminousIntensityDim": unit.LuminousIntensityDim,
   112  	"MassDim":              unit.MassDim,
   113  	"MoleDim":              unit.MoleDim,
   114  	"TemperatureDim":       unit.TemperatureDim,
   115  	"TimeDim":              unit.TimeDim,
   116  }
   117  
   118  type Constant struct {
   119  	Name        string
   120  	Value       float64
   121  	Dimensions  []Dimension
   122  	Comment     string
   123  	Uncertainty float64
   124  }
   125  
   126  type Dimension struct {
   127  	Name  string
   128  	Power int
   129  }
   130  
   131  func (c Constant) IsDefined() bool {
   132  	return definedEquivalentOf(unit.New(1, c.dimensions())) != ""
   133  }
   134  
   135  func (c Constant) Type() string {
   136  	typ := definedEquivalentOf(unit.New(1, c.dimensions()))
   137  	if typ == "" {
   138  		return strings.ToLower(c.Name[:1]) + c.Name[1:] + "Units"
   139  	}
   140  	return typ
   141  }
   142  
   143  func (c Constant) Units() string {
   144  	return c.dimensions().String()
   145  }
   146  
   147  func (c Constant) dimensions() unit.Dimensions {
   148  	dims := make(unit.Dimensions)
   149  	for _, d := range c.Dimensions {
   150  		dims[dimOf[d.Name]] = d.Power
   151  	}
   152  	return dims
   153  }
   154  
   155  // Generate a file for each of the constants.
   156  func main() {
   157  	for _, c := range constants {
   158  		generate(c)
   159  		generateTest(c)
   160  	}
   161  }
   162  
   163  const baseUnitTemplate = `// Code generated by "go generate github.com/gopherd/gonum/unit/constant”; DO NOT EDIT.
   164  
   165  // Copyright ©2019 The Gonum Authors. All rights reserved.
   166  // Use of this source code is governed by a BSD-style
   167  // license that can be found in the LICENSE file.
   168  
   169  package constant
   170  
   171  import "github.com/gopherd/gonum/unit"
   172  
   173  // {{.Comment}}{{if .Dimensions}}
   174  // {{$n := len .Dimensions}}The dimension{{if gt $n 1}}s{{end}} of {{.Name}} {{if eq $n 1}}is{{else}}are{{end}} {{.Units}}.{{end}} {{if not .Uncertainty}}The constant is exact.{{else}}The standard uncertainty of the constant is {{.Uncertainty}} {{.Units}}.{{end}}
   175  const {{.Name}} = {{.Type}}({{.Value}})
   176  `
   177  
   178  var baseUnit = template.Must(template.New("base").Parse(baseUnitTemplate))
   179  
   180  const methodTemplate = `// Code generated by "go generate github.com/gopherd/gonum/unit/constant”; DO NOT EDIT.
   181  
   182  // Copyright ©2019 The Gonum Authors. All rights reserved.
   183  // Use of this source code is governed by a BSD-style
   184  // license that can be found in the LICENSE file.
   185  
   186  package constant
   187  
   188  import (
   189  	"fmt"
   190  
   191  	"github.com/gopherd/gonum/unit"
   192  )
   193  
   194  // {{.Comment}}
   195  // {{$n := len .Dimensions}}The dimension{{if gt $n 1}}s{{end}} of {{.Name}} {{if eq $n 1}}is{{else}}are{{end}} {{.Units}}. {{if not .Uncertainty}}The constant is exact.{{else}}The standard uncertainty of the constant is {{.Uncertainty}} {{.Units}}.{{end}}
   196  const {{.Name}} = {{.Type}}({{.Value}})
   197  
   198  type {{.Type}} float64
   199  
   200  // Unit converts the {{.Type}} to a *unit.Unit
   201  func (cnst {{.Type}}) Unit() *unit.Unit {
   202  	return unit.New(float64(cnst), unit.Dimensions{
   203  		{{range .Dimensions}} unit.{{.Name}}: {{.Power}},
   204  		{{end}}
   205  		})
   206  }
   207  
   208  func (cnst {{.Type}}) Format(fs fmt.State, c rune) {
   209  	switch c {
   210  	case 'v':
   211  		if fs.Flag('#') {
   212  			fmt.Fprintf(fs, "%T(%v)", cnst, float64(cnst))
   213  			return
   214  		}
   215  		fallthrough
   216  	case 'e', 'E', 'f', 'F', 'g', 'G':
   217  		p, pOk := fs.Precision()
   218  		w, wOk := fs.Width()
   219  		switch {
   220  		case pOk && wOk:
   221  			fmt.Fprintf(fs, "%*.*"+string(c), w, p, cnst.Unit())
   222  		case pOk:
   223  			fmt.Fprintf(fs, "%.*"+string(c), p, cnst.Unit())
   224  		case wOk:
   225  			fmt.Fprintf(fs, "%*"+string(c), w, cnst.Unit())
   226  		default:
   227  			fmt.Fprintf(fs, "%"+string(c), cnst.Unit())
   228  		}
   229  	default:
   230  		fmt.Fprintf(fs, "%%!"+string(c)+"(constant.{{.Type}}=%v {{.Units}})", float64(cnst))
   231  	}
   232  }
   233  `
   234  
   235  var methods = template.Must(template.New("methods").Parse(methodTemplate))
   236  
   237  func generate(c Constant) {
   238  	lowerName := strings.ToLower(c.Name)
   239  	filename := lowerName + ".go"
   240  	f, err := os.Create(filename)
   241  	if err != nil {
   242  		log.Fatal(err)
   243  	}
   244  	defer f.Close()
   245  
   246  	var buf bytes.Buffer
   247  
   248  	if c.IsDefined() {
   249  		err = baseUnit.Execute(&buf, c)
   250  		if err != nil {
   251  			log.Fatal(err)
   252  		}
   253  	} else {
   254  		err = methods.Execute(&buf, c)
   255  		if err != nil {
   256  			log.Fatal(err)
   257  		}
   258  	}
   259  
   260  	b, err := format.Source(buf.Bytes())
   261  	if err != nil {
   262  		f.Write(buf.Bytes()) // This is here to debug bad format.
   263  		log.Fatalf("error formatting %q: %s", c.Name, err)
   264  	}
   265  
   266  	f.Write(b)
   267  }
   268  
   269  const testTemplate = `// Code generated by "go generate github.com/gopherd/gonum/unit/constant”; DO NOT EDIT.
   270  
   271  // Copyright ©2019 The Gonum Authors. All rights reserved.
   272  // Use of this source code is governed by a BSD-style
   273  // license that can be found in the LICENSE file.
   274  
   275  package constant
   276  
   277  import (
   278  	"fmt"
   279  	"testing"
   280  )
   281  
   282  func Test{{.Name}}Format(t *testing.T) {
   283  	t.Parallel()
   284  	for _, test := range []struct{
   285  		format string
   286  		want   string
   287  	}{
   288  		{"%v", "{{.Value}} {{.Units}}"},
   289  		{"%.1v", "{{printf "%.1v" .Value}} {{.Units}}"},
   290  		{"%50.1v", "{{$s := printf "%.1v %s" .Value .Units}}{{printf "%50s" $s}}"},
   291  		{"%50v", "{{$s := printf "%v %s" .Value .Units}}{{printf "%50s" $s}}"},
   292  		{"%1v", "{{.Value}} {{.Units}}"},
   293  		{"%#v", "constant.{{.Type}}({{.Value}})"},
   294  		{"%s", "%!s(constant.{{.Type}}={{.Value}} {{.Units}})"},
   295  	} {
   296  		got := fmt.Sprintf(test.format, {{.Name}})
   297  		if got != test.want {
   298  			t.Errorf("Format %q: got: %q want: %q", test.format, got, test.want)
   299  		}
   300  	}
   301  }
   302  `
   303  
   304  var tests = template.Must(template.New("test").Parse(testTemplate))
   305  
   306  func generateTest(c Constant) {
   307  	if c.IsDefined() {
   308  		return
   309  	}
   310  
   311  	lowerName := strings.ToLower(c.Name)
   312  	filename := lowerName + "_test.go"
   313  	f, err := os.Create(filename)
   314  	if err != nil {
   315  		log.Fatal(err)
   316  	}
   317  	defer f.Close()
   318  
   319  	var buf bytes.Buffer
   320  
   321  	err = tests.Execute(&buf, c)
   322  	if err != nil {
   323  		log.Fatal(err)
   324  	}
   325  
   326  	b, err := format.Source(buf.Bytes())
   327  	if err != nil {
   328  		f.Write(buf.Bytes()) // This is here to debug bad format.
   329  		log.Fatalf("error formatting test for %q: %s", c.Name, err)
   330  	}
   331  
   332  	f.Write(b)
   333  }