MODBUS-tk Read floating point values from slave in the master - python-3.x

I have created modbus slave to write data to the registers.
I am able to write both float values and integer values from the slave side.
In the modbus master I am able to access only the integer values but not able to read float values.
I went through this https://github.com/ljean/modbus-tk/issues/72 but that didn't solve my problem.
For the integer values reading I can use the below code and read the values.
master = modbus_tcp.TcpMaster()
master.set_timeout(time_out_period)
result = master.execute(slave = 100, function_code = 3 , starting_address = 0, quantity_of_x = 25)
But for the float values I used both the above and below code.
master = modbus_tcp.TcpMaster()
master.set_timeout(time_out_period)
result = master.execute(slave = 100, function_code = 3 , starting_address = 0, quantity_of_x = 25 , data_format='>f')
I get error while reading the float as,
unpack requires a bytes object of length 4

The quantity of x should be a multiple of 2. Because the float requires two 16 bit registers or words so if you want 25 it should be 50.

You also need to provide the correct data format reflective of how many individual float values(below are big endian) are trying to be unpacked;
1 float
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 2, data_format='>f'))
2 floats
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4, data_format='>ff'))
3 floats
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 6, data_format='>fff'))

It's easy, using Numpy.
For example:
import numpy as np
# Sample registers to read
start_address = 0
items = 10
# Get the reply from slave
reply = master.execute(1, cst.READ_HOLDING_REGISTERS, start_address, items*2)
# Convert the reply to Numpy array of type int16
d16 = np.array(reply, dtype=np.int16)
# Convert to an array of type float32
f32 = d16.view(dtype = np.float32)

Related

Sum of ranks using one sided communication but there is nothing being loaded into the target

I am aiming to create create a sum of ranks in a ring similar to a allreduce program but using one-sided communication.
For example, if there four processes in this system. The output would be:
PE0: Sum = 6
PE2: Sum = 6
PE3: Sum = 6
PE1: Sum = 6
However, with my current solution with one-sided communication, all the sums are 0.
My code so far:
#!/usr/bin/env python3
from mpi4py import MPI
import numpy as np
rcv_buf = np.empty((), dtype=np.intc) # uninitialized 0 dimensional integer array
status = MPI.Status()
comm_world = MPI.COMM_WORLD
my_rank = comm_world.Get_rank()
size = comm_world.Get_size()
right = (my_rank+1) % size;
left = (my_rank-1+size) % size;
snd_buf = np.array(my_rank, dtype=np.intc) # 0 dimensional integer array with 1 element initialized with the value of my_rank
sum = 0
copy = 0
# create a window
win = MPI.Win.Create(snd_buf, 1, MPI.INFO_NULL, comm=comm_world)
# we need a master process
# sync remote get call
for i in range(size):
win.Fence(0)
win.Get(snd_buf, left, copy)
win.Fence(0)
sum += copy
win.Free()
print(f"PE{my_rank}:\tSum = {copy}")
I'm not sure how to check that the Get call is working properly and if it is, is there any other way to load and store.
I was using the win.Get call incorrectly. In the documentation, the first parameter of the Get call is specified as origin (BufSpec) which I mistook for the origin value that was in the window which was snd_buf but it should be the buffer where you would want your answer to be stored. I also had to include a Put call to send the value of the rank to the next process. This makes the final code:
#!/usr/bin/env python3
from mpi4py import MPI
import numpy as np
rcv_buf = np.empty((), dtype=np.intc) # uninitialized 0 dimensional integer array
status = MPI.Status()
comm_world = MPI.COMM_WORLD
my_rank = comm_world.Get_rank()
size = comm_world.Get_size()
right = (my_rank+1) % size;
left = (my_rank-1+size) % size;
snd_buf = np.array(my_rank, dtype=np.intc) # 0 dimensional integer array with 1 element initialized with the value of my_rank
sum = 0
# create a window
win = MPI.Win.Create(snd_buf, 1, MPI.INFO_NULL, comm=comm_world)
# sync remote get call
for i in range(size):
win.Fence(0)
win.Put(snd_buf, left)
win.Fence(0)
win.Get(rcv_buf, right)
win.Fence(0)
sum += rcv_buf
print(f"PE{my_rank}:\tSum = {sum}")
win.Free()

Output of a sum integer from a function that iteratively minimizes an arrary

I'm trying to build a function (minSum) to minimize the sum of an array, of various lengths and values over any number of iterations.
The function contains two arguments - the name of an array (num) and the number of steps of modification (k). For each k-step of modification, the function will retrieve an element/integer from the num array, divide it by 2, and update the array with the ceiling of the halved value in the same index position as it was retrieved. Once the k value has been reached, the function should output the sum of the array as a single integer.
For example - if my array (num) is [10. 20. 7] and I will run it over 2 steps (k) the input for the function would be minSum(num, 2).
It would divide 10 by half in kstep 0 resulting in an array of (5, 20, 7)
It would divide 10 by half in kstep 0 resulting in an array of (5, 10, 7)
It would divide 10 by half in kstep 0 resulting in an array of (5, 10, 4) (4 being the ceiling of 3.5).
The output of this would be the sum 0f 5, 10, 4 = 19. By increasing the k-value we should be able to reduce the output to a lower value. In any case, I'm able to use the below code to achieve my goal with the exception of the output being a single integer (our testing system system only receives the final array). Any pointers here? Thanks!
import array as ar
import math
import numpy as np
# 1. INTEGER_ARRAY num
# 2. INTEGER k (number of steps of element removal, transformation and update)
def minSum(num, k):
arr = np.array(num)
i = 0
idx = 0
while i < k:
for element in np.nditer(arr):
thereduced = math.ceil(element/2)
np.put(arr, [idx], thereduced)
if i < arr.size-1:
idx += 1
thesum = int((sum(arr)))
i = i+1
return thesum
The sum() method returns a single value, so you might create an empty array, put the "thesum" value into the array and at the end of the function return the new array with the values, right now your code is just returning a single integer

Iterations over 2d numpy arrays with while and for statements

In the code supplied below I am trying to iterate over 2D numpy array [i][k]
Originally it is a code which was written in Fortran 77 which is older than my grandfather. I am trying to adapt it to python.
(for people interested whatabouts: it is a simple hydraulics transients event solver)
Bear in mind that all variables are introduced in my code which I don't paste here.
H = np.zeros((NS,50))
Q = np.zeros((NS,50))
Here I am assigning the first row values:
for i in range(NS):
H[0][i] = HR-i*R*Q0**2
Q[0][i] = Q0
CVP = .5*Q0**2/H[N]
T = 0
k = 0
TAU = 1
#Interior points:
HP = np.zeros((NS,50))
QP = np.zeros((NS,50))
while T<=Tmax:
T += dt
k += 1
for i in range(1,N):
CP = H[k][i-1]+Q[k][i-1]*(B-R*abs(Q[k][i-1]))
CM = H[k][i+1]-Q[k][i+1]*(B-R*abs(Q[k][i+1]))
HP[k][i-1] = 0.5*(CP+CM)
QP[k][i-1] = (HP[k][i-1]-CM)/B
#Boundary Conditions:
HP[k][0] = HR
QP[k][0] = Q[k][1]+(HP[k][0]-H[k][1]-R*Q[k][1]*abs(Q[k][1]))/B
if T == Tc:
TAU = 0
CV = 0
else:
TAU = (1.-T/Tc)**Em
CV = CVP*TAU**2
CP = H[k][N-1]+Q[k][N-1]*(B-R*abs(Q[k][N-1]))
QP[k][N] = -CV*B+np.sqrt(CV**2*(B**2)+2*CV*CP)
HP[k][N] = CP-B*QP[k][N]
for i in range(NS):
H[k][i] = HP[k][i]
Q[k][i] = QP[k][i]
Remember i is for rows and k is for columns
What I am expecting is that for all k number of columns the values should be calculated until T<=Tmax condition is met. I cannot figure out what my mistake is, I am getting the following errors:
RuntimeWarning: divide by zero encountered in true_divide
CVP = .5*Q0**2/H[N]
RuntimeWarning: invalid value encountered in multiply
QP[N][k] = -CV*B+np.sqrt(CV**2*(B**2)+2*CV*CP)
QP[N][k] = -CV*B+np.sqrt(CV**2*(B**2)+2*CV*CP)
ValueError: setting an array element with a sequence.
Looking at your first iteration:
H = np.zeros((NS,50))
Q = np.zeros((NS,50))
for i in range(NS):
H[0][i] = HR-i*R*Q0**2
Q[0][i] = Q0
The shape of H is (NS,50), but when you iterate over a range(NS) you apply that index to the 2nd dimension. Why? Shouldn't it apply to the dimension with size NS?
In numpy arrays have 'C' order by default. Last dimension is inner most. They can have a F (fortran) order, but let's not go there. Thinking of the 2d array as a table, we typically talk of rows and columns, though they don't have a formal definition in numpy.
Lets assume you want to set the first column to these values:
for i in range(NS):
H[i, 0] = HR - i*R*Q0**2
Q[i, 0] = Q0
But we can do the assignment whole rows or columns at a time. I believe new versions of Fortran also have these 'whole-array' functions.
Q[:, 0] = Q0
H[:, 0] = HR - np.arange(NS) * R * Q0**2
One point of caution when translating to Python. Indexing starts with 0; so does ranges and np.arange(...).
H[0][i] is functionally the same as H[0,i]. But when using slices you have to use the H[:,i] format.
I suspect your other iterations have similar problems, but I'll stop here for now.
Regarding the errors:
The first:
RuntimeWarning: divide by zero encountered in true_divide
CVP = .5*Q0**2/H[N]
You initialize H as zeros so it is normal that it complains of division by zero. Maybe you should add a conditional.
The third:
QP[N][k] = -CV*B+np.sqrt(CV**2*(B**2)+2*CV*CP)
ValueError: setting an array element with a sequence.
You define CVP = .5*Q0**2/H[N] and then CV = CVP*TAU**2 which is a sequence. And then you try to assign a derivate form it to QP[N][K] which is an element. You are trying to insert an array to a value.
For the second error I think it might be related to the third. If you could provide more information I would like to try to understand what happens.
Hope this has helped.

pandas value assignment with conditions on index

I am trying to calculate the Demarker Indicator in Python. Following describes how to calculate it:
Choose a predetermined period “X” (Standard value is “14”, although a value of “8” or “9” tends to be more sensitive);
Calculate DeMax = High – Previous High if >0, otherwise DeMax = 0;
Calculate DeMin = Previous Low – Low if >0, otherwise DeMin = 0;
DeM = MA of DeMax/(MA of DeMax + MA of DeMin).
Following is my attempt, df is a dataframe contains open high low close price with date as index:
df['DeMax'] = df['High'] - df['High'].shift(1)
df['DeMin'] = df['Low'].shift(1)-df['Low']
# Method 1: df['DeMax'][df['DeMax'] < 0] = 0.0
# Method 2: df[df['DeMax']< 0]['DeMax'] = 0.0
If I use Method 1, it is ok. But if I use Method2, I will get warning SettingWithCopyWarning, even I use copy method like this df['DeMax'] = df['High'].copy() - df['High'].copy().shift(1) won't solve the issue.
I have also checked that df['DeMax'][df['DeMax'] < 0] and df[test['DeMax']< 0]['DeMax'] are same pandas series, so why they behave differently if I try to assign values?
Also, if I do something like this
df['DeMax'] = df['High'] - df['High'].shift(1)
df['DeMin'] = df['Low'].shift(1)-df['Low']
a = df['DeMax'][df['DeMax'] < 0]
a = 0
Then a will be 0 instead of a pandas series, but I also expect df['DeMax'][df['DeMax'] < 0] will be 0, which does not happen, could anyone help? Thanks.
You are looking for .loc
df.loc[df['DeMax'] < 0,'DeMax']=0 # it will change all value less that 0 to 0

Rearranging a 3-D array using indices from sorting?

I have a 3-D array of random numbers of size [channels = 3, height = 10, width = 10].
Then I sorted it using sort command from pytorch along the columns and obtained the indices as well.
The corresponding index is shown below:
Now, I would like to return to the original matrix using these indices. I currently use for loops to do this (without considering the batches). The code is:
import torch
torch.manual_seed(1)
ch = 3
h = 10
w = 10
inp_unf = torch.randn(ch,h,w)
inp_sort, indices = torch.sort(inp_unf,1)
resort = torch.zeros(inp_sort.shape)
for i in range(ch):
for j in range(inp_sort.shape[1]):
for k in range (inp_sort.shape[2]):
temp = inp_sort[i,j,k]
resort[i,indices[i,j,k],k] = temp
I would like it to be vectorized considering batches as well i.e.input size is [batch, channel, height, width].
Using Tensor.scatter_()
You can directly scatter the sorted tensor back into its original state using the indices provided by sort():
torch.zeros(ch,h,w).scatter_(dim=1, index=indices, src=inp_sort)
The intuition is based on the previous answer below. As scatter() is basically the reverse of gather(), inp_reunf = inp_sort.gather(dim=1, index=reverse_indices) is the same as inp_reunf.scatter_(dim=1, index=indices, src=inp_sort):
Previous answer
Note: while correct, this is probably less performant, as calling the sort() operation a 2nd time.
You need to obtain the sorting "reverse indices", which can be done by "sorting the indices returned by sort()".
In other words, given x_sort, indices = x.sort(), you have x[indices] -> x_sort ; while what you want is reverse_indices such that x_sort[reverse_indices] -> x.
This can be obtained as follows: _, reverse_indices = indices.sort().
import torch
torch.manual_seed(1)
ch, h, w = 3, 10, 10
inp_unf = torch.randn(ch,h,w)
inp_sort, indices = inp_unf.sort(dim=1)
_, reverse_indices = indices.sort(dim=1)
inp_reunf = inp_sort.gather(dim=1, index=reverse_indices)
print(torch.equal(inp_unf, inp_reunf))
# True

Resources