Because the array is a constant the compiler can, when it knows the entry number (i.e., using a numeric literal) replace the access to the array with the contents of that array index - so it's not looking up the value of 240 at runtime, instead the value 240 has been used where the access to the array was.
However, when accessing it using a variable the compiler can't do that, so it has to actually access the array in RAM.
BUT the array isn't in RAM, it's in PROGMEM, so the address that it's being given for where the array is located is completely wrong. You're program is telling it "It's in the bedroom" - but you're omitting the important information of: "... of the house next-door.".
PROGMEM and RAM are two completely different things accessed through two completely separate data and address buses - just like two separate houses, they both have a number of rooms in them, and those rooms are called the same thing, but they're not the same room.
So instead of accessing an array by index like that you have to use special instructions to say "Get this value from PROGMEM", such as:
Serial.println(pgm_read_byte_near(COLSA + v));
That says "Give me the byte that is located at v addresses on from the start of the
COLSA array in PROGMEM.