1
2C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
3C MEVAL
4C$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
5 SUBROUTINE MEVAL(XVAL,YVAL,XTAB,YTAB,MTAB,NUM,NUME,EPS,ERR)
6C 10/15/26 MHP via Claude
7C code changed to take JSON from DIMENSIONS_MOD instead of declaring
8C PARAMETER(JSON=5000) here.
9 USE DIMENSIONS_MOD
10C 10/20/26 MHP via Claude
11C code changed to replace implicit typing with IMPLICIT NONE and
12C explicit declarations.
13 IMPLICIT NONE
14 INTEGER I,IND,LCN,LCN1,NCASE,NUM,NUM1,NUME,NUME1
15C
16C SHAPE PRESERVING QUADRATIC SPLINES
17C BY D.F.MCALLISTER & J.A.ROULIER
18C CODED BY S.L.DOOD & M.ROULIER
19C N.C. STATE UNIVERSITY
20C
21 REAL*8 XVAL(JSON),YVAL(JSON),XTAB(JSON),YTAB(JSON),
22 * * MTAB(JSON),V1,V2,W1,W2,Z1,Z2,Y1,Y2,E1,E2,SPLINE,EPS
23 INTEGER START,START1,END,END1,ERR,FND
24C
25C MEVAL CONTROLS THE EVALUATION OF AN OSCULATORY QUADRATIC SPLINE. THE
26C USER MAY PROVIDE HIS OWN SLOPES AT THE POINTS OF INTERPOLATION OR USE
27C THE SUBROUTINE 'SLOPES' TO CALCULATE SLOPES WHICH ARE CONSISTENT WITH
28C THE SHAPE OF THE DATA.
29C
30C
31C
32C ON INPUT--
33C
34C XVAL MUST BE A NONDECREASING VECTOR OF POINTS AT WHICH THE SPLINE
35C WILL BE EVALUATED.
36C
37C XTAB CONTAINS THE ABSCISSAS OF THE DATA POINTS TO BE INTERPOLATED.
38C XTAB MUST BE INCREASING.
39C
40C YTAB CONTAINS THE ORDINATES OF THE DATA POINTS TO BE INTERPOLATED.
41C
42C MTAB CONTAINS THE SLOPE OF THE SPLINE AT EACH POINT OF INTERPOLA-
43C TION.
44C
45C NUM IS THE NUMBER OF DATA POINTS (DIMENSION OF XTAB AND YTAB).
46C
47C NUME IS THE NUMBER OF POINTS OF EVALUATION (DIMENSION OF XVAL AND
48C YVAL).
49C
50C EPS IS A RELATIVE ERROR TOLERANCE USED IN SUBROUTINE 'CHOOSE'
51C TO DISTINGUISH THE SITUATION MTAB(I) OR MTAB(I+1) IS RELATIVELY
52C CLOSE TO THE SLOPE OR TWICE THE SLOPE OF THE LINEAR SEGMENT
53C BETWEEN XTAB(I) AND XTAB(I+1). IF THIS SITUATION OCCURS,
54C ROUNDOFF MAY CAUSE A CHANGE IN CONVEXITY OR MONOTONICITY OF THE
55C RESULTING SPLINE AND A CHANGE IN THE CASE NUMBER PROVIDED BY
56C CHOOSE. IF EPS IS NOT EQUAL TO ZERO, THEN EPS SHOULD BE GREATER
57C THAN OR EQUAL TO MACHINE EPSILON.
58C
59C
60C ON OUTPUT--
61C
62C YVAL CONTAINS THE IMAGES OF THE POINTS IN XVAL.
63C
64C ERR IS AN ERROR CODE--
65C ERR=0 - MEVAL RAN NORMALLY.
66C ERR=1 - XVAL(I) IS LESS THAN XTAB(1) FOR AT LEAST ONE I OR
67C XVAL(I) IS GREATER THAN XTAB(NUM) FOR AT LEAST ONE I.
68C MEVAL WILL EXTRAPOLATE TO PROVIDE FUNCTION VALUES FOR
69C THESE ABSCISSAS.
70C ERR=2 - XVAL(I+1) .LT. XVAL(I) FOR SOME I.
71C
72C AND
73C
74C MEVAL DOES NOT ALTER XVAL,XTAB,YTAB,MTAB,NUM,NUME.
75C
76C
77C MEVAL CALLS THE FOLLOWING SUBROUTINES OR FUNCTIONS:
78C SEARCH
79C CASES
80C CHOOSE
81C SPLINE
82C
83C---------------------------------------------------------------------
84C
85 save
86
87
88 START=1
89 END=NUME
90 ERR=0
91 IF (NUME .EQ. 1) GO TO 20
92C
93C DETERMINE IF XVAL IS NONDECREASING.
94 NUME1=NUME - 1
95 DO 10 I=1,NUME1
96 IF (XVAL(I+1) .GE. XVAL(I)) GO TO 10
97 ERR=2
98 GO TO 230
99 10 10 CONTINUE
100C
101C
102C IF XVAL(I) .LT. XTAB(1), THEN XVAL(I)=YTAB(1).
103C IF XVAL(I) .GT. XTAB(NUM), THEN XVAL(I)=YTAB(NUM).
104C
105C DETERMINE IF ANY OF THE POINTS IN XVAL ARE LESS THAN THE ABSCISSA OF
106C THE FIRST DATA POINT.
107 20 20 DO 30 I=1,NUME
108 IF (XVAL(I) .GE. XTAB(1)) GO TO 40
109 START=I+1
110 30 30 CONTINUE
111C
112C
113 40 40 NUME1=NUME+1
114C
115C DETERMINE IF ANY OF THE POINTS IN XVAL ARE GREATER THAN THE ABSCISSA
116C OF THE LAST DATA POINT.
117 DO 50 I=1,NUME
118 IND=NUME1-I
119 IF (XVAL(IND) .LE. XTAB(NUM)) GO TO 60
120 END = IND-1
121 50 50 CONTINUE
122C
123C
124C CALCULATE THE IMAGES OF POINTS OF EVALUATION WHOSE ABSCISSAS
125C ARE LESS THAN THE ABSCISSA OF THE FIRST DATA POINT.
126 60 60 IF (START .EQ. 1) GO TO 80
127C SET THE ERROR PARAMETER TO INDICATE THAT EXTRAPOLATION HAS OCCURRED.
128 ERR=1
129 CALL CHOOSE(XTAB(1),YTAB(1),MTAB(1),MTAB(2),XTAB(2),
130 * * YTAB(2),EPS,NCASE)
131 CALL CASES(XTAB(1),YTAB(1),MTAB(1),MTAB(2),XTAB(2),
132 * * YTAB(2),E1,E2,V1,V2,W1,W2,Z1,Z2,Y1,Y2,NCASE)
133 START1=START - 1
134 DO 70 I=1,START1
135 YVAL(I)= SPLINE(XVAL(I),Z1,Z2,XTAB(1),YTAB(1),XTAB(2),YTAB(2),
136 * * Y1,Y2,E2,W2,V2,NCASE)
137 70 70 CONTINUE
138 IF (NUME .EQ. 1) GO TO 230
139C
140C
141C SEARCH LOCATES THE INTERVAL IN WHICH THE FIRST IN-RANGE POINT OF
142C EVALUATION LIES.
143 80 80 IF ((NUME .EQ. 1) .AND. (END .NE. NUME)) GO TO 200
144 CALL SEARCH(XTAB,NUM,XVAL(START),LCN,FND)
145C
146 LCN1=LCN+1
147C
148C
149C IF THE FIRST IN-RANGE POINT OF EVALUATION IS EQUAL TO ONE OF THE DATA
150C POINTS, ASSIGN THE APPROPRIATE VALUE FROM YTAB. CONTINUE UNTIL A
151C POINT OF EVALUATION IS FOUND WHICH IS NOT EQUAL TO A DATA POINT.
152 IF (FND .EQ. 0) GO TO 130
153 90 90 YVAL(START)=YTAB(LCN)
154 START1=START
155 START=START+1
156 IF (START .GT. NUME) GO TO 230
157 IF (XVAL(START1) .EQ. XVAL(START)) GO TO 90
158C
159C KC 2025-05-30 fixed "Arithmetic IF statement"
160C 100 IF (XVAL(START) - XTAB(LCN1)) 130,110,120
161 100 100 IF (XVAL(START) .LT. XTAB(LCN1)) THEN
162 GOTO 130
163 ELSE IF (XVAL(START) .EQ. XTAB(LCN1)) THEN
164 GOTO 110
165 ELSE
166 GOTO 120
167 END IF
168C
169 110 110 YVAL(START)=YTAB(LCN1)
170 START1=START
171 START=START+1
172 IF (START .GT. NUME) GO TO 230
173 IF (XVAL(START) .NE. XVAL(START1)) GO TO 120
174 GO TO 110
175C
176 120 120 LCN=LCN1
177 LCN1=LCN1+1
178 GO TO 100
179C
180C
181C
182C CALCULATE THE IMAGES OF ALL THE POINTS WHICH LIE WITHIN RANGE OF THE
183C DATA.
184C
185 130 130 IF ((LCN .EQ. 1) .AND. (ERR .EQ. 1)) GO TO 140
186 CALL CHOOSE(XTAB(LCN),YTAB(LCN),MTAB(LCN),MTAB(LCN1),
187 * * XTAB(LCN1),YTAB(LCN1),EPS,NCASE)
188 CALL CASES(XTAB(LCN),YTAB(LCN),MTAB(LCN),MTAB(LCN1),
189 * * XTAB(LCN1),YTAB(LCN1),E1,E2,V1,V2,W1,W2,Z1,Z2,
190 * * Y1,Y2,NCASE)
191C
192 140 140 DO 190 I=START,END
193C
194C IF XVAL(I) -XTAB(LCN1) IS NEGATIVE, DO NOT RECALCULATE THE PARAMETERS
195C FOR THIS SECTION OF THE SPLINE SINCE THEY ARE ALREADY KNOWN.
196C KC 2025-05-30 fixed "Arithmetic IF statement"
197C IF (XVAL(I) - XTAB(LCN1)) 150,160,170
198 IF (XVAL(I) .LT. XTAB(LCN1)) THEN
199 GOTO 150
200 ELSE IF (XVAL(I) .EQ. XTAB(LCN1)) THEN
201 GOTO 160
202 ELSE
203 GOTO 170
204 END IF
205C
206 150 150 YVAL(I)=SPLINE(XVAL(I),Z1,Z2,XTAB(LCN),YTAB(LCN),XTAB(LCN1),
207 * * YTAB(LCN1),Y1,Y2,E2,W2,V2,NCASE)
208 GO TO 190
209C
210C IF XVAL(I) IS A DATA POINT, ITS IMAGE IS KNOWN.
211 160 160 YVAL(I)=YTAB(LCN1)
212 GO TO 190
213C
214C INCREMENT THE POINTERS WHICH GIVE THE LOCATION IN THE DATA VECTOR.
215 170 170 LCN=LCN1
216 LCN1=LCN+1
217C
218C DETERMINE THAT THE ROUTINE IS IN THE CORRECT PART OF THE SPLINE.
219C KC 2025-05-30 fixed "Arithmetic IF statement"
220C IF (XVAL(I) - XTAB(LCN1)) 180,160,170
221 IF (XVAL(I) .LT. XTAB(LCN1)) THEN
222 GOTO 180
223 ELSE IF (XVAL(I) .EQ. XTAB(LCN1)) THEN
224 GOTO 160
225 ELSE
226 GOTO 170
227 END IF
228C
229C CALL CHOOSE TO DETERMINE THE APPROPRIATE CASE AND THEN CALL
230C CASES TO COMPUTE THE PARAMETERS OF THE SPLINE.
231 180 180 CALL CHOOSE(XTAB(LCN),YTAB(LCN),MTAB(LCN),MTAB(LCN1),
232 * * XTAB(LCN1),YTAB(LCN1),EPS,NCASE)
233 CALL CASES(XTAB(LCN),YTAB(LCN),MTAB(LCN),MTAB(LCN1),
234 * * XTAB(LCN1),YTAB(LCN1),E1,E2,V1,V2,W1,W2,Z1,Z2,
235 * * Y1,Y2,NCASE)
236 GO TO 150
237 190 190 CONTINUE
238C
239C
240C CALCULATE THE IMAGES OF THE POINTS OF EVALUATION WHOSE ABSCISSAS
241C ARE GREATER THAN THE ABSCISSA OF THE LAST DATA POINT.
242 IF (END .EQ. NUME) GO TO 230
243 IF ((LCN1 .EQ. NUM) .AND. (XVAL(END) .NE. XTAB(NUM))) GO TO 210
244
245C Previously, when we arrived at 200 or 210, NUM1 could be improperly set.
246C The NUM1= lines below protect from that. llp 8/19/08
247
248C SET THE ERROR PRARMETER TO INDICATE THAT EXTRAPOLATION HAS OCCURRED.
249 200 200 ERR=1
250 NUM1=max(NUM-1,1)
251 CALL CHOOSE(XTAB(NUM1),YTAB(NUM1),MTAB(NUM1),MTAB(NUM),
252 * * XTAB(NUM),YTAB(NUM),EPS,NCASE)
253 CALL CASES(XTAB(NUM1),YTAB(NUM1),MTAB(NUM1),MTAB(NUM),
254 * * XTAB(NUM),YTAB(NUM),E1,E2,V1,V2,W1,W2,Z1,Z2,
255 * * Y1,Y2,NCASE)
256 210 210 END1=END + 1
257 NUM1=max(NUM-1,1)
258 DO 220 I=END1,NUME
259 YVAL(I)= SPLINE(XVAL(I),Z1,Z2,XTAB(NUM1),YTAB(NUM1),
260 * * XTAB(NUM),YTAB(NUM),Y1,Y2,E2,W2,V2,NCASE)
261 220 220 CONTINUE
262C
263C
264 230 230 RETURN
265 END
1"""Python version of meval.f (YREC development, 7be4133)."""
2
3from pyrec import dimensions_mod # noqa: F401 (JSON, the declared size)
4import numpy as np
5from pyrec.cases import cases
6from pyrec.choose import choose
7from pyrec.search import search
8from pyrec.spline import spline
9
10
11def meval(xval: np.ndarray, yval: np.ndarray, xtab: np.ndarray, ytab: np.ndarray, mtab: np.ndarray, num: int, nume: int,
12 eps: float, err: int) -> int:
13 """Evaluate McAllister and Roulier's shape-preserving quadratic spline.
14
15 Walks through the evaluation points ``xval`` and the data intervals
16 together: points equal to a data abscissa get the data value; for each
17 interval holding points, CHOOSE and CASES set the spline's parameters
18 once and SPLINE evaluates it; points beyond the ends are extrapolated
19 with the end intervals' splines. Called only by OSPLIN.
20
21 Translates: meval.f#MEVAL (7be4133)
22
23 Parameters
24 ----------
25 xval : ndarray
26 The points of evaluation, ``xval[1..nume]``, nondecreasing.
27 yval : ndarray
28 Set in place: the spline's values at ``xval``.
29 xtab, ytab, mtab : ndarray
30 The data abscissae (increasing), ordinates and slopes,
31 indices 1..num.
32 num, nume : int
33 The number of data points and of points of evaluation.
34 eps : float
35 Relative tolerance passed to CHOOSE.
36 err : int
37 Output only (passed in with any value).
38
39 Returns
40 -------
41 err : int
42 0 normal; 1 extrapolation occurred; 2 ``xval`` not sorted (then
43 nothing is computed).
44
45 """
46
47 # SAVE in the Fortran: every local is set before it is read within a
48 # call, so all are kept local here. The spline's parameters start at
49 # zero; CASES returns those a case does not set as passed in, and
50 # SPLINE reads only those the case set.
51 e1 = e2 = v1 = v2 = w1 = w2 = z1 = z2 = y1 = y2 = 0.0
52
53 # Fortran lines 88–90.
54 start = 1
55 end = nume
56 err = 0
57
58 # Determine if xval is nondecreasing (loop 10, Fortran lines 91–99).
59 if nume != 1:
60 for i in range(1, nume):
61 if xval[i + 1] < xval[i]:
62 # Suspected bug (see OSPLIN's page): OSPLIN does not check
63 # err, and yval is left as it was.
64 err = 2
65 return err # label 230
66
67 # Question (see the page): the Fortran's comment here says points
68 # beyond the data get the end values; the code extrapolates instead.
69 # Points below the first data abscissa (label 20, loop 30, Fortran
70 # lines 107–110).
71 for i in range(1, nume + 1):
72 if xval[i] >= xtab[1]:
73 break
74 start = i + 1
75
76 # Points above the last data abscissa (label 40, loop 50, Fortran
77 # lines 113–121).
78 for i in range(1, nume + 1):
79 ind = nume + 1 - i
80 if xval[ind] <= xtab[num]:
81 break
82 end = ind - 1
83
84 # Extrapolate below the first data point (label 60, Fortran lines
85 # 126–138).
86 if start != 1:
87 err = 1
88 ncase = choose(xtab[1], ytab[1], mtab[1], mtab[2], xtab[2],
89 ytab[2], eps, 0)
90 e1, e2, v1, v2, w1, w2, z1, z2, y1, y2 = cases(
91 xtab[1], ytab[1], mtab[1], mtab[2], xtab[2], ytab[2],
92 e1, e2, v1, v2, w1, w2, z1, z2, y1, y2, ncase)
93 for i in range(1, start): # loop 70
94 yval[i] = spline(xval[i], z1, z2, xtab[1], ytab[1], xtab[2],
95 ytab[2], y1, y2, e2, w2, v2, ncase)
96 if nume == 1:
97 return err
98
99 # Label 80 (Fortran lines 143–146).
100 goto_200 = (nume == 1) and (end != nume)
101 if not goto_200:
102 # The interval of the first in-range point. Suspected bug (see
103 # the page): if every point lies below xtab[1], start is
104 # nume + 1 and the Fortran reads XVAL(NUME+1) here.
105 lcn, fnd = search(xtab, num, xval[start], 0, 0)
106 lcn1 = lcn + 1
107
108 # Points equal to a data point take its value (label 90, Fortran
109 # lines 152–178).
110 if fnd != 0:
111 while True:
112 yval[start] = ytab[lcn]
113 start1 = start
114 start = start + 1
115 if start > nume:
116 return err
117 if xval[start1] != xval[start]:
118 break
119 # Advance until a point lies strictly inside an interval
120 # (labels 100, 110, 120).
121 while xval[start] >= xtab[lcn1]: # label 100
122 if xval[start] == xtab[lcn1]:
123 while True: # label 110
124 yval[start] = ytab[lcn1]
125 start1 = start
126 start = start + 1
127 if start > nume:
128 return err
129 if xval[start] != xval[start1]:
130 break
131 lcn = lcn1 # label 120
132 lcn1 = lcn1 + 1
133
134 # The points within range of the data (label 130, Fortran lines
135 # 185–190). With extrapolation below, the parameters of interval 1
136 # are known.
137 if not (lcn == 1 and err == 1):
138 ncase = choose(xtab[lcn], ytab[lcn], mtab[lcn], mtab[lcn1],
139 xtab[lcn1], ytab[lcn1], eps, 0)
140 e1, e2, v1, v2, w1, w2, z1, z2, y1, y2 = cases(
141 xtab[lcn], ytab[lcn], mtab[lcn], mtab[lcn1],
142 xtab[lcn1], ytab[lcn1],
143 e1, e2, v1, v2, w1, w2, z1, z2, y1, y2, ncase)
144
145 # Label 140, loop 190 (Fortran lines 192–237).
146 for i in range(start, end + 1):
147 if xval[i] < xtab[lcn1]:
148 # label 150: the parameters of this interval are known.
149 yval[i] = spline(xval[i], z1, z2, xtab[lcn], ytab[lcn],
150 xtab[lcn1], ytab[lcn1], y1, y2, e2, w2,
151 v2, ncase)
152 elif xval[i] == xtab[lcn1]:
153 yval[i] = ytab[lcn1] # label 160
154 else:
155 while True:
156 lcn = lcn1 # label 170
157 lcn1 = lcn + 1
158 if xval[i] < xtab[lcn1]:
159 # label 180, then 150
160 ncase = choose(xtab[lcn], ytab[lcn], mtab[lcn],
161 mtab[lcn1], xtab[lcn1], ytab[lcn1],
162 eps, 0)
163 e1, e2, v1, v2, w1, w2, z1, z2, y1, y2 = cases(
164 xtab[lcn], ytab[lcn], mtab[lcn], mtab[lcn1],
165 xtab[lcn1], ytab[lcn1],
166 e1, e2, v1, v2, w1, w2, z1, z2, y1,
167 y2, ncase)
168 yval[i] = spline(xval[i], z1, z2, xtab[lcn],
169 ytab[lcn], xtab[lcn1], ytab[lcn1],
170 y1, y2, e2, w2, v2, ncase)
171 break
172 if xval[i] == xtab[lcn1]:
173 yval[i] = ytab[lcn1] # label 160
174 break
175
176 # The points beyond the last data point (Fortran lines 242–243).
177 # Suspected bug (see the page): with every point above xtab[num],
178 # end is 0 and the Fortran reads XVAL(0).
179 if end == nume:
180 return err
181 goto_200 = not (lcn1 == num and xval[end] != xtab[num])
182
183 # Label 200: extrapolation above the last data point (Fortran lines
184 # 249–255).
185 if goto_200:
186 err = 1
187 num1 = max(num - 1, 1)
188 ncase = choose(xtab[num1], ytab[num1], mtab[num1], mtab[num],
189 xtab[num], ytab[num], eps, 0)
190 e1, e2, v1, v2, w1, w2, z1, z2, y1, y2 = cases(
191 xtab[num1], ytab[num1], mtab[num1], mtab[num], xtab[num],
192 ytab[num], e1, e2, v1, v2, w1, w2, z1, z2, y1, y2,
193 ncase)
194
195 # Label 210 (Fortran lines 256–261).
196 end1 = end + 1
197 num1 = max(num - 1, 1)
198 for i in range(end1, nume + 1): # loop 220
199 yval[i] = spline(xval[i], z1, z2, xtab[num1], ytab[num1],
200 xtab[num], ytab[num], y1, y2, e2, w2, v2, ncase)
201 return err