File: functionfromfile.go

package info (click to toggle)
mumax3 3.11.1-1
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 10,668 kB
  • sloc: makefile: 194; ansic: 155; sh: 86; javascript: 16
file content (145 lines) | stat: -rw-r--r-- 3,243 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package engine

import (
	"encoding/csv"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/mumax/3/util"
)

func init() {
	DeclFunc("FunctionFromDatafile", FunctionFromDatafile,
		"Creates an interpolation function using data from two columns in a csv file. "+
			"Arguments: filename, xColumnIdx, yColumnIdx, method (\"linear\", \"nearest\" or \"step\").")
}

func isStrictlyIncreasing(x []float64) bool {
	for i := 1; i < len(x); i++ {
		if x[i] <= x[i-1] {
			return false
		}
	}
	return true
}

func InterpolationFunction(xData, yData []float64, method string) func(float64) float64 {
	util.AssertMsg(len(xData) == len(yData), "Interpolation error: given data slices do not have the same length")
	util.AssertMsg(len(xData) != 0, "Interpolation error: data slices are empty")
	util.AssertMsg(isStrictlyIncreasing(xData), "Interpolation error: X values are not strictly increasing")

	switch method {
	case "nearest":
		return nearestInterpolationFunction(xData, yData)
	case "step":
		return stepInterpolationFunction(xData, yData)
	case "linear":
		return linearInterpolationFunction(xData, yData)
	default:
		util.Fatal("Interpolation method \"" + method + "\" is not implemented")
		return nil
	}
}

func nearestInterpolationFunction(xData, yData []float64) func(float64) float64 {
	return func(x float64) float64 {
		ib := 0 // index for the smallest xData value larger than x
		for ; ib < len(xData); ib++ {
			if x < xData[ib] {
				break
			}
		}

		if ib == 0 {
			return yData[0]
		}

		if ib == len(xData) {
			return yData[len(xData)-1]
		}

		ia := ib - 1 // index for the largest xData value smaller than x
		xa, ya := xData[ia], yData[ia]
		xb, yb := xData[ib], yData[ib]

		if x-xa < xb-x {
			return ya
		} else {
			return yb
		}
	}
}

func stepInterpolationFunction(xData, yData []float64) func(float64) float64 {
	return func(x float64) float64 {
		if x < xData[0] {
			return 0.0
		}

		for i := 0; i < len(xData)-1; i++ {
			if x >= xData[i] && x < xData[i+1] {
				return yData[i]
			}
		}

		return yData[len(yData)-1]
	}
}

func linearInterpolationFunction(xData, yData []float64) func(float64) float64 {
	return func(x float64) float64 {
		ib := 0 // index for the smallest xData value larger than x
		for ; ib < len(xData); ib++ {
			if x < xData[ib] {
				break
			}
		}

		if ib == 0 {
			return yData[0]
		}

		if ib == len(xData) {
			return yData[len(xData)-1]
		}

		ia := ib - 1 // index for the largest xData value smaller than x
		xa, ya := xData[ia], yData[ia]
		xb, yb := xData[ib], yData[ib]

		return ya + (x-xa)*(yb-ya)/(xb-xa)
	}
}

func FunctionFromDatafile(fname string, xCol, yCol int, method string) func(float64) float64 {
	csvfile, err := os.Open(fname)
	util.FatalErr(err)
	defer csvfile.Close()

	r := csv.NewReader(csvfile)
	r.Comment = '#'

	xData := make([]float64, 0)
	yData := make([]float64, 0)

	for {
		line, err := r.Read()
		if err == io.EOF {
			break
		} else {
			util.FatalErr(err)
		}

		x_, err := strconv.ParseFloat(strings.TrimSpace(line[xCol]), 64)
		util.FatalErr(err)
		y_, err := strconv.ParseFloat(strings.TrimSpace(line[yCol]), 64)
		util.FatalErr(err)

		xData = append(xData, x_)
		yData = append(yData, y_)
	}

	return InterpolationFunction(xData, yData, method)
}